diff --git a/CHANGELOG.md b/CHANGELOG.md index e9095de71..33bd396e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,39 @@ # PayPal iOS SDK Release Notes ## unreleased +* Breaking Changes + * PayPalNativePayments + * Remove entire PayPalNativePayments module + * PayPalWebPayments + * Replace delegate pattern with completion handlers and Swift concurrency + * Remove `PayPalWebCheckoutDelegate` and `PayPalVaultDelegate` + * Remove `start(request:)` method that uses delegate callbacks + * Remove `vault(vaultRequest:)` method that uses delegate callbacks + * Add `start(request:completion(PayPalWebCheckoutResult?, CoreSDKError?) -> Void)` to `PayPalWebCheckoutClient` + * Add `vault(vaultRequest:completion(PayPalVaultResult?, CoreSDKError?) -> Void)` to `PayPalWebCheckoutClient` + * Add `start(request:) async throws -> PayPalCheckoutResult` + * Add `vault(vaultRequest:) async throws -> PayPalVaultResult` + * CardPayments + * Replace delegate pattern with completion handlers and Swift concurrency + * Remove `CardDelegate` and `CardVaultDelegate` + * Remove `approveOrder(request:)` method that uses delegate callbacks + * Remove `vault(vaultRequest:)` method that uses delegate callbacks + * Add `approveOrder(request:completion:(CardResult?, CoreSDKError?) -> Void)` to `CardClient` + * Add `vault(request:completion:(CardVaultResult?, CoreSDKError?) -> Void)` to `CardClient` + * Add `approveOrder(request:) async throws -> CardResult` + * Add `vault(vaultRequest:) async throws -> CardVaultResult` * PayPalWebPayments * Deprecate `PayPalVaultRequest(url:setupTokenID:)` * Add `PayPalVaultRequest(setupTokenID:)` + * Rename `PayPalWebCheckoutClientError` to `PayPalError` + * Add `.checkoutCanceledError` and `vaultCanceledError` to `PayPalError` + * Add public static functions `isCheckoutCanceled(Error)` and `isVaultCanceled(Error)` to `PayPalError` to distinguish cancellation errors in PayPal flows. + * Make `PayPalError` public to expose cancellation error handling helpers +* CardPayments + * Rename `CardClientError` to `CardError` + * Add `threeDSecureCanceledError` to `CardError` + * Add public static function `isThreeDSecureCanceled(Error)` to `CardError` to distinguish cancellation error from threeDSecure verification + * Make `CardError` public to expose cancellation error handling helper ## 1.4.0 (2024-07-09) * PayPalNativePayments (DEPRECATED) diff --git a/Demo/Demo.xcodeproj/project.pbxproj b/Demo/Demo.xcodeproj/project.pbxproj index 32a29a8f7..74ad85889 100644 --- a/Demo/Demo.xcodeproj/project.pbxproj +++ b/Demo/Demo.xcodeproj/project.pbxproj @@ -52,17 +52,13 @@ 3BF999782A8AD072009CBDF2 /* CreatePaymentTokenView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF999772A8AD072009CBDF2 /* CreatePaymentTokenView.swift */; }; 3BF9997A2A8AE12C009CBDF2 /* PaymentTokenResultView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3BF999792A8AE12C009CBDF2 /* PaymentTokenResultView.swift */; }; 5301468C28918B4D00184F22 /* ApprovalResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5301468B28918B4D00184F22 /* ApprovalResult.swift */; }; - 536A5CA82898AA2A005C053D /* SwiftUINativeCheckoutDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 536A5CA72898AA2A005C053D /* SwiftUINativeCheckoutDemo.swift */; }; - 53B9E8EA28C93B4400719239 /* OrderRequestHelpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53B9E8E928C93B4400719239 /* OrderRequestHelpers.swift */; }; 8052E2A529B684BA00B33FBC /* PPRiskMagnes.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8052E2A229B684A600B33FBC /* PPRiskMagnes.xcframework */; }; 8052E2A629B684BA00B33FBC /* PPRiskMagnes.xcframework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 8052E2A229B684A600B33FBC /* PPRiskMagnes.xcframework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 806F1E4226B85369007A60E6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 806F1E4126B85369007A60E6 /* Assets.xcassets */; }; 80921C1D2710A0720040D76F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 80921C1C2710A0720040D76F /* LaunchScreen.storyboard */; }; - 80E4300C2AD82C8D003CA748 /* ShippingPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80E4300B2AD82C8D003CA748 /* ShippingPreference.swift */; }; 80F33CE826F8DE29006811B1 /* DemoMerchantAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80F33CE726F8DE29006811B1 /* DemoMerchantAPI.swift */; }; 80F33CED26F8E7A9006811B1 /* Order.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80F33CEC26F8E7A9006811B1 /* Order.swift */; }; 80F33CEF26F8E7CC006811B1 /* CreateOrderParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80F33CEE26F8E7CC006811B1 /* CreateOrderParams.swift */; }; - 80F33CF126F8E7D9006811B1 /* ProcessOrderParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80F33CF026F8E7D9006811B1 /* ProcessOrderParams.swift */; }; 80F33CF326F8EA50006811B1 /* DemoSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80F33CF226F8EA50006811B1 /* DemoSettings.swift */; }; BC6460CD2A12A2A0002B974B /* EmptyBodyParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC6460CC2A12A2A0002B974B /* EmptyBodyParams.swift */; }; BC9D4D2227C554090089E5B1 /* LaunchApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC9D4D2127C554090089E5B1 /* LaunchApp.swift */; }; @@ -84,17 +80,12 @@ CB1AC3BC2982BB130081AED6 /* PaymentButtons.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CB1AC3BA2982BB130081AED6 /* PaymentButtons.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CB1AC3C02982C4030081AED6 /* PayPalWebPayments.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB1AC3BF2982C4030081AED6 /* PayPalWebPayments.framework */; }; CB1AC3C12982C4030081AED6 /* PayPalWebPayments.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CB1AC3BF2982C4030081AED6 /* PayPalWebPayments.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CB1AC3C42982DBA10081AED6 /* PayPalNativePayments.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB1AC3C32982DBA10081AED6 /* PayPalNativePayments.framework */; }; - CB1AC3C52982DBA10081AED6 /* PayPalNativePayments.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CB1AC3C32982DBA10081AED6 /* PayPalNativePayments.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; CB1AC3C72982E32D0081AED6 /* FraudProtection.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CB1AC3C62982E32D0081AED6 /* FraudProtection.framework */; }; CB1AC3C82982E32D0081AED6 /* FraudProtection.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CB1AC3C62982E32D0081AED6 /* FraudProtection.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - CB34B32328BE3A9A001325B9 /* PayPalViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB34B32228BE3A9A001325B9 /* PayPalViewModel.swift */; }; CB9ED44C283FDA900081F4DE /* PaymentButtonEnums+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB9ED44B283FDA900081F4DE /* PaymentButtonEnums+Extension.swift */; }; CB9ED44E28411B120081F4DE /* SwiftUIPaymentButtonDemo.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB9ED44D28411B110081F4DE /* SwiftUIPaymentButtonDemo.swift */; }; - CBC16DD929ED90B600307117 /* UpdateOrderParams.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBC16DD829ED90B600307117 /* UpdateOrderParams.swift */; }; CBDEEA222989990200A460A6 /* CorePayments.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CBDEEA212989990200A460A6 /* CorePayments.framework */; }; CBDEEA232989990200A460A6 /* CorePayments.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = CBDEEA212989990200A460A6 /* CorePayments.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; - EE58CBCF2B5080C1003F2666 /* PayPalCheckout in Frameworks */ = {isa = PBXBuildFile; productRef = EE58CBCE2B5080C1003F2666 /* PayPalCheckout */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -119,7 +110,6 @@ CB1AC3BC2982BB130081AED6 /* PaymentButtons.framework in Embed Frameworks */, 8052E2A629B684BA00B33FBC /* PPRiskMagnes.xcframework in Embed Frameworks */, CB1AC3C82982E32D0081AED6 /* FraudProtection.framework in Embed Frameworks */, - CB1AC3C52982DBA10081AED6 /* PayPalNativePayments.framework in Embed Frameworks */, CB1AC3B92982AAD70081AED6 /* CardPayments.framework in Embed Frameworks */, ); name = "Embed Frameworks"; @@ -174,8 +164,6 @@ 3BF999792A8AE12C009CBDF2 /* PaymentTokenResultView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentTokenResultView.swift; sourceTree = ""; }; 5301468B28918B4D00184F22 /* ApprovalResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApprovalResult.swift; sourceTree = ""; }; 536A5CA22898A48C005C053D /* PayPalNativeCheckout.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PayPalNativeCheckout.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 536A5CA72898AA2A005C053D /* SwiftUINativeCheckoutDemo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUINativeCheckoutDemo.swift; sourceTree = ""; }; - 53B9E8E928C93B4400719239 /* OrderRequestHelpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OrderRequestHelpers.swift; sourceTree = ""; }; 8052E2A229B684A600B33FBC /* PPRiskMagnes.xcframework */ = {isa = PBXFileReference; expectedSignature = "AppleDeveloperProgram:XEZPTDFLAS:changching chi"; lastKnownFileType = wrapper.xcframework; name = PPRiskMagnes.xcframework; path = ../Frameworks/XCFrameworks/PPRiskMagnes.xcframework; sourceTree = ""; }; 805AB84E26B87A87003BEE0D /* PaymentsCore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PaymentsCore.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 806C7A812C000626000E85E8 /* Demo.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Demo.entitlements; sourceTree = ""; }; @@ -184,11 +172,9 @@ 806F1E4626B85369007A60E6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 80921C1C2710A0720040D76F /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = ""; }; 80DB6F1C26B89DDA00277E54 /* PayPal.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PayPal.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 80E4300B2AD82C8D003CA748 /* ShippingPreference.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShippingPreference.swift; sourceTree = ""; }; 80F33CE726F8DE29006811B1 /* DemoMerchantAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoMerchantAPI.swift; sourceTree = ""; }; 80F33CEC26F8E7A9006811B1 /* Order.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Order.swift; sourceTree = ""; }; 80F33CEE26F8E7CC006811B1 /* CreateOrderParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateOrderParams.swift; sourceTree = ""; }; - 80F33CF026F8E7D9006811B1 /* ProcessOrderParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProcessOrderParams.swift; sourceTree = ""; }; 80F33CF226F8EA50006811B1 /* DemoSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DemoSettings.swift; sourceTree = ""; }; BC6460CC2A12A2A0002B974B /* EmptyBodyParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyBodyParams.swift; sourceTree = ""; }; BC9D4D1527C54F3F0089E5B1 /* DemoUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DemoUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -212,10 +198,8 @@ CB1AC3BF2982C4030081AED6 /* PayPalWebPayments.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PayPalWebPayments.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CB1AC3C32982DBA10081AED6 /* PayPalNativePayments.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = PayPalNativePayments.framework; sourceTree = BUILT_PRODUCTS_DIR; }; CB1AC3C62982E32D0081AED6 /* FraudProtection.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = FraudProtection.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - CB34B32228BE3A9A001325B9 /* PayPalViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalViewModel.swift; sourceTree = ""; }; CB9ED44B283FDA900081F4DE /* PaymentButtonEnums+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PaymentButtonEnums+Extension.swift"; sourceTree = ""; }; CB9ED44D28411B110081F4DE /* SwiftUIPaymentButtonDemo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwiftUIPaymentButtonDemo.swift; sourceTree = ""; }; - CBC16DD829ED90B600307117 /* UpdateOrderParams.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateOrderParams.swift; sourceTree = ""; }; CBDEEA212989990200A460A6 /* CorePayments.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = CorePayments.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ @@ -227,11 +211,9 @@ CB1AC3B82982AAD70081AED6 /* CardPayments.framework in Frameworks */, CB1AC3C02982C4030081AED6 /* PayPalWebPayments.framework in Frameworks */, CB1AC3BB2982BB130081AED6 /* PaymentButtons.framework in Frameworks */, - EE58CBCF2B5080C1003F2666 /* PayPalCheckout in Frameworks */, CBDEEA222989990200A460A6 /* CorePayments.framework in Frameworks */, 8052E2A529B684BA00B33FBC /* PPRiskMagnes.xcframework in Frameworks */, CB1AC3C72982E32D0081AED6 /* FraudProtection.framework in Frameworks */, - CB1AC3C42982DBA10081AED6 /* PayPalNativePayments.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -321,7 +303,6 @@ 53B9E8E828C93B2B00719239 /* Helpers */ = { isa = PBXGroup; children = ( - 53B9E8E928C93B4400719239 /* OrderRequestHelpers.swift */, ); path = Helpers; sourceTree = ""; @@ -394,11 +375,8 @@ 80F33CEE26F8E7CC006811B1 /* CreateOrderParams.swift */, BC6460CC2A12A2A0002B974B /* EmptyBodyParams.swift */, 80F33CEC26F8E7A9006811B1 /* Order.swift */, - 80F33CF026F8E7D9006811B1 /* ProcessOrderParams.swift */, - CBC16DD829ED90B600307117 /* UpdateOrderParams.swift */, 3BDB348D2A7CB02C008100D7 /* CreateSetupTokenParam.swift */, 3BDB34912A7CB5DE008100D7 /* CreateSetupTokenResponse.swift */, - 80E4300B2AD82C8D003CA748 /* ShippingPreference.swift */, 3B22E8B92A842D8900962E34 /* PaymentTokenParam.swift */, 3B22E8BB2A84397600962E34 /* PaymentTokenResponse.swift */, ); @@ -436,7 +414,6 @@ BE4876A827567D4200802EAF /* ViewModels */ = { isa = PBXGroup; children = ( - CB34B32228BE3A9A001325B9 /* PayPalViewModel.swift */, 3B20273E2A89F24E0007907E /* CardVaultViewModel.swift */, 3BA0A58A2B1E240300330681 /* VaultViewModel.swift */, 3B2027402A8A72050007907E /* VaultState.swift */, @@ -476,7 +453,6 @@ 3BCCFE472A9D962E00C5102F /* CommonComponents */, 3B43290F2A8FD7FD00C5441A /* CardVaultViews */, CB9ED44D28411B110081F4DE /* SwiftUIPaymentButtonDemo.swift */, - 536A5CA72898AA2A005C053D /* SwiftUINativeCheckoutDemo.swift */, 3BCCFE4A2A9D985F00C5102F /* FeatureSelectionView.swift */, BE8117672B080472009867B9 /* CurrentState.swift */, ); @@ -510,7 +486,6 @@ ); name = Demo; packageProductDependencies = ( - EE58CBCE2B5080C1003F2666 /* PayPalCheckout */, ); productName = Demo; productReference = 806F1E3526B85367007A60E6 /* Demo.app */; @@ -563,7 +538,6 @@ ); mainGroup = 806F1E2C26B85367007A60E6; packageReferences = ( - EE58CBCD2B5080C1003F2666 /* XCRemoteSwiftPackageReference "paypalcheckout-ios" */, ); productRefGroup = 806F1E3626B85367007A60E6 /* Products */; projectDirPath = ""; @@ -610,7 +584,6 @@ 3B80D5102A291CB100D2EAC4 /* ClientIDResponse.swift in Sources */, 3BCCFE462A9D47AC00C5102F /* CardExtensions.swift in Sources */, BE5898952B2B91F800AA196E /* LabelViewText.swift in Sources */, - CB34B32328BE3A9A001325B9 /* PayPalViewModel.swift in Sources */, 3BC622072A97115700251B85 /* RoundedBlueButtonStyle.swift in Sources */, 3B4DD9A22A8982B000F4A716 /* CardFormView.swift in Sources */, BED04233271084DF00C80954 /* CardFormatter.swift in Sources */, @@ -620,8 +593,6 @@ 3B2027432A8A95EF0007907E /* SetupTokenResultView.swift in Sources */, 3BF999762A8AC093009CBDF2 /* UpdateSetupTokenResultView.swift in Sources */, BECD84A027036DC2007CCAE4 /* Environment.swift in Sources */, - 80F33CF126F8E7D9006811B1 /* ProcessOrderParams.swift in Sources */, - 53B9E8EA28C93B4400719239 /* OrderRequestHelpers.swift in Sources */, 3BCCFE4B2A9D985F00C5102F /* FeatureSelectionView.swift in Sources */, CB9ED44C283FDA900081F4DE /* PaymentButtonEnums+Extension.swift in Sources */, 3BB60B552B1FA00C00A298CF /* PayPalVaultViewModel.swift in Sources */, @@ -634,7 +605,6 @@ 80F33CF326F8EA50006811B1 /* DemoSettings.swift in Sources */, 3BA56FE72A9DC9D70081D14F /* CardPaymentViewModel.swift in Sources */, 3BA5700B2AA13C1C0081D14F /* CoreConfigManager.swift in Sources */, - 80E4300C2AD82C8D003CA748 /* ShippingPreference.swift in Sources */, 80F33CE826F8DE29006811B1 /* DemoMerchantAPI.swift in Sources */, 80F33CEF26F8E7CC006811B1 /* CreateOrderParams.swift in Sources */, BECD84A227036DDB007CCAE4 /* Intent.swift in Sources */, @@ -650,7 +620,6 @@ 3BA56FF22A9DCD440081D14F /* CardApprovalResultView.swift in Sources */, 3B3C51142B20F7CE009125FE /* PayPalVaultResultView.swift in Sources */, 3BA570012AA052E80081D14F /* PayPalWebPaymentsView.swift in Sources */, - CBC16DD929ED90B600307117 /* UpdateOrderParams.swift in Sources */, 3BA56FFA2A9FE4180081D14F /* CardOrderCompletionResultView.swift in Sources */, 3BA56FF42A9DCD790081D14F /* CardPaymentView.swift in Sources */, BE9F36D82745490400AFC7DA /* FloatingLabelTextField.swift in Sources */, @@ -659,7 +628,6 @@ 3BA570072AA0DF330081D14F /* PayPalWebButtonsView.swift in Sources */, 3BCCFE492A9D96CA00C5102F /* DemoApp.swift in Sources */, 3BA56FEC2A9DCBF30081D14F /* CreateOrderCardPaymentView.swift in Sources */, - 536A5CA82898AA2A005C053D /* SwiftUINativeCheckoutDemo.swift in Sources */, 3B20273F2A89F24E0007907E /* CardVaultViewModel.swift in Sources */, BC6460CD2A12A2A0002B974B /* EmptyBodyParams.swift in Sources */, 3BF999782A8AD072009CBDF2 /* CreatePaymentTokenView.swift in Sources */, @@ -934,25 +902,6 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - EE58CBCD2B5080C1003F2666 /* XCRemoteSwiftPackageReference "paypalcheckout-ios" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/paypal/paypalcheckout-ios"; - requirement = { - kind = upToNextMajorVersion; - minimumVersion = 1.3.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - EE58CBCE2B5080C1003F2666 /* PayPalCheckout */ = { - isa = XCSwiftPackageProductDependency; - package = EE58CBCD2B5080C1003F2666 /* XCRemoteSwiftPackageReference "paypalcheckout-ios" */; - productName = PayPalCheckout; - }; -/* End XCSwiftPackageProductDependency section */ }; rootObject = 806F1E2D26B85367007A60E6 /* Project object */; } diff --git a/Demo/Demo/Helpers/OrderRequestHelpers.swift b/Demo/Demo/Helpers/OrderRequestHelpers.swift deleted file mode 100644 index fe98cd36a..000000000 --- a/Demo/Demo/Helpers/OrderRequestHelpers.swift +++ /dev/null @@ -1,99 +0,0 @@ -import Foundation - -enum OrderRequestHelpers { - - static let orderAmount = 100.0 - static let currencyCode = "USD" - - static func getOrderRequest(_ shippingPreference: ShippingPreference) -> CreateOrderParams { - return CreateOrderParams( - applicationContext: ApplicationContext(userAction: "PAY_NOW", shippingPreference: shippingPreference.rawValue), - // TODO: - All demo features should support both AUTHORIZE & CAPTURE testing - intent: "AUTHORIZE", - purchaseUnits: [ - PurchaseUnit( - shipping: Shipping( - address: Shipping.Address( - addressLine1: "345 Sesame Street", - addressLine2: "Apt 9", - adminArea2: "New York City", - adminArea1: "NY", - countryCode: "US", - postalCode: "32422" - ), - name: Shipping.Name(fullName: "Cookie Monster"), - options: shippingPreference == .getFromFile ? getShippingMethods() : nil - ), - payee: Payee(merchantID: "X5XAHHCG636FA", emailAddress: "merchant@email.com"), - amount: Amount(currencyCode: "USD", value: String(orderAmount), breakdown: nil) - ) - ] - ) - } - - static func getAmount(shipping: Double = 3.99) -> Amount { - Amount( - currencyCode: currencyCode, - value: String(orderAmount + shipping), - breakdown: Amount.Breakdown( - shipping: ItemTotal(value: String(shipping), currencyValue: String(shipping), currencyCode: currencyCode), - itemTotal: ItemTotal( value: String(orderAmount), currencyValue: String(orderAmount), currencyCode: currencyCode) - ) - ) - } - - static func getShippingMethods(selectedID: String = "ShipTest1") -> [ShippingOption]? { - let shipOption1 = ShippingOption( - selected: false, - id: "ShipTest1", - amount: ItemTotal(value: "3.99", currencyValue: "3.99", currencyCode: currencyCode), - label: "Standard Shipping", - type: "SHIPPING" - ) - - let shipOption2 = ShippingOption( - selected: false, - id: "ShipTest2", - amount: ItemTotal(value: "0.99", currencyValue: "0.99", currencyCode: currencyCode), - label: "Cheap Shipping", - type: "SHIPPING" - ) - - let shipOption3 = ShippingOption( - selected: false, - id: "ShipTest3", - amount: ItemTotal(value: "7.99", currencyValue: "7.99", currencyCode: currencyCode), - label: "Express Shipping", - type: "SHIPPING" - ) - - let shipOption4 = ShippingOption( - selected: false, - id: "PickTest1", - amount: ItemTotal(value: "0", currencyValue: "0", currencyCode: currencyCode), - label: "Pick up from store", - type: "PICKUP" - ) - - let shipOption5 = ShippingOption( - selected: false, - id: "PickTest2", - amount: ItemTotal(value: "0", currencyValue: "0", currencyCode: currencyCode), - label: "Pick up from warehouse", - type: "PICKUP" - ) - - var shippingOptions = [shipOption1, shipOption2, shipOption3, shipOption4, shipOption5] - if let index = shippingOptions.firstIndex(where: { $0.id == selectedID }) { - let shippingMethod = shippingOptions[index] - shippingOptions[index] = ShippingOption( - selected: true, - id: selectedID, - amount: shippingMethod.amount, - label: shippingMethod.label, - type: shippingMethod.type - ) - } - return shippingOptions - } -} diff --git a/Demo/Demo/Models/ProcessOrderParams.swift b/Demo/Demo/Models/ProcessOrderParams.swift deleted file mode 100644 index 36f8ca995..000000000 --- a/Demo/Demo/Models/ProcessOrderParams.swift +++ /dev/null @@ -1,6 +0,0 @@ -struct ProcessOrderParams: Codable { - - let orderId: String - let intent: String - let countryCode: String -} diff --git a/Demo/Demo/Models/ShippingPreference.swift b/Demo/Demo/Models/ShippingPreference.swift deleted file mode 100644 index d42a3d228..000000000 --- a/Demo/Demo/Models/ShippingPreference.swift +++ /dev/null @@ -1,7 +0,0 @@ -enum ShippingPreference: String, CaseIterable, Identifiable { - case noShipping = "NO_SHIPPING" - case providedAddress = "SET_PROVIDED_ADDRESS" - case getFromFile = "GET_FROM_FILE" - - var id: ShippingPreference { self } -} diff --git a/Demo/Demo/Models/UpdateOrderParams.swift b/Demo/Demo/Models/UpdateOrderParams.swift deleted file mode 100644 index e755726ac..000000000 --- a/Demo/Demo/Models/UpdateOrderParams.swift +++ /dev/null @@ -1,68 +0,0 @@ -class UpdateOrderParams { - - let orderID: String - let updateOperations: [UpdateOrderOperation] - - init(orderID: String, shippingMethods: [ShippingOption]?, amount: Amount) { - self.orderID = orderID - self.updateOperations = [ - UpdateOrderOperation( - operation: "replace", - path: "/purchase_units/@reference_id=='default'/amount", - value: amount - ), - UpdateOrderOperation( - operation: "replace", - path: "/purchase_units/@reference_id=='default'/shipping/options", - value: shippingMethods - ) - ] - } -} - -class UpdateOrderOperation: Encodable { - - private enum CodingKeys: String, CodingKey { - case operation = "op" - case path - case value - } - - let operation: String - let referenceId: String? - let path: String - let value: PatchableValue? - - init( - operation: String, - path: String, - referenceId: String? = nil, - value: Encodable? = nil - ) { - self.operation = operation - self.referenceId = referenceId - self.path = path - - if let value = value { - self.value = PatchableValue(value: value) - } else { - self.value = nil - } - } - - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(operation, forKey: .operation) - try container.encode(path, forKey: .path) - try container.encodeIfPresent(value, forKey: .value) - } -} - -struct PatchableValue: Encodable { - - let value: Encodable - - func encode(to encoder: Encoder) throws { - try value.encode(to: encoder) - } -} diff --git a/Demo/Demo/Networking/DemoMerchantAPI.swift b/Demo/Demo/Networking/DemoMerchantAPI.swift index a26b817ec..8bf6716d9 100644 --- a/Demo/Demo/Networking/DemoMerchantAPI.swift +++ b/Demo/Demo/Networking/DemoMerchantAPI.swift @@ -116,18 +116,6 @@ final class DemoMerchantAPI { return try parse(from: data) } - /// This function replicates a way a merchant may go about patching an order on their server and is not part of the SDK flow. - /// - Parameters: - /// - updateOrderParams: the parameters to update the order with - /// - Throws: an error explaining why patching the order failed - func updateOrder(_ updateOrderParams: UpdateOrderParams, selectedMerchantIntegration: MerchantIntegration) async throws { - guard let url = buildBaseURL(with: "/orders/" + updateOrderParams.orderID) else { - throw URLResponseError.invalidURL - } - let urlRequest = buildURLRequest(method: "PATCH", url: url, body: updateOrderParams.updateOperations) - _ = try await data(for: urlRequest) - } - /// This function fetches a clientID to initialize any module of the SDK /// - Parameters: /// - environment: the current environment diff --git a/Demo/Demo/SwiftUIComponents/FeatureSelectionView.swift b/Demo/Demo/SwiftUIComponents/FeatureSelectionView.swift index 7abd2b72f..77a34eb99 100644 --- a/Demo/Demo/SwiftUIComponents/FeatureSelectionView.swift +++ b/Demo/Demo/SwiftUIComponents/FeatureSelectionView.swift @@ -49,11 +49,6 @@ struct FeatureSelectionView: View { } label: { Text("PayPal Vaulting") } - NavigationLink { - SwiftUINativeCheckoutDemo() - } label: { - Text("Native Checkout") - } NavigationLink { SwiftUIPaymentButtonDemo() } label: { diff --git a/Demo/Demo/SwiftUIComponents/PayPalVaultViews/PayPalVaultView.swift b/Demo/Demo/SwiftUIComponents/PayPalVaultViews/PayPalVaultView.swift index e1954dc55..5d0de7e71 100644 --- a/Demo/Demo/SwiftUIComponents/PayPalVaultViews/PayPalVaultView.swift +++ b/Demo/Demo/SwiftUIComponents/PayPalVaultViews/PayPalVaultView.swift @@ -15,13 +15,18 @@ struct PayPalVaultView: View { ) SetupTokenResultView(vaultViewModel: paypalVaultViewModel) if let setupTokenID = paypalVaultViewModel.state.setupToken?.id { - Button("Vault PayPal") { - Task { - await paypalVaultViewModel.vault(setupTokenID: setupTokenID) + ZStack { + Button("Vault PayPal") { + Task { + await paypalVaultViewModel.vault(setupTokenID: setupTokenID) + } + } + .buttonStyle(RoundedBlueButtonStyle()) + .padding() + if case .loading = paypalVaultViewModel.state.paypalVaultTokenResponse { + CircularProgressView() } } - .buttonStyle(RoundedBlueButtonStyle()) - .padding() } PayPalVaultResultView(viewModel: paypalVaultViewModel) if let paypalVaultResult = paypalVaultViewModel.state.paypalVaultToken { diff --git a/Demo/Demo/SwiftUIComponents/PayPalWebPayments/PayPalWebButtonsView.swift b/Demo/Demo/SwiftUIComponents/PayPalWebPayments/PayPalWebButtonsView.swift index 8516c0aee..03ad1a02b 100644 --- a/Demo/Demo/SwiftUIComponents/PayPalWebPayments/PayPalWebButtonsView.swift +++ b/Demo/Demo/SwiftUIComponents/PayPalWebPayments/PayPalWebButtonsView.swift @@ -24,19 +24,25 @@ struct PayPalWebButtonsView: View { Text("Pay Later").tag(PayPalWebCheckoutFundingSource.paylater) } .pickerStyle(SegmentedPickerStyle()) - - switch selectedFundingSource { - case .paypalCredit: - PayPalCreditButton.Representable(color: .black, size: .full) { - payPalWebViewModel.paymentButtonTapped(funding: .paypalCredit) - } - case .paylater: - PayPalPayLaterButton.Representable(color: .silver, edges: .softEdges, size: .full) { - payPalWebViewModel.paymentButtonTapped(funding: .paylater) + ZStack { + switch selectedFundingSource { + case .paypalCredit: + PayPalCreditButton.Representable(color: .black, size: .full) { + payPalWebViewModel.paymentButtonTapped(funding: .paypalCredit) + } + case .paylater: + PayPalPayLaterButton.Representable(color: .silver, edges: .softEdges, size: .full) { + payPalWebViewModel.paymentButtonTapped(funding: .paylater) + } + case .paypal: + PayPalButton.Representable(color: .blue, size: .full) { + payPalWebViewModel.paymentButtonTapped(funding: .paypal) + } } - case .paypal: - PayPalButton.Representable(color: .blue, size: .full) { - payPalWebViewModel.paymentButtonTapped(funding: .paypal) + if payPalWebViewModel.state == .loading && + payPalWebViewModel.checkoutResult == nil && + payPalWebViewModel.orderID != nil { + CircularProgressView() } } } diff --git a/Demo/Demo/SwiftUIComponents/PayPalWebPayments/PayPalWebCreateOrderView.swift b/Demo/Demo/SwiftUIComponents/PayPalWebPayments/PayPalWebCreateOrderView.swift index ba995324d..4b5adc2dd 100644 --- a/Demo/Demo/SwiftUIComponents/PayPalWebPayments/PayPalWebCreateOrderView.swift +++ b/Demo/Demo/SwiftUIComponents/PayPalWebPayments/PayPalWebCreateOrderView.swift @@ -40,7 +40,7 @@ struct PayPalWebCreateOrderView: View { } } .buttonStyle(RoundedBlueButtonStyle()) - if payPalWebViewModel.state == .loading && payPalWebViewModel.checkoutResult == nil { + if payPalWebViewModel.state == .loading && payPalWebViewModel.checkoutResult == nil && payPalWebViewModel.orderID == nil { CircularProgressView() } } diff --git a/Demo/Demo/SwiftUIComponents/PayPalWebPayments/PayPalWebViewModel.swift b/Demo/Demo/SwiftUIComponents/PayPalWebPayments/PayPalWebViewModel.swift index 5bd9bfad7..b33b8ce19 100644 --- a/Demo/Demo/SwiftUIComponents/PayPalWebPayments/PayPalWebViewModel.swift +++ b/Demo/Demo/SwiftUIComponents/PayPalWebPayments/PayPalWebViewModel.swift @@ -3,7 +3,7 @@ import CorePayments import PayPalWebPayments import FraudProtection -class PayPalWebViewModel: ObservableObject, PayPalWebCheckoutDelegate { +class PayPalWebViewModel: ObservableObject { @Published var state: CurrentState = .idle @Published var intent: Intent = .authorize @@ -62,8 +62,8 @@ class PayPalWebViewModel: ObservableObject, PayPalWebCheckoutDelegate { func paymentButtonTapped(funding: PayPalWebCheckoutFundingSource) { Task { do { + self.updateState(.loading) payPalWebCheckoutClient = try await getPayPalClient() - payPalWebCheckoutClient?.delegate = self guard let payPalWebCheckoutClient else { print("Error initializing PayPalWebCheckoutClient") return @@ -71,7 +71,19 @@ class PayPalWebViewModel: ObservableObject, PayPalWebCheckoutDelegate { if let orderID { let payPalRequest = PayPalWebCheckoutRequest(orderID: orderID, fundingSource: funding) - payPalWebCheckoutClient.start(request: payPalRequest) + payPalWebCheckoutClient.start(request: payPalRequest) { result, error in + if let error { + if PayPalError.isCheckoutCanceled(error) { + print("Canceled") + self.updateState(.idle) + } else { + self.updateState(.error(message: error.localizedDescription)) + } + } else { + self.updateState(.success) + self.checkoutResult = result + } + } } updateState(.success) } catch { @@ -131,22 +143,4 @@ class PayPalWebViewModel: ObservableObject, PayPalWebCheckoutDelegate { self.state = state } } - - // MARK: - PayPalWeb Checkout Delegate - - func payPal( - _ payPalClient: PayPalWebCheckoutClient, - didFinishWithResult result: PayPalWebCheckoutResult - ) { - updateState(.success) - checkoutResult = result - } - - func payPal(_ payPalClient: PayPalWebCheckoutClient, didFinishWithError error: CoreSDKError) { - updateState(.error(message: error.localizedDescription)) - } - - func payPalDidCancel(_ payPalClient: PayPalWebCheckoutClient) { - print("PayPal Checkout Canceled") - } } diff --git a/Demo/Demo/SwiftUIComponents/SwiftUINativeCheckoutDemo.swift b/Demo/Demo/SwiftUIComponents/SwiftUINativeCheckoutDemo.swift deleted file mode 100644 index dd3c7e4d0..000000000 --- a/Demo/Demo/SwiftUIComponents/SwiftUINativeCheckoutDemo.swift +++ /dev/null @@ -1,113 +0,0 @@ -import Foundation -import SwiftUI -import PaymentButtons - -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -struct SwiftUINativeCheckoutDemo: View { - - @StateObject var viewModel = PayPalViewModel() - - @State var shippingTypeSelection = ShippingPreference.noShipping - - var body: some View { - switch viewModel.state { - case .initial: - getClientIDView() - case .loading(let content): - loadingView(content) - case let .mainContent(title, content, isFlowComplete): - checkoutView(title, content, isFlowComplete) - case .error(let message): - errorView("Error \(message)") - } - } - - func checkoutView(_ title: String, _ content: String, _ isFlowComplete: Bool) -> some View { - VStack(spacing: 16) { - HStack { - Text("Shipping type selection:") - Spacer() - Picker("", selection: $shippingTypeSelection) { - ForEach(ShippingPreference.allCases, id: \.self) { type in - Text(type.rawValue) - } - } - } - .padding(16) - Divider() - VStack { - Text(title) - .font(.system(size: 24)) - .frame(maxWidth: .infinity, alignment: .leading) - .padding([.leading, .bottom], 16) - Text(content) - .font(.system(size: 16)) - .frame(maxWidth: .infinity, alignment: .leading) - .padding([.leading, .trailing, .bottom], 16) - } - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top) - VStack { - Button(isFlowComplete ? "Try Again" : "Start Checkout" ) { - isFlowComplete ? viewModel.retry() : startNativeCheckout() - } - .foregroundColor(.white) - .padding() - .frame(maxWidth: .infinity) - .background(.blue) - .cornerRadius(10) - .padding(.horizontal, 16) - } - .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .bottom) - } - .frame(maxHeight: .infinity) - .padding(.top, 32) - } - - func getClientIDView() -> some View { - NavigationView { - VStack(spacing: 16) { - Button("Get ClientID") { - viewModel.getClientID() - } - .foregroundColor(.white) - .padding() - .frame(maxWidth: .infinity) - .background(.blue) - .cornerRadius(10) - .padding(.horizontal, 16) - } - } - } - - func loadingView(_ content: String) -> some View { - VStack(spacing: 16) { - ProgressView(content) - .progressViewStyle(CircularProgressViewStyle()) - } - } - - func errorView(_ message: String) -> some View { - VStack(spacing: 16) { - Text(message) - } - } - - func startNativeCheckout() { - switch shippingTypeSelection { - case .noShipping: - viewModel.checkoutWithNoShipping() - case .providedAddress: - viewModel.checkoutWithProvidedAddress() - case .getFromFile: - viewModel.checkoutWithGetFromFile() - } - } -} - -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -struct SwiftUINativeCheckoutDemo_Preview: PreviewProvider { - - static var previews: some View { - SwiftUINativeCheckoutDemo() - } -} diff --git a/Demo/Demo/ViewModels/CardPaymentViewModel.swift b/Demo/Demo/ViewModels/CardPaymentViewModel.swift index 7f6ab7ca7..d18aa0017 100644 --- a/Demo/Demo/ViewModels/CardPaymentViewModel.swift +++ b/Demo/Demo/ViewModels/CardPaymentViewModel.swift @@ -3,7 +3,7 @@ import CardPayments import CorePayments import FraudProtection -class CardPaymentViewModel: ObservableObject, CardDelegate { +class CardPaymentViewModel: ObservableObject { @Published var state = CardPaymentState() private var payPalDataCollector: PayPalDataCollector? @@ -117,16 +117,31 @@ class CardPaymentViewModel: ObservableObject, CardDelegate { let config = try await configManager.getCoreConfig() cardClient = CardClient(config: config) payPalDataCollector = PayPalDataCollector(config: config) - cardClient?.delegate = self let cardRequest = CardRequest(orderID: orderID, card: card, sca: sca) - cardClient?.approveOrder(request: cardRequest) + cardClient?.approveOrder(request: cardRequest) { result, error in + if let error { + if CardError.isThreeDSecureCanceled(error) { + self.setApprovalCancelResult() + } else { + self.setApprovalFailureResult(vaultError: error) + } + } else if let result { + self.setApprovalSuccessResult( + approveResult: CardPaymentState.CardResult( + id: result.orderID, + status: result.status, + didAttemptThreeDSecureAuthentication: result.didAttemptThreeDSecureAuthentication + ) + ) + } + } } catch { - self.state.approveResultResponse = .error(message: error.localizedDescription) + setApprovalFailureResult(vaultError: error) print("failed in checkout with card. \(error.localizedDescription)") } } - func approveResultSuccessResult(approveResult: CardPaymentState.CardResult) { + func setApprovalSuccessResult(approveResult: CardPaymentState.CardResult) { DispatchQueue.main.async { self.state.approveResultResponse = .loaded( approveResult @@ -134,44 +149,16 @@ class CardPaymentViewModel: ObservableObject, CardDelegate { } } - func setUpdateSetupTokenFailureResult(vaultError: CorePayments.CoreSDKError) { + func setApprovalFailureResult(vaultError: Error) { DispatchQueue.main.async { self.state.approveResultResponse = .error(message: vaultError.localizedDescription) } } - // MARK: - Card Delegate - - func card(_ cardClient: CardPayments.CardClient, didFinishWithResult result: CardPayments.CardResult) { - approveResultSuccessResult( - approveResult: CardPaymentState.CardResult( - id: result.orderID, - status: result.status, - didAttemptThreeDSecureAuthentication: result.didAttemptThreeDSecureAuthentication - ) - ) - } - - func card(_ cardClient: CardPayments.CardClient, didFinishWithError error: CorePayments.CoreSDKError) { - print("Error here") - DispatchQueue.main.async { - self.state.approveResultResponse = .error(message: error.localizedDescription) - } - } - - func cardDidCancel(_ cardClient: CardPayments.CardClient) { - print("Card Payment Canceled") + func setApprovalCancelResult() { + print("Canceled") DispatchQueue.main.async { self.state.approveResultResponse = .idle - self.state.approveResult = nil } } - - func cardThreeDSecureWillLaunch(_ cardClient: CardPayments.CardClient) { - print("About to launch 3DS") - } - - func cardThreeDSecureDidFinish(_ cardClient: CardPayments.CardClient) { - print("Finished 3DS") - } } diff --git a/Demo/Demo/ViewModels/CardVaultViewModel.swift b/Demo/Demo/ViewModels/CardVaultViewModel.swift index 8b5f06f6a..562d59e75 100644 --- a/Demo/Demo/ViewModels/CardVaultViewModel.swift +++ b/Demo/Demo/ViewModels/CardVaultViewModel.swift @@ -2,7 +2,7 @@ import Foundation import CardPayments import CorePayments -class CardVaultViewModel: VaultViewModel, CardVaultDelegate { +class CardVaultViewModel: VaultViewModel { let configManager = CoreConfigManager(domain: "Card Vault") @@ -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)") } } @@ -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( @@ -42,35 +47,13 @@ class CardVaultViewModel: VaultViewModel, CardVaultDelegate { ) ) } else if let vaultError { - self.state.updateSetupTokenResponse = .error(message: vaultError.localizedDescription) + if CardError.isThreeDSecureCanceled(vaultError) { + print("Canceled") + self.state.updateSetupTokenResponse = .idle + } else { + self.state.updateSetupTokenResponse = .error(message: vaultError.localizedDescription) + } } } } - - // 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") - } } diff --git a/Demo/Demo/ViewModels/PayPalVaultViewModel.swift b/Demo/Demo/ViewModels/PayPalVaultViewModel.swift index 61cfccfcc..f796330ed 100644 --- a/Demo/Demo/ViewModels/PayPalVaultViewModel.swift +++ b/Demo/Demo/ViewModels/PayPalVaultViewModel.swift @@ -2,7 +2,7 @@ import UIKit import PayPalWebPayments import CorePayments -class PayPalVaultViewModel: VaultViewModel, PayPalVaultDelegate { +class PayPalVaultViewModel: VaultViewModel { let configManager = CoreConfigManager(domain: "PayPal Vault") @@ -13,36 +13,30 @@ class PayPalVaultViewModel: VaultViewModel, PayPalVaultDelegate { do { let config = try await configManager.getCoreConfig() let paypalClient = PayPalWebCheckoutClient(config: config) - paypalClient.vaultDelegate = self let vaultRequest = PayPalVaultRequest(setupTokenID: setupTokenID) - paypalClient.vault(vaultRequest) + paypalClient.vault(vaultRequest) { result, error in + if let error { + if PayPalError.isVaultCanceled(error) { + DispatchQueue.main.async { + print("Canceled") + self.state.paypalVaultTokenResponse = .idle + } + } else { + DispatchQueue.main.async { + self.state.paypalVaultTokenResponse = .error(message: error.localizedDescription) + } + } + } else if let result { + DispatchQueue.main.async { + self.state.paypalVaultTokenResponse = .loaded(result) + } + } + } } catch { print("Error in vaulting PayPal Payment") + DispatchQueue.main.async { + self.state.paypalVaultTokenResponse = .error(message: error.localizedDescription) + } } } - - // MARK: - PayPalVault Delegate - - func paypal( - _ paypalWebClient: PayPalWebPayments.PayPalWebCheckoutClient, - didFinishWithVaultResult paypalVaultResult: PayPalVaultResult - ) { - DispatchQueue.main.async { - self.state.paypalVaultTokenResponse = .loaded(paypalVaultResult) - } - } - - func paypal( - _ paypalWebClient: PayPalWebPayments.PayPalWebCheckoutClient, - didFinishWithVaultError vaultError: CorePayments.CoreSDKError - ) { - - DispatchQueue.main.async { - self.state.paypalVaultTokenResponse = .error(message: vaultError.localizedDescription) - } - } - - func paypalDidCancel(_ payPalWebClient: PayPalWebCheckoutClient) { - print("PayPal Checkout Canceled") - } } diff --git a/Demo/Demo/ViewModels/PayPalViewModel.swift b/Demo/Demo/ViewModels/PayPalViewModel.swift deleted file mode 100644 index f004a97bf..000000000 --- a/Demo/Demo/ViewModels/PayPalViewModel.swift +++ /dev/null @@ -1,144 +0,0 @@ -import Foundation -import PayPalNativePayments -import CorePayments - -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -class PayPalViewModel: ObservableObject { - - enum State { - case initial - case loading(content: String) - case mainContent(title: String, content: String, flowComplete: Bool) - case error(message: String) - } - - @Published private(set) var state = State.initial - @Published var selectedMerchantIntegration: MerchantIntegration = .direct - private var payPalClient: PayPalNativeCheckoutClient? - private var shippingPreference: ShippingPreference = .noShipping - private var orderID = "" - - func getClientID() { - state = .loading(content: "Getting clientID") - Task { - guard let clientID = await getClientID() else { - publishStateToMainThread(.error(message: "Unable to fetch clientID")) - return - } - payPalClient = PayPalNativeCheckoutClient(config: CoreConfig(clientID: clientID, environment: CorePayments.Environment.sandbox)) - payPalClient?.delegate = self - payPalClient?.shippingDelegate = self - publishStateToMainThread(.mainContent(title: "ClientID", content: clientID, flowComplete: false)) - } - } - - func retry() { - payPalClient = nil - state = .initial - shippingPreference = .noShipping - } - - func checkoutWithNoShipping() { - checkout(ShippingPreference.noShipping) - } - - func checkoutWithProvidedAddress() { - checkout(ShippingPreference.providedAddress) - } - - func checkoutWithGetFromFile() { - checkout(ShippingPreference.getFromFile) - } - - private func checkout(_ shippingPreference: ShippingPreference) { - state = .loading(content: "Initializing checkout") - Task { - do { - orderID = try await self.getOrderID(shippingPreference) - self.shippingPreference = shippingPreference - - let request = PayPalNativeCheckoutRequest(orderID: orderID) - await self.payPalClient?.start(request: request) - } catch let error { - publishStateToMainThread(.mainContent(title: "Error", content: "\(error.localizedDescription)", flowComplete: true)) - } - } - } - - private func getOrderID(_ shippingPreference: ShippingPreference) async throws -> String { - let order = try await DemoMerchantAPI.sharedService.createOrder( - orderParams: OrderRequestHelpers.getOrderRequest(shippingPreference), - selectedMerchantIntegration: selectedMerchantIntegration - ) - return order.id - } - - func getClientID() async -> String? { - await DemoMerchantAPI.sharedService.getClientID( - environment: DemoSettings.environment, selectedMerchantIntegration: selectedMerchantIntegration - ) - } - - private func publishStateToMainThread(_ state: State) { - DispatchQueue.main.async { - self.state = state - } - } -} - -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -extension PayPalViewModel: PayPalNativeCheckoutDelegate { - - func paypal(_ payPalClient: PayPalNativeCheckoutClient, didFinishWithResult result: PayPalNativeCheckoutResult) { - publishStateToMainThread(.mainContent(title: "Complete", content: "OrderId: \(result.orderID)", flowComplete: true)) - } - - func paypal(_ payPalClient: PayPalNativeCheckoutClient, didFinishWithError error: CoreSDKError) { - publishStateToMainThread(.mainContent(title: "Error", content: "\(error.localizedDescription)", flowComplete: true)) - } - - func paypalDidCancel(_ payPalClient: PayPalNativeCheckoutClient) { - publishStateToMainThread(.mainContent(title: "Cancelled", content: "User Cancelled", flowComplete: true)) - } - - func paypalWillStart(_ payPalClient: PayPalNativeCheckoutClient) { - publishStateToMainThread(.mainContent(title: "Starting", content: "PayPal is about to start", flowComplete: true)) - } -} - -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -extension PayPalViewModel: PayPalNativeShippingDelegate { - - func paypal( - _ payPalClient: PayPalNativeCheckoutClient, - didShippingAddressChange shippingAddress: PayPalNativeShippingAddress, - withAction shippingActions: PayPalNativePaysheetActions - ) { - publishStateToMainThread(.mainContent(title: "User action", content: "Shipping address changed", flowComplete: false)) - if shippingAddress.adminArea1?.isEmpty ?? true || shippingAddress.adminArea1 == "NV" { - shippingActions.reject() - } else { - shippingActions.approve() - } - } - - func paypal( - _ payPalClient: PayPalNativeCheckoutClient, - didShippingMethodChange shippingMethod: PayPalNativeShippingMethod, - withAction shippingActions: PayPalNativePaysheetActions - ) { - publishStateToMainThread(.mainContent(title: "User action", content: "Shipping method changed", flowComplete: false)) - Task { - do { - let shippingMethods = OrderRequestHelpers.getShippingMethods(selectedID: shippingMethod.id) - let amount = OrderRequestHelpers.getAmount(shipping: Double(shippingMethod.value ?? "0.0") ?? 0.0) - let params = UpdateOrderParams(orderID: orderID, shippingMethods: shippingMethods, amount: amount) - try await DemoMerchantAPI.sharedService.updateOrder(params, selectedMerchantIntegration: selectedMerchantIntegration) - shippingActions.approve() - } catch let error { - shippingActions.reject() - publishStateToMainThread(.mainContent(title: "Error", content: "\(error.localizedDescription)", flowComplete: true)) - } - } - } -} diff --git a/Demo/Settings.bundle/Root.plist b/Demo/Settings.bundle/Root.plist deleted file mode 100644 index 278b3707f..000000000 --- a/Demo/Settings.bundle/Root.plist +++ /dev/null @@ -1,89 +0,0 @@ - - - - - StringsTable - Root - PreferenceSpecifiers - - - Type - PSGroupSpecifier - Title - Settings - - - Type - PSMultiValueSpecifier - Title - Environment - Key - environment - DefaultValue - sandbox - Titles - - Sandbox - Live - - Values - - sandbox - live - - - - Type - PSMultiValueSpecifier - Title - Intent - Key - intent - DefaultValue - capture - Titles - - Capture - Authorize - - Values - - capture - authorize - - - - Type - PSGroupSpecifier - Title - Feature - - - Type - PSMultiValueSpecifier - Title - Demo Type - Key - demo_type - DefaultValue - - Titles - - Card - CardVault - PayPal Web Checkout - Payment Button Customization - PayPal Native Checkout - - Values - - card - cardVault - payPalWebCheckout - paymentButtonCustomization - payPalNativeCheckout - - - - - diff --git a/MOBILE_CHECKOUT_MIGRATION_GUIDE.md b/MOBILE_CHECKOUT_MIGRATION_GUIDE.md deleted file mode 100644 index cd2edd28b..000000000 --- a/MOBILE_CHECKOUT_MIGRATION_GUIDE.md +++ /dev/null @@ -1,182 +0,0 @@ -# PayPal Mobile Checkout SDK: Migration Guide - -This guide outlines how to update your integration from using the soon-to-be-deprecated [PayPal Mobile Checkout SDK](https://developer.paypal.com/limited-release/paypal-mobile-checkout/) to the new PayPal Mobile [iOS SDK](https://github.com/paypal/paypal-ios). - -## Pre-Requisites -In order to use this migration guide, you must: - -1. Have a server-side integration with the [PayPal Orders v2 API](https://developer.paypal.com/docs/api/orders/v2/). Please update to Orders v2 if you're on [Payments V1](https://developer.paypal.com/docs/api/payments/v1/) or [NVP/SOAP](https://developer.paypal.com/api/nvp-soap/). -1. Obtain your client ID. Follow the steps in [Get Started](https://developer.paypal.com/api/rest/#link-getstarted) to create a client ID in your PayPal Developer Dashboard. -1. Enable your server to create an [Order ID](https://developer.paypal.com/docs/api/orders/v2/). -1. Enable your server to [PATCH](https://developer.paypal.com/docs/api/orders/v2/#orders_patch) an order. - * _Note:_ This is **only required** if you create your order ID with [`shipping_preference`](https://developer.paypal.com/docs/api/orders/v2/#definition-order_application_context) = `GET_FROM_FILE`. See step 6 in the guides below. - -## Client-Side - -*Assuming the pre-requisites are met, this migration should take ~1 developer day to complete.* - -1. Add the new SDK to your app - * Swift Package Manger - * In Xcode, add the PayPal SDK as a [package dependency](https://developer.apple.com/documentation/swift_packages/adding_package_dependencies_to_your_app) to your Xcode project. Enter https://github.com/paypal/paypal-ios.git as the package URL. - * Tick the `PayPalNativePayments`, `PaymentButtons`, and `CorePayments` boxes to add the libraries to your app. - - * CocoaPods - * Include the `PayPalNativePayments`, and `PaymentButtons` sub-modules in your `Podfile`: - ```ruby - pod 'PayPal/PayPalNativePayments' - pod 'PayPal/PaymentButtons' - ``` - - *Note*: Updating your existing `paypalcheckout-ios` dependency to the latest will resolve any dependency conflicts. - -2. Update Configuration - - * Remove `CheckoutConfig` and related `set()` methods. - * Instantiate a `CoreConfig` with your client ID from the [pre-requisite](#pre-requisites) steps. - * Construct a `PayPalNativeCheckoutClient`. - * Set delegates (more details to follow). - - ```diff - private func configurePayPalCheckout() { - - let config = CheckoutConfig( - - clientID: "", - - environment: .sandbox - - ) - - Checkout.set(config: config) - - Checkout.setCreateOrderCallback { createOrderAction in - - createOrderAction.set(orderId: "") - - } - - + let coreConfig = CoreConfig(clientID: """, environment: CorePayments.Environment.sandbox) - + paypalClient = PayPalNativeCheckoutClient(config: coreConfig) - - + paypalClient?.delegate = self // always required - + paypalClient?.shippingDelegate = self // required for `GET_FROM_FILE` orders - } - ``` - -3. Update your Button - - * Update your UI to display a `PaymentButtons.PayPalButton()`, instead of a `PaymentButtonContainer()`. - * The `PayPalButton` needs a corresponding `@objc` action method. - - ```diff - private func addPayPalButton() { - - let container = PaymentButtonContainer() - + let paypalButton = PaymentButtons.PayPalButton() - + paypalButton.addTarget(self, action: #selector(payPalButtonTapped), for: .touchUpInside) - - - view.addSubview(container) - + view.addSubview(paypalButton) - } - ``` - -4. Implement a button action method - - * Create a `PayPalNativeCheckoutRequest` with your Order ID from the [pre-requisite](#pre-requisites) steps. - * Call `PayPalNativeCheckoutClient.start()` to present the PayPal Paysheet. - - ```diff - + @objc private func payPalButtonTapped() { - + let request = PayPalNativeCheckoutRequest(orderID: "") - + Task { await self.paypalClient?.start(request: request) } - + } - ``` - -5. Implement delegate & remove `Checkout` callbacks - - * Implement the required `PayPalNativeCheckoutDelegate`. This is how your app will receive notifications of the PayPal flow's success, cancel, error, and willStart events. - * Remove the analogous `Checkout` singleton `setCallback()` methods. - - ```diff - private func configurePayPalCheckout() { - - Checkout.setOnApproveCallback { approval in - - approval.actions.capture { response, error in - - // Handle result of order approval (authorize or capture) - - } - - - - Checkout.setOnCancelCallback { - - // Handle cancel case - - } - - - - Checkout.setOnErrorCallback { error in - - // Handle error case - - } - - - - Checkout.setOnShippingChangeCallback { shippingChange, shippingChangeAction in - - // Handle user shipping address & method selection change - - } - } - - + extension ViewController: PayPalNativeCheckoutDelegate { - - + func paypal(_ payPalClient: PayPalNativePayments.PayPalNativeCheckoutClient, didFinishWithResult result: PayPalNativePayments.PayPalNativeCheckoutResult) { - + // Handle result of order approval (authorize or capture) - + } - - + func paypal(_ payPalClient: PayPalNativePayments.PayPalNativeCheckoutClient, didFinishWithError error: CorePayments.CoreSDKError) { - + // Handle error case - + } - - + func paypalDidCancel(_ payPalClient: PayPalNativePayments.PayPalNativeCheckoutClient) { - + // Handle cancel case - + } - - + func paypalWillStart(_ payPalClient: PayPalNativePayments.PayPalNativeCheckoutClient) { - + // Handle any UI clean-up before paysheet presents - + } - + } - ``` - -6. Implement shipping delegate - - :warning: Only implement `PayPalNativeShippingDelegate` if your order ID was created with [`shipping_preference`](https://developer.paypal.com/docs/api/orders/v2/#definition-experience_context_base) = `GET_FROM_FILE`. If you created your order ID with `shipping_preference` = `NO_SHIPPING` or `SET_PROVIDED_ADDRESS`, **skip this step** (step 6). - - - * `PayPalNativeShippingDelegate` notifies your app when the user updates their shipping address **or** shipping method. - * In the previous SDK, both shipping change types were lumped into one `ShippingChangeCallback`. - * You are required to PATCH the order details on your server if the shipping method (or amount) changes. Do this with the [PayPal Orders API - Update order](https://developer.paypal.com/docs/api/orders/v2/#orders_patch) functionality. - - ```diff - + extension ViewController: PayPalNativeShippingDelegate { - - + func paypal( - + _ payPalClient: PayPalNativeCheckoutClient, - + didShippingAddressChange shippingAddress: PayPalNativeShippingAddress, - + withAction shippingActions: PayPalNativePaysheetActions - + ) { - + // called when the user updates their chosen shipping address - - + // REQUIRED: you must call actions.approve() or actions.reject() in this callback - + actions.approve() - - + // OPTIONAL: you can optionally patch your order. Once complete, call actions.approve() if successful or actions.reject() if not. - + } - - + func paypal( - + _ payPalClient: PayPalNativeCheckoutClient, - + didShippingMethodChange shippingMethod: PayPalNativeShippingMethod, - + withAction shippingActions: PayPalNativePaysheetActions - + ) { - + // Handle user shipping method selection change - + - + // REQUIRED: patch your order server-side with the updated shipping amount - + // Once complete, call `actions.approve()` or `actions.reject()` - + if patchOrder() == .success { - + actions.approve() - + } else { - + actions.reject() - + } - + } - + } - ``` - -7. Remove the old SDK dependency - -* Swift Package Manager - * Remove the `paypalcheckout-ios` package from your Xcode project's "Package Dependencies" - * ![](https://iili.io/HO1okCB.png) - -* CocoaPods - * Remove `pod 'PayPalCheckout'` from your Podfile - * Refresh your local `/Pods` diff --git a/Package.swift b/Package.swift index 0520906ae..a0d6486d5 100644 --- a/Package.swift +++ b/Package.swift @@ -12,10 +12,6 @@ let package = Package( name: "CorePayments", targets: ["CorePayments"] ), - .library( - name: "PayPalNativePayments", - targets: ["PayPalNativePayments"] - ), .library( name: "PaymentButtons", targets: ["PaymentButtons"] @@ -46,11 +42,6 @@ let package = Package( dependencies: ["CorePayments"], resources: [.copy("PrivacyInfo.xcprivacy")] ), - .target( - name: "PayPalNativePayments", - dependencies: ["CorePayments", "PayPalCheckout"], - resources: [.copy("PrivacyInfo.xcprivacy")] - ), .target( name: "PaymentButtons", dependencies: ["CorePayments"], @@ -69,11 +60,6 @@ let package = Package( .binaryTarget( name: "PPRiskMagnes", path: "Frameworks/XCFrameworks/PPRiskMagnes.xcframework" - ), - .binaryTarget( - name: "PayPalCheckout", - url: "https://github.com/paypal/paypalcheckout-ios/releases/download/1.3.0/PayPalCheckout.xcframework.zip", - checksum: "d65186f38f390cb9ae0431ecacf726774f7f89f5474c48244a07d17b248aa035" ) ] ) diff --git a/PayPal.podspec b/PayPal.podspec index cc1c841b1..43b832851 100644 --- a/PayPal.podspec +++ b/PayPal.podspec @@ -17,13 +17,6 @@ Pod::Spec.new do |s| s.resource_bundle = { "CardPayments_PrivacyInfo" => "Sources/CardPayments/PrivacyInfo.xcprivacy" } end - s.subspec "PayPalNativePayments" do |s| - s.source_files = "Sources/PayPalNativePayments/**/*.swift" - s.dependency "PayPal/CorePayments" - s.dependency "PayPalCheckout", "1.3.0" - s.resource_bundle = { "PayPalNativePayments_PrivacyInfo" => "Sources/PayPalNativePayments/PrivacyInfo.xcprivacy" } - end - s.subspec "PaymentButtons" do |s| s.source_files = "Sources/PaymentButtons/*.swift" s.dependency "PayPal/CorePayments" diff --git a/PayPal.xcodeproj/project.pbxproj b/PayPal.xcodeproj/project.pbxproj index 4e6722c4f..ec47e1328 100644 --- a/PayPal.xcodeproj/project.pbxproj +++ b/PayPal.xcodeproj/project.pbxproj @@ -11,16 +11,10 @@ 0647E70E2714962800F8E517 /* Address.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0647E70D2714962800F8E517 /* Address.swift */; }; 065A4DBC26FCD8090007014A /* CoreSDKError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065A4DBB26FCD8080007014A /* CoreSDKError.swift */; }; 065A4DBF26FCDA5B0007014A /* CardClient_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065A4DBE26FCDA5B0007014A /* CardClient_Tests.swift */; }; - 065EBD8C272865E4001E81C4 /* PayPalClient_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065EBD8B272865E4001E81C4 /* PayPalClient_Tests.swift */; }; - 067C92E527270FDE009E3054 /* PayPalEnvironment_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 067C92E427270FDE009E3054 /* PayPalEnvironment_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 */; }; - 3B3C51122B20C962009125FE /* PayPalVaultDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3C51112B20C962009125FE /* PayPalVaultDelegate.swift */; }; - 3B3C51182B2221C9009125FE /* MockPayPalVaultDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3C51172B2221C9009125FE /* MockPayPalVaultDelegate.swift */; }; 3B3C511E2B2395B5009125FE /* PayPalVaultResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B3C511D2B2395B5009125FE /* PayPalVaultResult.swift */; }; 3B783DC32B7A69C2004623DB /* FakeUpdateSetupTokenResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B783DC22B7A69C2004623DB /* FakeUpdateSetupTokenResponse.swift */; }; 3B79E4F72A8503CA00C01D06 /* UpdateVaultVariables.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3B79E4F62A8503C900C01D06 /* UpdateVaultVariables.swift */; }; @@ -30,14 +24,11 @@ 3BE738682B9A66D800598F05 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3BE738622B9A482800598F05 /* PrivacyInfo.xcprivacy */; }; 3BE738692B9A66E600598F05 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3BE738632B9A4A4500598F05 /* PrivacyInfo.xcprivacy */; }; 3BE7386A2B9A66EE00598F05 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3BE738642B9A566200598F05 /* PrivacyInfo.xcprivacy */; }; - 3BE7386B2B9A66F600598F05 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3BE738652B9A58AE00598F05 /* PrivacyInfo.xcprivacy */; }; 3BE7386C2B9A66FC00598F05 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3BE738672B9A598200598F05 /* PrivacyInfo.xcprivacy */; }; 3BE7386D2B9A670400598F05 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3BE738662B9A593100598F05 /* PrivacyInfo.xcprivacy */; }; 3D1763A22720722A00652E1C /* CardResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D1763A12720722A00652E1C /* CardResult.swift */; }; 3DC42BA927187E8300B71645 /* ErrorResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3DC42BA827187E8300B71645 /* ErrorResponse.swift */; }; - 53A2A4E228A182AC0093441C /* NativeCheckoutProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53A2A4E128A182AC0093441C /* NativeCheckoutProvider.swift */; }; 8008D2052A9E54FF0003CAF4 /* CheckoutOrdersAPI_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8008D2042A9E54FF0003CAF4 /* CheckoutOrdersAPI_Tests.swift */; }; - 80132D7229008C000088D30D /* TestShared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 80E743F8270E40CE00BACECA /* TestShared.framework */; }; 8021B69029144E6D000FBC54 /* PayPalCoreConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8021B68F29144E6D000FBC54 /* PayPalCoreConstants.swift */; }; 802C4A742945670400896A5D /* MockHTTP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 802C4A732945670400896A5D /* MockHTTP.swift */; }; 802C4A762945676E00896A5D /* AnalyticsService_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 802C4A752945676E00896A5D /* AnalyticsService_Tests.swift */; }; @@ -53,8 +44,6 @@ 804E62822937EBCE004B9FEF /* HTTP.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804E62812937EBCE004B9FEF /* HTTP.swift */; }; 804E628629380B04004B9FEF /* AnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804E628529380B04004B9FEF /* AnalyticsService.swift */; }; 805AB85926B88882003BEE0D /* CorePayments.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 80B9F85126B8750000D67843 /* CorePayments.framework */; platformFilter = ios; }; - 8071AFA529C8BD8C008A39E9 /* PayPalNativeShippingMethod_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8071AFA429C8BD8C008A39E9 /* PayPalNativeShippingMethod_Tests.swift */; }; - 8071AFA729C8BEF3008A39E9 /* PayPalNativeShippingAddress_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8071AFA629C8BEF3008A39E9 /* PayPalNativeShippingAddress_Tests.swift */; }; 807BF58F2A2A5D19002F32B3 /* HTTPResponseParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 807BF58E2A2A5D19002F32B3 /* HTTPResponseParser.swift */; }; 807BF5912A2A5D48002F32B3 /* HTTPResponseParser_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 807BF5902A2A5D48002F32B3 /* HTTPResponseParser_Tests.swift */; }; 807C5E6929102D9800ECECD8 /* AnalyticsEventData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 807C5E6829102D9800ECECD8 /* AnalyticsEventData.swift */; }; @@ -67,14 +56,8 @@ 80B27AF12A9E9EE60008EA45 /* VaultPaymentTokensAPI_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80B27AF02A9E9EE60008EA45 /* VaultPaymentTokensAPI_Tests.swift */; }; 80B8B2FC2A8EBBFD00AB60CD /* VaultPaymentTokensAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80B8B2FB2A8EBBFD00AB60CD /* VaultPaymentTokensAPI.swift */; }; 80B96AAE2A980F6B00C62916 /* MockTrackingEventsAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 802EFBD72A9685DF00AB709D /* MockTrackingEventsAPI.swift */; }; - 80D0C1382731CC9B00548A3D /* PayPalNativeCheckoutClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE7116152723227200165069 /* PayPalNativeCheckoutClient.swift */; }; 80DB2F762980795D00CFB86A /* CorePaymentsError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80DB2F752980795D00CFB86A /* CorePaymentsError.swift */; }; - 80DB6F1726B89DC700277E54 /* CorePayments.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 80B9F85126B8750000D67843 /* CorePayments.framework */; platformFilter = ios; }; - 80DBC9DA29C340D900462539 /* PayPalNativeCheckoutResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80DBC9D929C340D900462539 /* PayPalNativeCheckoutResult.swift */; }; - 80DBC9DC29C350A900462539 /* PayPalNativeCheckoutRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80DBC9DB29C350A900462539 /* PayPalNativeCheckoutRequest.swift */; }; - 80DBC9DE29C3B57200462539 /* PayPalNativeShippingAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80DBC9DD29C3B57200462539 /* PayPalNativeShippingAddress.swift */; }; - 80DBC9E029C3B8A800462539 /* PayPalNativeShippingMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80DBC9DF29C3B8A800462539 /* PayPalNativeShippingMethod.swift */; }; - 80DCC59E2719DB6F00EC7C5A /* CardClientError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80DCC59D2719DB6F00EC7C5A /* CardClientError.swift */; }; + 80DCC59E2719DB6F00EC7C5A /* CardError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80DCC59D2719DB6F00EC7C5A /* CardError.swift */; }; 80E237DF2A84434B00FF18CA /* HTTPRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80E237DE2A84434B00FF18CA /* HTTPRequest.swift */; }; 80E2FDBE2A83528B0045593D /* CheckoutOrdersAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80E2FDBD2A83528B0045593D /* CheckoutOrdersAPI.swift */; }; 80E2FDC12A83535A0045593D /* TrackingEventsAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80E2FDBF2A8353550045593D /* TrackingEventsAPI.swift */; }; @@ -90,18 +73,14 @@ BC0A82A6270B9954006E9A21 /* ConfirmPaymentSourceRequest_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 065A4DC226FCE1D20007014A /* ConfirmPaymentSourceRequest_Tests.swift */; }; BC171FB12A8C156300B26DCB /* CoreConfig+MagnesSDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC171FB02A8C156300B26DCB /* CoreConfig+MagnesSDK.swift */; }; BC171FB22A8D6FE500B26DCB /* CorePayments.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 80B9F85126B8750000D67843 /* CorePayments.framework */; }; - BC43406F27E91C2700F70193 /* PayPalCheckout in Frameworks */ = {isa = PBXBuildFile; productRef = BC43406E27E91C2700F70193 /* PayPalCheckout */; }; BC7F8123275FC1350011EDC8 /* CardRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC7F8122275FC1350011EDC8 /* CardRequest.swift */; }; - BC900B7D27AC307A00D48DBA /* PayPalNativeCheckoutDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC900B7C27AC307A00D48DBA /* PayPalNativeCheckoutDelegate.swift */; }; BC9C18D427D2775A0019B541 /* MockDeviceInspector.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC9C18D327D2775A0019B541 /* MockDeviceInspector.swift */; }; BC9C18D827D279C70019B541 /* MagnesSDKResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC9C18D727D279C70019B541 /* MagnesSDKResult.swift */; }; BC9C18DA27D27CE40019B541 /* MockMagnesSDKResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC9C18D927D27CE40019B541 /* MockMagnesSDKResult.swift */; }; BC9C18DE27D2A1610019B541 /* DeviceInspector_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC9C18DD27D2A1610019B541 /* DeviceInspector_Tests.swift */; }; BCD5C47E27E9200800B074D5 /* CorePayments.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 80B9F85126B8750000D67843 /* CorePayments.framework */; platformFilter = ios; }; - BCE8A7F327EA531C00AC301B /* MockPayPalWebDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE8A7F227EA531C00AC301B /* MockPayPalWebDelegate.swift */; }; BCE8A7F927EA555800AC301B /* PayPalWebCheckoutClient_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE8A7F827EA555800AC301B /* PayPalWebCheckoutClient_Tests.swift */; }; BCE8A7FB27EA5B1D00AC301B /* Environment+PayPalWebCheckout_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE8A7FA27EA5B1D00AC301B /* Environment+PayPalWebCheckout_Tests.swift */; }; - BCF59E5F27E91ECC00CDFD47 /* NativeCheckoutStartable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D25238127344F330099E4EB /* NativeCheckoutStartable.swift */; }; BCF735DB27D1583400A52E03 /* TestShared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 80E743F8270E40CE00BACECA /* TestShared.framework */; platformFilter = ios; }; BCF735E327D158B400A52E03 /* PPRiskMagnes.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCF735E227D158B400A52E03 /* PPRiskMagnes.xcframework */; platformFilter = ios; }; BCF735E427D158E900A52E03 /* PPRiskMagnes.xcframework in Frameworks */ = {isa = PBXBuildFile; fileRef = BCF735E227D158B400A52E03 /* PPRiskMagnes.xcframework */; platformFilter = ios; }; @@ -125,19 +104,15 @@ BCFAC71F27ED04C800C3AF00 /* Coordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEDB7FE32788AB8E00CEA554 /* Coordinator.swift */; }; BE4F785327EB656400FF4C0E /* Environment+PayPalWebCheckout.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE4F784327EB629100FF4C0E /* Environment+PayPalWebCheckout.swift */; }; BE4F785427EB656400FF4C0E /* PayPalWebCheckoutClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE4F784627EB629100FF4C0E /* PayPalWebCheckoutClient.swift */; }; - BE4F785527EB656400FF4C0E /* PayPalWebCheckoutClientError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE4F784427EB629100FF4C0E /* PayPalWebCheckoutClientError.swift */; }; - BE4F785627EB656400FF4C0E /* PayPalWebCheckoutDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE4F784927EB629100FF4C0E /* PayPalWebCheckoutDelegate.swift */; }; + BE4F785527EB656400FF4C0E /* PayPalError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE4F784427EB629100FF4C0E /* PayPalError.swift */; }; BE4F785727EB656400FF4C0E /* PayPalWebCheckoutRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE4F784527EB629100FF4C0E /* PayPalWebCheckoutRequest.swift */; }; BE4F785827EB656400FF4C0E /* PayPalWebCheckoutResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE4F784727EB629100FF4C0E /* PayPalWebCheckoutResult.swift */; }; - BE71161C27234B8A00165069 /* PayPalNativePaymentsError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE71161B27234B8A00165069 /* PayPalNativePaymentsError.swift */; }; - BE711621272358E200165069 /* Environment+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE711620272358E200165069 /* Environment+Extension.swift */; }; BEA100E726EF9EDA0036A6A5 /* NetworkingClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA100E626EF9EDA0036A6A5 /* NetworkingClient.swift */; }; BEA100EC26EFA7790036A6A5 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA100EB26EFA7790036A6A5 /* HTTPMethod.swift */; }; BEA100EE26EFA7990036A6A5 /* HTTPHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA100ED26EFA7990036A6A5 /* HTTPHeader.swift */; }; BEA100F026EFA7C20036A6A5 /* NetworkingClientError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA100EF26EFA7C20036A6A5 /* NetworkingClientError.swift */; }; BEA100F226EFA7DE0036A6A5 /* Environment.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA100F126EFA7DE0036A6A5 /* Environment.swift */; }; CB16E6D8285B7A2B00FD6F52 /* FakeConfirmPaymentResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB16E6D7285B7A2B00FD6F52 /* FakeConfirmPaymentResponse.swift */; }; - CB16E6DA285B7B7300FD6F52 /* MockCardDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB16E6D9285B7B7300FD6F52 /* MockCardDelegate.swift */; }; CB1A47F22820AFED00BD8184 /* PayPalPayLaterButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB1A47F12820AFED00BD8184 /* PayPalPayLaterButton.swift */; }; CB1A47F42820BA5D00BD8184 /* PaymentButtonEdges.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB1A47F32820BA5D00BD8184 /* PaymentButtonEdges.swift */; }; CB1A47F62820BAA600BD8184 /* PaymentButtonLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB1A47F52820BAA600BD8184 /* PaymentButtonLabel.swift */; }; @@ -145,22 +120,14 @@ CB1A47FA2820BF6B00BD8184 /* PaymentButtonFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB1A47F92820BF6B00BD8184 /* PaymentButtonFont.swift */; }; CB1A47FE2820C10700BD8184 /* PaymentButtonFundingSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB1A47FD2820C10700BD8184 /* PaymentButtonFundingSource.swift */; }; CB1A48052822BCED00BD8184 /* PaymentButton+ImageAsset.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB1A48042822BCED00BD8184 /* PaymentButton+ImageAsset.swift */; }; - CB1AC3C22982CDB10081AED6 /* MockNativeCheckoutProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D25238B273979170099E4EB /* MockNativeCheckoutProvider.swift */; }; CB22C018291049500097E592 /* PayPalPayLaterButton_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB22C017291049500097E592 /* PayPalPayLaterButton_Tests.swift */; }; CB4BE27D2847AF6F00EA2DD1 /* SCA.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4BE27C2847AF6F00EA2DD1 /* SCA.swift */; }; CB4BE27E2847EA7D00EA2DD1 /* WebAuthenticationSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE4F784827EB629100FF4C0E /* WebAuthenticationSession.swift */; }; - CB4BE2802847F01000EA2DD1 /* CardDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4BE27F2847F01000EA2DD1 /* CardDelegate.swift */; }; CB4BE28A28512A9800EA2DD1 /* MockQuededURLSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4BE28928512A9800EA2DD1 /* MockQuededURLSession.swift */; }; CB4BE28B2851423900EA2DD1 /* MockWebAuthenticationSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE8A7F627EA54A000AC301B /* MockWebAuthenticationSession.swift */; }; CB4BE28C2851427600EA2DD1 /* MockViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE8A7F427EA544000AC301B /* MockViewController.swift */; }; CB51FF7227FCB947001A97F5 /* PayPalWebCheckoutFundingSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB51FF7127FCB947001A97F5 /* PayPalWebCheckoutFundingSource.swift */; }; CBC16DD529E99B4600307117 /* PaymentSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = CB4BE285284802C200EA2DD1 /* PaymentSource.swift */; }; - CBC16DD729E99D0D00307117 /* PayPalNativePaysheetActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBC16DD629E99D0D00307117 /* PayPalNativePaysheetActions.swift */; }; - CBC16DDB29EE1BF800307117 /* MockPayPalNativeShipping.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBC16DDA29EE1BF800307117 /* MockPayPalNativeShipping.swift */; }; - CBC16DDD29EE2C9000307117 /* MockShippingChangeActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBC16DDC29EE2C9000307117 /* MockShippingChangeActions.swift */; }; - CBC16DE029EE2F8200307117 /* PayPalNativePaysheetAction_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBC16DDF29EE2F8200307117 /* PayPalNativePaysheetAction_Tests.swift */; }; - CBC16DF629EECCB900307117 /* NativeCheckoutProvider_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBC16DF529EECCB900307117 /* NativeCheckoutProvider_Tests.swift */; }; - CBD6004728D0C24A00C3EFF6 /* MockPayPalDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = CBD6004628D0C24900C3EFF6 /* MockPayPalDelegate.swift */; }; E6022E802857C6BE008B0E27 /* GraphQLHTTPResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = E64623222836A69E008AC8E1 /* GraphQLHTTPResponse.swift */; }; E64763712899B60C00074113 /* MockNetworkingClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E64763702899B60C00074113 /* MockNetworkingClient.swift */; }; /* End PBXBuildFile section */ @@ -173,13 +140,6 @@ remoteGlobalIDString = 80B9F85026B8750000D67843; remoteInfo = PaymentsCore; }; - 80903D4826BADDD7003A381D /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = OBJ_1 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 80DB6F0D26B89D9600277E54; - remoteInfo = PayPal; - }; 80E8DAEB26B8785D00FAFC3F /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = OBJ_1 /* Project object */; @@ -216,18 +176,12 @@ 065A4DBB26FCD8080007014A /* CoreSDKError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreSDKError.swift; sourceTree = ""; }; 065A4DBE26FCDA5B0007014A /* CardClient_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardClient_Tests.swift; sourceTree = ""; }; 065A4DC226FCE1D20007014A /* ConfirmPaymentSourceRequest_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmPaymentSourceRequest_Tests.swift; sourceTree = ""; }; - 065EBD8B272865E4001E81C4 /* PayPalClient_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalClient_Tests.swift; sourceTree = ""; }; - 067C92E427270FDE009E3054 /* PayPalEnvironment_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalEnvironment_Tests.swift; sourceTree = ""; }; 06CE009826F3D1660000CC46 /* CoreConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreConfig.swift; sourceTree = ""; }; 06CE009A26F3D5A40000CC46 /* CardClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardClient.swift; sourceTree = ""; }; 06CE009F26F3DF100000CC46 /* ConfirmPaymentSourceRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmPaymentSourceRequest.swift; sourceTree = ""; }; 06CE00A226F3E32A0000CC46 /* ConfirmPaymentSourceResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmPaymentSourceResponse.swift; sourceTree = ""; }; - 3B109B3A2A85B54800D8135F /* MockCardVaultDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCardVaultDelegate.swift; sourceTree = ""; }; - 3B22E8B52A840ECF00962E34 /* CardVaultDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardVaultDelegate.swift; sourceTree = ""; }; 3B22E8B72A841AEA00962E34 /* CardVaultResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardVaultResult.swift; sourceTree = ""; }; 3B29C3962B9148F70077741D /* PayPalVaultRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalVaultRequest.swift; sourceTree = ""; }; - 3B3C51112B20C962009125FE /* PayPalVaultDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalVaultDelegate.swift; sourceTree = ""; }; - 3B3C51172B2221C9009125FE /* MockPayPalVaultDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPayPalVaultDelegate.swift; sourceTree = ""; }; 3B3C511D2B2395B5009125FE /* PayPalVaultResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalVaultResult.swift; sourceTree = ""; }; 3B783DC22B7A69C2004623DB /* FakeUpdateSetupTokenResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeUpdateSetupTokenResponse.swift; sourceTree = ""; }; 3B79E4F62A8503C900C01D06 /* UpdateVaultVariables.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UpdateVaultVariables.swift; sourceTree = ""; }; @@ -237,14 +191,10 @@ 3BE738622B9A482800598F05 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 3BE738632B9A4A4500598F05 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 3BE738642B9A566200598F05 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; - 3BE738652B9A58AE00598F05 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 3BE738662B9A593100598F05 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 3BE738672B9A598200598F05 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 3D1763A12720722A00652E1C /* CardResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardResult.swift; sourceTree = ""; }; - 3D25238127344F330099E4EB /* NativeCheckoutStartable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = NativeCheckoutStartable.swift; path = Sources/PayPalNativePayments/NativeCheckoutStartable.swift; sourceTree = SOURCE_ROOT; }; - 3D25238B273979170099E4EB /* MockNativeCheckoutProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockNativeCheckoutProvider.swift; sourceTree = ""; }; 3DC42BA827187E8300B71645 /* ErrorResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorResponse.swift; sourceTree = ""; }; - 53A2A4E128A182AC0093441C /* NativeCheckoutProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeCheckoutProvider.swift; sourceTree = ""; }; 8008D2042A9E54FF0003CAF4 /* CheckoutOrdersAPI_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckoutOrdersAPI_Tests.swift; sourceTree = ""; }; 8021B68F29144E6D000FBC54 /* PayPalCoreConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalCoreConstants.swift; sourceTree = ""; }; 802C4A732945670400896A5D /* MockHTTP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockHTTP.swift; sourceTree = ""; }; @@ -258,8 +208,6 @@ 803C31B8270E4C560067D36E /* MockURLSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockURLSession.swift; sourceTree = ""; }; 804E62812937EBCE004B9FEF /* HTTP.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTP.swift; sourceTree = ""; }; 804E628529380B04004B9FEF /* AnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsService.swift; sourceTree = ""; }; - 8071AFA429C8BD8C008A39E9 /* PayPalNativeShippingMethod_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalNativeShippingMethod_Tests.swift; sourceTree = ""; }; - 8071AFA629C8BEF3008A39E9 /* PayPalNativeShippingAddress_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalNativeShippingAddress_Tests.swift; sourceTree = ""; }; 807BF58E2A2A5D19002F32B3 /* HTTPResponseParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPResponseParser.swift; sourceTree = ""; }; 807BF5902A2A5D48002F32B3 /* HTTPResponseParser_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPResponseParser_Tests.swift; sourceTree = ""; }; 807C5E6829102D9800ECECD8 /* AnalyticsEventData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsEventData.swift; sourceTree = ""; }; @@ -273,12 +221,7 @@ 80B8B2FB2A8EBBFD00AB60CD /* VaultPaymentTokensAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VaultPaymentTokensAPI.swift; sourceTree = ""; }; 80B9F85126B8750000D67843 /* CorePayments.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CorePayments.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 80DB2F752980795D00CFB86A /* CorePaymentsError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CorePaymentsError.swift; sourceTree = ""; }; - 80DB6F0E26B89D9600277E54 /* PayPalNativePayments.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PayPalNativePayments.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 80DBC9D929C340D900462539 /* PayPalNativeCheckoutResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalNativeCheckoutResult.swift; sourceTree = ""; }; - 80DBC9DB29C350A900462539 /* PayPalNativeCheckoutRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalNativeCheckoutRequest.swift; sourceTree = ""; }; - 80DBC9DD29C3B57200462539 /* PayPalNativeShippingAddress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalNativeShippingAddress.swift; sourceTree = ""; }; - 80DBC9DF29C3B8A800462539 /* PayPalNativeShippingMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalNativeShippingMethod.swift; sourceTree = ""; }; - 80DCC59D2719DB6F00EC7C5A /* CardClientError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardClientError.swift; sourceTree = ""; }; + 80DCC59D2719DB6F00EC7C5A /* CardError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardError.swift; sourceTree = ""; }; 80E237DE2A84434B00FF18CA /* HTTPRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPRequest.swift; sourceTree = ""; }; 80E2FDBD2A83528B0045593D /* CheckoutOrdersAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CheckoutOrdersAPI.swift; sourceTree = ""; }; 80E2FDBF2A8353550045593D /* TrackingEventsAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackingEventsAPI.swift; sourceTree = ""; }; @@ -294,14 +237,12 @@ BC04837327B2FC7300FA7B46 /* URLSession+URLSessionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+URLSessionProtocol.swift"; sourceTree = ""; }; BC171FB02A8C156300B26DCB /* CoreConfig+MagnesSDK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreConfig+MagnesSDK.swift"; sourceTree = ""; }; BC7F8122275FC1350011EDC8 /* CardRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardRequest.swift; sourceTree = ""; }; - BC900B7C27AC307A00D48DBA /* PayPalNativeCheckoutDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalNativeCheckoutDelegate.swift; sourceTree = ""; }; BC9C18D327D2775A0019B541 /* MockDeviceInspector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDeviceInspector.swift; sourceTree = ""; }; BC9C18D727D279C70019B541 /* MagnesSDKResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MagnesSDKResult.swift; sourceTree = ""; }; BC9C18D927D27CE40019B541 /* MockMagnesSDKResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMagnesSDKResult.swift; sourceTree = ""; }; BC9C18DD27D2A1610019B541 /* DeviceInspector_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeviceInspector_Tests.swift; sourceTree = ""; }; BCD5C48527E9200800B074D5 /* PayPalWebPayments.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PayPalWebPayments.framework; sourceTree = BUILT_PRODUCTS_DIR; }; BCD5C49427E9201400B074D5 /* PayPalWebCheckoutTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = PayPalWebCheckoutTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - BCE8A7F227EA531C00AC301B /* MockPayPalWebDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPayPalWebDelegate.swift; sourceTree = ""; }; BCE8A7F427EA544000AC301B /* MockViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockViewController.swift; sourceTree = ""; }; BCE8A7F627EA54A000AC301B /* MockWebAuthenticationSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockWebAuthenticationSession.swift; sourceTree = ""; }; BCE8A7F827EA555800AC301B /* PayPalWebCheckoutClient_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalWebCheckoutClient_Tests.swift; sourceTree = ""; }; @@ -324,15 +265,11 @@ BE00B7AA2743FD9F00758C63 /* PaymentButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentButton.swift; sourceTree = ""; }; BE00B7AC27444FE900758C63 /* PayPalButton_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalButton_Tests.swift; sourceTree = ""; }; BE4F784327EB629100FF4C0E /* Environment+PayPalWebCheckout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Environment+PayPalWebCheckout.swift"; sourceTree = ""; }; - BE4F784427EB629100FF4C0E /* PayPalWebCheckoutClientError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayPalWebCheckoutClientError.swift; sourceTree = ""; }; + BE4F784427EB629100FF4C0E /* PayPalError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayPalError.swift; sourceTree = ""; }; BE4F784527EB629100FF4C0E /* PayPalWebCheckoutRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayPalWebCheckoutRequest.swift; sourceTree = ""; }; BE4F784627EB629100FF4C0E /* PayPalWebCheckoutClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayPalWebCheckoutClient.swift; sourceTree = ""; }; BE4F784727EB629100FF4C0E /* PayPalWebCheckoutResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayPalWebCheckoutResult.swift; sourceTree = ""; }; BE4F784827EB629100FF4C0E /* WebAuthenticationSession.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebAuthenticationSession.swift; sourceTree = ""; }; - BE4F784927EB629100FF4C0E /* PayPalWebCheckoutDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PayPalWebCheckoutDelegate.swift; sourceTree = ""; }; - BE7116152723227200165069 /* PayPalNativeCheckoutClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalNativeCheckoutClient.swift; sourceTree = ""; }; - BE71161B27234B8A00165069 /* PayPalNativePaymentsError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalNativePaymentsError.swift; sourceTree = ""; }; - BE711620272358E200165069 /* Environment+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Environment+Extension.swift"; sourceTree = ""; }; BE9F36DE274859A000AFC7DA /* PaymentButtonColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentButtonColor.swift; sourceTree = ""; }; BE9F36E3275520E700AFC7DA /* PayPalCreditButton_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalCreditButton_Tests.swift; sourceTree = ""; }; BEA100E626EF9EDA0036A6A5 /* NetworkingClient.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkingClient.swift; sourceTree = ""; }; @@ -343,7 +280,6 @@ BEDB7FE32788AB8E00CEA554 /* Coordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator.swift; sourceTree = ""; }; BEF3FF1627AC5DF3006B4B69 /* Coordinator_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Coordinator_Tests.swift; sourceTree = ""; }; CB16E6D7285B7A2B00FD6F52 /* FakeConfirmPaymentResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeConfirmPaymentResponse.swift; sourceTree = ""; }; - CB16E6D9285B7B7300FD6F52 /* MockCardDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockCardDelegate.swift; sourceTree = ""; }; CB1A47F12820AFED00BD8184 /* PayPalPayLaterButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalPayLaterButton.swift; sourceTree = ""; }; CB1A47F32820BA5D00BD8184 /* PaymentButtonEdges.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentButtonEdges.swift; sourceTree = ""; }; CB1A47F52820BAA600BD8184 /* PaymentButtonLabel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentButtonLabel.swift; sourceTree = ""; }; @@ -353,21 +289,13 @@ CB1A48042822BCED00BD8184 /* PaymentButton+ImageAsset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "PaymentButton+ImageAsset.swift"; sourceTree = ""; }; CB22C017291049500097E592 /* PayPalPayLaterButton_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalPayLaterButton_Tests.swift; sourceTree = ""; }; CB4BE27C2847AF6F00EA2DD1 /* SCA.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SCA.swift; sourceTree = ""; }; - CB4BE27F2847F01000EA2DD1 /* CardDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardDelegate.swift; sourceTree = ""; }; CB4BE285284802C200EA2DD1 /* PaymentSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PaymentSource.swift; sourceTree = ""; }; CB4BE28928512A9800EA2DD1 /* MockQuededURLSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockQuededURLSession.swift; sourceTree = ""; }; CB51FF7127FCB947001A97F5 /* PayPalWebCheckoutFundingSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalWebCheckoutFundingSource.swift; sourceTree = ""; }; - CBC16DD629E99D0D00307117 /* PayPalNativePaysheetActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalNativePaysheetActions.swift; sourceTree = ""; }; - CBC16DDA29EE1BF800307117 /* MockPayPalNativeShipping.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPayPalNativeShipping.swift; sourceTree = ""; }; - CBC16DDC29EE2C9000307117 /* MockShippingChangeActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockShippingChangeActions.swift; sourceTree = ""; }; - CBC16DDF29EE2F8200307117 /* PayPalNativePaysheetAction_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PayPalNativePaysheetAction_Tests.swift; sourceTree = ""; }; - CBC16DF529EECCB900307117 /* NativeCheckoutProvider_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeCheckoutProvider_Tests.swift; sourceTree = ""; }; - CBD6004628D0C24900C3EFF6 /* MockPayPalDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockPayPalDelegate.swift; sourceTree = ""; }; E64623222836A69E008AC8E1 /* GraphQLHTTPResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GraphQLHTTPResponse.swift; sourceTree = ""; }; E64763702899B60C00074113 /* MockNetworkingClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockNetworkingClient.swift; sourceTree = ""; }; OBJ_16 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; - "PayPal::PayPalTests::Product" /* PayPalNativeCheckoutTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; path = PayPalNativeCheckoutTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -387,15 +315,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 80DB6F0B26B89D9600277E54 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 80DB6F1726B89DC700277E54 /* CorePayments.framework in Frameworks */, - BC43406F27E91C2700F70193 /* PayPalCheckout in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 80E743F5270E40CE00BACECA /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -470,14 +389,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - OBJ_41 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 0; - files = ( - 80132D7229008C000088D30D /* TestShared.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ @@ -503,7 +414,6 @@ 80E8DAE726B8785D00FAFC3F /* CardPaymentsTests */, 8036C1DE270F9BCF00C0F091 /* PaymentsCoreTests */, BCF735F427D2708E00A52E03 /* FraudProtectionTests */, - OBJ_11 /* PayPalNativePaymentsTests */, BCFAC72227ED05D900C3AF00 /* PaymentButtonsTests */, BCE8A7F027EA52BF00AC301B /* PayPalWebPaymentsTests */, 80E743F9270E40CE00BACECA /* TestShared */, @@ -562,8 +472,6 @@ isa = PBXGroup; children = ( CB16E6D7285B7A2B00FD6F52 /* FakeConfirmPaymentResponse.swift */, - CB16E6D9285B7B7300FD6F52 /* MockCardDelegate.swift */, - 3B109B3A2A85B54800D8135F /* MockCardVaultDelegate.swift */, 808E3EDC2A981F240017FE46 /* MockCheckoutOrdersAPI.swift */, 808E3EDE2A981F390017FE46 /* MockVaultPaymentTokensAPI.swift */, 3B783DC22B7A69C2004623DB /* FakeUpdateSetupTokenResponse.swift */, @@ -595,26 +503,6 @@ path = Sources/CorePayments; sourceTree = ""; }; - 80DB6F0F26B89D9600277E54 /* PayPalNativePayments */ = { - isa = PBXGroup; - children = ( - BE711620272358E200165069 /* Environment+Extension.swift */, - 53A2A4E128A182AC0093441C /* NativeCheckoutProvider.swift */, - 3D25238127344F330099E4EB /* NativeCheckoutStartable.swift */, - BE7116152723227200165069 /* PayPalNativeCheckoutClient.swift */, - BC900B7C27AC307A00D48DBA /* PayPalNativeCheckoutDelegate.swift */, - 80DBC9DB29C350A900462539 /* PayPalNativeCheckoutRequest.swift */, - 80DBC9D929C340D900462539 /* PayPalNativeCheckoutResult.swift */, - BE71161B27234B8A00165069 /* PayPalNativePaymentsError.swift */, - 80DBC9DD29C3B57200462539 /* PayPalNativeShippingAddress.swift */, - 80DBC9DF29C3B8A800462539 /* PayPalNativeShippingMethod.swift */, - CBC16DD629E99D0D00307117 /* PayPalNativePaysheetActions.swift */, - 3BE738652B9A58AE00598F05 /* PrivacyInfo.xcprivacy */, - ); - name = PayPalNativePayments; - path = Sources/PayPalNativePayments; - sourceTree = ""; - }; 80DBC9D829C336D500462539 /* APIRequests */ = { isa = PBXGroup; children = ( @@ -647,9 +535,7 @@ isa = PBXGroup; children = ( 06CE009A26F3D5A40000CC46 /* CardClient.swift */, - 80DCC59D2719DB6F00EC7C5A /* CardClientError.swift */, - CB4BE27F2847F01000EA2DD1 /* CardDelegate.swift */, - 3B22E8B52A840ECF00962E34 /* CardVaultDelegate.swift */, + 80DCC59D2719DB6F00EC7C5A /* CardError.swift */, 80DBC9D829C336D500462539 /* APIRequests */, 065A4DBD26FCDA270007014A /* Models */, 3BE738622B9A482800598F05 /* PrivacyInfo.xcprivacy */, @@ -683,8 +569,6 @@ BCE8A7F127EA52EB00AC301B /* Mocks */ = { isa = PBXGroup; children = ( - BCE8A7F227EA531C00AC301B /* MockPayPalWebDelegate.swift */, - 3B3C51172B2221C9009125FE /* MockPayPalVaultDelegate.swift */, ); path = Mocks; sourceTree = ""; @@ -755,12 +639,10 @@ children = ( BE4F784327EB629100FF4C0E /* Environment+PayPalWebCheckout.swift */, BE4F784627EB629100FF4C0E /* PayPalWebCheckoutClient.swift */, - BE4F784427EB629100FF4C0E /* PayPalWebCheckoutClientError.swift */, - BE4F784927EB629100FF4C0E /* PayPalWebCheckoutDelegate.swift */, + BE4F784427EB629100FF4C0E /* PayPalError.swift */, BE4F784527EB629100FF4C0E /* PayPalWebCheckoutRequest.swift */, BE4F784727EB629100FF4C0E /* PayPalWebCheckoutResult.swift */, CB51FF7127FCB947001A97F5 /* PayPalWebCheckoutFundingSource.swift */, - 3B3C51112B20C962009125FE /* PayPalVaultDelegate.swift */, 3B3C511D2B2395B5009125FE /* PayPalVaultResult.swift */, 3B29C3962B9148F70077741D /* PayPalVaultRequest.swift */, 3BE738662B9A593100598F05 /* PrivacyInfo.xcprivacy */, @@ -811,33 +693,13 @@ path = GraphQL; sourceTree = ""; }; - OBJ_11 /* PayPalNativePaymentsTests */ = { - isa = PBXGroup; - children = ( - 3D25238B273979170099E4EB /* MockNativeCheckoutProvider.swift */, - CBD6004628D0C24900C3EFF6 /* MockPayPalDelegate.swift */, - 065EBD8B272865E4001E81C4 /* PayPalClient_Tests.swift */, - 067C92E427270FDE009E3054 /* PayPalEnvironment_Tests.swift */, - 8071AFA629C8BEF3008A39E9 /* PayPalNativeShippingAddress_Tests.swift */, - 8071AFA429C8BD8C008A39E9 /* PayPalNativeShippingMethod_Tests.swift */, - CBC16DDA29EE1BF800307117 /* MockPayPalNativeShipping.swift */, - CBC16DDC29EE2C9000307117 /* MockShippingChangeActions.swift */, - CBC16DDF29EE2F8200307117 /* PayPalNativePaysheetAction_Tests.swift */, - CBC16DF529EECCB900307117 /* NativeCheckoutProvider_Tests.swift */, - ); - name = PayPalNativePaymentsTests; - path = UnitTests/PayPalNativePaymentsTests; - sourceTree = SOURCE_ROOT; - }; OBJ_13 /* Products */ = { isa = PBXGroup; children = ( - "PayPal::PayPalTests::Product" /* PayPalNativeCheckoutTests.xctest */, 80B9F85126B8750000D67843 /* CorePayments.framework */, 8034A9E326B875C90055AF13 /* CorePaymentsTests.xctest */, 80E8DAD926B8783800FAFC3F /* CardPayments.framework */, 80E8DAE626B8785D00FAFC3F /* CardPaymentsTests.xctest */, - 80DB6F0E26B89D9600277E54 /* PayPalNativePayments.framework */, 80E743F8270E40CE00BACECA /* TestShared.framework */, BCF735D227D1583200A52E03 /* FraudProtection.framework */, BCF735E127D1583400A52E03 /* FraudProtectionTests.xctest */, @@ -869,7 +731,6 @@ 80E8DADA26B8783800FAFC3F /* CardPayments */, 80B9F85226B8750000D67843 /* CorePayments */, BCF735C127D157CD00A52E03 /* FraudProtection */, - 80DB6F0F26B89D9600277E54 /* PayPalNativePayments */, BCFAC71927ED044300C3AF00 /* PaymentButtons */, BE4F784227EB627100FF4C0E /* PayPalWebPayments */, ); @@ -879,13 +740,6 @@ /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ - 80DB6F0926B89D9600277E54 /* Headers */ = { - isa = PBXHeadersBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; 80E743F3270E40CE00BACECA /* Headers */ = { isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; @@ -946,27 +800,6 @@ productReference = 80B9F85126B8750000D67843 /* CorePayments.framework */; productType = "com.apple.product-type.framework"; }; - 80DB6F0D26B89D9600277E54 /* PayPalNativePayments */ = { - isa = PBXNativeTarget; - buildConfigurationList = 80DB6F1226B89D9600277E54 /* Build configuration list for PBXNativeTarget "PayPalNativePayments" */; - buildPhases = ( - 80DB6F0926B89D9600277E54 /* Headers */, - 80DB6F0A26B89D9600277E54 /* Sources */, - 80DB6F0B26B89D9600277E54 /* Frameworks */, - 80DB6F0C26B89D9600277E54 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - name = PayPalNativePayments; - packageProductDependencies = ( - BC43406E27E91C2700F70193 /* PayPalCheckout */, - ); - productName = PayPal; - productReference = 80DB6F0E26B89D9600277E54 /* PayPalNativePayments.framework */; - productType = "com.apple.product-type.framework"; - }; 80E743F7270E40CE00BACECA /* TestShared */ = { isa = PBXNativeTarget; buildConfigurationList = 80E743FC270E40CF00BACECA /* Build configuration list for PBXNativeTarget "TestShared" */; @@ -1129,23 +962,6 @@ productReference = BCFAC71827ED043200C3AF00 /* PayPalUITests.xctest */; productType = "com.apple.product-type.bundle.unit-test"; }; - "PayPal::PayPalTests" /* PayPalNativePaymentsTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = OBJ_36 /* Build configuration list for PBXNativeTarget "PayPalNativePaymentsTests" */; - buildPhases = ( - OBJ_39 /* Sources */, - OBJ_41 /* Frameworks */, - ); - buildRules = ( - ); - dependencies = ( - 80903D4926BADDD7003A381D /* PBXTargetDependency */, - ); - name = PayPalNativePaymentsTests; - productName = PayPalTests; - productReference = "PayPal::PayPalTests::Product" /* PayPalNativeCheckoutTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -1167,10 +983,6 @@ LastSwiftMigration = 1300; ProvisioningStyle = Automatic; }; - 80DB6F0D26B89D9600277E54 = { - CreatedOnToolsVersion = 13.0; - LastSwiftMigration = 1300; - }; 80E743F7270E40CE00BACECA = { CreatedOnToolsVersion = 13.0; LastSwiftMigration = 1300; @@ -1206,7 +1018,6 @@ ); mainGroup = OBJ_5; packageReferences = ( - 0659BFCB2721D3A3001FB3BF /* XCRemoteSwiftPackageReference "paypalcheckout-ios" */, ); productRefGroup = OBJ_13 /* Products */; projectDirPath = ""; @@ -1218,8 +1029,6 @@ 8034A9E226B875C90055AF13 /* CorePaymentsTests */, BCF735C227D1583200A52E03 /* FraudProtection */, BCF735D327D1583400A52E03 /* FraudProtectionTests */, - 80DB6F0D26B89D9600277E54 /* PayPalNativePayments */, - "PayPal::PayPalTests" /* PayPalNativePaymentsTests */, BCFAC6EB27ED042500C3AF00 /* PaymentButtons */, BCFAC70A27ED043100C3AF00 /* PaymentButtonsTests */, BCD5C46727E9200800B074D5 /* PayPalWebPayments */, @@ -1245,14 +1054,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 80DB6F0C26B89D9600277E54 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 3BE7386B2B9A66F600598F05 /* PrivacyInfo.xcprivacy in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 80E743F6270E40CE00BACECA /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -1379,24 +1180,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 80DB6F0A26B89D9600277E54 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - BCF59E5F27E91ECC00CDFD47 /* NativeCheckoutStartable.swift in Sources */, - BC900B7D27AC307A00D48DBA /* PayPalNativeCheckoutDelegate.swift in Sources */, - CBC16DD729E99D0D00307117 /* PayPalNativePaysheetActions.swift in Sources */, - 53A2A4E228A182AC0093441C /* NativeCheckoutProvider.swift in Sources */, - 80DBC9DC29C350A900462539 /* PayPalNativeCheckoutRequest.swift in Sources */, - 80DBC9DA29C340D900462539 /* PayPalNativeCheckoutResult.swift in Sources */, - 80D0C1382731CC9B00548A3D /* PayPalNativeCheckoutClient.swift in Sources */, - 80DBC9E029C3B8A800462539 /* PayPalNativeShippingMethod.swift in Sources */, - 80DBC9DE29C3B57200462539 /* PayPalNativeShippingAddress.swift in Sources */, - BE711621272358E200165069 /* Environment+Extension.swift in Sources */, - BE71161C27234B8A00165069 /* PayPalNativePaymentsError.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 80E743F4270E40CE00BACECA /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -1422,13 +1205,11 @@ 8048D28C270B9DE00072214A /* ConfirmPaymentSourceResponse.swift in Sources */, 3D1763A22720722A00652E1C /* CardResult.swift in Sources */, BC0A82A5270B9533006E9A21 /* ConfirmPaymentSourceRequest.swift in Sources */, - CB4BE2802847F01000EA2DD1 /* CardDelegate.swift in Sources */, 3BD82DBB2A835AF900CBE764 /* UpdateSetupTokenResponse.swift in Sources */, 80E8DAE126B8784600FAFC3F /* Card.swift in Sources */, 3B22E8B82A841AEA00962E34 /* CardVaultResult.swift in Sources */, CBC16DD529E99B4600307117 /* PaymentSource.swift in Sources */, - 80DCC59E2719DB6F00EC7C5A /* CardClientError.swift in Sources */, - 3B22E8B62A840ECF00962E34 /* CardVaultDelegate.swift in Sources */, + 80DCC59E2719DB6F00EC7C5A /* CardError.swift in Sources */, 3BDB34942A80CE6E008100D7 /* CardVaultRequest.swift in Sources */, 80B8B2FC2A8EBBFD00AB60CD /* VaultPaymentTokensAPI.swift in Sources */, CB4BE27D2847AF6F00EA2DD1 /* SCA.swift in Sources */, @@ -1443,10 +1224,8 @@ 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 */, 3B783DC32B7A69C2004623DB /* FakeUpdateSetupTokenResponse.swift in Sources */, BC0A82A6270B9954006E9A21 /* ConfirmPaymentSourceRequest_Tests.swift in Sources */, 808E3EDD2A981F240017FE46 /* MockCheckoutOrdersAPI.swift in Sources */, @@ -1462,9 +1241,7 @@ 3B3C511E2B2395B5009125FE /* PayPalVaultResult.swift in Sources */, BE4F785327EB656400FF4C0E /* Environment+PayPalWebCheckout.swift in Sources */, BE4F785427EB656400FF4C0E /* PayPalWebCheckoutClient.swift in Sources */, - 3B3C51122B20C962009125FE /* PayPalVaultDelegate.swift in Sources */, - BE4F785527EB656400FF4C0E /* PayPalWebCheckoutClientError.swift in Sources */, - BE4F785627EB656400FF4C0E /* PayPalWebCheckoutDelegate.swift in Sources */, + BE4F785527EB656400FF4C0E /* PayPalError.swift in Sources */, BE4F785727EB656400FF4C0E /* PayPalWebCheckoutRequest.swift in Sources */, BE4F785827EB656400FF4C0E /* PayPalWebCheckoutResult.swift in Sources */, CB51FF7227FCB947001A97F5 /* PayPalWebCheckoutFundingSource.swift in Sources */, @@ -1475,10 +1252,8 @@ isa = PBXSourcesBuildPhase; buildActionMask = 0; files = ( - 3B3C51182B2221C9009125FE /* MockPayPalVaultDelegate.swift in Sources */, BCE8A7FB27EA5B1D00AC301B /* Environment+PayPalWebCheckout_Tests.swift in Sources */, BCE8A7F927EA555800AC301B /* PayPalWebCheckoutClient_Tests.swift in Sources */, - BCE8A7F327EA531C00AC301B /* MockPayPalWebDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1538,23 +1313,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - OBJ_39 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 0; - files = ( - 8071AFA729C8BEF3008A39E9 /* PayPalNativeShippingAddress_Tests.swift in Sources */, - CBC16DF629EECCB900307117 /* NativeCheckoutProvider_Tests.swift in Sources */, - 8071AFA529C8BD8C008A39E9 /* PayPalNativeShippingMethod_Tests.swift in Sources */, - CBC16DE029EE2F8200307117 /* PayPalNativePaysheetAction_Tests.swift in Sources */, - 067C92E527270FDE009E3054 /* PayPalEnvironment_Tests.swift in Sources */, - 065EBD8C272865E4001E81C4 /* PayPalClient_Tests.swift in Sources */, - CB1AC3C22982CDB10081AED6 /* MockNativeCheckoutProvider.swift in Sources */, - CBC16DDD29EE2C9000307117 /* MockShippingChangeActions.swift in Sources */, - CBC16DDB29EE1BF800307117 /* MockPayPalNativeShipping.swift in Sources */, - CBD6004728D0C24A00C3EFF6 /* MockPayPalDelegate.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -1563,11 +1321,6 @@ target = 80B9F85026B8750000D67843 /* CorePayments */; targetProxy = 8034A9E826B875C90055AF13 /* PBXContainerItemProxy */; }; - 80903D4926BADDD7003A381D /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 80DB6F0D26B89D9600277E54 /* PayPalNativePayments */; - targetProxy = 80903D4826BADDD7003A381D /* PBXContainerItemProxy */; - }; 80E8DAEC26B8785D00FAFC3F /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 80E8DAD826B8783800FAFC3F /* CardPayments */; @@ -1884,163 +1637,6 @@ }; name = Release; }; - 80DB6F1326B89D9600277E54 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 43253H4X22; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_STRICT_OBJC_MSGSEND = YES; - ENABLE_TESTABILITY = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_DYNAMIC_NO_PIC = NO; - GCC_NO_COMMON_BLOCKS = YES; - GCC_PREPROCESSOR_DEFINITIONS = ( - "DEBUG=1", - "$(inherited)", - ); - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.paypal.ios-sdk.PayPalNativePayments"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SUPPORTS_MACCATALYST = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Debug; - }; - 80DB6F1426B89D9600277E54 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ALWAYS_SEARCH_USER_PATHS = NO; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_CXX_LANGUAGE_STANDARD = "gnu++17"; - CLANG_CXX_LIBRARY = "libc++"; - CLANG_ENABLE_MODULES = YES; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; - CLANG_WARN_BOOL_CONVERSION = YES; - CLANG_WARN_COMMA = YES; - CLANG_WARN_CONSTANT_CONVERSION = YES; - CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; - CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_EMPTY_BODY = YES; - CLANG_WARN_ENUM_CONVERSION = YES; - CLANG_WARN_INFINITE_RECURSION = YES; - CLANG_WARN_INT_CONVERSION = YES; - CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; - CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; - CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; - CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; - CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; - CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; - CLANG_WARN_STRICT_PROTOTYPES = YES; - CLANG_WARN_SUSPICIOUS_MOVE = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CLANG_WARN_UNREACHABLE_CODE = YES; - CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEFINES_MODULE = YES; - DEVELOPMENT_TEAM = 43253H4X22; - DYLIB_COMPATIBILITY_VERSION = 1; - DYLIB_CURRENT_VERSION = 1; - DYLIB_INSTALL_NAME_BASE = "@rpath"; - ENABLE_NS_ASSERTIONS = NO; - ENABLE_STRICT_OBJC_MSGSEND = YES; - GCC_C_LANGUAGE_STANDARD = gnu11; - GCC_NO_COMMON_BLOCKS = YES; - GCC_WARN_64_TO_32_BIT_CONVERSION = YES; - GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; - GCC_WARN_UNDECLARED_SELECTOR = YES; - GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; - GCC_WARN_UNUSED_FUNCTION = YES; - GCC_WARN_UNUSED_VARIABLE = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@loader_path/Frameworks", - ); - MARKETING_VERSION = 1.0; - MTL_ENABLE_DEBUG_INFO = NO; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = "com.paypal.ios-sdk.PayPalNativePayments"; - PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; - SDKROOT = iphoneos; - SKIP_INSTALL = YES; - SUPPORTS_MACCATALYST = NO; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - VALIDATE_PRODUCT = YES; - VERSIONING_SYSTEM = "apple-generic"; - VERSION_INFO_PREFIX = ""; - }; - name = Release; - }; 80E743FD270E40CF00BACECA /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2649,7 +2245,6 @@ BCD5C49227E9201400B074D5 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 1; DRIVERKIT_DEPLOYMENT_TARGET = 19.0; @@ -2681,7 +2276,6 @@ BCD5C49327E9201400B074D5 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { - ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = 1; DRIVERKIT_DEPLOYMENT_TARGET = 19.0; @@ -3248,66 +2842,6 @@ }; name = Debug; }; - OBJ_37 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; - DRIVERKIT_DEPLOYMENT_TARGET = 19.0; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - GENERATE_INFOPLIST_FILE = YES; - HEADER_SEARCH_PATHS = "$(inherited)"; - INFOPLIST_FILE = ""; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@loader_path/../Frameworks", - "@loader_path/Frameworks", - ); - OTHER_CFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = "com.paypal.ios-sdk.PayPalNativePaymentsTests"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; - SWIFT_VERSION = 5.0; - TARGET_NAME = PayPalNativeCheckoutTests; - }; - name = Debug; - }; - OBJ_38 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - CLANG_ENABLE_MODULES = YES; - CURRENT_PROJECT_VERSION = 1; - DRIVERKIT_DEPLOYMENT_TARGET = 19.0; - EMBEDDED_CONTENT_CONTAINS_SWIFT = YES; - FRAMEWORK_SEARCH_PATHS = ( - "$(inherited)", - "$(PLATFORM_DIR)/Developer/Library/Frameworks", - ); - GENERATE_INFOPLIST_FILE = YES; - HEADER_SEARCH_PATHS = "$(inherited)"; - INFOPLIST_FILE = ""; - IPHONEOS_DEPLOYMENT_TARGET = 14.0; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@loader_path/../Frameworks", - "@loader_path/Frameworks", - ); - OTHER_CFLAGS = "$(inherited)"; - OTHER_LDFLAGS = "$(inherited)"; - OTHER_SWIFT_FLAGS = "$(inherited)"; - PRODUCT_BUNDLE_IDENTIFIER = "com.paypal.ios-sdk.PayPalNativePayments"; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited)"; - SWIFT_VERSION = 5.0; - TARGET_NAME = PayPalNativeCheckoutTests; - }; - name = Release; - }; OBJ_4 /* Release */ = { isa = XCBuildConfiguration; buildSettings = { @@ -3353,15 +2887,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 80DB6F1226B89D9600277E54 /* Build configuration list for PBXNativeTarget "PayPalNativePayments" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 80DB6F1326B89D9600277E54 /* Debug */, - 80DB6F1426B89D9600277E54 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 80E743FC270E40CF00BACECA /* Build configuration list for PBXNativeTarget "TestShared" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -3452,35 +2977,7 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - OBJ_36 /* Build configuration list for PBXNativeTarget "PayPalNativePaymentsTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - OBJ_37 /* Debug */, - OBJ_38 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; /* End XCConfigurationList section */ - -/* Begin XCRemoteSwiftPackageReference section */ - 0659BFCB2721D3A3001FB3BF /* XCRemoteSwiftPackageReference "paypalcheckout-ios" */ = { - isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/paypal/paypalcheckout-ios"; - requirement = { - kind = exactVersion; - version = 1.3.0; - }; - }; -/* End XCRemoteSwiftPackageReference section */ - -/* Begin XCSwiftPackageProductDependency section */ - BC43406E27E91C2700F70193 /* PayPalCheckout */ = { - isa = XCSwiftPackageProductDependency; - package = 0659BFCB2721D3A3001FB3BF /* XCRemoteSwiftPackageReference "paypalcheckout-ios" */; - productName = PayPalCheckout; - }; -/* End XCSwiftPackageProductDependency section */ }; rootObject = OBJ_1 /* Project object */; } diff --git a/PayPal.xcworkspace/xcshareddata/swiftpm/Package.resolved b/PayPal.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 5deda352e..000000000 --- a/PayPal.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,14 +0,0 @@ -{ - "pins" : [ - { - "identity" : "paypalcheckout-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/paypal/paypalcheckout-ios", - "state" : { - "revision" : "f477176da4c6780c8586b493c604f0ab74d9be49", - "version" : "1.2.0" - } - } - ], - "version" : 2 -} diff --git a/SampleApps/SPMTest/SPMTest.xcodeproj/project.pbxproj b/SampleApps/SPMTest/SPMTest.xcodeproj/project.pbxproj index 7bb60ea1b..43db70158 100644 --- a/SampleApps/SPMTest/SPMTest.xcodeproj/project.pbxproj +++ b/SampleApps/SPMTest/SPMTest.xcodeproj/project.pbxproj @@ -7,12 +7,11 @@ objects = { /* Begin PBXBuildFile section */ - 3B23E8E42A7030E90075E792 /* CardPayments in Frameworks */ = {isa = PBXBuildFile; productRef = 3B23E8E32A7030E90075E792 /* CardPayments */; }; - 3B23E8E62A7030E90075E792 /* CorePayments in Frameworks */ = {isa = PBXBuildFile; productRef = 3B23E8E52A7030E90075E792 /* CorePayments */; }; - 3B23E8E82A7030E90075E792 /* FraudProtection in Frameworks */ = {isa = PBXBuildFile; productRef = 3B23E8E72A7030E90075E792 /* FraudProtection */; }; - 3B23E8EA2A7030E90075E792 /* PayPalNativePayments in Frameworks */ = {isa = PBXBuildFile; productRef = 3B23E8E92A7030E90075E792 /* PayPalNativePayments */; }; - 3B23E8EC2A7030E90075E792 /* PayPalWebPayments in Frameworks */ = {isa = PBXBuildFile; productRef = 3B23E8EB2A7030E90075E792 /* PayPalWebPayments */; }; - 3B23E8EE2A7030E90075E792 /* PaymentButtons in Frameworks */ = {isa = PBXBuildFile; productRef = 3B23E8ED2A7030E90075E792 /* PaymentButtons */; }; + 3B3F5DBB2CB85AB200DEB561 /* CardPayments in Frameworks */ = {isa = PBXBuildFile; productRef = 3B3F5DBA2CB85AB200DEB561 /* CardPayments */; }; + 3B3F5DBD2CB85AB200DEB561 /* CorePayments in Frameworks */ = {isa = PBXBuildFile; productRef = 3B3F5DBC2CB85AB200DEB561 /* CorePayments */; }; + 3B3F5DBF2CB85AB200DEB561 /* FraudProtection in Frameworks */ = {isa = PBXBuildFile; productRef = 3B3F5DBE2CB85AB200DEB561 /* FraudProtection */; }; + 3B3F5DC12CB85AB200DEB561 /* PayPalWebPayments in Frameworks */ = {isa = PBXBuildFile; productRef = 3B3F5DC02CB85AB200DEB561 /* PayPalWebPayments */; }; + 3B3F5DC32CB85F9700DEB561 /* PaymentButtons in Frameworks */ = {isa = PBXBuildFile; productRef = 3B3F5DC22CB85F9700DEB561 /* PaymentButtons */; }; 80C6FD6726BD8AFF00B73E76 /* SPMTestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80C6FD6626BD8AFF00B73E76 /* SPMTestApp.swift */; }; 80C6FD6926BD8AFF00B73E76 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80C6FD6826BD8AFF00B73E76 /* ContentView.swift */; }; 80C6FD6B26BD8B0000B73E76 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 80C6FD6A26BD8B0000B73E76 /* Assets.xcassets */; }; @@ -32,12 +31,11 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 3B23E8E62A7030E90075E792 /* CorePayments in Frameworks */, - 3B23E8EE2A7030E90075E792 /* PaymentButtons in Frameworks */, - 3B23E8E42A7030E90075E792 /* CardPayments in Frameworks */, - 3B23E8EC2A7030E90075E792 /* PayPalWebPayments in Frameworks */, - 3B23E8E82A7030E90075E792 /* FraudProtection in Frameworks */, - 3B23E8EA2A7030E90075E792 /* PayPalNativePayments in Frameworks */, + 3B3F5DBD2CB85AB200DEB561 /* CorePayments in Frameworks */, + 3B3F5DC32CB85F9700DEB561 /* PaymentButtons in Frameworks */, + 3B3F5DBB2CB85AB200DEB561 /* CardPayments in Frameworks */, + 3B3F5DBF2CB85AB200DEB561 /* FraudProtection in Frameworks */, + 3B3F5DC12CB85AB200DEB561 /* PayPalWebPayments in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -104,12 +102,11 @@ ); name = SPMTest; packageProductDependencies = ( - 3B23E8E32A7030E90075E792 /* CardPayments */, - 3B23E8E52A7030E90075E792 /* CorePayments */, - 3B23E8E72A7030E90075E792 /* FraudProtection */, - 3B23E8E92A7030E90075E792 /* PayPalNativePayments */, - 3B23E8EB2A7030E90075E792 /* PayPalWebPayments */, - 3B23E8ED2A7030E90075E792 /* PaymentButtons */, + 3B3F5DBA2CB85AB200DEB561 /* CardPayments */, + 3B3F5DBC2CB85AB200DEB561 /* CorePayments */, + 3B3F5DBE2CB85AB200DEB561 /* FraudProtection */, + 3B3F5DC02CB85AB200DEB561 /* PayPalWebPayments */, + 3B3F5DC22CB85F9700DEB561 /* PaymentButtons */, ); productName = SPMTest; productReference = 80C6FD6326BD8AFF00B73E76 /* SPMTest.app */; @@ -140,7 +137,7 @@ ); mainGroup = 80C6FD5A26BD8AFF00B73E76; packageReferences = ( - 3B23E8E22A7030E90075E792 /* XCRemoteSwiftPackageReference "paypal-ios" */, + 3B3F5DB92CB85AB200DEB561 /* XCRemoteSwiftPackageReference "paypal-ios" */, ); productRefGroup = 80C6FD6426BD8AFF00B73E76 /* Products */; projectDirPath = ""; @@ -376,9 +373,9 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 3B23E8E22A7030E90075E792 /* XCRemoteSwiftPackageReference "paypal-ios" */ = { + 3B3F5DB92CB85AB200DEB561 /* XCRemoteSwiftPackageReference "paypal-ios" */ = { isa = XCRemoteSwiftPackageReference; - repositoryURL = "https://github.com/paypal/paypal-ios"; + repositoryURL = "https://github.com/paypal/paypal-ios.git"; requirement = { branch = main; kind = branch; @@ -387,34 +384,29 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 3B23E8E32A7030E90075E792 /* CardPayments */ = { + 3B3F5DBA2CB85AB200DEB561 /* CardPayments */ = { isa = XCSwiftPackageProductDependency; - package = 3B23E8E22A7030E90075E792 /* XCRemoteSwiftPackageReference "paypal-ios" */; + package = 3B3F5DB92CB85AB200DEB561 /* XCRemoteSwiftPackageReference "paypal-ios" */; productName = CardPayments; }; - 3B23E8E52A7030E90075E792 /* CorePayments */ = { + 3B3F5DBC2CB85AB200DEB561 /* CorePayments */ = { isa = XCSwiftPackageProductDependency; - package = 3B23E8E22A7030E90075E792 /* XCRemoteSwiftPackageReference "paypal-ios" */; + package = 3B3F5DB92CB85AB200DEB561 /* XCRemoteSwiftPackageReference "paypal-ios" */; productName = CorePayments; }; - 3B23E8E72A7030E90075E792 /* FraudProtection */ = { + 3B3F5DBE2CB85AB200DEB561 /* FraudProtection */ = { isa = XCSwiftPackageProductDependency; - package = 3B23E8E22A7030E90075E792 /* XCRemoteSwiftPackageReference "paypal-ios" */; + package = 3B3F5DB92CB85AB200DEB561 /* XCRemoteSwiftPackageReference "paypal-ios" */; productName = FraudProtection; }; - 3B23E8E92A7030E90075E792 /* PayPalNativePayments */ = { + 3B3F5DC02CB85AB200DEB561 /* PayPalWebPayments */ = { isa = XCSwiftPackageProductDependency; - package = 3B23E8E22A7030E90075E792 /* XCRemoteSwiftPackageReference "paypal-ios" */; - productName = PayPalNativePayments; - }; - 3B23E8EB2A7030E90075E792 /* PayPalWebPayments */ = { - isa = XCSwiftPackageProductDependency; - package = 3B23E8E22A7030E90075E792 /* XCRemoteSwiftPackageReference "paypal-ios" */; + package = 3B3F5DB92CB85AB200DEB561 /* XCRemoteSwiftPackageReference "paypal-ios" */; productName = PayPalWebPayments; }; - 3B23E8ED2A7030E90075E792 /* PaymentButtons */ = { + 3B3F5DC22CB85F9700DEB561 /* PaymentButtons */ = { isa = XCSwiftPackageProductDependency; - package = 3B23E8E22A7030E90075E792 /* XCRemoteSwiftPackageReference "paypal-ios" */; + package = 3B3F5DB92CB85AB200DEB561 /* XCRemoteSwiftPackageReference "paypal-ios" */; productName = PaymentButtons; }; /* End XCSwiftPackageProductDependency section */ diff --git a/SampleApps/SPMTest/SPMTest.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/SampleApps/SPMTest/SPMTest.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved deleted file mode 100644 index 49c3f73ac..000000000 --- a/SampleApps/SPMTest/SPMTest.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ /dev/null @@ -1,23 +0,0 @@ -{ - "pins" : [ - { - "identity" : "paypal-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/paypal/paypal-ios", - "state" : { - "branch" : "main", - "revision" : "924418c4dafbcbd820c35c02c13acdda2d9a820e" - } - }, - { - "identity" : "paypalcheckout-ios", - "kind" : "remoteSourceControl", - "location" : "https://github.com/paypal/paypalcheckout-ios", - "state" : { - "revision" : "7c6750e1316c6a3d656e90497271de68c63219f1", - "version" : "1.1.0" - } - } - ], - "version" : 2 -} diff --git a/SampleApps/SPMTest/SPMTest/ContentView.swift b/SampleApps/SPMTest/SPMTest/ContentView.swift index 49de5079e..39230da95 100644 --- a/SampleApps/SPMTest/SPMTest/ContentView.swift +++ b/SampleApps/SPMTest/SPMTest/ContentView.swift @@ -1,7 +1,6 @@ import SwiftUI import CardPayments import PayPalWebPayments -import PayPalNativePayments import FraudProtection import PaymentButtons import CorePayments diff --git a/Sources/CardPayments/CardClient.swift b/Sources/CardPayments/CardClient.swift index 8c77236d4..c40d5ffb1 100644 --- a/Sources/CardPayments/CardClient.swift +++ b/Sources/CardPayments/CardClient.swift @@ -5,9 +5,6 @@ import CorePayments #endif public class CardClient: NSObject { - - public weak var delegate: CardDelegate? - public weak var vaultDelegate: CardVaultDelegate? private let checkoutOrdersAPI: CheckoutOrdersAPI private let vaultAPI: VaultPaymentTokensAPI @@ -43,7 +40,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?, CoreSDKError?) -> Void) { analyticsService = AnalyticsService(coreConfig: config, setupToken: vaultRequest.setupTokenID) analyticsService?.sendEvent("card-payments:vault-wo-purchase:started") Task { @@ -53,29 +53,51 @@ 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: CardError.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: CardError.vaultTokenError, completion: completion) + } + } + } + + /// Updates a setup token with a payment method. Performs + /// 3DS verification if required. If verification is performed, SDK returns a property `didAttemptThreeDSecureAuthentication`. + /// If `didAttempt3DSecureVerification` is `true`, check verification status with `/v3/vault/setup-token/{id}` in your server. + /// - Parameters: + /// - vaultRequest: The request containing setupTokenID and card + /// - Returns: `CardVaultResult` if successful + /// - Throws: An `Error` describing failure + public func vault(_ vaultRequest: CardVaultRequest) async throws -> CardVaultResult { + try await withCheckedThrowingContinuation { continuation in + vault(vaultRequest) { result, error in + if let error { + continuation.resume(throwing: error) + } else if let result { + continuation.resume(returning: result) + } } } } - + /// Approve an order with a card, which validates buyer's card, and if valid, attaches the card as the payment source to the order. /// After the order has been successfully approved, you will need to handle capturing/authorizing the order in your server. /// - Parameters: /// - orderId: Order id for approval /// - request: The request containing the card - public func approveOrder(request: CardRequest) { + /// - 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) { analyticsService = AnalyticsService(coreConfig: config, orderID: request.orderID) analyticsService?.sendEvent("card-payments:3ds:started") Task { @@ -87,33 +109,52 @@ public class CardClient: NSObject { guard getQueryStringParameter(url: url, param: "flow") == "3ds", url.contains("helios"), let url = URL(string: url) else { - self.notifyFailure(with: CardClientError.threeDSecureURLError) + self.notifyCheckoutFailure(with: CardError.threeDSecureURLError, completion: completion) return } analyticsService?.sendEvent("card-payments:3ds:confirm-payment-source:challenge-required") - startThreeDSecureChallenge(url: url, orderId: result.id) + startThreeDSecureChallenge(url: url, orderId: result.id, completion: completion) } else { analyticsService?.sendEvent("card-payments:3ds:confirm-payment-source:succeeded") let cardResult = CardResult(orderID: result.id, status: result.status, didAttemptThreeDSecureAuthentication: false) - notifySuccess(for: cardResult) + notifyCheckoutSuccess(for: cardResult, completion: completion) } } catch let error as CoreSDKError { analyticsService?.sendEvent("card-payments:3ds:confirm-payment-source:failed") - notifyFailure(with: error) + notifyCheckoutFailure(with: error, completion: completion) } catch { analyticsService?.sendEvent("card-payments:3ds:confirm-payment-source:failed") - notifyFailure(with: CardClientError.unknownError) + notifyCheckoutFailure(with: CardError.unknownError, completion: completion) + } + } + } + + /// Approve an order with a card, which validates buyer's card, and if valid, attaches the card as the payment source to the order. + /// After the order has been successfully approved, you will need to handle capturing/authorizing the order in your server. + /// - Parameters: + /// - orderId: Order id for approval + /// - request: The request containing the card + /// - Returns: `CardResult` if successful + /// - Throws: An `Error` describing failure + public func approveOrder(request: CardRequest) async throws -> CardResult { + try await withCheckedThrowingContinuation { continuation in + approveOrder(request: request) { result, error in + if let error { + continuation.resume(throwing: error) + } else if let result { + continuation.resume(returning: result) + } } } } private func startThreeDSecureChallenge( url: URL, - orderId: String + orderId: String, + completion: @escaping (CardResult?, CoreSDKError?) -> Void ) { - delegate?.cardThreeDSecureWillLaunch(self) webAuthenticationSession.start( url: url, @@ -126,20 +167,19 @@ public class CardClient: NSObject { } }, sessionDidComplete: { _, error in - self.delegate?.cardThreeDSecureDidFinish(self) if let error = error { switch error { case ASWebAuthenticationSessionError.canceledLogin: - self.notifyCancellation() + self.notifyCheckoutCancelWithError(with: CardError.threeDSecureCanceledError, completion: completion) return default: - self.notifyFailure(with: CardClientError.threeDSecureError(error)) + self.notifyCheckoutFailure(with: CardError.threeDSecureError(error), completion: completion) return } } let cardResult = CardResult(orderID: orderId, status: nil, didAttemptThreeDSecureAuthentication: true) - self.notifySuccess(for: cardResult) + self.notifyCheckoutSuccess(for: cardResult, completion: completion) } ) } @@ -149,8 +189,11 @@ 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?, CoreSDKError?) -> Void + ) { webAuthenticationSession.start( url: url, @@ -164,52 +207,51 @@ public class CardClient: NSObject { } }, sessionDidComplete: { _, error in - self.vaultDelegate?.cardThreeDSecureDidFinish(self) if let error = error { switch error { case ASWebAuthenticationSessionError.canceledLogin: - self.notifyVaultCancellation() + self.notifyVaultCancelWithError(with: CardError.threeDSecureCanceledError, completion: completion) return default: - self.notifyVaultFailure(with: CardClientError.threeDSecureError(error)) + self.notifyVaultFailure(with: CardError.threeDSecureError(error), completion: completion) return } } let cardVaultResult = CardVaultResult(setupTokenID: setupTokenID, status: nil, didAttemptThreeDSecureAuthentication: true) - self.notifyVaultSuccess(for: cardVaultResult) + self.notifyVaultSuccess(for: cardVaultResult, completion: completion) } ) } - private func notifySuccess(for result: CardResult) { + private func notifyCheckoutSuccess(for result: CardResult, completion: (CardResult?, CoreSDKError?) -> Void) { analyticsService?.sendEvent("card-payments:3ds:succeeded") - delegate?.card(self, didFinishWithResult: result) + completion(result, nil) } - private func notifyFailure(with error: CoreSDKError) { + private func notifyCheckoutFailure(with error: CoreSDKError, completion: (CardResult?, CoreSDKError?) -> Void) { analyticsService?.sendEvent("card-payments:3ds:failed") - delegate?.card(self, didFinishWithError: error) + completion(nil, error) } - - private func notifyVaultSuccess(for vaultResult: CardVaultResult) { - analyticsService?.sendEvent("card-payments:vault-wo-purchase:succeeded") - vaultDelegate?.card(self, didFinishWithVaultResult: vaultResult) + + private func notifyCheckoutCancelWithError(with error: CoreSDKError, completion: (CardResult?, CoreSDKError?) -> Void) { + analyticsService?.sendEvent("card-payments:3ds:challenge:user-canceled") + completion(nil, error) } - private func notifyVaultFailure(with vaultError: CoreSDKError) { - analyticsService?.sendEvent("card-payments:vault-wo-purchase:failed") - vaultDelegate?.card(self, didFinishWithVaultError: vaultError) + private func notifyVaultSuccess(for vaultResult: CardVaultResult, completion: (CardVaultResult?, CoreSDKError?) -> Void) { + analyticsService?.sendEvent("card-payments:vault-wo-purchase:succeeded") + completion(vaultResult, nil) } - private func notifyCancellation() { - analyticsService?.sendEvent("card-payments:3ds:challenge:user-canceled") - delegate?.cardDidCancel(self) + private func notifyVaultFailure(with vaultError: CoreSDKError, completion: (CardVaultResult?, CoreSDKError?) -> Void) { + analyticsService?.sendEvent("card-payments:vault-wo-purchase:failed") + completion(nil, vaultError) } - private func notifyVaultCancellation() { + private func notifyVaultCancelWithError(with vaultError: CoreSDKError, completion: (CardVaultResult?, CoreSDKError?) -> Void) { analyticsService?.sendEvent("card-payments:vault-wo-purchase:challenge:canceled") - vaultDelegate?.cardThreeDSecureDidCancel(self) + completion(nil, vaultError) } } diff --git a/Sources/CardPayments/CardDelegate.swift b/Sources/CardPayments/CardDelegate.swift deleted file mode 100644 index 77c5cace6..000000000 --- a/Sources/CardPayments/CardDelegate.swift +++ /dev/null @@ -1,35 +0,0 @@ -import Foundation -#if canImport(CorePayments) -import CorePayments -#endif - -/// Card delegate to handle events from CardClient -public protocol CardDelegate: AnyObject { - - /// Notify that the Card flow finished with a successful result - /// - Parameters: - /// - client: the CardClient associated with delegate - /// - didFinishWithResult: the successful result from the flow - func card(_ cardClient: CardClient, didFinishWithResult result: CardResult) - - /// Notify that an error occurred in the Cardl flow - /// - Parameters: - /// - client: the CardClient associated with delegate - /// - didFinishWithError: the error returned by the Card flow - func card(_ cardClient: CardClient, didFinishWithError error: CoreSDKError) - - /// Notify that the Card flow has been cancelled - /// - Parameters: - /// - client: the CardClient associated with delegate - func cardDidCancel(_ cardClient: CardClient) - - /// Notify that the 3DS challenge will be launched - /// - Parameters: - /// - client: the CardClient associated with delegate - func cardThreeDSecureWillLaunch(_ cardClient: CardClient) - - /// Notify that the 3DS challenge has finished - /// - Parameters: - /// - client: the CardClient associated with delegate - func cardThreeDSecureDidFinish(_ cardClient: CardClient) -} diff --git a/Sources/CardPayments/CardClientError.swift b/Sources/CardPayments/CardError.swift similarity index 77% rename from Sources/CardPayments/CardClientError.swift rename to Sources/CardPayments/CardError.swift index 3a6522626..f296e7548 100644 --- a/Sources/CardPayments/CardClientError.swift +++ b/Sources/CardPayments/CardError.swift @@ -3,7 +3,7 @@ import Foundation import CorePayments #endif -enum CardClientError { +public enum CardError { static let domain = "CardClientErrorDomain" @@ -37,6 +37,9 @@ enum CardClientError { /// 9. Malformed Deeplink URL from 3DS case malformedDeeplinkURLError + + /// 10. Cancellation from 3DS verification + case threeDSecureCanceledError } static let unknownError = CoreSDKError( @@ -64,7 +67,13 @@ enum CardClientError { domain: domain, errorDescription: "An invalid 3DS URL was returned. Contact developer.paypal.com/support." ) - + + static let threeDSecureCanceledError = CoreSDKError( + code: Code.threeDSecureCanceledError.rawValue, + domain: domain, + errorDescription: "3DS verification has been canceled by the user." + ) + static let noVaultTokenDataError = CoreSDKError( code: Code.noVaultTokenDataError.rawValue, domain: domain, @@ -82,4 +91,12 @@ enum CardClientError { domain: domain, errorDescription: "GraphQLClient is unexpectedly nil." ) + + // Helper function that allows handling of threeDSecure websession cancel errors separately without having to check error code and domain properties. + public static func isThreeDSecureCanceled(_ error: Error) -> Bool { + guard let error = error as? CoreSDKError else { + return false + } + return error.domain == CardError.domain && error.code == CardError.threeDSecureCanceledError.code + } } diff --git a/Sources/CardPayments/CardVaultDelegate.swift b/Sources/CardPayments/CardVaultDelegate.swift deleted file mode 100644 index 4a85cb2aa..000000000 --- a/Sources/CardPayments/CardVaultDelegate.swift +++ /dev/null @@ -1,35 +0,0 @@ -import Foundation -#if canImport(CorePayments) -import CorePayments -#endif - -/// CardVault delegate to handle events from CardClient -public protocol CardVaultDelegate: AnyObject { - - /// Notify that the Card vault flow finished with a successful result - /// - Parameters: - /// - client: the CardClient associated with delegate - /// - didFinishWithResult: the successful result from the flow - func card(_ cardClient: CardClient, didFinishWithVaultResult vaultResult: CardVaultResult) - - /// Notify that an error occurred in the Card vault flow - /// - Parameters: - /// - client: the CardClient associated with delegate - /// - didFinishWithError: the error returned by the Card vault flow - func card(_ cardClient: CardClient, didFinishWithVaultError vaultError: CoreSDKError) - - /// Notify that the ThreeDSecure has been cancelled - /// - Parameters: - /// - client: the CardClient associated with delegate - func cardThreeDSecureDidCancel(_ cardClient: CardClient) - - /// Notify that the 3DS challenge will be launched - /// - Parameters: - /// - client: the CardClient associated with delegate - func cardThreeDSecureWillLaunch(_ cardClient: CardClient) - - /// Notify that the 3DS challenge has finished - /// - Parameters: - /// - client: the CardClient associated with delegate - func cardThreeDSecureDidFinish(_ cardClient: CardClient) -} diff --git a/Sources/CorePayments/Networking/HTTP.swift b/Sources/CorePayments/Networking/HTTP.swift index 3cce967ff..ef7ad4231 100644 --- a/Sources/CorePayments/Networking/HTTP.swift +++ b/Sources/CorePayments/Networking/HTTP.swift @@ -22,8 +22,16 @@ class HTTP { httpRequest.headers.forEach { key, value in urlRequest.addValue(value, forHTTPHeaderField: key.rawValue) } - - let (data, response) = try await urlSession.performRequest(with: urlRequest) + + let (data, response): (Data, URLResponse) + do { + (data, response) = try await urlSession.performRequest(with: urlRequest) + } catch _ as URLError { + throw NetworkingClientError.urlSessionError + } catch { + throw NetworkingClientError.unknownError + } + guard let response = response as? HTTPURLResponse else { throw NetworkingClientError.invalidURLResponseError } diff --git a/Sources/CorePayments/Networking/NetworkingClientError.swift b/Sources/CorePayments/Networking/NetworkingClientError.swift index c4cddf902..2a6c7deba 100644 --- a/Sources/CorePayments/Networking/NetworkingClientError.swift +++ b/Sources/CorePayments/Networking/NetworkingClientError.swift @@ -37,13 +37,11 @@ enum NetworkingClientError { errorDescription: "An unknown error occured. Contact developer.paypal.com/support." ) - static let urlSessionError: (String) -> CoreSDKError = { description in - CoreSDKError( - code: Code.urlSessionError.rawValue, - domain: domain, - errorDescription: description - ) - } + static let urlSessionError = CoreSDKError( + code: Code.urlSessionError.rawValue, + domain: domain, + errorDescription: "An error occured during network call. Contact developer.paypal.com/support." + ) static let jsonDecodingError: (String) -> CoreSDKError = { description in CoreSDKError( @@ -80,7 +78,7 @@ enum NetworkingClientError { } static let noGraphQLDataKey = CoreSDKError( - code: Code.noResponseData.rawValue, + code: Code.noGraphQLDataKey.rawValue, domain: domain, errorDescription: "An error occured due to missing `data` key in GraphQL query response. Contact developer.paypal.com/support." ) diff --git a/Sources/PayPalNativePayments/Environment+Extension.swift b/Sources/PayPalNativePayments/Environment+Extension.swift deleted file mode 100644 index cb7ffb1cb..000000000 --- a/Sources/PayPalNativePayments/Environment+Extension.swift +++ /dev/null @@ -1,23 +0,0 @@ -import PayPalCheckout - -#if canImport(CorePayments) -import CorePayments -#endif - -#if COCOAPODS -private typealias PayPalEnvironment = PayPal.Environment -#else -private typealias PayPalEnvironment = CorePayments.Environment -#endif - -extension PayPalEnvironment { - - func toNativeCheckoutSDKEnvironment() -> PayPalCheckout.Environment { - switch self { - case .sandbox: - return .sandbox - case .live: - return .live - } - } -} diff --git a/Sources/PayPalNativePayments/NativeCheckoutProvider.swift b/Sources/PayPalNativePayments/NativeCheckoutProvider.swift deleted file mode 100644 index 9938f6b80..000000000 --- a/Sources/PayPalNativePayments/NativeCheckoutProvider.swift +++ /dev/null @@ -1,87 +0,0 @@ -import Foundation -import UIKit -import PayPalCheckout -#if canImport(CorePayments) -import CorePayments -#endif - -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -class NativeCheckoutProvider: NativeCheckoutStartable { - - /// Used in POST body for FPTI analytics. - var correlationID: String? - - private let checkout: CheckoutProtocol.Type - - init(_ mxo: CheckoutProtocol.Type = Checkout.self) { - self.checkout = mxo - } - - // swiftlint:disable:next function_parameter_count - func start( - presentingViewController: UIViewController?, - orderID: String, - onStartableApprove: @escaping StartableApproveCallback, - onStartableShippingChange: @escaping StartableShippingCallback, - onStartableCancel: @escaping StartableCancelCallback, - onStartableError: @escaping StartableErrorCallback, - nxoConfig: CheckoutConfig - ) { - checkout.showsExitAlert = false - checkout.set(config: nxoConfig) - guaranteeMainThread { - self.checkout.start( - presentingViewController: presentingViewController, - createOrder: { createOrderAction in - createOrderAction.set(orderId: orderID) - }, - onApprove: { approval in - self.correlationID = approval.data.correlationIDs.riskCorrelationID - onStartableApprove(approval.data.ecToken, approval.data.payerID) - }, - onShippingChange: { shippingChangeData, shippingChangeActions in - let type = shippingChangeData.type - let shippingActions = PayPalNativePaysheetActions(shippingChangeActions) - let shippingAddress = PayPalNativeShippingAddress(shippingChangeData.selectedShippingAddress) - var shippingMethod: PayPalNativeShippingMethod? - if let selectedMethod = shippingChangeData.selectedShippingMethod { - shippingMethod = PayPalNativeShippingMethod(selectedMethod) - } - onStartableShippingChange(type, shippingActions, shippingAddress, shippingMethod) - }, - onCancel: { onStartableCancel() }, - onError: { error in - self.correlationID = error.correlationIDs.riskCorrelationID - onStartableError(error.reason) - } - ) - } - } - - private func guaranteeMainThread(_ work: @escaping () -> Void) { - if Thread.isMainThread { - work() - } else { - DispatchQueue.main.async(execute: work) - } - } -} - -protocol CheckoutProtocol { - - // swiftlint:disable:next function_parameter_count - static func start( - presentingViewController: UIViewController?, - createOrder: PayPalCheckout.CheckoutConfig.CreateOrderCallback?, - onApprove: PayPalCheckout.CheckoutConfig.ApprovalCallback?, - onShippingChange: PayPalCheckout.CheckoutConfig.ShippingChangeCallback?, - onCancel: PayPalCheckout.CheckoutConfig.CancelCallback?, - onError: PayPalCheckout.CheckoutConfig.ErrorCallback? - ) - - static var showsExitAlert: Bool { get set } - - static func set(config: PayPalCheckout.CheckoutConfig) -} - -extension Checkout: CheckoutProtocol { } diff --git a/Sources/PayPalNativePayments/NativeCheckoutStartable.swift b/Sources/PayPalNativePayments/NativeCheckoutStartable.swift deleted file mode 100644 index 9e68d2e9b..000000000 --- a/Sources/PayPalNativePayments/NativeCheckoutStartable.swift +++ /dev/null @@ -1,34 +0,0 @@ -import Foundation -import UIKit -import PayPalCheckout -#if canImport(CorePayments) -import CorePayments -#endif - -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -protocol NativeCheckoutStartable { - - /// Used in POST body for FPTI analytics. - var correlationID: String? { get set } - - typealias StartableApproveCallback = (String, String) -> Void - typealias StartableShippingCallback = ( - ShippingChangeType, - PayPalNativePaysheetActions, - PayPalNativeShippingAddress, - PayPalNativeShippingMethod? - ) -> Void - typealias StartableCancelCallback = () -> Void - typealias StartableErrorCallback = (String) -> Void - - // swiftlint:disable:next function_parameter_count - func start( - presentingViewController: UIViewController?, - orderID: String, - onStartableApprove: @escaping StartableApproveCallback, - onStartableShippingChange: @escaping StartableShippingCallback, - onStartableCancel: @escaping StartableCancelCallback, - onStartableError: @escaping StartableErrorCallback, - nxoConfig: CheckoutConfig - ) -} diff --git a/Sources/PayPalNativePayments/PayPalNativeCheckoutClient.swift b/Sources/PayPalNativePayments/PayPalNativeCheckoutClient.swift deleted file mode 100644 index 29aa37fbb..000000000 --- a/Sources/PayPalNativePayments/PayPalNativeCheckoutClient.swift +++ /dev/null @@ -1,135 +0,0 @@ -import UIKit -import PayPalCheckout -#if canImport(CorePayments) -import CorePayments -#endif - -/// PayPal Paysheet to handle PayPal transaction -/// encapsulates instance to communicate with nxo -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -public class PayPalNativeCheckoutClient { - - public weak var delegate: PayPalNativeCheckoutDelegate? - public weak var shippingDelegate: PayPalNativeShippingDelegate? - - /// Used in POST body for FPTI analytics. - private var correlationID: String? - private let nativeCheckoutProvider: NativeCheckoutStartable - private let networkingClient: NetworkingClient - private let config: CoreConfig - private var analyticsService: AnalyticsService? - - /// Initialize a PayPalNativeCheckoutClient to process PayPal transaction - /// - Parameters: - /// - config: The CoreConfig object - @available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") - public convenience init(config: CoreConfig) { - self.init( - config: config, - nativeCheckoutProvider: NativeCheckoutProvider(), - networkingClient: NetworkingClient(coreConfig: config) - ) - } - - init(config: CoreConfig, nativeCheckoutProvider: NativeCheckoutStartable, networkingClient: NetworkingClient) { - self.config = config - self.nativeCheckoutProvider = nativeCheckoutProvider - self.networkingClient = networkingClient - } - - // NEXT_MAJOR_VERSION: - Change to non-async - /// Present PayPal Paysheet and start a PayPal transaction - /// - Parameters: - /// - request: The PayPalNativeCheckoutRequest for the transaction - /// - presentingViewController: the ViewController to present PayPalPaysheet on, if not provided, the Paysheet will be presented on your top-most ViewController - @available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") - public func start( - request: PayPalNativeCheckoutRequest, - presentingViewController: UIViewController? = nil - ) async { - correlationID = State.correlationIDs.riskCorrelationID - analyticsService = AnalyticsService(coreConfig: config, orderID: request.orderID) - - let nxoConfig = CheckoutConfig( - clientID: config.clientID, - createOrder: nil, - onApprove: nil, - onShippingChange: nil, - onCancel: nil, - onError: nil, - environment: config.environment.toNativeCheckoutSDKEnvironment() - ) - nxoConfig.authConfig.userEmail = request.userAuthenticationEmail - delegate?.paypalWillStart(self) - - analyticsService?.sendEvent("paypal-native-payments:started") - self.nativeCheckoutProvider.start( - presentingViewController: presentingViewController, - orderID: request.orderID, - onStartableApprove: { ecToken, payerID in - let result = PayPalNativeCheckoutResult( - orderID: ecToken, - payerID: payerID - ) - - self.notifySuccess(for: result) - }, - onStartableShippingChange: { shippingType, shippingAction, shippingAddress, shippingMethod in - switch shippingType { - case .shippingAddress: - self.notifyShippingChange(shippingActions: shippingAction, shippingAddress: shippingAddress) - case .shippingMethod: - guard let selectedShippingMethod = shippingMethod else { - return - } - self.notifyShippingMethod( - shippingActions: shippingAction, - shippingMethod: selectedShippingMethod - ) - @unknown default: - break // do nothing - } - }, - onStartableCancel: { - self.notifyCancellation() - }, - onStartableError: { errorReason in - self.notifyFailure(with: errorReason) - }, - nxoConfig: nxoConfig - ) - } - - private func notifySuccess(for result: PayPalNativeCheckoutResult) { - analyticsService?.sendEvent("paypal-native-payments:succeeded", correlationID: nativeCheckoutProvider.correlationID) - delegate?.paypal(self, didFinishWithResult: result) - } - - private func notifyFailure(with errorDescription: String) { - analyticsService?.sendEvent("paypal-native-payments:failed", correlationID: nativeCheckoutProvider.correlationID) - - let error = PayPalNativePaymentsError.nativeCheckoutSDKError(errorDescription) - delegate?.paypal(self, didFinishWithError: error) - } - - private func notifyCancellation() { - analyticsService?.sendEvent("paypal-native-payments:canceled", correlationID: correlationID) - delegate?.paypalDidCancel(self) - } - - private func notifyShippingMethod( - shippingActions: PayPalNativePaysheetActions, - shippingMethod: PayPalNativeShippingMethod - ) { - analyticsService?.sendEvent("paypal-native-payments:shipping-method-changed") - shippingDelegate?.paypal(self, didShippingMethodChange: shippingMethod, withAction: shippingActions) - } - - private func notifyShippingChange( - shippingActions: PayPalNativePaysheetActions, - shippingAddress: PayPalNativeShippingAddress - ) { - analyticsService?.sendEvent("paypal-native-payments:shipping-address-changed") - shippingDelegate?.paypal(self, didShippingAddressChange: shippingAddress, withAction: shippingActions) - } -} diff --git a/Sources/PayPalNativePayments/PayPalNativeCheckoutDelegate.swift b/Sources/PayPalNativePayments/PayPalNativeCheckoutDelegate.swift deleted file mode 100644 index ae0636ca8..000000000 --- a/Sources/PayPalNativePayments/PayPalNativeCheckoutDelegate.swift +++ /dev/null @@ -1,68 +0,0 @@ -import Foundation -#if canImport(CorePayments) -import CorePayments -#endif -import PayPalCheckout - -/// A required delegate to handle events from `PayPalNativeCheckoutClient.start()` -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -public protocol PayPalNativeCheckoutDelegate: AnyObject { - - /// Notify that the PayPal flow finished with a successful result - /// - Parameters: - /// - didFinishWithResult: the successful result from the flow - func paypal(_ payPalClient: PayPalNativeCheckoutClient, didFinishWithResult result: PayPalNativeCheckoutResult) - - /// Notify that an error occurred in the PayPal flow - /// - Parameters: - /// - didFinishWithError: the error returned by the PayPal flow - func paypal(_ payPalClient: PayPalNativeCheckoutClient, didFinishWithError error: CoreSDKError) - - /// Notify that the PayPal flow has been cancelled - /// - Parameters: - /// - client: the PayPalClient associated with delegate - func paypalDidCancel(_ payPalClient: PayPalNativeCheckoutClient) - - /// Notify that the PayPal paysheet is about to appear - /// - Parameters: - /// - client: the PayPalClient associated with delegate - func paypalWillStart(_ payPalClient: PayPalNativeCheckoutClient) -} - -/// A delegate to receive notifications if the user changes their shipping information. -/// -/// This is **only required** if the order ID was created with `shipping_preferences = GET_FROM_FILE`. [See Orders V2 documentation](https://developer.paypal.com/docs/api/orders/v2/#definition-order_application_context). If the order ID was created with `shipping_preferences = NO_SHIPPING` or `SET_PROVIDED_ADDRESS`, don't implement this protocol. -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -public protocol PayPalNativeShippingDelegate: AnyObject { - - /// Notify when the users selected shipping address changes. Use `PayPalNativeShippingActions.approve` - /// or `PayPalNativeShippingActions.reject` to approve or reject the newly selected shipping address. - /// Optionally, if the order needs to be patched, call `PayPalNativeShippingActions.approve` once - /// patching has completed successfully. - /// - Parameters: - /// - payPalClient: the PayPalClient associated with delegate - /// - shippingAddress: the user's most recently selected shipping address - /// - shippingActions: actions to perform after a change in shipping address - func paypal( - _ payPalClient: PayPalNativeCheckoutClient, - didShippingAddressChange shippingAddress: PayPalNativeShippingAddress, - withAction shippingActions: PayPalNativePaysheetActions - ) - - /// Notify when the users selected a different shipping method. To reflect the newly selected - /// shipping method in the paysheet, patch the order on your server with operation `replace`, with all of the - /// shipping methods (marking the new one as selected). You can also update the amount to reflect - /// the new shipping cost. Once patching completes, its mandatory to call `PayPalNativeShippingActions.approve` or - /// `PayPalNativeShippingActions.reject` to either accept or reject the changes and continue the flow. - /// Visit https://developer.paypal.com/docs/api/orders/v2/#orders_patch for - /// more detailed information on patching an order. - /// - Parameters: - /// - payPalClient: the PayPalClient associated with delegate - /// - shippingMethod: the user's most recently selected shipping method - /// - shippingActions: actions to perform after a change in shipping method - func paypal( - _ payPalClient: PayPalNativeCheckoutClient, - didShippingMethodChange shippingMethod: PayPalNativeShippingMethod, - withAction shippingActions: PayPalNativePaysheetActions - ) -} diff --git a/Sources/PayPalNativePayments/PayPalNativeCheckoutRequest.swift b/Sources/PayPalNativePayments/PayPalNativeCheckoutRequest.swift deleted file mode 100644 index c7dd4caa9..000000000 --- a/Sources/PayPalNativePayments/PayPalNativeCheckoutRequest.swift +++ /dev/null @@ -1,20 +0,0 @@ -import Foundation - -/// Used to configure options for approving a PayPal native order -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -public struct PayPalNativeCheckoutRequest { - - /// The order ID associated with the request. - public let orderID: String - - /// Optional. User email to initiate a quicker authentication flow in cases where the user has a PayPal Account with the same email. - public let userAuthenticationEmail: String? - - /// Creates an instance of a PayPalNativeCheckoutRequest. - /// - Parameter orderID: The ID of the order to be approved. - /// - Parameter userAuthenticationEmail: Optional. User email to initiate a quicker authentication flow in cases where the user has a PayPal Account with the same email. - public init(orderID: String, userAuthenticationEmail: String? = nil) { - self.orderID = orderID - self.userAuthenticationEmail = userAuthenticationEmail - } -} diff --git a/Sources/PayPalNativePayments/PayPalNativeCheckoutResult.swift b/Sources/PayPalNativePayments/PayPalNativeCheckoutResult.swift deleted file mode 100644 index e6e1c35a6..000000000 --- a/Sources/PayPalNativePayments/PayPalNativeCheckoutResult.swift +++ /dev/null @@ -1,12 +0,0 @@ -import Foundation - -/// The result of a PayPal native payment flow. -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -public struct PayPalNativeCheckoutResult { - - /// The order ID associated with the transaction. - public let orderID: String - - /// The Payer ID (or user ID) associated with the transaction. - public let payerID: String -} diff --git a/Sources/PayPalNativePayments/PayPalNativePaymentsError.swift b/Sources/PayPalNativePayments/PayPalNativePaymentsError.swift deleted file mode 100644 index 24bf284ba..000000000 --- a/Sources/PayPalNativePayments/PayPalNativePaymentsError.swift +++ /dev/null @@ -1,27 +0,0 @@ -#if canImport(CorePayments) -import CorePayments -#endif - -enum PayPalNativePaymentsError { - - static let domain = "PayPalErrorDomain" - - enum Code: Int { - /// 0. An unknown error occurred. - case unknown - - /// 1. Error returned from the PayPal Checkout SDK - case nativeCheckoutSDKError - - /// 2. An error occured fetching details for this Order ID - case getOrderDetailsError - } - - static let nativeCheckoutSDKError: (String) -> CoreSDKError = { description in - CoreSDKError( - code: Code.nativeCheckoutSDKError.rawValue, - domain: domain, - errorDescription: description - ) - } -} diff --git a/Sources/PayPalNativePayments/PayPalNativePaysheetActions.swift b/Sources/PayPalNativePayments/PayPalNativePaysheetActions.swift deleted file mode 100644 index c4f491822..000000000 --- a/Sources/PayPalNativePayments/PayPalNativePaysheetActions.swift +++ /dev/null @@ -1,34 +0,0 @@ -import PayPalCheckout - -/// The actions that can be used to update the Paysheet UI after `PayPalNativeShippingDelegate` methods are invoked. -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -public class PayPalNativePaysheetActions { - - private let shippingActions: ShippingActionsProtocol - - init(_ shippingActions: ShippingActionsProtocol) { - self.shippingActions = shippingActions - } - - /// Call reject when a buyer selects a shipping option that is not supported or has entered a shipping address that is not supported. - /// The paysheet will require the buyer to fix the issue before continuing with the order. - /// Remove the error message by calling approve - public func reject() { self.shippingActions.reject() } - - - /// Will refresh the paysheet with the latest updates to the current order. Call `approve` when: - /// - A buyer selects a shipping address that is supported. Removes the error message on the - /// paysheet displayed after calling `reject`. - /// - After an order has been successfully patched on your server (e.g, update amount after new shipping method - /// has been selected), to see the changes reflected on paysheet. - /// For more information on patching an order, visit: https://developer.paypal.com/docs/api/orders/v2/#orders_patch - public func approve() { self.shippingActions.approve() } -} - -protocol ShippingActionsProtocol { - - func reject() - func approve() -} - -extension ShippingChangeAction: ShippingActionsProtocol { } diff --git a/Sources/PayPalNativePayments/PayPalNativeShippingAddress.swift b/Sources/PayPalNativePayments/PayPalNativeShippingAddress.swift deleted file mode 100644 index fab0cd5e4..000000000 --- a/Sources/PayPalNativePayments/PayPalNativeShippingAddress.swift +++ /dev/null @@ -1,56 +0,0 @@ -import PayPalCheckout - -// Wrapper for PayPalCheckout.ShippingChangeAddress -/// The user's selected shipping address via the PayPal Native Checkout UI. -/// -/// If you want to show shipping options in the PayPal Native Paysheet, -/// provide `purchase_units[].shipping.options` when creating an orderID with -/// the [`orders/v2` API](https://developer.paypal.com/docs/api/orders/v2/#definition-purchase_unit) on your server. Otherwise, our Paysheet won't display any shipping options. -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -public struct PayPalNativeShippingAddress { - - /// The ID of the shipping address - public let addressID: String? - - /// The highest level sub-division in a country, which is usually a province, state, or ISO-3166-2 subdivision. - /// Format for postal delivery. For example, `CA` and not `California`. Value, by country, is: - /// - UK: A county. - /// - US: A state. - /// - Canada: A province. - /// - Japan: A prefecture. - /// - Switzerland: A kanton. - public let adminArea1: String? - - /// The city, town, or village. - public let adminArea2: String? - - /// The postal code, which is the zip code or equivalent. Typically required for countries with a postal code or an equivalent. - public let postalCode: String? - - /// The two-character ISO 3166-1 code that identifies the country or region. - /// For more information, refer to: https://developer.paypal.com/api/rest/reference/country-codes/ - public let countryCode: String? - - init(_ shippingChangeAddress: PayPalCheckout.ShippingChangeAddress) { - self.addressID = shippingChangeAddress.addressID - self.adminArea1 = shippingChangeAddress.adminArea1 - self.adminArea2 = shippingChangeAddress.adminArea2 - self.postalCode = shippingChangeAddress.postalCode - self.countryCode = shippingChangeAddress.countryCode - } - - /// For testing - init( - addressID: String? = nil, - adminArea1: String? = nil, - adminArea2: String? = nil, - postalCode: String? = nil, - countryCode: String? = nil - ) { - self.addressID = addressID - self.adminArea1 = adminArea1 - self.adminArea2 = adminArea2 - self.postalCode = postalCode - self.countryCode = countryCode - } -} diff --git a/Sources/PayPalNativePayments/PayPalNativeShippingMethod.swift b/Sources/PayPalNativePayments/PayPalNativeShippingMethod.swift deleted file mode 100644 index 003945db1..000000000 --- a/Sources/PayPalNativePayments/PayPalNativeShippingMethod.swift +++ /dev/null @@ -1,98 +0,0 @@ -import PayPalCheckout - -// Wrapper for PayPalCheckout.ShippingMethod -/// The user's selected shipping method via the PayPal Native Checkout UI. -/// -/// If you want to show shipping options in the PayPal Native Paysheet, -/// provide `purchase_units[].shipping.options` when creating an orderID with -/// the [`orders/v2` API](https://developer.paypal.com/docs/api/orders/v2/#definition-purchase_unit) on your server. Otherwise, our Paysheet won't display any shipping options. -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -public struct PayPalNativeShippingMethod { - - /// The method by which the payer wants to get their items. - public enum DeliveryType: Int, CaseIterable, Codable { - - /// The payer intends to receive the items at a specified address. - case shipping - - /// The payer intends to pick up the items at a specified address. For example, a store address. - case pickup - - /// No delivery type specified. - case none - } - - /// A unique ID that identifies a payer-selected shipping option. - public let id: String - - /// A description that the payer sees, which helps them choose an appropriate shipping option. - /// For example, Free Shipping, USPS Priority Shipping, Expédition prioritaire USPS, or USPS yōuxiān fā huò. - /// Localize this description to the payer's locale. - public let label: String - - /// If true it represents the shipping option that the merchant expects to be selected for the buyer - /// when they view the shipping options within the PayPal Checkout experience. - /// The selected shipping option must match the shipping cost in the order breakdown. - /// Only one shipping option per purchase unit can be selected. - public let selected: Bool - - /// The method by which the payer wants to get their items. - public let type: DeliveryType - - /// The shipping cost for the selected option, which might be: - /// An integer for currencies like JPY that are not typically fractional. - /// A decimal fraction for currencies like TND that are subdivided into thousandths. - /// - /// - Maximum length: 32. - /// - Pattern: ^((-?[0-9]+)|(-?([0-9]+)?[.][0-9]+))$ - public let value: String? - - /// The [three-character ISO-4217 currency code](https://developer.paypal.com/docs/api/reference/currency-codes/) - /// that identifies the currency. - /// - /// Currency code in text format (example: "USD") - public let currencyCode: String? - - init(_ shippingMethod: PayPalCheckout.ShippingMethod) { - self.id = shippingMethod.id - self.label = shippingMethod.label - self.selected = shippingMethod.selected - self.type = shippingMethod.type.toMerchantFacingShippingType() - self.value = shippingMethod.amount?.value - self.currencyCode = shippingMethod.amount?.currencyCodeString - } - - // For testing - init( - id: String, - label: String, - selected: Bool, - type: DeliveryType, - value: String, - currencyCode: String - ) { - self.id = id - self.label = label - self.selected = selected - self.type = type - self.value = value - self.currencyCode = currencyCode - } -} - -@available(*, deprecated, message: "PayPalNativePayments Module is deprecated, use PayPalWebPayments Module instead") -extension PayPalCheckout.ShippingType { - - func toMerchantFacingShippingType() -> PayPalNativeShippingMethod.DeliveryType { - switch self { - case .shipping: - return PayPalNativeShippingMethod.DeliveryType.shipping - case .pickup: - return PayPalNativeShippingMethod.DeliveryType.pickup - case .none: - return PayPalNativeShippingMethod.DeliveryType.none - @unknown default: - return PayPalNativeShippingMethod.DeliveryType.none - } - } -} diff --git a/Sources/PayPalNativePayments/PrivacyInfo.xcprivacy b/Sources/PayPalNativePayments/PrivacyInfo.xcprivacy deleted file mode 100644 index 3e4701fe1..000000000 --- a/Sources/PayPalNativePayments/PrivacyInfo.xcprivacy +++ /dev/null @@ -1,21 +0,0 @@ - - - - - NSPrivacyCollectedDataTypes - - - NSPrivacyCollectedDataType - NSPrivacyCollectedDataTypeEmailAddress - NSPrivacyCollectedDataTypeLinked - - NSPrivacyCollectedDataTypeTracking - - NSPrivacyCollectedDataTypePurposes - - NSPrivacyCollectedDataTypePurposeAppFunctionality - - - - - diff --git a/Sources/PayPalWebPayments/PayPalError.swift b/Sources/PayPalWebPayments/PayPalError.swift new file mode 100644 index 000000000..a46674ebd --- /dev/null +++ b/Sources/PayPalWebPayments/PayPalError.swift @@ -0,0 +1,87 @@ +import Foundation + +#if canImport(CorePayments) +import CorePayments +#endif + +public enum PayPalError { + + static let domain = "PayPalClientErrorDomain" + + enum Code: Int { + /// 0. An unknown error occurred. + case unknown + + /// 1. An error occurred during web authentication session. + case webSessionError + + /// 2. Error constructing PayPal URL. + case payPalURLError + + /// 3. Result did not contain the expected data. + case malformedResultError + + /// 4. Vault result did not return a token id + case payPalVaultResponseError + + /// 5. Checkout websession is cancelled by the user + case checkoutCanceledError + + /// 6. Vault websession is cancelled by the user + case vaultCanceledError + } + + static let webSessionError: (Error) -> CoreSDKError = { error in + CoreSDKError( + code: Code.webSessionError.rawValue, + domain: domain, + errorDescription: error.localizedDescription + ) + } + + static let payPalURLError = CoreSDKError( + code: Code.payPalURLError.rawValue, + domain: domain, + errorDescription: "Error constructing URL for PayPal request." + ) + + static let malformedResultError = CoreSDKError( + code: Code.malformedResultError.rawValue, + domain: domain, + errorDescription: "Result did not contain the expected data." + ) + + static let payPalVaultResponseError = CoreSDKError( + code: Code.payPalVaultResponseError.rawValue, + domain: domain, + errorDescription: "Error parsing PayPal vault response" + ) + + static let checkoutCanceledError = CoreSDKError( + code: Code.checkoutCanceledError.rawValue, + domain: domain, + errorDescription: "PayPal checkout has been canceled by the user" + ) + + static let vaultCanceledError = CoreSDKError( + code: Code.vaultCanceledError.rawValue, + domain: domain, + errorDescription: "PayPal vault has been canceled by the user" + ) + + // Helper function that allows handling of PayPal checkout cancel errors separately without having to cast the error to CoreSDKError and checking code and domain properties. + public static func isCheckoutCanceled(_ error: Error) -> Bool { + guard let error = error as? CoreSDKError else { + return false + } + return error.domain == PayPalError.domain && error.code == PayPalError.checkoutCanceledError.code + } + + // Helper function that allows handling of PayPal vault cancel errors separately without having to cast the error to CoreSDKError and checking code and domain properties. + public static func isVaultCanceled(_ error: Error) -> Bool { + guard let error = error as? CoreSDKError else { + return false + } + return error.domain == PayPalError.domain && error.code == PayPalError.vaultCanceledError.code + } +} diff --git a/Sources/PayPalWebPayments/PayPalVaultDelegate.swift b/Sources/PayPalWebPayments/PayPalVaultDelegate.swift deleted file mode 100644 index f189b31fa..000000000 --- a/Sources/PayPalWebPayments/PayPalVaultDelegate.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Foundation -#if canImport(CorePayments) -import CorePayments -#endif - -/// PayPalVault delegate to vault results from PayPalWebCheckoutClient -public protocol PayPalVaultDelegate: AnyObject { - - /// Notify that the PayPal vault flow finished with a successful result - /// - Parameters: - /// - client: the PayPalWebCheckoutClient associated with delegate - /// - didFinishWithResult: the successful result from the flow - func paypal(_ paypalWebClient: PayPalWebCheckoutClient, didFinishWithVaultResult paypalVaultResult: PayPalVaultResult) - - /// Notify that an error occurred in the PayPal vault flow - /// - Parameters: - /// - client: the PayPalWebCheckoutClien associated with delegate - /// - didFinishWithError: the error returned by the PayPal vault flow - func paypal(_ paypalWebClient: PayPalWebCheckoutClient, didFinishWithVaultError vaultError: CoreSDKError) - - /// Notify that a cancellation occurred in the PayPal vault flow - /// - Parameters: - /// - client: the PayPalWebCheckoutClien associated with delegate - func paypalDidCancel(_ paypalWebClient: PayPalWebCheckoutClient) -} diff --git a/Sources/PayPalWebPayments/PayPalWebCheckoutClient.swift b/Sources/PayPalWebPayments/PayPalWebCheckoutClient.swift index 3ff509d55..0f797ec03 100644 --- a/Sources/PayPalWebPayments/PayPalWebCheckoutClient.swift +++ b/Sources/PayPalWebPayments/PayPalWebCheckoutClient.swift @@ -6,14 +6,12 @@ import CorePayments public class PayPalWebCheckoutClient: NSObject { - public weak var vaultDelegate: PayPalVaultDelegate? - public weak var delegate: PayPalWebCheckoutDelegate? let config: CoreConfig private let webAuthenticationSession: WebAuthenticationSession private let networkingClient: NetworkingClient private var analyticsService: AnalyticsService? - /// Initialize a PayPalNativeCheckoutClient to process PayPal transaction + /// Initialize a PayPalWebCheckoutClient to process PayPal transaction /// - Parameters: /// - config: The CoreConfig object public init(config: CoreConfig) { @@ -32,7 +30,10 @@ public class PayPalWebCheckoutClient: NSObject { /// Launch the PayPal web flow /// - Parameters: /// - request: the PayPalRequest for the transaction - public func start(request: PayPalWebCheckoutRequest) { + /// - 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) { analyticsService = AnalyticsService(coreConfig: config, orderID: request.orderID) analyticsService?.sendEvent("paypal-web-payments:started") @@ -44,7 +45,7 @@ public class PayPalWebCheckoutClient: NSObject { guard let payPalCheckoutURL = URL(string: payPalCheckoutURLString), let payPalCheckoutURLComponents = payPalCheckoutReturnURL(payPalCheckoutURL: payPalCheckoutURL) else { - self.notifyFailure(with: PayPalWebCheckoutClientError.payPalURLError) + self.notifyCheckoutFailure(with: PayPalError.payPalURLError, completion: completion) return } @@ -62,10 +63,13 @@ public class PayPalWebCheckoutClient: NSObject { if let error = error { switch error { case ASWebAuthenticationSessionError.canceledLogin: - self.notifyCancellation() + self.notifyCheckoutCancelWithError( + with: PayPalError.checkoutCanceledError, + completion: completion + ) return default: - self.notifyFailure(with: PayPalWebCheckoutClientError.webSessionError(error)) + self.notifyCheckoutFailure(with: PayPalError.webSessionError(error), completion: completion) return } } @@ -73,17 +77,34 @@ public class PayPalWebCheckoutClient: NSObject { if let url = url { guard let orderID = self.getQueryStringParameter(url: url.absoluteString, param: "token"), let payerID = self.getQueryStringParameter(url: url.absoluteString, param: "PayerID") else { - self.notifyFailure(with: PayPalWebCheckoutClientError.malformedResultError) + self.notifyCheckoutFailure(with: PayPalError.malformedResultError, completion: completion) return } let result = PayPalWebCheckoutResult(orderID: orderID, payerID: payerID) - self.notifySuccess(for: result) + self.notifyCheckoutSuccess(for: result, completion: completion) } } ) } + /// Launch the PayPal web flow + /// - Parameters: + /// - request: the PayPalRequest for the transaction + /// - Returns: A `PayPalWebCheckoutResult` if successful + /// - Throws: An `Error` 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 { + continuation.resume(returning: result) + } + } + } + } + func payPalCheckoutReturnURL(payPalCheckoutURL: URL) -> URL? { let bundleID = PayPalCoreConstants.callbackURLScheme let redirectURLString = "\(bundleID)://x-callback-url/paypal-sdk/paypal-checkout" @@ -101,7 +122,10 @@ public class PayPalWebCheckoutClient: NSObject { /// After setupToken successfullly attaches a payment method, you will need to create a payment token with the setup token /// - Parameters: /// - vaultRequest: Request created with url for vault approval and setupTokenID - public func vault(_ vaultRequest: PayPalVaultRequest) { + /// - 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) { analyticsService = AnalyticsService(coreConfig: config, setupToken: vaultRequest.setupTokenID) analyticsService?.sendEvent("paypal-web-payments:vault-wo-purchase:started") @@ -110,7 +134,7 @@ public class PayPalWebCheckoutClient: NSObject { vaultURLComponents?.queryItems = queryItems guard let vaultCheckoutURL = vaultURLComponents?.url else { - notifyVaultFailure(with: PayPalWebCheckoutClientError.payPalURLError) + notifyVaultFailure(with: PayPalError.payPalURLError, completion: completion) return } @@ -128,10 +152,13 @@ public class PayPalWebCheckoutClient: NSObject { if let error = error { switch error { case ASWebAuthenticationSessionError.canceledLogin: - self.notifyVaultCancellation() + self.notifyVaultCancelWithError( + with: PayPalError.vaultCanceledError, + completion: completion + ) return default: - self.notifyVaultFailure(with: PayPalWebCheckoutClientError.webSessionError(error)) + self.notifyVaultFailure(with: PayPalError.webSessionError(error), completion: completion) return } } @@ -141,51 +168,68 @@ public class PayPalWebCheckoutClient: NSObject { let approvalSessionID = self.getQueryStringParameter(url: url.absoluteString, param: "approval_session_id"), !tokenID.isEmpty, !approvalSessionID.isEmpty else { - self.notifyVaultFailure(with: PayPalWebCheckoutClientError.payPalVaultResponseError) + self.notifyVaultFailure(with: PayPalError.payPalVaultResponseError, completion: completion) return } let paypalVaultResult = PayPalVaultResult(tokenID: tokenID, approvalSessionID: approvalSessionID) - self.notifyVaultSuccess(for: paypalVaultResult) + self.notifyVaultSuccess(for: paypalVaultResult, completion: completion) } } ) } + /// Starts a web session for vaulting PayPal Payment Method + /// After setupToken successfullly attaches a payment method, you will need to create a payment token with the setup token + /// - Parameters: + /// - vaultRequest: Request created with url for vault approval and setupTokenID + /// - Returns: `PayPalVaultResult`if successful + /// - Throws: An `Error` 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 { + continuation.resume(returning: result) + } + } + } + } + private func getQueryStringParameter(url: String, param: String) -> String? { guard let url = URLComponents(string: url) else { return nil } return url.queryItems?.first { $0.name == param }?.value } - private func notifySuccess(for result: PayPalWebCheckoutResult) { - let payPalResult = PayPalWebCheckoutResult(orderID: result.orderID, payerID: result.payerID) - analyticsService?.sendEvent("paypal-web-payments:succeeded") - delegate?.payPal(self, didFinishWithResult: payPalResult) + private func notifyCheckoutSuccess(for result: PayPalWebCheckoutResult, completion: (PayPalWebCheckoutResult?, CoreSDKError?) -> Void) { + self.analyticsService?.sendEvent("paypal-web-payments:succeeded") + completion(result, nil) } - private func notifyFailure(with error: CoreSDKError) { - analyticsService?.sendEvent("paypal-web-payments:failed") - delegate?.payPal(self, didFinishWithError: error) + private func notifyCheckoutFailure(with error: CoreSDKError, completion: (PayPalWebCheckoutResult?, CoreSDKError?) -> Void) { + self.analyticsService?.sendEvent("paypal-web-payments:failed") + completion(nil, error) } - private func notifyCancellation() { + private func notifyCheckoutCancelWithError(with error: CoreSDKError, completion: (PayPalWebCheckoutResult?, CoreSDKError?) -> Void) { analyticsService?.sendEvent("paypal-web-payments:browser-login:canceled") - delegate?.payPalDidCancel(self) + completion(nil, error) } - private func notifyVaultSuccess(for result: PayPalVaultResult) { + private func notifyVaultSuccess(for result: PayPalVaultResult, completion: (PayPalVaultResult?, CoreSDKError?) -> Void) { analyticsService?.sendEvent("paypal-web-payments:vault-wo-purchase:succeeded") - vaultDelegate?.paypal(self, didFinishWithVaultResult: result) + completion(result, nil) } - private func notifyVaultFailure(with error: CoreSDKError) { + private func notifyVaultFailure(with error: CoreSDKError, completion: (PayPalVaultResult?, CoreSDKError?) -> Void) { analyticsService?.sendEvent("paypal-web-payments:vault-wo-purchase:failed") - vaultDelegate?.paypal(self, didFinishWithVaultError: error) + completion(nil, error) } - private func notifyVaultCancellation() { + private func notifyVaultCancelWithError(with vaultError: CoreSDKError, completion: (PayPalVaultResult?, CoreSDKError?) -> Void) { analyticsService?.sendEvent("paypal-web-payments:vault-wo-purchase:canceled") - vaultDelegate?.paypalDidCancel(self) + completion(nil, vaultError) } } diff --git a/Sources/PayPalWebPayments/PayPalWebCheckoutClientError.swift b/Sources/PayPalWebPayments/PayPalWebCheckoutClientError.swift deleted file mode 100644 index ccfd943fe..000000000 --- a/Sources/PayPalWebPayments/PayPalWebCheckoutClientError.swift +++ /dev/null @@ -1,53 +0,0 @@ -import Foundation - -#if canImport(CorePayments) -import CorePayments -#endif - -enum PayPalWebCheckoutClientError { - - static let domain = "PayPalClientErrorDomain" - - enum Code: Int { - /// 0. An unknown error occurred. - case unknown - - /// 1. An error occurred during web authentication session. - case webSessionError - - /// 2. Error constructing PayPal URL. - case payPalURLError - - /// 3. Result did not contain the expected data. - case malformedResultError - - /// 4. Vault result did not return a token id - case paypalVaultResponseError - } - - static let webSessionError: (Error) -> CoreSDKError = { error in - CoreSDKError( - code: Code.webSessionError.rawValue, - domain: domain, - errorDescription: error.localizedDescription - ) - } - - static let payPalURLError = CoreSDKError( - code: Code.payPalURLError.rawValue, - domain: domain, - errorDescription: "Error constructing URL for PayPal request." - ) - - static let malformedResultError = CoreSDKError( - code: Code.malformedResultError.rawValue, - domain: domain, - errorDescription: "Result did not contain the expected data." - ) - - static let payPalVaultResponseError = CoreSDKError( - code: Code.paypalVaultResponseError.rawValue, - domain: domain, - errorDescription: "Error parsing paypal vault response" - ) -} diff --git a/Sources/PayPalWebPayments/PayPalWebCheckoutDelegate.swift b/Sources/PayPalWebPayments/PayPalWebCheckoutDelegate.swift deleted file mode 100644 index 5fcfc8ecc..000000000 --- a/Sources/PayPalWebPayments/PayPalWebCheckoutDelegate.swift +++ /dev/null @@ -1,25 +0,0 @@ -import Foundation -#if canImport(CorePayments) -import CorePayments -#endif - -/// PayPal delegate to handle events from PayPalNativeCheckoutClient -public protocol PayPalWebCheckoutDelegate: AnyObject { - - /// Notify that the PayPal flow finished with a successful result - /// - Parameters: - /// - client: the PayPalClient associated with delegate - /// - didFinishWithResult: the successful result from the flow - func payPal(_ payPalClient: PayPalWebCheckoutClient, didFinishWithResult result: PayPalWebCheckoutResult) - - /// Notify that an error occurred in the PayPal flow - /// - Parameters: - /// - client: the PayPalClient associated with delegate - /// - didFinishWithError: the error returned by the PayPal flow - func payPal(_ payPalClient: PayPalWebCheckoutClient, didFinishWithError error: CoreSDKError) - - /// Notify that the PayPal flow has been cancelled - /// - Parameters: - /// - client: the PayPalClient associated with delegate - func payPalDidCancel(_ payPalClient: PayPalWebCheckoutClient) -} diff --git a/UnitTests/CardPaymentsTests/CardClient_Tests.swift b/UnitTests/CardPaymentsTests/CardClient_Tests.swift index e97295047..9231870b2 100644 --- a/UnitTests/CardPaymentsTests/CardClient_Tests.swift +++ b/UnitTests/CardPaymentsTests/CardClient_Tests.swift @@ -5,7 +5,8 @@ import AuthenticationServices @testable import CardPayments @testable import TestShared -// swiftlint:disable type_body_length file_length +// swiftlint:disable type_body_length +// swiftlint:disable file_length class CardClient_Tests: XCTestCase { // MARK: - Helper Properties @@ -22,7 +23,6 @@ class CardClient_Tests: XCTestCase { let mockWebAuthSession = MockWebAuthenticationSession() var mockNetworkingClient: MockNetworkingClient! - var mockCardVaultDelegate: MockCardVaultDelegate! var mockCheckoutOrdersAPI: MockCheckoutOrdersAPI! var mockVaultAPI: MockVaultPaymentTokensAPI! @@ -60,18 +60,15 @@ class CardClient_Tests: XCTestCase { mockVaultAPI.stubSetupTokenResponse = updateSetupTokenResponse let expectation = expectation(description: "vault completed") - let cardVaultDelegate = MockCardVaultDelegate(success: {_, result in - XCTAssertEqual(result.setupTokenID, setupTokenID) - XCTAssertEqual(result.status, vaultStatus) - XCTAssertFalse(result.didAttemptThreeDSecureAuthentication) + + sut.vault(vaultRequest) { result, error in + XCTAssertEqual(result?.setupTokenID, setupTokenID) + XCTAssertEqual(result?.status, vaultStatus) + XCTAssertNil(error) expectation.fulfill() - }, error: {_, _ in - XCTFail("Invoked error() callback. Should invoke success().") - }) - sut.vaultDelegate = cardVaultDelegate - sut.vault(vaultRequest) - - waitForExpectations(timeout: 10) + } + + waitForExpectations(timeout: 2, handler: nil) } func testVault_withValid3DSURLResponse_returnsSuccess() { @@ -84,21 +81,23 @@ class CardClient_Tests: XCTestCase { mockVaultAPI.stubSetupTokenResponse = updateSetupTokenResponse let expectation = expectation(description: "vault completed") - let cardVaultDelegate = MockCardVaultDelegate(success: {_, result in - XCTAssertEqual(result.setupTokenID, setupTokenID) - XCTAssertNil(result.status) - XCTAssertTrue(result.didAttemptThreeDSecureAuthentication) + 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") + } + + XCTAssertNil(error) expectation.fulfill() - }, error: {_, _ in - XCTFail("Invoked error() callback. Should invoke success().") - }) - sut.vaultDelegate = cardVaultDelegate - sut.vault(vaultRequest) + } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } - func testVault_withInvalid3DSURLResponse_returnsSuccess() { + func testVault_withInvalid3DSURLResponse_returnsError() { let setupTokenID = "testToken1" let vaultStatus = "PAYER_ACTION_REQUIRED" let vaultRequest = CardVaultRequest(card: card, setupTokenID: setupTokenID) @@ -108,18 +107,20 @@ class CardClient_Tests: XCTestCase { mockVaultAPI.stubSetupTokenResponse = updateSetupTokenResponse let expectation = expectation(description: "vault completed") - let cardVaultDelegate = MockCardVaultDelegate(success: {_, _ in - XCTFail("Invoked success() callback. Should invoke error().") - }, error: {_, error in - XCTAssertEqual(error.code, CardClientError.threeDSecureURLError.code) - XCTAssertEqual(error.domain, CardClientError.domain) - XCTAssertEqual(error.localizedDescription, CardClientError.threeDSecureURLError.localizedDescription) + + sut.vault(vaultRequest) { result, error in + XCTAssertNil(result) + if 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() - }) - sut.vaultDelegate = cardVaultDelegate - sut.vault(vaultRequest) + } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } func testVault_whenVaultAPIError_bubblesError() { @@ -129,18 +130,20 @@ class CardClient_Tests: XCTestCase { mockVaultAPI.stubError = CoreSDKError(code: 123, domain: "fake-domain", errorDescription: "api-error") let expectation = expectation(description: "vault completed") - let cardVaultDelegate = MockCardVaultDelegate(success: {_, _ in - XCTFail("Invoked success() callback. Should invoke error().") - }, error: {_, error in - XCTAssertEqual(error.domain, "fake-domain") - XCTAssertEqual(error.code, 123) - XCTAssertEqual(error.localizedDescription, "api-error") + + sut.vault(vaultRequest) { result, error in + XCTAssertNil(result) + if 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() - }) - sut.vaultDelegate = cardVaultDelegate - sut.vault(vaultRequest) + } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } func testVault_whenUnknownError_returnsVaultError() { @@ -150,18 +153,19 @@ class CardClient_Tests: XCTestCase { mockVaultAPI.stubError = NSError(domain: "some-domain", code: 123, userInfo: [NSLocalizedDescriptionKey: "some-description"]) let expectation = expectation(description: "vault completed") - let cardVaultDelegate = MockCardVaultDelegate(success: {_, _ in - XCTFail("Invoked success() callback. Should invoke error().") - }, error: {_, error in - XCTAssertEqual(error.domain, CardClientError.domain) - XCTAssertEqual(error.code, CardClientError.Code.vaultTokenError.rawValue) - XCTAssertEqual(error.localizedDescription, "An error occurred while vaulting a card.") + sut.vault(vaultRequest) { result, error in + XCTAssertNil(result) + if 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() - }) - sut.vaultDelegate = cardVaultDelegate - sut.vault(vaultRequest) + } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } func test_vault_withThreeDSecure_browserSwitchLaunches_vaultReturnsSuccess() { @@ -171,26 +175,19 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "vault() completed") - let mockCardVaultDelegate = MockCardVaultDelegate( - success: {_, result in + sut.vault(cardVaultRequest) { result, error in + XCTAssertNil(error) + if let result { XCTAssertEqual(result.setupTokenID, "testSetupTokenId") XCTAssertNil(result.status) XCTAssertTrue(result.didAttemptThreeDSecureAuthentication) - expectation.fulfill() - }, - error: { _, error in - XCTFail(error.localizedDescription) - expectation.fulfill() - }, - cancel: { _ in XCTFail("Invoked cancel() callback. Should invoke success().") }, - threeDSWillLaunch: { _ in XCTAssert(true) }, - threeDSLaunched: { _ in XCTAssert(true) } - ) - - sut.vaultDelegate = mockCardVaultDelegate - sut.vault(cardVaultRequest) + } else { + XCTFail("Expected result not to be nil") + } + expectation.fulfill() + } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } func testVault_withThreeDSecure_userCancelsBrowser() { @@ -203,63 +200,45 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "vault() completed") - let mockCardVaultDelegate = MockCardVaultDelegate( - success: {_, _ in - XCTFail("Invoked success() callback. Should invoke cancel().") - expectation.fulfill() - }, - error: { _, error in - XCTFail(error.localizedDescription) - expectation.fulfill() - }, - cancel: { _ in - XCTAssert(true) - expectation.fulfill() - }, - threeDSWillLaunch: { _ in XCTAssert(true) }, - threeDSLaunched: { _ in XCTAssert(true) } - ) - - sut.vaultDelegate = mockCardVaultDelegate - sut.vault(cardVaultRequest) + sut.vault(cardVaultRequest) { result, error in + XCTAssertNil(result) + if 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() + } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } func testVault_withThreeDSecure_browserReturnsError() { mockVaultAPI.stubSetupTokenResponse = FakeUpdateSetupTokenResponse.withValid3DSURL mockWebAuthSession.cannedErrorResponse = CoreSDKError( - code: CardClientError.Code.threeDSecureError.rawValue, - domain: CardClientError.domain, + code: CardError.Code.threeDSecureError.rawValue, + domain: CardError.domain, errorDescription: "Mock web session error description." ) let expectation = expectation(description: "vault() completed") - let mockCardVaultDelegate = MockCardVaultDelegate( - success: {_, _ in - XCTFail("Invoked success() callback. Should invoke error().") - expectation.fulfill() - }, - error: { _, error in - XCTAssertEqual(error.domain, CardClientError.domain) - XCTAssertEqual(error.code, CardClientError.Code.threeDSecureError.rawValue) + sut.vault(cardVaultRequest) { result, error in + XCTAssertNil(result) + if let error { + XCTAssertEqual(error.domain, CardError.domain) + XCTAssertEqual(error.code, CardError.Code.threeDSecureError.rawValue) XCTAssertEqual(error.localizedDescription, "Mock web session error description.") - expectation.fulfill() - }, - cancel: { _ in - XCTFail("Invoked cancel() callback. Should invoke error().") - expectation.fulfill() - }, - threeDSWillLaunch: { _ in XCTAssert(true) }, - threeDSLaunched: { _ in XCTAssert(true) } - ) - - sut.vaultDelegate = mockCardVaultDelegate - sut.vault(cardVaultRequest) + } else { + XCTFail("Expected error not to be nil") + } + expectation.fulfill() + } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } // MARK: - approveOrder() tests @@ -269,21 +248,19 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "approveOrder() completed") - let mockCardDelegate = MockCardDelegate(success: {_, _ in - XCTFail("Invoked success() callback. Should invoke error().") - }, error: { _, err in - XCTAssertEqual(err.code, 3) - XCTAssertEqual(err.domain, "CardClientErrorDomain") - XCTAssertEqual(err.errorDescription, "An invalid 3DS URL was returned. Contact developer.paypal.com/support.") + sut.approveOrder(request: cardRequest) { result, error in + XCTAssertNil(result) + if 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() - }, threeDSWillLaunch: { _ in - XCTFail("Invoked willLaunch() callback. Should invoke error().") - }) - - sut.delegate = mockCardDelegate - sut.approveOrder(request: cardRequest) + } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } func testApproveOrder_withNoThreeDSecure_returnsOrderData() { @@ -291,21 +268,20 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "approveOrder() completed") - let mockCardDelegate = MockCardDelegate(success: {_, result in - XCTAssertEqual(result.orderID, "testOrderId") - XCTAssertEqual(result.status, "APPROVED") - XCTAssertFalse(result.didAttemptThreeDSecureAuthentication) - expectation.fulfill() - }, error: { _, _ in - XCTFail("Invoked error() callback. Should invoke success().") - }, threeDSWillLaunch: { _ in - XCTFail("Invoked willLaunch() callback. Should invoke success().") - }) + 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.delegate = mockCardDelegate - sut.approveOrder(request: cardRequest) + XCTAssertNil(error) + expectation.fulfill() + } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } func testApproveOrder_whenConfirmPaymentSDKError_bubblesError() { @@ -313,21 +289,19 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "approveOrder() completed") - let mockCardDelegate = MockCardDelegate(success: {_, _ in - XCTFail("Invoked success() callback. Should invoke error().") - }, error: { _, error in - XCTAssertEqual(error.domain, "sdk-domain") - XCTAssertEqual(error.code, 123) - XCTAssertEqual(error.localizedDescription, "sdk-error-desc") + sut.approveOrder(request: cardRequest) { result, error in + XCTAssertNil(result) + if 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() - }, threeDSWillLaunch: { _ in - XCTFail("Invoked willLaunch() callback. Should invoke error().") - }) + } - sut.delegate = mockCardDelegate - sut.approveOrder(request: cardRequest) - - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } func testApproveOrder_whenConfirmPaymentGeneralError_returnsUnknownError() { @@ -339,21 +313,19 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "approveOrder() completed") - let mockCardDelegate = MockCardDelegate(success: {_, _ in - XCTFail("Invoked success() callback. Should invoke error().") - }, error: { _, error in - XCTAssertEqual(error.domain, CardClientError.domain) - XCTAssertEqual(error.code, CardClientError.Code.unknown.rawValue) - XCTAssertNotNil(error.localizedDescription) - expectation.fulfill() - }, threeDSWillLaunch: { _ in - XCTFail("Invoked willLaunch() callback. Should invoke error().") - }) - - sut.delegate = mockCardDelegate - sut.approveOrder(request: cardRequest) + sut.approveOrder(request: cardRequest) { result, error in + XCTAssertNil(result) + if let error = 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") + } + } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } func testApproveOrder_withThreeDSecure_browserSwitchLaunches_getOrderReturnsSuccess() { @@ -363,25 +335,19 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "approveOrder() completed") - let mockCardDelegate = MockCardDelegate( - success: {_, result in + sut.approveOrder(request: cardRequest) { result, error in + XCTAssertNil(error) + if let result { XCTAssertEqual(result.orderID, "testOrderId") XCTAssertNil(result.status) XCTAssertTrue(result.didAttemptThreeDSecureAuthentication) - expectation.fulfill() - }, - error: { _, error in - XCTFail(error.localizedDescription) - expectation.fulfill() - }, - cancel: { _ in XCTFail("Invoked cancel() callback. Should invoke success().") }, - threeDSWillLaunch: { _ in XCTAssert(true) }, - threeDSLaunched: { _ in XCTAssert(true) }) - - sut.delegate = mockCardDelegate - sut.approveOrder(request: cardRequest) + } else { + XCTFail("Expected non-nil result") + } + expectation.fulfill() + } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } func testApproveOrder_withThreeDSecure_userCancelsBrowser() { @@ -394,60 +360,68 @@ class CardClient_Tests: XCTestCase { let expectation = expectation(description: "approveOrder() completed") - let mockCardDelegate = MockCardDelegate( - success: {_, _ in - XCTFail("Invoked success() callback. Should invoke cancel().") - expectation.fulfill() - }, - error: { _, error in - XCTFail(error.localizedDescription) - expectation.fulfill() - }, - cancel: { _ in - XCTAssert(true) - expectation.fulfill() - }, - threeDSWillLaunch: { _ in XCTAssert(true) }, - threeDSLaunched: { _ in XCTAssert(true) }) - - sut.delegate = mockCardDelegate - sut.approveOrder(request: cardRequest) + sut.approveOrder(request: cardRequest) { result, error in + XCTAssertNil(result) + if let error = error { + XCTAssertEqual(error.domain, CardError.domain) + XCTAssertEqual(error.code, CardError.threeDSecureCanceledError.code) + XCTAssertEqual(error.localizedDescription, CardError.threeDSecureCanceledError.localizedDescription) + } else { + XCTFail("Expected error") + } + expectation.fulfill() + } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } func testApproveOrder_withThreeDSecure_browserReturnsError() { mockCheckoutOrdersAPI.stubConfirmResponse = FakeConfirmPaymentResponse.withValid3DSURL mockWebAuthSession.cannedErrorResponse = CoreSDKError( - code: CardClientError.Code.threeDSecureError.rawValue, - domain: CardClientError.domain, + code: CardError.Code.threeDSecureError.rawValue, + domain: CardError.domain, errorDescription: "Mock web session error description." ) let expectation = expectation(description: "approveOrder() completed") - let mockCardDelegate = MockCardDelegate( - success: {_, _ in - XCTFail("Invoked success() callback. Should invoke error().") - expectation.fulfill() - }, - error: { _, error in - XCTAssertEqual(error.domain, CardClientError.domain) - XCTAssertEqual(error.code, CardClientError.Code.threeDSecureError.rawValue) + sut.approveOrder(request: cardRequest) { result, error in + XCTAssertNil(result) + if let error = error { + XCTAssertEqual(error.domain, CardError.domain) + XCTAssertEqual(error.code, CardError.Code.threeDSecureError.rawValue) XCTAssertEqual(error.localizedDescription, "Mock web session error description.") - expectation.fulfill() - }, - cancel: { _ in - XCTFail("Invoked cancel() callback. Should invoke error().") - expectation.fulfill() - }, - threeDSWillLaunch: { _ in XCTAssert(true) }, - threeDSLaunched: { _ in XCTAssert(true) }) + } else { + XCTFail("Expected error") + } + expectation.fulfill() + } + + waitForExpectations(timeout: 2, handler: nil) + } + + // MARK: - Helper function test + + func testApproveOrder_withThreeDSecure_userCancelsBrowser_returns_isThreeDSecureCanceledTrue() { + mockCheckoutOrdersAPI.stubConfirmResponse = FakeConfirmPaymentResponse.withValid3DSURL + mockWebAuthSession.cannedErrorResponse = ASWebAuthenticationSessionError( + .canceledLogin, + userInfo: ["Description": "Mock cancellation error description."] + ) - sut.delegate = mockCardDelegate - sut.approveOrder(request: cardRequest) + let expectation = expectation(description: "approveOrder() completed") + + sut.approveOrder(request: cardRequest) { result, error in + XCTAssertNil(result) + if let error = error { + XCTAssertTrue(CardError.isThreeDSecureCanceled(error)) + } else { + XCTFail("Expected error due to user cancellation") + } + expectation.fulfill() + } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } } diff --git a/UnitTests/CardPaymentsTests/Mocks/MockCardDelegate.swift b/UnitTests/CardPaymentsTests/Mocks/MockCardDelegate.swift deleted file mode 100644 index 2b9b5a71a..000000000 --- a/UnitTests/CardPaymentsTests/Mocks/MockCardDelegate.swift +++ /dev/null @@ -1,45 +0,0 @@ -@testable import CorePayments -@testable import CardPayments - -class MockCardDelegate: CardDelegate { - - private var success: ((CardClient, CardResult) -> Void)? - private var failure: ((CardClient, CoreSDKError) -> Void)? - private var cancel: ((CardClient) -> Void)? - private var threeDSWillLaunch: ((CardClient) -> Void)? - private var threeDSLaunched: ((CardClient) -> Void)? - - required init( - success: ((CardClient, CardResult) -> Void)? = nil, - error: ((CardClient, CoreSDKError) -> Void)? = nil, - cancel: ((CardClient) -> Void)? = nil, - threeDSWillLaunch: ((CardClient) -> Void)? = nil, - threeDSLaunched: ((CardClient) -> Void)? = nil - ) { - self.success = success - self.failure = error - self.cancel = cancel - self.threeDSWillLaunch = threeDSWillLaunch - self.threeDSLaunched = threeDSLaunched - } - - func card(_ cardClient: CardClient, didFinishWithResult result: CardResult) { - success?(cardClient, result) - } - - func card(_ cardClient: CardClient, didFinishWithError error: CoreSDKError) { - failure?(cardClient, error) - } - - func cardDidCancel(_ cardClient: CardClient) { - cancel?(cardClient) - } - - func cardThreeDSecureWillLaunch(_ cardClient: CardClient) { - threeDSWillLaunch?(cardClient) - } - - func cardThreeDSecureDidFinish(_ cardClient: CardClient) { - threeDSLaunched?(cardClient) - } -} diff --git a/UnitTests/CardPaymentsTests/Mocks/MockCardVaultDelegate.swift b/UnitTests/CardPaymentsTests/Mocks/MockCardVaultDelegate.swift deleted file mode 100644 index a27c8cd20..000000000 --- a/UnitTests/CardPaymentsTests/Mocks/MockCardVaultDelegate.swift +++ /dev/null @@ -1,45 +0,0 @@ -@testable import CorePayments -@testable import CardPayments - -class MockCardVaultDelegate: CardVaultDelegate { - - private var success: ((CardClient, CardVaultResult) -> Void)? - private var failure: ((CardClient, CoreSDKError) -> Void)? - private var cancel: ((CardClient) -> Void)? - private var threeDSWillLaunch: ((CardClient) -> Void)? - private var threeDSLaunched: ((CardClient) -> Void)? - - required init( - success: ((CardClient, CardVaultResult) -> Void)? = nil, - error: ((CardClient, CoreSDKError) -> Void)? = nil, - cancel: ((CardClient) -> Void)? = nil, - threeDSWillLaunch: ((CardClient) -> Void)? = nil, - threeDSLaunched: ((CardClient) -> Void)? = nil - ) { - self.success = success - self.failure = error - self.cancel = cancel - self.threeDSWillLaunch = threeDSWillLaunch - self.threeDSLaunched = threeDSLaunched - } - - func card(_ cardClient: CardClient, didFinishWithVaultResult vaultResult: CardPayments.CardVaultResult) { - success?(cardClient, vaultResult) - } - - func card(_ cardClient: CardClient, didFinishWithVaultError vaultError: CorePayments.CoreSDKError) { - failure?(cardClient, vaultError) - } - - func cardThreeDSecureDidCancel(_ cardClient: CardClient) { - cancel?(cardClient) - } - - func cardThreeDSecureWillLaunch(_ cardClient: CardClient) { - threeDSWillLaunch?(cardClient) - } - - func cardThreeDSecureDidFinish(_ cardClient: CardClient) { - threeDSLaunched?(cardClient) - } -} diff --git a/UnitTests/PayPalNativePaymentsTests/MockNativeCheckoutProvider.swift b/UnitTests/PayPalNativePaymentsTests/MockNativeCheckoutProvider.swift deleted file mode 100644 index 22769c326..000000000 --- a/UnitTests/PayPalNativePaymentsTests/MockNativeCheckoutProvider.swift +++ /dev/null @@ -1,57 +0,0 @@ -import Foundation -import UIKit - -@testable import PayPalNativePayments -@testable import CorePayments -import PayPalCheckout -import XCTest - -class MockNativeCheckoutProvider: NativeCheckoutStartable { - - var correlationID: String? - var userAuthenticationEmail: String? - var onCancel: StartableCancelCallback? - var onError: StartableErrorCallback? - var onApprove: StartableApproveCallback? - var onShippingChange: StartableShippingCallback? - - required init(nxoConfig: CheckoutConfig) { } - - // todo: implemenet cases for other callbacks - func start( - presentingViewController: UIViewController?, - orderID: String, - onStartableApprove: @escaping StartableApproveCallback, - onStartableShippingChange: @escaping StartableShippingCallback, - onStartableCancel: @escaping StartableCancelCallback, - onStartableError: @escaping StartableErrorCallback, - nxoConfig: CheckoutConfig - ) { - self.onCancel = onStartableCancel - self.onError = onStartableError - self.onApprove = onStartableApprove - self.onShippingChange = onStartableShippingChange - self.userAuthenticationEmail = nxoConfig.authConfig.userEmail - } - - func triggerCancel() { - onCancel?() - } - - func triggerError(errorReason: String) { - onError?(errorReason) - } - - func triggerApprove(orderdID: String, payerID: String) { - onApprove?(orderdID, payerID) - } - - func triggerShippingChange( - type: ShippingChangeType, - actions: PayPalNativePaysheetActions, - address: PayPalNativeShippingAddress, - method: PayPalNativeShippingMethod? = nil - ) { - onShippingChange?(type, actions, address, method) - } -} diff --git a/UnitTests/PayPalNativePaymentsTests/MockPayPalDelegate.swift b/UnitTests/PayPalNativePaymentsTests/MockPayPalDelegate.swift deleted file mode 100644 index 8b930817c..000000000 --- a/UnitTests/PayPalNativePaymentsTests/MockPayPalDelegate.swift +++ /dev/null @@ -1,27 +0,0 @@ -import PayPalCheckout -@testable import CorePayments -@testable import PayPalNativePayments - -class MockPayPalDelegate: PayPalNativeCheckoutDelegate { - - var capturedResult: PayPalNativeCheckoutResult? - var capturedError: CoreSDKError? - var paypalDidCancel = false - var paypalDidStart = false - - func paypal(_ payPalClient: PayPalNativeCheckoutClient, didFinishWithResult result: PayPalNativeCheckoutResult) { - capturedResult = result - } - - func paypal(_ payPalClient: PayPalNativeCheckoutClient, didFinishWithError error: CoreSDKError) { - capturedError = error - } - - func paypalDidCancel(_ payPalClient: PayPalNativeCheckoutClient) { - paypalDidCancel = true - } - - func paypalWillStart(_ payPalClient: PayPalNativeCheckoutClient) { - paypalDidStart = true - } -} diff --git a/UnitTests/PayPalNativePaymentsTests/MockPayPalNativeShipping.swift b/UnitTests/PayPalNativePaymentsTests/MockPayPalNativeShipping.swift deleted file mode 100644 index 09cb986c1..000000000 --- a/UnitTests/PayPalNativePaymentsTests/MockPayPalNativeShipping.swift +++ /dev/null @@ -1,23 +0,0 @@ -@testable import PayPalNativePayments - -class MockPayPalNativeShipping: PayPalNativeShippingDelegate { - - var capturedShippingAddress: PayPalNativeShippingAddress? - var capturedShippingMethod: PayPalNativeShippingMethod? - - func paypal( - _ payPalClient: PayPalNativePayments.PayPalNativeCheckoutClient, - didShippingAddressChange shippingAddress: PayPalNativePayments.PayPalNativeShippingAddress, - withAction shippingActions: PayPalNativePayments.PayPalNativePaysheetActions - ) { - capturedShippingAddress = shippingAddress - } - - func paypal( - _ payPalClient: PayPalNativePayments.PayPalNativeCheckoutClient, - didShippingMethodChange shippingMethod: PayPalNativePayments.PayPalNativeShippingMethod, - withAction shippingActions: PayPalNativePayments.PayPalNativePaysheetActions - ) { - capturedShippingMethod = shippingMethod - } -} diff --git a/UnitTests/PayPalNativePaymentsTests/MockShippingChangeActions.swift b/UnitTests/PayPalNativePaymentsTests/MockShippingChangeActions.swift deleted file mode 100644 index e7c137c14..000000000 --- a/UnitTests/PayPalNativePaymentsTests/MockShippingChangeActions.swift +++ /dev/null @@ -1,15 +0,0 @@ -@testable import PayPalNativePayments - -class MockShippingChangeActions: ShippingActionsProtocol { - - var rejectInvoked = false - var approveInvoked = false - - func reject() { - rejectInvoked = true - } - - func approve() { - approveInvoked = true - } -} diff --git a/UnitTests/PayPalNativePaymentsTests/NativeCheckoutProvider_Tests.swift b/UnitTests/PayPalNativePaymentsTests/NativeCheckoutProvider_Tests.swift deleted file mode 100644 index eed2a7f7a..000000000 --- a/UnitTests/PayPalNativePaymentsTests/NativeCheckoutProvider_Tests.swift +++ /dev/null @@ -1,47 +0,0 @@ -import UIKit -import PayPalCheckout -@testable import PayPalNativePayments -import XCTest - -class NativeCheckoutProvider_Tests: XCTestCase { - - func testStart_whenStartIsCalled_checkoutIsInitialized() { - let nativeCheckoutProvider = NativeCheckoutProvider(MockCheckout.self) - nativeCheckoutProvider.start( - presentingViewController: nil, - orderID: "", - onStartableApprove: { _, _ in }, - onStartableShippingChange: { _, _, _, _ in }, - onStartableCancel: { }, - onStartableError: { _ in }, - nxoConfig: CheckoutConfig(clientID: "") - ) - - XCTAssertTrue(MockCheckout.startInvoked) - XCTAssertTrue(MockCheckout.isConfigSet) - XCTAssertFalse(MockCheckout.showsExitAlert) - } -} - -class MockCheckout: CheckoutProtocol { - - static func start( - presentingViewController: UIViewController?, - createOrder: PayPalCheckout.CheckoutConfig.CreateOrderCallback?, - onApprove: PayPalCheckout.CheckoutConfig.ApprovalCallback?, - onShippingChange: PayPalCheckout.CheckoutConfig.ShippingChangeCallback?, - onCancel: PayPalCheckout.CheckoutConfig.CancelCallback?, - onError: PayPalCheckout.CheckoutConfig.ErrorCallback? - ) { - startInvoked = true - } - - static var showsExitAlert = false - - static func set(config: PayPalCheckout.CheckoutConfig) { - isConfigSet = true - } - - static var startInvoked = false - static var isConfigSet = false -} diff --git a/UnitTests/PayPalNativePaymentsTests/PayPalClient_Tests.swift b/UnitTests/PayPalNativePaymentsTests/PayPalClient_Tests.swift deleted file mode 100644 index d869a1120..000000000 --- a/UnitTests/PayPalNativePaymentsTests/PayPalClient_Tests.swift +++ /dev/null @@ -1,141 +0,0 @@ -import XCTest -import PayPalCheckout -@testable import CorePayments -@testable import PayPalNativePayments -@testable import TestShared - -class PayPalClient_Tests: XCTestCase { - - let config = CoreConfig(clientID: "testClientID", environment: .sandbox) - lazy var mockNetworkingClient = MockNetworkingClient(coreConfig: config) - - let nxoConfig = CheckoutConfig( - clientID: "testClientID", - createOrder: nil, - onApprove: nil, - onShippingChange: nil, - onCancel: nil, - onError: nil, - environment: .sandbox - ) - - let request = PayPalNativeCheckoutRequest(orderID: "fake-order-id") - - lazy var mockNativeCheckoutProvider = MockNativeCheckoutProvider(nxoConfig: nxoConfig) - lazy var payPalClient = PayPalNativeCheckoutClient( - config: config, - nativeCheckoutProvider: mockNativeCheckoutProvider, - networkingClient: mockNetworkingClient - ) - - func testUserAuthenticationIsPassed_returnsRequestEmail() async { - let modifiedRequest = PayPalNativeCheckoutRequest(orderID: "fake-order-id", userAuthenticationEmail: "fake_user_email@mock.paypal.com") - let mockPayPalDelegate = MockPayPalDelegate() - payPalClient.delegate = mockPayPalDelegate - await payPalClient.start(request: modifiedRequest) - XCTAssertEqual(mockNativeCheckoutProvider.userAuthenticationEmail, "fake_user_email@mock.paypal.com") - } - - func testUserAuthenticationIsNil_returnsNilForEmail() async { - let modifiedRequest = PayPalNativeCheckoutRequest(orderID: "fake-order-id") - let mockPayPalDelegate = MockPayPalDelegate() - payPalClient.delegate = mockPayPalDelegate - await payPalClient.start(request: request) - XCTAssertNil(mockNativeCheckoutProvider.userAuthenticationEmail) - } - - func testStart_whenNativeSDKOnApproveCalled_returnsPayPalResult() async { - - let mockOrderID = "mock_order_id" - let mockPayerID = "mock_payer_id" - let mockPayPalDelegate = MockPayPalDelegate() - payPalClient.delegate = mockPayPalDelegate - await payPalClient.start(request: request) - mockNativeCheckoutProvider.triggerApprove(orderdID: mockOrderID, payerID: mockPayerID) - let result = mockPayPalDelegate.capturedResult - XCTAssertEqual(result?.orderID, mockOrderID) - XCTAssertEqual(result?.payerID, mockPayerID) - } - - func testStart_whenNativeSDKOnCancelCalled_returnsCancellation() async { - - let mockPayPalDelegate = MockPayPalDelegate() - payPalClient.delegate = mockPayPalDelegate - await payPalClient.start(request: request) - mockNativeCheckoutProvider.triggerCancel() - XCTAssert(mockPayPalDelegate.paypalDidCancel) - } - - func testStart_whenNativeSDKOnStart_callbackIsTriggered() async { - - let mockPayPalDelegate = MockPayPalDelegate() - payPalClient.delegate = mockPayPalDelegate - await payPalClient.start(request: request) - XCTAssert(mockPayPalDelegate.paypalDidStart) - } - - func testStart_whenNativeSDKOnErrorCalled_returnsCheckoutError() async { - - let errorMessage = "error_message" - let mockPayPalDelegate = MockPayPalDelegate() - payPalClient.delegate = mockPayPalDelegate - await payPalClient.start(request: request) - mockNativeCheckoutProvider.triggerError(errorReason: errorMessage) - XCTAssertEqual(mockPayPalDelegate.capturedError?.errorDescription, errorMessage) - } - - func testStart_whenNativeSDKShippingAddressChange_returnsNewAddress() async { - let mockShippingDelegate = MockPayPalNativeShipping() - let mockShippingAddress = PayPalNativeShippingAddress( - addressID: "id", - adminArea1: "area1", - adminArea2: "area2", - postalCode: "postal_code", - countryCode: "us" - ) - - payPalClient.shippingDelegate = mockShippingDelegate - await payPalClient.start(request: request) - mockNativeCheckoutProvider.triggerShippingChange( - type: .shippingAddress, - actions: PayPalNativePaysheetActions(MockShippingChangeActions()), - address: mockShippingAddress - ) - - let capturedShippingAddress = mockShippingDelegate.capturedShippingAddress - XCTAssertEqual(mockShippingAddress.addressID, capturedShippingAddress?.addressID) - XCTAssertEqual(mockShippingAddress.adminArea1, capturedShippingAddress?.adminArea1) - XCTAssertEqual(mockShippingAddress.adminArea2, capturedShippingAddress?.adminArea2) - XCTAssertEqual(mockShippingAddress.postalCode, capturedShippingAddress?.postalCode) - XCTAssertEqual(mockShippingAddress.countryCode, capturedShippingAddress?.countryCode) - } - - func testStart_whenNativeSDKShippingMethodChange_returnsNewMethod() async { - - let mockShippingDelegate = MockPayPalNativeShipping() - let mockShippingMethod = PayPalNativeShippingMethod( - id: "id", - label: "label", - selected: true, - type: .shipping, - value: "0.0", - currencyCode: "usd" - ) - payPalClient.shippingDelegate = mockShippingDelegate - await payPalClient.start(request: request) - mockNativeCheckoutProvider.triggerShippingChange( - type: .shippingMethod, - actions: PayPalNativePaysheetActions(MockShippingChangeActions()), - address: PayPalNativeShippingAddress(), - method: mockShippingMethod - ) - - let capturedShippingMethod = mockShippingDelegate.capturedShippingMethod - XCTAssertEqual(mockShippingMethod.id, capturedShippingMethod?.id) - XCTAssertEqual(mockShippingMethod.label, capturedShippingMethod?.label) - XCTAssertEqual(mockShippingMethod.currencyCode, capturedShippingMethod?.currencyCode) - XCTAssertEqual(mockShippingMethod.value, capturedShippingMethod?.value) - XCTAssertEqual(mockShippingMethod.type, capturedShippingMethod?.type) - XCTAssertEqual(mockShippingMethod.selected, capturedShippingMethod?.selected) - } -} diff --git a/UnitTests/PayPalNativePaymentsTests/PayPalEnvironment_Tests.swift b/UnitTests/PayPalNativePaymentsTests/PayPalEnvironment_Tests.swift deleted file mode 100644 index bcb7c2f0d..000000000 --- a/UnitTests/PayPalNativePaymentsTests/PayPalEnvironment_Tests.swift +++ /dev/null @@ -1,14 +0,0 @@ -import XCTest -@testable import CorePayments -@testable import PayPalNativePayments - -class PayPalEnvironment_Tests: XCTestCase { - - func testPayPalEnvironment_convertsToPayPalCheckoutEnvironmentCorrectly() { - let sandbox = Environment.sandbox - let live = Environment.live - - XCTAssertEqual(sandbox.toNativeCheckoutSDKEnvironment(), .sandbox) - XCTAssertEqual(live.toNativeCheckoutSDKEnvironment(), .live) - } -} diff --git a/UnitTests/PayPalNativePaymentsTests/PayPalNativePaysheetAction_Tests.swift b/UnitTests/PayPalNativePaymentsTests/PayPalNativePaysheetAction_Tests.swift deleted file mode 100644 index 3e2212e54..000000000 --- a/UnitTests/PayPalNativePaymentsTests/PayPalNativePaysheetAction_Tests.swift +++ /dev/null @@ -1,21 +0,0 @@ -import XCTest -@testable import PayPalNativePayments - -class PayPalNativePaysheetAction_Tests: XCTestCase { - - func testReject_whenRejectIsCalled_wrappedRejectIsInvoked() { - let mockWrappedShippingActions = MockShippingChangeActions() - let payPalNativeShippingActions = PayPalNativePaysheetActions(mockWrappedShippingActions) - - payPalNativeShippingActions.reject() - XCTAssertTrue(mockWrappedShippingActions.rejectInvoked) - } - - func testApprove_whenApproveIsCalled_wrappedApproveIsInvoked() { - let mockWrappedShippingActions = MockShippingChangeActions() - let payPalNativeShippingActions = PayPalNativePaysheetActions(mockWrappedShippingActions) - - payPalNativeShippingActions.approve() - XCTAssertTrue(mockWrappedShippingActions.approveInvoked) - } -} diff --git a/UnitTests/PayPalNativePaymentsTests/PayPalNativeShippingAddress_Tests.swift b/UnitTests/PayPalNativePaymentsTests/PayPalNativeShippingAddress_Tests.swift deleted file mode 100644 index dc68a908c..000000000 --- a/UnitTests/PayPalNativePaymentsTests/PayPalNativeShippingAddress_Tests.swift +++ /dev/null @@ -1,23 +0,0 @@ -import XCTest -import PayPalCheckout -@testable import PayPalNativePayments - -class PayPalNativeShippingAddress_Tests: XCTestCase { - - func testInit_convertsFromNXOTypeCorrectly() { - let nxoShippingAddress = PayPalCheckout.ShippingChangeAddress( - addressID: "fake-address-id", - adminArea1: "fake-admin-area-1", - adminArea2: "fake-admin-area-2", - postalCode: "fake-postal-code", - countryCode: "fake-country-code" - ) - - let sut = PayPalNativeShippingAddress(nxoShippingAddress) - XCTAssertEqual(sut.addressID, "fake-address-id") - XCTAssertEqual(sut.adminArea1, "fake-admin-area-1") - XCTAssertEqual(sut.adminArea2, "fake-admin-area-2") - XCTAssertEqual(sut.postalCode, "fake-postal-code") - XCTAssertEqual(sut.countryCode, "fake-country-code") - } -} diff --git a/UnitTests/PayPalNativePaymentsTests/PayPalNativeShippingMethod_Tests.swift b/UnitTests/PayPalNativePaymentsTests/PayPalNativeShippingMethod_Tests.swift deleted file mode 100644 index 344498e7e..000000000 --- a/UnitTests/PayPalNativePaymentsTests/PayPalNativeShippingMethod_Tests.swift +++ /dev/null @@ -1,27 +0,0 @@ -import XCTest -import PayPalCheckout -@testable import PayPalNativePayments - -class PayPalNativeShippingMethod_Tests: XCTestCase { - - func testInit_convertsFromNXOTypeCorrectly() { - let nxoUnitAmount = PayPalCheckout.UnitAmount( - currencyCode: CurrencyCode.usd, - value: "10.00" - ) - let nxoShippingMethod = PayPalCheckout.ShippingMethod( - id: "fake-id", - label: "fake-label", - selected: true, - type: PayPalCheckout.ShippingType.pickup, - amount: nxoUnitAmount) - - let sut = PayPalNativeShippingMethod(nxoShippingMethod) - XCTAssertEqual(sut.id, "fake-id") - XCTAssertEqual(sut.label, "fake-label") - XCTAssertTrue(sut.selected) - XCTAssertEqual(sut.type, PayPalNativeShippingMethod.DeliveryType.pickup) - XCTAssertEqual(sut.value, "10.00") - XCTAssertEqual(sut.currencyCode, "USD") - } -} diff --git a/UnitTests/PayPalWebPaymentsTests/Mocks/MockPayPalVaultDelegate.swift b/UnitTests/PayPalWebPaymentsTests/Mocks/MockPayPalVaultDelegate.swift deleted file mode 100644 index 7b8fc6ea3..000000000 --- a/UnitTests/PayPalWebPaymentsTests/Mocks/MockPayPalVaultDelegate.swift +++ /dev/null @@ -1,37 +0,0 @@ -@testable import CorePayments -@testable import PayPalWebPayments - -class MockPayPalVaultDelegate: PayPalVaultDelegate { - - private var success: ((PayPalWebCheckoutClient, PayPalVaultResult) -> Void)? - private var failure: ((PayPalWebCheckoutClient, CoreSDKError) -> Void)? - private var cancel: ((PayPalWebCheckoutClient) -> Void)? - - required init( - success: ((PayPalWebCheckoutClient, PayPalVaultResult) -> Void)? = nil, - error: ((PayPalWebCheckoutClient, CoreSDKError) -> Void)? = nil, - cancel: ((PayPalWebCheckoutClient) -> Void)? = nil - ) { - self.success = success - self.failure = error - self.cancel = cancel - } - - func paypal( - _ paypalWebClient: PayPalWebPayments.PayPalWebCheckoutClient, - didFinishWithVaultResult paypalVaultResult: PayPalVaultResult - ) { - success?(paypalWebClient, paypalVaultResult) - } - - func paypal( - _ paypalWebClient: PayPalWebPayments.PayPalWebCheckoutClient, - didFinishWithVaultError vaultError: CorePayments.CoreSDKError - ) { - failure?(paypalWebClient, vaultError) - } - - func paypalDidCancel(_ paypalWebClient: PayPalWebPayments.PayPalWebCheckoutClient) { - cancel?(paypalWebClient) - } -} diff --git a/UnitTests/PayPalWebPaymentsTests/Mocks/MockPayPalWebDelegate.swift b/UnitTests/PayPalWebPaymentsTests/Mocks/MockPayPalWebDelegate.swift deleted file mode 100644 index a92bb94dc..000000000 --- a/UnitTests/PayPalWebPaymentsTests/Mocks/MockPayPalWebDelegate.swift +++ /dev/null @@ -1,21 +0,0 @@ -import CorePayments -import PayPalWebPayments - -class MockPayPalWebDelegate: PayPalWebCheckoutDelegate { - - var capturedResult: PayPalWebCheckoutResult? - var capturedError: CoreSDKError? - var paypalDidCancel = false - - func payPal(_ payPalClient: PayPalWebCheckoutClient, didFinishWithResult result: PayPalWebCheckoutResult) { - capturedResult = result - } - - func payPal(_ payPalClient: PayPalWebCheckoutClient, didFinishWithError error: CoreSDKError) { - capturedError = error - } - - func payPalDidCancel(_ payPalClient: PayPalWebCheckoutClient) { - paypalDidCancel = true - } -} diff --git a/UnitTests/PayPalWebPaymentsTests/PayPalWebCheckoutClient_Tests.swift b/UnitTests/PayPalWebPaymentsTests/PayPalWebCheckoutClient_Tests.swift index 8c4fe553b..8866d2b5e 100644 --- a/UnitTests/PayPalWebPaymentsTests/PayPalWebCheckoutClient_Tests.swift +++ b/UnitTests/PayPalWebPaymentsTests/PayPalWebCheckoutClient_Tests.swift @@ -26,8 +26,8 @@ class PayPalClient_Tests: XCTestCase { func testVault_whenSandbox_launchesCorrectURLInWebSession() { let vaultRequest = PayPalVaultRequest(setupTokenID: "fake-token") - payPalClient.vault(vaultRequest) - + payPalClient.vault(vaultRequest) { _, _ in } + XCTAssertEqual(mockWebAuthenticationSession.lastLaunchedURL?.absoluteString, "https://sandbox.paypal.com/agreements/approve?approval_session_id=fake-token") } @@ -40,8 +40,8 @@ class PayPalClient_Tests: XCTestCase { ) let vaultRequest = PayPalVaultRequest(setupTokenID: "fake-token") - payPalClient.vault(vaultRequest) - + payPalClient.vault(vaultRequest) { _, _ in } + XCTAssertEqual(mockWebAuthenticationSession.lastLaunchedURL?.absoluteString, "https://paypal.com/agreements/approve?approval_session_id=fake-token") } @@ -53,18 +53,16 @@ class PayPalClient_Tests: XCTestCase { let expectedTokenIDResult = "fakeTokenID" let expectedSessionIDResult = "fakeSessionID" - let mockVaultDelegate = MockPayPalVaultDelegate(success: {_, result in - XCTAssertEqual(expectedTokenIDResult, result.tokenID) - XCTAssertEqual(expectedSessionIDResult, result.approvalSessionID) - expectation.fulfill() - }, error: {_, _ in - XCTFail("Invoked error() callback. Should invoke success().") - }) - payPalClient.vaultDelegate = mockVaultDelegate + let vaultRequest = PayPalVaultRequest(setupTokenID: "fakeTokenID") - payPalClient.vault(vaultRequest) + payPalClient.vault(vaultRequest) { result, error in + XCTAssertEqual(expectedTokenIDResult, result?.tokenID) + XCTAssertEqual(expectedSessionIDResult, result?.approvalSessionID) + XCTAssertNil(error) + expectation.fulfill() + } - waitForExpectations(timeout: 10) + waitForExpectations(timeout: 2, handler: nil) } func testVault_whenWebSession_cancelled() { @@ -76,17 +74,41 @@ class PayPalClient_Tests: XCTestCase { let expectation = expectation(description: "vault(url:) completed") - let mockVaultDelegate = MockPayPalVaultDelegate(success: {_, _ in - XCTFail("Invoked success callback. Should invoke cancel().") - }, error: {_, _ in - XCTFail("Invoked error() callback. Should invoke success().") - }, cancel: { _ in - XCTAssert(true) + let vaultRequest = PayPalVaultRequest(setupTokenID: "fakeTokenID") + payPalClient.vault(vaultRequest) { result, error in + XCTAssertNil(result) + if 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() - }) - payPalClient.vaultDelegate = mockVaultDelegate + } + + waitForExpectations(timeout: 10) + } + + func testVault_whenWebSession_cancelled_returnsIsVaultCanceledTrue() { + + mockWebAuthenticationSession.cannedErrorResponse = ASWebAuthenticationSessionError( + .canceledLogin, + userInfo: ["Description": "Mock cancellation error description."] + ) + + let expectation = expectation(description: "vault(url:) completed") + let vaultRequest = PayPalVaultRequest(setupTokenID: "fakeTokenID") - payPalClient.vault(vaultRequest) + payPalClient.vault(vaultRequest) { result, error in + if let error { + XCTAssertNil(result) + XCTAssertTrue(PayPalError.isVaultCanceled(error)) + } else { + XCTFail("Expected error from PayPal vault cancellation") + } + expectation.fulfill() + } waitForExpectations(timeout: 10) } @@ -94,25 +116,25 @@ class PayPalClient_Tests: XCTestCase { func testVault_whenWebSession_returnsDefaultError() { let expectedError = CoreSDKError( - code: PayPalWebCheckoutClientError.Code.webSessionError.rawValue, - domain: PayPalWebCheckoutClientError.domain, - errorDescription: PayPalWebCheckoutClientError.payPalVaultResponseError.errorDescription + code: PayPalError.Code.webSessionError.rawValue, + domain: PayPalError.domain, + errorDescription: PayPalError.payPalVaultResponseError.errorDescription ) mockWebAuthenticationSession.cannedErrorResponse = expectedError let expectation = expectation(description: "vault(url:) completed") - let mockVaultDelegate = MockPayPalVaultDelegate(success: {_, _ in - XCTFail("Invoked success callback. Should invoke error().") - }, error: {_, vaultError in - XCTAssertEqual(vaultError.code, expectedError.code) - expectation.fulfill() - }, cancel: { _ in - XCTFail("Invoked cancel callback. Should invoke error().") - }) - payPalClient.vaultDelegate = mockVaultDelegate let vaultRequest = PayPalVaultRequest(setupTokenID: "fakeTokenID") - payPalClient.vault(vaultRequest) + payPalClient.vault(vaultRequest) { result, error in + XCTAssertNil(result) + if 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) } @@ -124,29 +146,29 @@ class PayPalClient_Tests: XCTestCase { let expectation = expectation(description: "vault(url:) completed") let expectedError = CoreSDKError( - code: PayPalWebCheckoutClientError.payPalVaultResponseError.code, - domain: PayPalWebCheckoutClientError.domain, - errorDescription: PayPalWebCheckoutClientError.payPalVaultResponseError.errorDescription + code: PayPalError.payPalVaultResponseError.code, + domain: PayPalError.domain, + errorDescription: PayPalError.payPalVaultResponseError.errorDescription ) - let mockVaultDelegate = MockPayPalVaultDelegate(success: {_, _ in - XCTFail("Invoked success() callback. Should invoke error().") - }, error: {_, vaultError in - XCTAssertEqual(vaultError.code, expectedError.code) - expectation.fulfill() - }) - payPalClient.vaultDelegate = mockVaultDelegate let vaultRequest = PayPalVaultRequest(setupTokenID: "fakeTokenID") - payPalClient.vault(vaultRequest) + payPalClient.vault(vaultRequest) { result, error in + XCTAssertNil(result) + if 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) } - func testStart_whenNativeSDKOnCancelCalled_returnsCancellationError() { + func testStart_whenWebAuthenticationSessionCancelCalled_returnsCancellationError() { let request = PayPalWebCheckoutRequest(orderID: "1234") - let delegate = MockPayPalWebDelegate() - payPalClient.delegate = delegate mockWebAuthenticationSession.cannedErrorResponse = ASWebAuthenticationSessionError( _bridgedNSError: NSError( domain: ASWebAuthenticationSessionError.errorDomain, @@ -155,58 +177,104 @@ class PayPalClient_Tests: XCTestCase { ) ) - payPalClient.start(request: request) + let expectation = self.expectation(description: "Call back invoked with error") + payPalClient.start(request: request) { result, error in + XCTAssertNil(result) + if 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() + } + + waitForExpectations(timeout: 2, handler: nil) + } + + func testStart_whenWebSession_cancelled_returnsIsCheckoutCanceledTrue() { + + let request = PayPalWebCheckoutRequest(orderID: "1234") + + mockWebAuthenticationSession.cannedErrorResponse = ASWebAuthenticationSessionError( + _bridgedNSError: NSError( + domain: ASWebAuthenticationSessionError.errorDomain, + code: ASWebAuthenticationSessionError.canceledLogin.rawValue, + userInfo: ["Description": "Mock cancellation error description."] + ) + ) + + let expectation = self.expectation(description: "Call back invoked with error") + payPalClient.start(request: request) { result, error in + XCTAssertNil(result) + if let error { + XCTAssertTrue(PayPalError.isCheckoutCanceled(error)) + } else { + XCTFail("Expected error from PayPal checkout cancellation.") + } + expectation.fulfill() + } - XCTAssertTrue(delegate.paypalDidCancel) + waitForExpectations(timeout: 2, handler: nil) } + func testStart_whenWebAuthenticationSessions_returnsWebSessionError() { let request = PayPalWebCheckoutRequest(orderID: "1234") - let delegate = MockPayPalWebDelegate() - payPalClient.delegate = delegate mockWebAuthenticationSession.cannedErrorResponse = CoreSDKError( - code: PayPalWebCheckoutClientError.Code.webSessionError.rawValue, - domain: PayPalWebCheckoutClientError.domain, + code: PayPalError.Code.webSessionError.rawValue, + domain: PayPalError.domain, errorDescription: "Mock web session error description." ) - payPalClient.start(request: request) - - let error = delegate.capturedError - - XCTAssertEqual(error?.domain, PayPalWebCheckoutClientError.domain) - XCTAssertEqual(error?.code, PayPalWebCheckoutClientError.Code.webSessionError.rawValue) - XCTAssertEqual(error?.localizedDescription, "Mock web session error description.") + let expectation = self.expectation(description: "Call back invoked with error") + payPalClient.start(request: request) { result, error in + XCTAssertNil(result) + if 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) } func testStart_whenResultURLMissingParameters_returnsMalformedResultError() { let request = PayPalWebCheckoutRequest(orderID: "1234") - let delegate = MockPayPalWebDelegate() - payPalClient.delegate = delegate mockWebAuthenticationSession.cannedResponseURL = URL(string: "https://fakeURL?PayerID=98765") - payPalClient.start(request: request) - - let error = delegate.capturedError - - XCTAssertEqual(error?.domain, PayPalWebCheckoutClientError.domain) - XCTAssertEqual(error?.code, PayPalWebCheckoutClientError.Code.malformedResultError.rawValue) - XCTAssertEqual(error?.localizedDescription, "Result did not contain the expected data.") + let expectation = self.expectation(description: "Call back invoked with error") + payPalClient.start(request: request) { result, error in + XCTAssertNil(result) + if 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) } func testStart_whenWebResultIsSuccessful_returnsSuccessfulResult() { let request = PayPalWebCheckoutRequest(orderID: "1234") - let delegate = MockPayPalWebDelegate() - payPalClient.delegate = delegate mockWebAuthenticationSession.cannedResponseURL = URL(string: "https://fakeURL?token=1234&PayerID=98765") - payPalClient.start(request: request) - - let result = delegate.capturedResult - - XCTAssertEqual(result?.orderID, "1234") - XCTAssertEqual(result?.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) + expectation.fulfill() + } + waitForExpectations(timeout: 2, handler: nil) } func testpayPalCheckoutReturnURL_returnsCorrectURL() { diff --git a/UnitTests/PaymentsCoreTests/HTTP_Tests.swift b/UnitTests/PaymentsCoreTests/HTTP_Tests.swift index 41eb770fe..01a64886a 100644 --- a/UnitTests/PaymentsCoreTests/HTTP_Tests.swift +++ b/UnitTests/PaymentsCoreTests/HTTP_Tests.swift @@ -57,8 +57,12 @@ class HTTP_Tests: XCTestCase { do { _ = try await sut.performRequest(fakeHTTPRequest) XCTFail("Request succeeded. Expected error.") - } catch let error { - XCTAssertTrue(serverError === (error as AnyObject)) + } catch let error as CoreSDKError { + XCTAssertEqual(error.domain, NetworkingClientError.domain) + XCTAssertEqual(error.code, NetworkingClientError.Code.urlSessionError.rawValue) + XCTAssertEqual(error.localizedDescription, "An error occured during network call. Contact developer.paypal.com/support.") + } catch { + XCTFail("Unexpected error type") } } diff --git a/v2_MIGRATION_GUIDE.md b/v2_MIGRATION_GUIDE.md new file mode 100644 index 000000000..8abf87731 --- /dev/null +++ b/v2_MIGRATION_GUIDE.md @@ -0,0 +1,196 @@ +# Migrating from Delegates to Completion Handlers + +## Overview +Version 2.0 of the SDK transitions from delgate-based flows to completion handler-based flows. This change simplifies the integration and provides better compatibility with modern async/await patterns. + +### Key Changes + +### Important Change: Cancellation Handling +In v2.0, cancellations (e.g., 3DS cancellations, PayPal web flow cancellations) are now returned as errors rather than as separate delegate methods. There are new helper static functions, to help you discern threeDSecure cancellation errors and PayPal web flow cancellation errors. +- `CardError.threeDSecureCanceled(Error)` will return true for 3DS cancellation errors received during card payment or card vaulting flows. +- `PayPalError.isCheckoutCanceled(Error)` will return true for user cancellation during PayPalWebCheckout session. +- `PayPalError.isVaultCanceled(Error)` will return true for user cancellation during PayPal vault session. + +### CardClient Changes + +```swift +// Old Delgate-based +class MyViewController: CardDelegate { + func setupPayment() { + let cardClient = CardClient(config: config) + cardClient.delegate = self + cardClient.approveOrder(request: cardRequest) + } + + func card(_ cardClient: CardClient, didFinishWithResult result: CardResult) { + // Handle success + } + + func card(_ cardClient: CardClient, didFinishWithError error: Error) { + // Handle error + } + + func cardDidCancel(_ cardClient: CardClient) { + // Handle cancellation + } +} + +// New (Completion-based) +class MyViewController { + func setupPayment() { + let cardClient = CardClient(config: config) + cardClient.approveOrder(request: cardRequest) { [weak self] result, error in + if let error = error { + // if threeDSecure is canceled by user + if CardError.isThreeDSecureCanceled(error) { + // handle cancel error + } else { + // handle other errors + } + return + } + if let result = result { + // handle success + } + } + } +} +``` + +### PayPalWebCheckoutClient Changes + +```Swift +// Old (Delegate-based) +class MyViewController: PayPalWebCheckoutDelegate { + func startPayPalFlow() { + let payPalClient = PayPalWebCheckoutClient(config: config) + payPalClient.delegate = self + payPalClient.approveOrder(request: paypalRequest) + } + + func payPal(_ payPalClient: PayPalWebCheckoutClient, didFinishWithResult result: PayPalWebCheckoutResult) { + // Handle success + } + + func payPal(_ payPalClient: PayPalWebCheckoutClient, didFinishWithError error: Error) { + // Handle error + } + + func payPalDidCancel(_ payPalClient: PayPalCheckoutClient) { + // Handle cancellation + } +} + +// New (Completion-based) +class MyViewController { + func setupPayment() { + let payPalClient = PayPalWebCheckoutClient(config: config) + payPalClient.start(request: paypalRequest) { [weak self] result, error in + if let error = error { + // if PayPal webflow is canceled by user + if PayPalError.isCheckoutCanceled(error) { + // handle cancel error + } else { + // handle all other errors + } + return + } + if let result = result { + // handle success + } + } + } +} +``` + +## Async/Await +The SDK now provides async/await support, offering a more concise way to handle asynchronous operations. + +### CardClient +```swift +class MyViewController { + func setupPayment() async { + let cardClient = CardClient(config: config) + do { + let result = try await cardClient.approveOrder(request: cardRequest) + // payment successful + handleSuccess(result) + } catch { + handleError(error) + } + } +} +``` + +### PayPalWebCheckoutClient +```swift +class MyViewController { + func startPayPalFlow() async { + let payPalClient = PayPalWebCheckoutClient(config: config) + do { + let result = try await payPalClient.start(request: paypalRequest) + // Payment successful + handleSuccess(result) + } catch { + handleError() + } + } +} +``` + +## Migration Steps + +### 1. Update SDK Version +- Update your dependency manager (CocoaPods or SPM) to the latest SDK version + +### 2. Remove Delegate Implementation +```diff +// Remove delegate protocol conformance +- class MyViewController: CardDelegate { ++ class MyViewController { + +// Remove delegate property assignment +-cardClient.delegate = self + +// Remove delegate methods +- func card(_ cardClient: CardClient, didFinishWithResult result: CardResult) { +- func card(_ cardClient: CardClient, didFinishWithError error: Error) { +- func cardDidCancel(_ cardClient: CardClient ) { +``` + +### 3. Update SDK Flow Implementation + + Option 1: Using completion handlers +```swift +func processPayment() { + showLoadingIndicator() + + cardClient.approveOrder(request: cardRequest) { [weak self] result, error in + guard let self = self else { return } + removeLoadingIndicator() + + if let error = error { + handleError() + return + } + + if let result = result { + handleSuccess(result) + } + } +} +``` + Option 2: Using async/await +```swift +func processPayment() async { + showLoadingIndicator() + defer { removeLoadingIndicator() } + + do { + let result = try await cardClient.approveOrder(request: cardRequest) + handleSuccess(result) + } catch { + handleError() + } +} +```