diff --git a/CHANGELOG.md b/CHANGELOG.md index 73f625c5..0047973e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,17 @@ # PayPal iOS SDK Release Notes +## Unreleased +* Breaking Changes + * PayPalWebPayments + * Update completion handler types to use `Result` instead of optional tuples + * Change `start` completion from `(PayPalWebCheckoutResult?, CoreSDKError?)` to `Result` + * Change `vault` completion from `(PayPalVaultResult?, CoreSDKError?)` to `Result` + * CardPayments + * Update completion handler types to use `Result` instead of optional tuples + * Change `approveOrder` completion from `(CardResult?, CoreSDKError?)` to `Result` + * Change `vault` completion from `(CardVaultResult?, CoreSDKError?)` to `Result` + ## 2.0.0-beta2 (2024-12-11) * CorePayments * Make `CoreSDKError` conform to Equatable diff --git a/Demo/Demo/CardPayments/CardPaymentViewModel/CardPaymentViewModel.swift b/Demo/Demo/CardPayments/CardPaymentViewModel/CardPaymentViewModel.swift index 578339ee..93d0121d 100644 --- a/Demo/Demo/CardPayments/CardPaymentViewModel/CardPaymentViewModel.swift +++ b/Demo/Demo/CardPayments/CardPaymentViewModel/CardPaymentViewModel.swift @@ -118,21 +118,22 @@ class CardPaymentViewModel: ObservableObject { cardClient = CardClient(config: config) payPalDataCollector = PayPalDataCollector(config: config) let cardRequest = CardRequest(orderID: orderID, card: card, sca: sca) - cardClient?.approveOrder(request: cardRequest) { result, error in - if let error { + cardClient?.approveOrder(request: cardRequest) { result in + switch result { + case .success(let cardResult): + self.setApprovalSuccessResult( + approveResult: CardPaymentState.CardResult( + id: cardResult.orderID, + status: cardResult.status, + didAttemptThreeDSecureAuthentication: cardResult.didAttemptThreeDSecureAuthentication + ) + ) + case .failure(let error): if error == CardError.threeDSecureCanceledError { self.setApprovalCancelResult() } else { self.setApprovalFailureResult(error: error) } - } else if let result { - self.setApprovalSuccessResult( - approveResult: CardPaymentState.CardResult( - id: result.orderID, - status: result.status, - didAttemptThreeDSecureAuthentication: result.didAttemptThreeDSecureAuthentication - ) - ) } } } catch { diff --git a/Demo/Demo/CardVault/CardVaultViewModel/CardVaultViewModel.swift b/Demo/Demo/CardVault/CardVaultViewModel/CardVaultViewModel.swift index 6d3a6ecc..f6987451 100644 --- a/Demo/Demo/CardVault/CardVaultViewModel/CardVaultViewModel.swift +++ b/Demo/Demo/CardVault/CardVaultViewModel/CardVaultViewModel.swift @@ -14,10 +14,11 @@ class CardVaultViewModel: VaultViewModel { let config = try await configManager.getCoreConfig() let cardClient = CardClient(config: config) let cardVaultRequest = CardVaultRequest(card: card, setupTokenID: setupToken) - cardClient.vault(cardVaultRequest) { result, error in - if let result { - self.setUpdateSetupTokenResult(vaultResult: result, vaultError: nil) - } else if let error { + cardClient.vault(cardVaultRequest) { result in + switch result { + case .success(let cardVaultResult): + self.setUpdateSetupTokenResult(vaultResult: cardVaultResult, vaultError: nil) + case .failure(let error): self.setUpdateSetupTokenResult(vaultResult: nil, vaultError: error) } } diff --git a/Demo/Demo/PayPalVault/PayPalVaultViewModel/PayPalVaultViewModel.swift b/Demo/Demo/PayPalVault/PayPalVaultViewModel/PayPalVaultViewModel.swift index aebf4c65..5c968b73 100644 --- a/Demo/Demo/PayPalVault/PayPalVaultViewModel/PayPalVaultViewModel.swift +++ b/Demo/Demo/PayPalVault/PayPalVaultViewModel/PayPalVaultViewModel.swift @@ -14,8 +14,13 @@ class PayPalVaultViewModel: VaultViewModel { let config = try await configManager.getCoreConfig() let paypalClient = PayPalWebCheckoutClient(config: config) let vaultRequest = PayPalVaultRequest(setupTokenID: setupTokenID) - paypalClient.vault(vaultRequest) { result, error in - if let error { + paypalClient.vault(vaultRequest) { result in + switch result { + case .success(let cardVaultResult): + DispatchQueue.main.async { + self.state.paypalVaultTokenResponse = .loaded(cardVaultResult) + } + case .failure(let error): if error == PayPalError.vaultCanceledError { DispatchQueue.main.async { print("Canceled") @@ -26,10 +31,6 @@ class PayPalVaultViewModel: VaultViewModel { self.state.paypalVaultTokenResponse = .error(message: error.localizedDescription) } } - } else if let result { - DispatchQueue.main.async { - self.state.paypalVaultTokenResponse = .loaded(result) - } } } } catch { diff --git a/Demo/Demo/PayPalWebPayments/PayPalWebViewModel/PayPalWebViewModel.swift b/Demo/Demo/PayPalWebPayments/PayPalWebViewModel/PayPalWebViewModel.swift index 77eeead0..ab363f41 100644 --- a/Demo/Demo/PayPalWebPayments/PayPalWebViewModel/PayPalWebViewModel.swift +++ b/Demo/Demo/PayPalWebPayments/PayPalWebViewModel/PayPalWebViewModel.swift @@ -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") @@ -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))") - } } } } diff --git a/Sources/CardPayments/CardClient.swift b/Sources/CardPayments/CardClient.swift index e31b48e9..1d1124f8 100644 --- a/Sources/CardPayments/CardClient.swift +++ b/Sources/CardPayments/CardClient.swift @@ -41,9 +41,13 @@ 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 `CardVaultResult` with `setupTokenID` and `status` are returned and `error` will be `nil`; - /// if it fails, `CardVaultResult will be `nil` and `error` will describe the failure - public func vault(_ vaultRequest: CardVaultRequest, completion: @escaping (CardVaultResult?, CoreSDKError?) -> Void) { + /// 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) -> Void) { analyticsService = AnalyticsService(coreConfig: config, setupToken: vaultRequest.setupTokenID) analyticsService?.sendEvent("card-payments:vault-wo-purchase:started") Task { @@ -79,11 +83,12 @@ public class CardClient: NSObject { /// - Throws: A `CoreSDKError` describing failure public func vault(_ vaultRequest: CardVaultRequest) async throws -> CardVaultResult { try await withCheckedThrowingContinuation { continuation in - vault(vaultRequest) { result, error in - if let error { + vault(vaultRequest) { result in + switch result { + case .success(let cardVaultResult): + continuation.resume(returning: cardVaultResult) + case .failure(let error): continuation.resume(throwing: error) - } else if let result { - continuation.resume(returning: result) } } } @@ -94,10 +99,14 @@ 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 `CardResult` with `orderID` , `status` and `didAttemptThreeDSecureAuthentication` are returned and `error` will be `nil`; - /// if it fails, `CardResult will be `nil` and `error` will describe the failure - public func approveOrder(request: CardRequest, completion: @escaping (CardResult?, 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. + /// - `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) -> Void) { analyticsService = AnalyticsService(coreConfig: config, orderID: request.orderID) analyticsService?.sendEvent("card-payments:approve-order:started") Task { @@ -136,11 +145,12 @@ public class CardClient: NSObject { /// - Throws: A `CoreSDKError` describing failure public func approveOrder(request: CardRequest) async throws -> CardResult { try await withCheckedThrowingContinuation { continuation in - approveOrder(request: request) { result, error in - if let error { + approveOrder(request: request) { result in + switch result { + case .success(let cardResult): + continuation.resume(returning: cardResult) + case .failure(let error): continuation.resume(throwing: error) - } else if let result { - continuation.resume(returning: result) } } } @@ -149,7 +159,7 @@ public class CardClient: NSObject { private func startThreeDSecureChallenge( url: URL, orderId: String, - completion: @escaping (CardResult?, CoreSDKError?) -> Void + completion: @escaping (Result) -> Void ) { webAuthenticationSession.start( @@ -188,7 +198,7 @@ public class CardClient: NSObject { private func startVaultThreeDSecureChallenge( url: URL, setupTokenID: String, - completion: @escaping (CardVaultResult?, CoreSDKError?) -> Void + completion: @escaping (Result) -> Void ) { webAuthenticationSession.start( @@ -220,54 +230,54 @@ public class CardClient: NSObject { ) } - private func notifyCheckoutSuccess(for result: CardResult, completion: (CardResult?, CoreSDKError?) -> Void) { + private func notifyCheckoutSuccess(for result: CardResult, completion: (Result) -> Void) { analyticsService?.sendEvent("card-payments:approve-order:succeeded") - completion(result, nil) + completion(.success(result)) } - private func notify3dsCheckoutSuccess(for result: CardResult, completion: (CardResult?, CoreSDKError?) -> Void) { + private func notify3dsCheckoutSuccess(for result: CardResult, completion: (Result) -> Void) { analyticsService?.sendEvent("card-payments:approve-order:auth-challenge:succeeded") - completion(result, nil) + completion(.success(result)) } - private func notifyCheckoutFailure(with error: CoreSDKError, completion: (CardResult?, CoreSDKError?) -> Void) { + private func notifyCheckoutFailure(with error: CoreSDKError, completion: (Result) -> Void) { analyticsService?.sendEvent("card-payments:approve-order:failed") - completion(nil, error) + completion(.failure(error)) } - private func notify3dsCheckoutFailure(with error: CoreSDKError, completion: (CardResult?, CoreSDKError?) -> Void) { + private func notify3dsCheckoutFailure(with error: CoreSDKError, completion: (Result) -> Void) { analyticsService?.sendEvent("card-payments:approve-order:auth-challenge:failed") - completion(nil, error) + completion(.failure(error)) } - private func notify3dsCheckoutCancelWithError(with error: CoreSDKError, completion: (CardResult?, CoreSDKError?) -> Void) { + private func notify3dsCheckoutCancelWithError(with error: CoreSDKError, completion: (Result) -> Void) { analyticsService?.sendEvent("card-payments:approve-order:auth-challenge:canceled") - completion(nil, error) + completion(.failure(error)) } - private func notifyVaultSuccess(for vaultResult: CardVaultResult, completion: (CardVaultResult?, CoreSDKError?) -> Void) { + private func notifyVaultSuccess(for vaultResult: CardVaultResult, completion: (Result) -> Void) { analyticsService?.sendEvent("card-payments:vault-wo-purchase:succeeded") - completion(vaultResult, nil) + completion(.success(vaultResult)) } - private func notify3dsVaultSuccess(for vaultResult: CardVaultResult, completion: (CardVaultResult?, CoreSDKError?) -> Void) { + private func notify3dsVaultSuccess(for vaultResult: CardVaultResult, completion: (Result) -> Void) { analyticsService?.sendEvent("card-payments:vault-wo-purchase:auth-challenge:succeeded") - completion(vaultResult, nil) + completion(.success(vaultResult)) } - private func notifyVaultFailure(with vaultError: CoreSDKError, completion: (CardVaultResult?, CoreSDKError?) -> Void) { + private func notifyVaultFailure(with vaultError: CoreSDKError, completion: (Result) -> Void) { analyticsService?.sendEvent("card-payments:vault-wo-purchase:failed") - completion(nil, vaultError) + completion(.failure(vaultError)) } - private func notify3dsVaultFailure(with vaultError: CoreSDKError, completion: (CardVaultResult?, CoreSDKError?) -> Void) { + private func notify3dsVaultFailure(with vaultError: CoreSDKError, completion: (Result) -> Void) { analyticsService?.sendEvent("card-payments:vault-wo-purchase:auth-challenge:failed") - completion(nil, vaultError) + completion(.failure(vaultError)) } - private func notify3dsVaultCancelWithError(with vaultError: CoreSDKError, completion: (CardVaultResult?, CoreSDKError?) -> Void) { + private func notify3dsVaultCancelWithError(with vaultError: CoreSDKError, completion: (Result) -> Void) { analyticsService?.sendEvent("card-payments:vault-wo-purchase:auth-challenge:canceled") - completion(nil, vaultError) + completion(.failure(vaultError)) } } diff --git a/Sources/PayPalWebPayments/PayPalWebCheckoutClient.swift b/Sources/PayPalWebPayments/PayPalWebCheckoutClient.swift index 9a002e5c..e7b9d126 100644 --- a/Sources/PayPalWebPayments/PayPalWebCheckoutClient.swift +++ b/Sources/PayPalWebPayments/PayPalWebCheckoutClient.swift @@ -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(PayPalCheckoutResult)` 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) -> Void) { analyticsService = AnalyticsService(coreConfig: config, orderID: request.orderID) analyticsService?.sendEvent("paypal-web-payments:checkout:started") @@ -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) } } } @@ -123,9 +127,12 @@ public class PayPalWebCheckoutClient: NSObject { /// - Parameters: /// - vaultRequest: Request created with url for vault approval and setupTokenID /// - completion: A completion block that is invoked when the request is completed. If the request succeeds, - /// a `PayPalVaultResult` with `tokenID` and `approvalSessionID` are returned and `error` will be `nil`; - /// if it fails, `PayPalVaultResult will be `nil` and `error` will describe the failure - public func vault(_ vaultRequest: PayPalVaultRequest, completion: @escaping (PayPalVaultResult?, CoreSDKError?) -> Void) { + /// The closure returns a `PayPalVaultResult`: + /// - `.success(PayPalVaultResult)` containing: + /// - `tokenID`: The ID of the setup token. + /// - `approvalSessionID`: id of the PayPalWebCheckout session + /// - `.failure(CoreSDKError)`: Describes the reason for failure. + public func vault(_ vaultRequest: PayPalVaultRequest, completion: @escaping (Result) -> Void) { analyticsService = AnalyticsService(coreConfig: config, setupToken: vaultRequest.setupTokenID) analyticsService?.sendEvent("paypal-web-payments:vault-wo-purchase:started") @@ -187,11 +194,12 @@ public class PayPalWebCheckoutClient: NSObject { /// - Throws: A `CoreSDKError` describing failure public func vault(_ vaultRequest: PayPalVaultRequest) async throws -> PayPalVaultResult { try await withCheckedThrowingContinuation { continuation in - vault(vaultRequest) { result, error in - if let error { - continuation.resume(throwing: error) - } else if let result { + vault(vaultRequest) { result in + switch result { + case .success(let result): continuation.resume(returning: result) + case .failure(let error): + continuation.resume(throwing: error) } } } @@ -202,34 +210,43 @@ 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) -> 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) -> 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) -> Void + ) { analyticsService?.sendEvent("paypal-web-payments:checkout:canceled") - completion(nil, error) + completion(.failure(error)) } - private func notifyVaultSuccess(for result: PayPalVaultResult, completion: (PayPalVaultResult?, CoreSDKError?) -> Void) { + private func notifyVaultSuccess(for result: PayPalVaultResult, completion: (Result) -> Void) { analyticsService?.sendEvent("paypal-web-payments:vault-wo-purchase:succeeded") - completion(result, nil) + completion(.success(result)) } - private func notifyVaultFailure(with error: CoreSDKError, completion: (PayPalVaultResult?, CoreSDKError?) -> Void) { + private func notifyVaultFailure(with error: CoreSDKError, completion: (Result) -> Void) { analyticsService?.sendEvent("paypal-web-payments:vault-wo-purchase:failed") - completion(nil, error) + completion(.failure(error)) } - private func notifyVaultCancelWithError(with vaultError: CoreSDKError, completion: (PayPalVaultResult?, CoreSDKError?) -> Void) { + private func notifyVaultCancelWithError(with vaultError: CoreSDKError, completion: (Result) -> Void) { analyticsService?.sendEvent("paypal-web-payments:vault-wo-purchase:canceled") - completion(nil, vaultError) + completion(.failure(vaultError)) } } diff --git a/UnitTests/CardPaymentsTests/CardClient_Tests.swift b/UnitTests/CardPaymentsTests/CardClient_Tests.swift index 9231870b..177704be 100644 --- a/UnitTests/CardPaymentsTests/CardClient_Tests.swift +++ b/UnitTests/CardPaymentsTests/CardClient_Tests.swift @@ -61,13 +61,16 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "vault completed") - sut.vault(vaultRequest) { result, error in - XCTAssertEqual(result?.setupTokenID, setupTokenID) - XCTAssertEqual(result?.status, vaultStatus) - XCTAssertNil(error) + sut.vault(vaultRequest) { result in + switch result { + case .success(let cardVaultResult): + XCTAssertEqual(cardVaultResult.setupTokenID, setupTokenID) + XCTAssertEqual(cardVaultResult.status, vaultStatus) + case .failure: + XCTFail("Expected success with CardVaultResult") + } expectation.fulfill() } - waitForExpectations(timeout: 2, handler: nil) } @@ -81,16 +84,15 @@ class CardClient_Tests: XCTestCase { mockVaultAPI.stubSetupTokenResponse = updateSetupTokenResponse let expectation = expectation(description: "vault completed") - sut.vault(vaultRequest) { result, error in - if let result { - XCTAssertEqual(result.setupTokenID, setupTokenID) - XCTAssertNil(result.status) - XCTAssertTrue(result.didAttemptThreeDSecureAuthentication) - } else { - XCTFail("expected result not to be nil") + sut.vault(vaultRequest) { result in + switch result { + case .success(let cardVaultResult): + XCTAssertEqual(cardVaultResult.setupTokenID, setupTokenID) + XCTAssertNil(cardVaultResult.status) + XCTAssertTrue(cardVaultResult.didAttemptThreeDSecureAuthentication) + case .failure: + XCTFail("Expected success with CardVaultResult") } - - XCTAssertNil(error) expectation.fulfill() } @@ -108,14 +110,14 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "vault completed") - sut.vault(vaultRequest) { result, error in - XCTAssertNil(result) - if let error { + sut.vault(vaultRequest) { result in + switch result { + case .success: + XCTFail("Expected failure with error") + case .failure(let error): XCTAssertEqual(error.domain, CardError.domain) XCTAssertEqual(error.code, CardError.threeDSecureURLError.code) XCTAssertEqual(error.localizedDescription, CardError.threeDSecureURLError.localizedDescription) - } else { - XCTFail("Expected error not to be nil") } expectation.fulfill() } @@ -126,19 +128,19 @@ class CardClient_Tests: XCTestCase { func testVault_whenVaultAPIError_bubblesError() { let setupTokenID = "testToken1" let vaultRequest = CardVaultRequest(card: card, setupTokenID: setupTokenID) - + mockVaultAPI.stubError = CoreSDKError(code: 123, domain: "fake-domain", errorDescription: "api-error") let expectation = expectation(description: "vault completed") - sut.vault(vaultRequest) { result, error in - XCTAssertNil(result) - if let error { + sut.vault(vaultRequest) { result in + switch result { + case .success: + XCTFail("Expected failure with error") + case .failure(let error): XCTAssertEqual(error.domain, "fake-domain") XCTAssertEqual(error.code, 123) XCTAssertEqual(error.localizedDescription, "api-error") - } else { - XCTFail("Expected error not to be nil") } expectation.fulfill() } @@ -153,14 +155,14 @@ class CardClient_Tests: XCTestCase { mockVaultAPI.stubError = NSError(domain: "some-domain", code: 123, userInfo: [NSLocalizedDescriptionKey: "some-description"]) let expectation = expectation(description: "vault completed") - sut.vault(vaultRequest) { result, error in - XCTAssertNil(result) - if let error { + sut.vault(vaultRequest) { result in + switch result { + case .success: + XCTFail("Expected failure with error") + case .failure(let error): XCTAssertEqual(error.domain, CardError.domain) XCTAssertEqual(error.code, CardError.Code.vaultTokenError.rawValue) XCTAssertEqual(error.localizedDescription, "An error occurred while vaulting a card.") - } else { - XCTFail("Expected error not to be nil") } expectation.fulfill() } @@ -175,14 +177,14 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "vault() completed") - sut.vault(cardVaultRequest) { result, error in - XCTAssertNil(error) - if let result { - XCTAssertEqual(result.setupTokenID, "testSetupTokenId") - XCTAssertNil(result.status) - XCTAssertTrue(result.didAttemptThreeDSecureAuthentication) - } else { - XCTFail("Expected result not to be nil") + sut.vault(cardVaultRequest) { result in + switch result { + case .success(let cardVaultResult): + XCTAssertEqual(cardVaultResult.setupTokenID, "testSetupTokenId") + XCTAssertNil(cardVaultResult.status) + XCTAssertTrue(cardVaultResult.didAttemptThreeDSecureAuthentication) + case .failure: + XCTFail("Expected success with CardVaultResult") } expectation.fulfill() } @@ -200,14 +202,14 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "vault() completed") - sut.vault(cardVaultRequest) { result, error in - XCTAssertNil(result) - if let error { + sut.vault(cardVaultRequest) { result in + switch result { + case .success: + XCTFail("Expected failure with error") + case .failure(let error): XCTAssertEqual(error.domain, CardError.domain) XCTAssertEqual(error.code, CardError.Code.threeDSecureCanceledError.rawValue) XCTAssertEqual(error.localizedDescription, CardError.threeDSecureCanceledError.localizedDescription) - } else { - XCTFail("Expected error not to be nil") } expectation.fulfill() } @@ -226,14 +228,14 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "vault() completed") - sut.vault(cardVaultRequest) { result, error in - XCTAssertNil(result) - if let error { + sut.vault(cardVaultRequest) { result in + switch result { + case .success: + XCTFail("Expected failure with error") + case .failure(let error): XCTAssertEqual(error.domain, CardError.domain) XCTAssertEqual(error.code, CardError.Code.threeDSecureError.rawValue) XCTAssertEqual(error.localizedDescription, "Mock web session error description.") - } else { - XCTFail("Expected error not to be nil") } expectation.fulfill() } @@ -248,14 +250,14 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "approveOrder() completed") - sut.approveOrder(request: cardRequest) { result, error in - XCTAssertNil(result) - if let error { + sut.approveOrder(request: cardRequest) { result in + switch result { + case .success: + XCTFail("Expected failure with error") + case .failure(let error): XCTAssertEqual(error.code, 3) XCTAssertEqual(error.domain, "CardClientErrorDomain") XCTAssertEqual(error.errorDescription, "An invalid 3DS URL was returned. Contact developer.paypal.com/support.") - } else { - XCTFail("Expected error not to be nil") } expectation.fulfill() } @@ -265,19 +267,17 @@ class CardClient_Tests: XCTestCase { func testApproveOrder_withNoThreeDSecure_returnsOrderData() { mockCheckoutOrdersAPI.stubConfirmResponse = FakeConfirmPaymentResponse.without3DS - let expectation = expectation(description: "approveOrder() completed") - sut.approveOrder(request: cardRequest) { result, error in - if let result { - XCTAssertEqual(result.orderID, "testOrderId") - XCTAssertEqual(result.status, "APPROVED") - XCTAssertFalse(result.didAttemptThreeDSecureAuthentication) - } else { - XCTFail("expected result not to be nil") + sut.approveOrder(request: cardRequest) { result in + switch result { + case .success(let cardResult): + XCTAssertEqual(cardResult.orderID, "testOrderId") + XCTAssertEqual(cardResult.status, "APPROVED") + XCTAssertFalse(cardResult.didAttemptThreeDSecureAuthentication) + case .failure: + XCTFail("Expected success with CardResult") } - - XCTAssertNil(error) expectation.fulfill() } @@ -289,18 +289,17 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "approveOrder() completed") - sut.approveOrder(request: cardRequest) { result, error in - XCTAssertNil(result) - if let error { + sut.approveOrder(request: cardRequest) { result in + switch result { + case .success: + XCTFail("Expected failure with error") + case .failure(let error): XCTAssertEqual(error.code, 123) XCTAssertEqual(error.domain, "sdk-domain") XCTAssertEqual(error.errorDescription, "sdk-error-desc") - } else { - XCTFail("Expected error not to be nil") } expectation.fulfill() } - waitForExpectations(timeout: 2, handler: nil) } @@ -313,16 +312,16 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "approveOrder() completed") - sut.approveOrder(request: cardRequest) { result, error in - XCTAssertNil(result) - if let error = error { + sut.approveOrder(request: cardRequest) { result in + switch result { + case .success: + XCTFail("Expected failure with error") + case .failure(let error): XCTAssertEqual(error.domain, CardError.domain) XCTAssertEqual(error.code, CardError.Code.unknown.rawValue) XCTAssertNotNil(error.localizedDescription) - expectation.fulfill() - } else { - XCTFail("Expected error not to be nil") } + expectation.fulfill() } waitForExpectations(timeout: 2, handler: nil) @@ -332,17 +331,16 @@ class CardClient_Tests: XCTestCase { mockCheckoutOrdersAPI.stubConfirmResponse = FakeConfirmPaymentResponse.withValid3DSURL mockWebAuthSession.cannedResponseURL = .init(string: "sdk.ios.paypal://card/success?state=undefined&code=undefined&liability_shift=POSSIBLE") - let expectation = expectation(description: "approveOrder() completed") - sut.approveOrder(request: cardRequest) { result, error in - XCTAssertNil(error) - if let result { - XCTAssertEqual(result.orderID, "testOrderId") - XCTAssertNil(result.status) - XCTAssertTrue(result.didAttemptThreeDSecureAuthentication) - } else { - XCTFail("Expected non-nil result") + sut.approveOrder(request: cardRequest) { result in + switch result { + case .success(let cardResult): + XCTAssertEqual(cardResult.orderID, "testOrderId") + XCTAssertNil(cardResult.status) + XCTAssertTrue(cardResult.didAttemptThreeDSecureAuthentication) + case .failure: + XCTFail("Expected success with CardResult") } expectation.fulfill() } @@ -360,14 +358,14 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "approveOrder() completed") - sut.approveOrder(request: cardRequest) { result, error in - XCTAssertNil(result) - if let error = error { + sut.approveOrder(request: cardRequest) { result in + switch result { + case .success: + XCTFail("Expected failure with error") + case .failure(let error): XCTAssertEqual(error.domain, CardError.domain) XCTAssertEqual(error.code, CardError.threeDSecureCanceledError.code) XCTAssertEqual(error.localizedDescription, CardError.threeDSecureCanceledError.localizedDescription) - } else { - XCTFail("Expected error") } expectation.fulfill() } @@ -386,14 +384,14 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "approveOrder() completed") - sut.approveOrder(request: cardRequest) { result, error in - XCTAssertNil(result) - if let error = error { + sut.approveOrder(request: cardRequest) { result in + switch result { + case .success: + XCTFail("Expected failure with error") + case .failure(let error): XCTAssertEqual(error.domain, CardError.domain) XCTAssertEqual(error.code, CardError.Code.threeDSecureError.rawValue) XCTAssertEqual(error.localizedDescription, "Mock web session error description.") - } else { - XCTFail("Expected error") } expectation.fulfill() } @@ -412,12 +410,12 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "approveOrder() completed") - sut.approveOrder(request: cardRequest) { result, error in - XCTAssertNil(result) - if let error = error { + sut.approveOrder(request: cardRequest) { result in + switch result { + case .success: + XCTFail("Expected failure with cancellation error") + case .failure(let error): XCTAssertTrue(CardError.isThreeDSecureCanceled(error)) - } else { - XCTFail("Expected error due to user cancellation") } expectation.fulfill() } diff --git a/UnitTests/PayPalWebPaymentsTests/PayPalWebCheckoutClient_Tests.swift b/UnitTests/PayPalWebPaymentsTests/PayPalWebCheckoutClient_Tests.swift index 8866d2b5..497c4644 100644 --- a/UnitTests/PayPalWebPaymentsTests/PayPalWebCheckoutClient_Tests.swift +++ b/UnitTests/PayPalWebPaymentsTests/PayPalWebCheckoutClient_Tests.swift @@ -26,7 +26,7 @@ class PayPalClient_Tests: XCTestCase { func testVault_whenSandbox_launchesCorrectURLInWebSession() { let vaultRequest = PayPalVaultRequest(setupTokenID: "fake-token") - payPalClient.vault(vaultRequest) { _, _ in } + payPalClient.vault(vaultRequest) { _ in } XCTAssertEqual(mockWebAuthenticationSession.lastLaunchedURL?.absoluteString, "https://sandbox.paypal.com/agreements/approve?approval_session_id=fake-token") } @@ -40,7 +40,7 @@ class PayPalClient_Tests: XCTestCase { ) let vaultRequest = PayPalVaultRequest(setupTokenID: "fake-token") - payPalClient.vault(vaultRequest) { _, _ in } + payPalClient.vault(vaultRequest) { _ in } XCTAssertEqual(mockWebAuthenticationSession.lastLaunchedURL?.absoluteString, "https://paypal.com/agreements/approve?approval_session_id=fake-token") } @@ -55,10 +55,14 @@ class PayPalClient_Tests: XCTestCase { let expectedSessionIDResult = "fakeSessionID" let vaultRequest = PayPalVaultRequest(setupTokenID: "fakeTokenID") - payPalClient.vault(vaultRequest) { result, error in - XCTAssertEqual(expectedTokenIDResult, result?.tokenID) - XCTAssertEqual(expectedSessionIDResult, result?.approvalSessionID) - XCTAssertNil(error) + payPalClient.vault(vaultRequest) { result in + switch result { + case .success(let cardVaultResult): + XCTAssertEqual(expectedTokenIDResult, cardVaultResult.tokenID) + XCTAssertEqual(expectedSessionIDResult, cardVaultResult.approvalSessionID) + case .failure: + XCTFail("Expected success with CardVaultResult") + } expectation.fulfill() } @@ -75,14 +79,14 @@ class PayPalClient_Tests: XCTestCase { let expectation = expectation(description: "vault(url:) completed") let vaultRequest = PayPalVaultRequest(setupTokenID: "fakeTokenID") - payPalClient.vault(vaultRequest) { result, error in - XCTAssertNil(result) - if let error { + payPalClient.vault(vaultRequest) { 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.vaultCanceledError.rawValue) XCTAssertEqual(error.localizedDescription, "PayPal vault has been canceled by the user") - } else { - XCTFail("Expected error not to be nil") } expectation.fulfill() } @@ -100,12 +104,12 @@ class PayPalClient_Tests: XCTestCase { let expectation = expectation(description: "vault(url:) completed") let vaultRequest = PayPalVaultRequest(setupTokenID: "fakeTokenID") - payPalClient.vault(vaultRequest) { result, error in - if let error { - XCTAssertNil(result) + payPalClient.vault(vaultRequest) { result in + switch result { + case .success: + XCTFail("Expected failure with cancellation error") + case .failure(let error): XCTAssertTrue(PayPalError.isVaultCanceled(error)) - } else { - XCTFail("Expected error from PayPal vault cancellation") } expectation.fulfill() } @@ -125,13 +129,13 @@ class PayPalClient_Tests: XCTestCase { let expectation = expectation(description: "vault(url:) completed") let vaultRequest = PayPalVaultRequest(setupTokenID: "fakeTokenID") - payPalClient.vault(vaultRequest) { result, error in - XCTAssertNil(result) - if let error { + payPalClient.vault(vaultRequest) { result in + switch result { + case .success: + XCTFail("Expected failure with error") + case .failure(let error): XCTAssertEqual(error.domain, expectedError.domain) XCTAssertEqual(error.code, expectedError.code) - } else { - XCTFail("Expected error not to be nil") } expectation.fulfill() } @@ -140,29 +144,29 @@ class PayPalClient_Tests: XCTestCase { } func testVault_whenSuccessUrl_missingToken_returnsError() { - + mockWebAuthenticationSession.cannedResponseURL = URL(string: "sdk.ios.paypal://vault/success?approval_token_id=&approval_session_id=fakeSessionID") - + let expectation = expectation(description: "vault(url:) completed") - + let expectedError = CoreSDKError( code: PayPalError.payPalVaultResponseError.code, domain: PayPalError.domain, errorDescription: PayPalError.payPalVaultResponseError.errorDescription ) - + let vaultRequest = PayPalVaultRequest(setupTokenID: "fakeTokenID") - payPalClient.vault(vaultRequest) { result, error in - XCTAssertNil(result) - if let error { + payPalClient.vault(vaultRequest) { result in + switch result { + case .success: + XCTFail("Expected failure with error") + case .failure(let error): XCTAssertEqual(error.domain, expectedError.domain) XCTAssertEqual(error.code, expectedError.code) - } else { - XCTFail("Expected error not to be nil") } expectation.fulfill() } - + waitForExpectations(timeout: 10) } @@ -178,14 +182,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() } @@ -206,12 +210,12 @@ 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() } @@ -219,7 +223,6 @@ class PayPalClient_Tests: XCTestCase { waitForExpectations(timeout: 2, handler: nil) } - func testStart_whenWebAuthenticationSessions_returnsWebSessionError() { let request = PayPalWebCheckoutRequest(orderID: "1234") @@ -230,17 +233,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) } @@ -249,17 +254,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) } @@ -268,12 +274,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) }