Skip to content

Commit

Permalink
Card vault to completion, unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
KunJeongPark committed Oct 22, 2024
1 parent fa66cbb commit c2178bc
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 251 deletions.
42 changes: 10 additions & 32 deletions Demo/Demo/ViewModels/CardVaultViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Foundation
import CardPayments
import CorePayments

class CardVaultViewModel: VaultViewModel, CardVaultDelegate {
class CardVaultViewModel: VaultViewModel {

let configManager = CoreConfigManager(domain: "Card Vault")

Expand All @@ -13,11 +13,16 @@ class CardVaultViewModel: VaultViewModel, CardVaultDelegate {
do {
let config = try await configManager.getCoreConfig()
let cardClient = CardClient(config: config)
cardClient.vaultDelegate = self
let cardVaultRequest = CardVaultRequest(card: card, setupTokenID: setupToken)
cardClient.vault(cardVaultRequest)
cardClient.vault(cardVaultRequest) { result, error in
if let result {
self.setUpdateSetupTokenResult(vaultResult: result, vaultError: nil)
} else if let error {
self.setUpdateSetupTokenResult(vaultResult: nil, vaultError: error)
}
}
} catch {
self.state.updateSetupTokenResponse = .error(message: error.localizedDescription)
self.setUpdateSetupTokenResult(vaultResult: nil, vaultError: error)
print("failed in updating setup token. \(error.localizedDescription)")
}
}
Expand All @@ -31,7 +36,7 @@ class CardVaultViewModel: VaultViewModel, CardVaultDelegate {
return enabled
}

func setUpdateSetupTokenResult(vaultResult: CardVaultResult? = nil, vaultError: CoreSDKError? = nil) {
func setUpdateSetupTokenResult(vaultResult: CardVaultResult? = nil, vaultError: Error? = nil) {
DispatchQueue.main.async {
if let vaultResult {
self.state.updateSetupTokenResponse = .loaded(
Expand All @@ -46,31 +51,4 @@ class CardVaultViewModel: VaultViewModel, CardVaultDelegate {
}
}
}

// MARK: - CardVault Delegate

func card(_ cardClient: CardPayments.CardClient, didFinishWithVaultResult vaultResult: CardPayments.CardVaultResult) {
print("vaultResult: \(vaultResult)")
setUpdateSetupTokenResult(vaultResult: vaultResult)
}

func card(_ cardClient: CardPayments.CardClient, didFinishWithVaultError vaultError: CorePayments.CoreSDKError) {
print("error: \(vaultError.errorDescription ?? "")")
setUpdateSetupTokenResult(vaultError: vaultError)
}

func cardThreeDSecureDidCancel(_ cardClient: CardClient) {
DispatchQueue.main.async {
self.state.updateSetupTokenResponse = .idle
self.state.updateSetupToken = nil
}
}

func cardThreeDSecureWillLaunch(_ cardClient: CardPayments.CardClient) {
print("About to launch 3DS")
}

func cardThreeDSecureDidFinish(_ cardClient: CardPayments.CardClient) {
print("Finished 3DS")
}
}
8 changes: 0 additions & 8 deletions PayPal.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
065A4DBF26FCDA5B0007014A /* CardClient_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065A4DBE26FCDA5B0007014A /* CardClient_Tests.swift */; };
06CE009926F3D1660000CC46 /* CoreConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06CE009826F3D1660000CC46 /* CoreConfig.swift */; };
06CE009B26F3D5A40000CC46 /* CardClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 06CE009A26F3D5A40000CC46 /* CardClient.swift */; };
3B109B3C2A85CC6200D8135F /* MockCardVaultDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B109B3A2A85B54800D8135F /* MockCardVaultDelegate.swift */; };
3B22E8B62A840ECF00962E34 /* CardVaultDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B22E8B52A840ECF00962E34 /* CardVaultDelegate.swift */; };
3B22E8B82A841AEA00962E34 /* CardVaultResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B22E8B72A841AEA00962E34 /* CardVaultResult.swift */; };
3B29C3972B9148F70077741D /* PayPalVaultRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B29C3962B9148F70077741D /* PayPalVaultRequest.swift */; };
3B3C511E2B2395B5009125FE /* PayPalVaultResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3C511D2B2395B5009125FE /* PayPalVaultResult.swift */; };
Expand Down Expand Up @@ -184,8 +182,6 @@
06CE009A26F3D5A40000CC46 /* CardClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardClient.swift; sourceTree = "<group>"; };
06CE009F26F3DF100000CC46 /* ConfirmPaymentSourceRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmPaymentSourceRequest.swift; sourceTree = "<group>"; };
06CE00A226F3E32A0000CC46 /* ConfirmPaymentSourceResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmPaymentSourceResponse.swift; sourceTree = "<group>"; };
3B109B3A2A85B54800D8135F /* MockCardVaultDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCardVaultDelegate.swift; sourceTree = "<group>"; };
3B22E8B52A840ECF00962E34 /* CardVaultDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardVaultDelegate.swift; sourceTree = "<group>"; };
3B22E8B72A841AEA00962E34 /* CardVaultResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardVaultResult.swift; sourceTree = "<group>"; };
3B29C3962B9148F70077741D /* PayPalVaultRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalVaultRequest.swift; sourceTree = "<group>"; };
3B3C511D2B2395B5009125FE /* PayPalVaultResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalVaultResult.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -481,7 +477,6 @@
children = (
CB16E6D7285B7A2B00FD6F52 /* FakeConfirmPaymentResponse.swift */,
CB16E6D9285B7B7300FD6F52 /* MockCardDelegate.swift */,
3B109B3A2A85B54800D8135F /* MockCardVaultDelegate.swift */,
808E3EDC2A981F240017FE46 /* MockCheckoutOrdersAPI.swift */,
808E3EDE2A981F390017FE46 /* MockVaultPaymentTokensAPI.swift */,
3B783DC22B7A69C2004623DB /* FakeUpdateSetupTokenResponse.swift */,
Expand Down Expand Up @@ -547,7 +542,6 @@
06CE009A26F3D5A40000CC46 /* CardClient.swift */,
80DCC59D2719DB6F00EC7C5A /* CardClientError.swift */,
CB4BE27F2847F01000EA2DD1 /* CardDelegate.swift */,
3B22E8B52A840ECF00962E34 /* CardVaultDelegate.swift */,
80DBC9D829C336D500462539 /* APIRequests */,
065A4DBD26FCDA270007014A /* Models */,
3BE738622B9A482800598F05 /* PrivacyInfo.xcprivacy */,
Expand Down Expand Up @@ -1223,7 +1217,6 @@
3B22E8B82A841AEA00962E34 /* CardVaultResult.swift in Sources */,
CBC16DD529E99B4600307117 /* PaymentSource.swift in Sources */,
80DCC59E2719DB6F00EC7C5A /* CardClientError.swift in Sources */,
3B22E8B62A840ECF00962E34 /* CardVaultDelegate.swift in Sources */,
3BDB34942A80CE6E008100D7 /* CardVaultRequest.swift in Sources */,
80B8B2FC2A8EBBFD00AB60CD /* VaultPaymentTokensAPI.swift in Sources */,
CB4BE27D2847AF6F00EA2DD1 /* SCA.swift in Sources */,
Expand All @@ -1238,7 +1231,6 @@
files = (
065A4DBF26FCDA5B0007014A /* CardClient_Tests.swift in Sources */,
8008D2052A9E54FF0003CAF4 /* CheckoutOrdersAPI_Tests.swift in Sources */,
3B109B3C2A85CC6200D8135F /* MockCardVaultDelegate.swift in Sources */,
80B27AF12A9E9EE60008EA45 /* VaultPaymentTokensAPI_Tests.swift in Sources */,
CB16E6D8285B7A2B00FD6F52 /* FakeConfirmPaymentResponse.swift in Sources */,
CB16E6DA285B7B7300FD6F52 /* MockCardDelegate.swift in Sources */,
Expand Down
39 changes: 17 additions & 22 deletions Sources/CardPayments/CardClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import CorePayments
public class CardClient: NSObject {

public weak var delegate: CardDelegate?
public weak var vaultDelegate: CardVaultDelegate?

private let checkoutOrdersAPI: CheckoutOrdersAPI
private let vaultAPI: VaultPaymentTokensAPI
Expand Down Expand Up @@ -43,7 +42,10 @@ public class CardClient: NSObject {
/// If `didAttempt3DSecureVerification` is `true`, check verification status with `/v3/vault/setup-token/{id}` in your server.
/// - Parameters:
/// - vaultRequest: The request containing setupTokenID and card
public func vault(_ vaultRequest: CardVaultRequest) {
/// - 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?, Error?) -> Void) {
analyticsService = AnalyticsService(coreConfig: config, setupToken: vaultRequest.setupTokenID)
analyticsService?.sendEvent("card-payments:vault-wo-purchase:started")
Task {
Expand All @@ -53,19 +55,19 @@ public class CardClient: NSObject {
if result.status == "PAYER_ACTION_REQUIRED",
let urlString = result.links.first(where: { $0.rel == "approve" })?.href {
guard urlString.contains("helios"), let url = URL(string: urlString) else {
self.notifyVaultFailure(with: CardClientError.threeDSecureURLError)
self.notifyVaultFailure(with: CardClientError.threeDSecureURLError, completion: completion)
return
}
analyticsService?.sendEvent("card-payments:vault-wo-purchase:challenge-required")
startVaultThreeDSecureChallenge(url: url, setupTokenID: vaultRequest.setupTokenID)
startVaultThreeDSecureChallenge(url: url, setupTokenID: vaultRequest.setupTokenID, completion: completion)
} else {
let vaultResult = CardVaultResult(setupTokenID: result.id, status: result.status, didAttemptThreeDSecureAuthentication: false)
notifyVaultSuccess(for: vaultResult)
notifyVaultSuccess(for: vaultResult, completion: completion)
}
} catch let error as CoreSDKError {
notifyVaultFailure(with: error)
notifyVaultFailure(with: error, completion: completion)
} catch {
notifyVaultFailure(with: CardClientError.vaultTokenError)
notifyVaultFailure(with: CardClientError.vaultTokenError, completion: completion)
}
}
}
Expand Down Expand Up @@ -149,8 +151,7 @@ public class CardClient: NSObject {
return url.queryItems?.first { $0.name == param }?.value
}

private func startVaultThreeDSecureChallenge(url: URL, setupTokenID: String) {
vaultDelegate?.cardThreeDSecureWillLaunch(self)
private func startVaultThreeDSecureChallenge(url: URL, setupTokenID: String, completion: @escaping (CardVaultResult?, Error?) -> Void) {

webAuthenticationSession.start(
url: url,
Expand All @@ -164,20 +165,19 @@ public class CardClient: NSObject {
}
},
sessionDidComplete: { _, error in
self.vaultDelegate?.cardThreeDSecureDidFinish(self)
if let error = error {
switch error {
case ASWebAuthenticationSessionError.canceledLogin:
self.notifyVaultCancellation()
self.notifyVaultFailure(with: CardClientError.threeDSecureCancellation, completion: completion)
return
default:
self.notifyVaultFailure(with: CardClientError.threeDSecureError(error))
self.notifyVaultFailure(with: CardClientError.threeDSecureError(error), completion: completion)
return
}
}

let cardVaultResult = CardVaultResult(setupTokenID: setupTokenID, status: nil, didAttemptThreeDSecureAuthentication: true)
self.notifyVaultSuccess(for: cardVaultResult)
self.notifyVaultSuccess(for: cardVaultResult, completion: completion)
}
)
}
Expand All @@ -192,25 +192,20 @@ public class CardClient: NSObject {
delegate?.card(self, didFinishWithError: error)
}

private func notifyVaultSuccess(for vaultResult: CardVaultResult) {
private func notifyVaultSuccess(for vaultResult: CardVaultResult, completion: (CardVaultResult?, Error?) -> Void) {
analyticsService?.sendEvent("card-payments:vault-wo-purchase:succeeded")
vaultDelegate?.card(self, didFinishWithVaultResult: vaultResult)
completion(vaultResult, nil)
}

private func notifyVaultFailure(with vaultError: CoreSDKError) {
private func notifyVaultFailure(with vaultError: CoreSDKError, completion: (CardVaultResult?, Error?) -> Void) {
analyticsService?.sendEvent("card-payments:vault-wo-purchase:failed")
vaultDelegate?.card(self, didFinishWithVaultError: vaultError)
completion(nil, vaultError)
}

private func notifyCancellation() {
analyticsService?.sendEvent("card-payments:3ds:challenge:user-canceled")
delegate?.cardDidCancel(self)
}

private func notifyVaultCancellation() {
analyticsService?.sendEvent("card-payments:vault-wo-purchase:challenge:canceled")
vaultDelegate?.cardThreeDSecureDidCancel(self)
}
}

// MARK: - ASWebAuthenticationPresentationContextProviding conformance
Expand Down
11 changes: 10 additions & 1 deletion Sources/CardPayments/CardClientError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ enum CardClientError {

/// 9. Malformed Deeplink URL from 3DS
case malformedDeeplinkURLError

/// 10. Cancellation from 3DS verification
case threeDSCancellation
}

static let unknownError = CoreSDKError(
Expand Down Expand Up @@ -64,7 +67,13 @@ enum CardClientError {
domain: domain,
errorDescription: "An invalid 3DS URL was returned. Contact developer.paypal.com/support."
)


static let threeDSecureCancellation = CoreSDKError(
code: Code.threeDSCancellation.rawValue,
domain: domain,
errorDescription: "3DS verification has been cancelled by the user."
)

static let noVaultTokenDataError = CoreSDKError(
code: Code.noVaultTokenDataError.rawValue,
domain: domain,
Expand Down
35 changes: 0 additions & 35 deletions Sources/CardPayments/CardVaultDelegate.swift

This file was deleted.

Loading

0 comments on commit c2178bc

Please sign in to comment.