Skip to content

Commit

Permalink
Result type for PayPal checkout, docStrings
Browse files Browse the repository at this point in the history
  • Loading branch information
KunJeongPark committed Jan 21, 2025
1 parent 48b1164 commit 3aa8012
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 54 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,17 @@ class PayPalWebViewModel: ObservableObject {

if let orderID = state.createOrder?.id {
let payPalRequest = PayPalWebCheckoutRequest(orderID: orderID, fundingSource: funding)
payPalWebCheckoutClient.start(request: payPalRequest) { result, error in
if let error {
payPalWebCheckoutClient.start(request: payPalRequest) { result in
switch result {
case .success(let paypalResult):
DispatchQueue.main.async {
self.state.approveResultResponse = .loaded(
PayPalPaymentState.ApprovalResult(id: paypalResult.orderID, status: "APPROVED")
)
self.checkoutResult = paypalResult
print("✅ Checkout result: \(String(describing: paypalResult))")
}
case .failure(let error):
DispatchQueue.main.async {
if error == PayPalError.checkoutCanceledError {
print("Canceled")
Expand All @@ -94,14 +103,6 @@ class PayPalWebViewModel: ObservableObject {
self.state.approveResultResponse = .error(message: error.localizedDescription)
}
}
} else {
DispatchQueue.main.async {
self.state.approveResultResponse = .loaded(
PayPalPaymentState.ApprovalResult(id: orderID, status: "APPROVED")
)
self.checkoutResult = result
print("✅ Checkout result: \(String(describing: result))")
}
}
}
}
Expand Down
18 changes: 13 additions & 5 deletions Sources/CardPayments/CardClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,12 @@ public class CardClient: NSObject {
/// - Parameters:
/// - vaultRequest: The request containing setupTokenID and card
/// - completion: A completion block that is invoked when the request is completed. If the request succeeds,
/// a `Result` type with `.success(CardVaultResult)` with `setupTokenID` and `status` are returned;
/// if it fails, `Result` type with `.failure(CoreSDKError)` that describes the failure will be returned
/// The closure returns a `Result`:
/// - `.success(CardVaultResult)` containing:
/// - `setupTokenID`: The ID of the token that was updated.
/// - `status`: The setup token status.
/// - `didAttemptThreeDSecureAuthentication`: A flag indicating if 3D Secure authentication was attempted.
/// - `.failure(CoreSDKError)`: Describes the reason for failure.
public func vault(_ vaultRequest: CardVaultRequest, completion: @escaping (Result<CardVaultResult, CoreSDKError>) -> Void) {
analyticsService = AnalyticsService(coreConfig: config, setupToken: vaultRequest.setupTokenID)
analyticsService?.sendEvent("card-payments:vault-wo-purchase:started")
Expand Down Expand Up @@ -95,9 +99,13 @@ public class CardClient: NSObject {
/// - Parameters:
/// - orderId: Order id for approval
/// - request: The request containing the card
/// - completion: A completion block that is invoked when the request is completed. If the request succeeds,
/// a `Result` type will be returned with `.success(CardResult)` with `orderID` , `status` and `didAttemptThreeDSecureAuthentication`;
/// if it fails, `Result` type with `.failure(CoreSDKError)` that describes the failure will be returned
/// - completion: A completion block that is invoked when the request is completed.
/// The closure returns a `Result`:
/// - `.success(CardResult)` containing:
/// - `orderID`: The ID of the approved order.
/// - `status`: The approval status.
/// - `didAttemptThreeDSecureAuthentication`: A flag indicating if 3D Secure authentication was attempted.
/// - `.failure(CoreSDKError)`: Describes the reason for failure.
public func approveOrder(request: CardRequest, completion: @escaping (Result<CardResult, CoreSDKError>) -> Void) {
analyticsService = AnalyticsService(coreConfig: config, orderID: request.orderID)
analyticsService?.sendEvent("card-payments:approve-order:started")
Expand Down
41 changes: 27 additions & 14 deletions Sources/PayPalWebPayments/PayPalWebCheckoutClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,13 @@ public class PayPalWebCheckoutClient: NSObject {
/// Launch the PayPal web flow
/// - Parameters:
/// - request: the PayPalRequest for the transaction
/// - completion: A completion block that is invoked when the request is completed. If the request succeeds,
/// a `PayPalWebCheckoutResult` with `orderID` and `payerID` are returned and `error` will be `nil`;
/// if it fails, `PayPalWebCheckoutResult will be `nil` and `error` will describe the failure
public func start(request: PayPalWebCheckoutRequest, completion: @escaping (PayPalWebCheckoutResult?, CoreSDKError?) -> Void) {
/// - completion: A completion block that is invoked when the request is completed.
/// The closure returns a `Result`:
/// - `.success(CardResult)` containing:
/// - `orderID`: The ID of the approved order.
/// - `payerID`: Payer ID (or user id associated with the transaction
/// - `.failure(CoreSDKError)`: Describes the reason for failure.
public func start(request: PayPalWebCheckoutRequest, completion: @escaping (Result<PayPalWebCheckoutResult, CoreSDKError>) -> Void) {
analyticsService = AnalyticsService(coreConfig: config, orderID: request.orderID)
analyticsService?.sendEvent("paypal-web-payments:checkout:started")

Expand Down Expand Up @@ -95,11 +98,12 @@ public class PayPalWebCheckoutClient: NSObject {
/// - Throws: A `CoreSDKError` describing the failure
public func start(request: PayPalWebCheckoutRequest) async throws -> PayPalWebCheckoutResult {
try await withCheckedThrowingContinuation { continuation in
start(request: request) { result, error in
if let error {
continuation.resume(throwing: error)
} else if let result {
start(request: request) { result in
switch result {
case .success(let result):
continuation.resume(returning: result)
case .failure(let error):
continuation.resume(throwing: error)
}
}
}
Expand Down Expand Up @@ -202,19 +206,28 @@ public class PayPalWebCheckoutClient: NSObject {
return url.queryItems?.first { $0.name == param }?.value
}

private func notifyCheckoutSuccess(for result: PayPalWebCheckoutResult, completion: (PayPalWebCheckoutResult?, CoreSDKError?) -> Void) {
private func notifyCheckoutSuccess(
for result: PayPalWebCheckoutResult,
completion: (Result<PayPalWebCheckoutResult, CoreSDKError>) -> Void
) {
self.analyticsService?.sendEvent("paypal-web-payments:checkout:succeeded")
completion(result, nil)
completion(.success(result))
}

private func notifyCheckoutFailure(with error: CoreSDKError, completion: (PayPalWebCheckoutResult?, CoreSDKError?) -> Void) {
private func notifyCheckoutFailure(
with error: CoreSDKError,
completion: (Result<PayPalWebCheckoutResult, CoreSDKError>) -> Void
) {
self.analyticsService?.sendEvent("paypal-web-payments:checkout:failed")
completion(nil, error)
completion(.failure(error))
}

private func notifyCheckoutCancelWithError(with error: CoreSDKError, completion: (PayPalWebCheckoutResult?, CoreSDKError?) -> Void) {
private func notifyCheckoutCancelWithError(
with error: CoreSDKError,
completion: (Result<PayPalWebCheckoutResult, CoreSDKError>) -> Void
) {
analyticsService?.sendEvent("paypal-web-payments:checkout:canceled")
completion(nil, error)
completion(.failure(error))
}

private func notifyVaultSuccess(for result: PayPalVaultResult, completion: (PayPalVaultResult?, CoreSDKError?) -> Void) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -178,14 +178,14 @@ class PayPalClient_Tests: XCTestCase {
)

let expectation = self.expectation(description: "Call back invoked with error")
payPalClient.start(request: request) { result, error in
XCTAssertNil(result)
if let error {
payPalClient.start(request: request) { result in
switch result {
case .success:
XCTFail("Expected failure with error")
case .failure(let error):
XCTAssertEqual(error.domain, PayPalError.domain)
XCTAssertEqual(error.code, PayPalError.checkoutCanceledError.code)
XCTAssertEqual(error.localizedDescription, PayPalError.checkoutCanceledError.localizedDescription)
} else {
XCTFail("Expected error not to be nil")
}
expectation.fulfill()
}
Expand All @@ -206,20 +206,19 @@ class PayPalClient_Tests: XCTestCase {
)

let expectation = self.expectation(description: "Call back invoked with error")
payPalClient.start(request: request) { result, error in
XCTAssertNil(result)
if let error {
payPalClient.start(request: request) { result in
switch result {
case .success:
XCTFail("Expected failure with error")
case .failure(let error):
XCTAssertTrue(PayPalError.isCheckoutCanceled(error))
} else {
XCTFail("Expected error from PayPal checkout cancellation.")
}
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)
}


func testStart_whenWebAuthenticationSessions_returnsWebSessionError() {
let request = PayPalWebCheckoutRequest(orderID: "1234")

Expand All @@ -230,17 +229,19 @@ class PayPalClient_Tests: XCTestCase {
)

let expectation = self.expectation(description: "Call back invoked with error")
payPalClient.start(request: request) { result, error in
XCTAssertNil(result)
if let error {

payPalClient.start(request: request) { result in
switch result {
case .success:
XCTFail("Expected failure with error")
case .failure(let error):
XCTAssertEqual(error.domain, PayPalError.domain)
XCTAssertEqual(error.code, PayPalError.Code.webSessionError.rawValue)
XCTAssertEqual(error.localizedDescription, "Mock web session error description.")
} else {
XCTFail("Expected error not to be nil")
}
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)
}

Expand All @@ -249,17 +250,18 @@ class PayPalClient_Tests: XCTestCase {

mockWebAuthenticationSession.cannedResponseURL = URL(string: "https://fakeURL?PayerID=98765")
let expectation = self.expectation(description: "Call back invoked with error")
payPalClient.start(request: request) { result, error in
XCTAssertNil(result)
if let error {
payPalClient.start(request: request) { result in
switch result {
case .success:
XCTFail("Expected failure with error")
case .failure(let error):
XCTAssertEqual(error.domain, PayPalError.domain)
XCTAssertEqual(error.code, PayPalError.Code.malformedResultError.rawValue)
XCTAssertEqual(error.localizedDescription, "Result did not contain the expected data.")
} else {
XCTFail("Expected error not to be nil")
}
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)
}

Expand All @@ -268,12 +270,17 @@ class PayPalClient_Tests: XCTestCase {

mockWebAuthenticationSession.cannedResponseURL = URL(string: "https://fakeURL?token=1234&PayerID=98765")
let expectation = self.expectation(description: "Call back invoked with error")
payPalClient.start(request: request) { result, error in
XCTAssertEqual(result?.orderID, "1234")
XCTAssertEqual(result?.payerID, "98765")
XCTAssertNil(error)
payPalClient.start(request: request) { result in
switch result {
case .success(let result):
XCTAssertEqual(result.orderID, "1234")
XCTAssertEqual(result.payerID, "98765")
case .failure:
XCTFail("Expected success with PayPalCheckoutResult")
}
expectation.fulfill()
}

waitForExpectations(timeout: 2, handler: nil)
}

Expand Down

0 comments on commit 3aa8012

Please sign in to comment.