Skip to content

Commit

Permalink
Return Result type instead of tuple of optionals (#316)
Browse files Browse the repository at this point in the history
* card approve with Result type

* card vault Result type

* Result type for PayPal checkout, docStrings

* paypal start docstrings error

* Result type for PayPal vault

* CHANGELOG

* typo in docstrings
  • Loading branch information
KunJeongPark authored Jan 21, 2025
1 parent ee43afa commit 768e971
Show file tree
Hide file tree
Showing 9 changed files with 300 additions and 249 deletions.
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

0 comments on commit 768e971

Please sign in to comment.