Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Return Result type instead of tuple of optionals #316

Merged
merged 7 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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<PayPalWebCheckoutResult, CoreSDKError>`
* Change `vault` completion from `(PayPalVaultResult?, CoreSDKError?)` to `Result<PayPalVaultResult, CoreSDKError>`
* CardPayments
* Update completion handler types to use `Result` instead of optional tuples
* Change `approveOrder` completion from `(CardResult?, CoreSDKError?)` to `Result<CardResult, CoreSDKError>`
* Change `vault` completion from `(CardVaultResult?, CoreSDKError?)` to `Result<CardVaultResult, CoreSDKError>`

## 2.0.0-beta2 (2024-12-11)
* CorePayments
* Make `CoreSDKError` conform to Equatable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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 {
Expand Down
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
84 changes: 47 additions & 37 deletions Sources/CardPayments/CardClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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<CardVaultResult, CoreSDKError>) -> Void) {
analyticsService = AnalyticsService(coreConfig: config, setupToken: vaultRequest.setupTokenID)
analyticsService?.sendEvent("card-payments:vault-wo-purchase:started")
Task {
Expand Down Expand Up @@ -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)
}
}
}
Expand All @@ -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<CardResult, CoreSDKError>) -> Void) {
analyticsService = AnalyticsService(coreConfig: config, orderID: request.orderID)
analyticsService?.sendEvent("card-payments:approve-order:started")
Task {
Expand Down Expand Up @@ -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)
}
}
}
Expand All @@ -149,7 +159,7 @@ public class CardClient: NSObject {
private func startThreeDSecureChallenge(
url: URL,
orderId: String,
completion: @escaping (CardResult?, CoreSDKError?) -> Void
completion: @escaping (Result<CardResult, CoreSDKError>) -> Void
) {

webAuthenticationSession.start(
Expand Down Expand Up @@ -188,7 +198,7 @@ public class CardClient: NSObject {
private func startVaultThreeDSecureChallenge(
url: URL,
setupTokenID: String,
completion: @escaping (CardVaultResult?, CoreSDKError?) -> Void
completion: @escaping (Result<CardVaultResult, CoreSDKError>) -> Void
) {

webAuthenticationSession.start(
Expand Down Expand Up @@ -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<CardResult, CoreSDKError>) -> 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<CardResult, CoreSDKError>) -> 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<CardResult, CoreSDKError>) -> 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<CardResult, CoreSDKError>) -> 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<CardResult, CoreSDKError>) -> 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<CardVaultResult, CoreSDKError>) -> 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<CardVaultResult, CoreSDKError>) -> 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<CardVaultResult, CoreSDKError>) -> 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<CardVaultResult, CoreSDKError>) -> 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<CardVaultResult, CoreSDKError>) -> Void) {
analyticsService?.sendEvent("card-payments:vault-wo-purchase:auth-challenge:canceled")
completion(nil, vaultError)
completion(.failure(vaultError))
}
}

Expand Down
Loading