From 407e758fecdc2caba8ee3282ad5667b510ad767b Mon Sep 17 00:00:00 2001 From: Jax DesMarais-Leder Date: Fri, 2 Aug 2024 15:09:42 -0500 Subject: [PATCH 1/8] update app install check --- Sources/BraintreePayPal/BTPayPalClient.swift | 10 +--------- Sources/BraintreePayPal/BTPayPalVaultRequest.swift | 4 +++- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/Sources/BraintreePayPal/BTPayPalClient.swift b/Sources/BraintreePayPal/BTPayPalClient.swift index 6cf56ed62f..bfae26aedc 100644 --- a/Sources/BraintreePayPal/BTPayPalClient.swift +++ b/Sources/BraintreePayPal/BTPayPalClient.swift @@ -37,9 +37,6 @@ import BraintreeDataCollector /// This allows us to set and return a completion in our methods that otherwise cannot require a completion. var appSwitchCompletion: (BTPayPalAccountNonce?, Error?) -> Void = { _, _ in } - /// Exposed for testing to check if the PayPal app is installed - var payPalAppInstalled: Bool = false - /// True if `tokenize()` was called with a Vault request object type var isVaultRequest: Bool = false @@ -311,8 +308,7 @@ import BraintreeDataCollector request: BTPayPalRequest, completion: @escaping (BTPayPalAccountNonce?, Error?) -> Void ) { - payPalAppInstalled = application.isPayPalAppInstalled() - linkType = (request as? BTPayPalVaultRequest)?.enablePayPalAppSwitch == true && payPalAppInstalled ? "universal" : "deeplink" + linkType = (request as? BTPayPalVaultRequest)?.enablePayPalAppSwitch == true ? "universal" : "deeplink" apiClient.sendAnalyticsEvent(BTPayPalAnalytics.tokenizeStarted, isVaultRequest: isVaultRequest, linkType: linkType) apiClient.fetchOrReturnRemoteConfiguration { configuration, error in @@ -333,10 +329,6 @@ import BraintreeDataCollector return } - if !self.payPalAppInstalled { - (request as? BTPayPalVaultRequest)?.enablePayPalAppSwitch = false - } - self.payPalRequest = request self.apiClient.post( request.hermesPath, diff --git a/Sources/BraintreePayPal/BTPayPalVaultRequest.swift b/Sources/BraintreePayPal/BTPayPalVaultRequest.swift index ae7d8657bf..8edf6e8ab9 100644 --- a/Sources/BraintreePayPal/BTPayPalVaultRequest.swift +++ b/Sources/BraintreePayPal/BTPayPalVaultRequest.swift @@ -19,6 +19,8 @@ import BraintreeCore /// - Warning: This property is currently in beta and may change or be removed in future releases. var enablePayPalAppSwitch: Bool = false + private var application: URLOpener = UIApplication.shared + // MARK: - Initializers /// Initializes a PayPal Vault request for the PayPal App Switch flow @@ -34,7 +36,7 @@ import BraintreeCore offerCredit: Bool = false ) { self.init(offerCredit: offerCredit, userAuthenticationEmail: userAuthenticationEmail) - self.enablePayPalAppSwitch = enablePayPalAppSwitch + self.enablePayPalAppSwitch = !application.isPayPalAppInstalled() ? false : enablePayPalAppSwitch } /// Initializes a PayPal Vault request From d2dce53a7f70e149938a8432e2b5cbdf161b666d Mon Sep 17 00:00:00 2001 From: Jax DesMarais-Leder Date: Fri, 2 Aug 2024 15:22:45 -0500 Subject: [PATCH 2/8] add LinkType enum; update linkType logic --- Braintree.xcodeproj/project.pbxproj | 4 ++++ Sources/BraintreeCore/Analytics/LinkType.swift | 6 ++++++ Sources/BraintreeCore/BTAPIClient.swift | 4 ++-- .../BraintreePayPal/BTPayPalApprovalURLParser.swift | 5 +++-- Sources/BraintreePayPal/BTPayPalClient.swift | 4 ++-- Sources/BraintreePayPal/BTPayPalReturnURL.swift | 4 ++-- Sources/BraintreePayPal/BTPayPalVaultRequest.swift | 3 ++- Sources/BraintreeVenmo/BTVenmoClient.swift | 4 ++-- .../Analytics/FPTIBatchData_Tests.swift | 2 +- .../BraintreePayPalTests/BTPayPalClient_Tests.swift | 12 +++++++----- UnitTests/BraintreeTestShared/MockAPIClient.swift | 4 ++-- .../BraintreeVenmoTests/BTVenmoClient_Tests.swift | 6 +++--- 12 files changed, 36 insertions(+), 22 deletions(-) create mode 100644 Sources/BraintreeCore/Analytics/LinkType.swift diff --git a/Braintree.xcodeproj/project.pbxproj b/Braintree.xcodeproj/project.pbxproj index da061a903b..cd6871774a 100644 --- a/Braintree.xcodeproj/project.pbxproj +++ b/Braintree.xcodeproj/project.pbxproj @@ -337,6 +337,7 @@ BED00CAE28A5419900D74AEC /* BTBinData.swift in Sources */ = {isa = PBXBuildFile; fileRef = BED00CAD28A5419900D74AEC /* BTBinData.swift */; }; BED00CB028A579D700D74AEC /* BTClientToken.swift in Sources */ = {isa = PBXBuildFile; fileRef = BED00CAF28A579D700D74AEC /* BTClientToken.swift */; }; BED00CB228A57AD400D74AEC /* BTClientTokenError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BED00CB128A57AD400D74AEC /* BTClientTokenError.swift */; }; + BED3A2C62C5D74B20034D9A6 /* LinkType.swift in Sources */ = {isa = PBXBuildFile; fileRef = BED3A2C52C5D74AC0034D9A6 /* LinkType.swift */; }; BED7493628579BAC0074C818 /* BTURLUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = BED7493528579BAC0074C818 /* BTURLUtils.swift */; }; BEDA91A028EDDE64007441D9 /* FakeAnalyticsService.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEDA919F28EDDE64007441D9 /* FakeAnalyticsService.swift */; }; BEDB820229B109EE00075AF3 /* BTApplePayAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEDB820129B109EE00075AF3 /* BTApplePayAnalytics.swift */; }; @@ -1066,6 +1067,7 @@ BED00CAD28A5419900D74AEC /* BTBinData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTBinData.swift; sourceTree = ""; }; BED00CAF28A579D700D74AEC /* BTClientToken.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTClientToken.swift; sourceTree = ""; }; BED00CB128A57AD400D74AEC /* BTClientTokenError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTClientTokenError.swift; sourceTree = ""; }; + BED3A2C52C5D74AC0034D9A6 /* LinkType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkType.swift; sourceTree = ""; }; BED7493528579BAC0074C818 /* BTURLUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTURLUtils.swift; sourceTree = ""; }; BEDA919F28EDDE64007441D9 /* FakeAnalyticsService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeAnalyticsService.swift; sourceTree = ""; }; BEDB820129B109EE00075AF3 /* BTApplePayAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTApplePayAnalytics.swift; sourceTree = ""; }; @@ -1599,6 +1601,7 @@ BEE2E4E5290080BD00C03FDD /* BTAnalyticsServiceError.swift */, BEF388C02BE52CD2000965C8 /* BTCoreAnalytics.swift */, 800E78C329E0DD5300D1B0FC /* FPTIBatchData.swift */, + BED3A2C52C5D74AC0034D9A6 /* LinkType.swift */, 457D7FC72C29CEC300EF6523 /* RepeatingTimer.swift */, ); path = Analytics; @@ -3312,6 +3315,7 @@ BE63A3A7288F3026001936DA /* BTPostalAddress.swift in Sources */, BE2F98D028A2BCCD008EF189 /* BTConfiguration.swift in Sources */, 804DC45D2B2D08FF00F17A15 /* BTConfigurationRequest.swift in Sources */, + BED3A2C62C5D74B20034D9A6 /* LinkType.swift in Sources */, BED00CB228A57AD400D74AEC /* BTClientTokenError.swift in Sources */, BE24C67328E73E810067B11A /* BTAPIClientHTTPType.swift in Sources */, 457D7FC82C29CEC300EF6523 /* RepeatingTimer.swift in Sources */, diff --git a/Sources/BraintreeCore/Analytics/LinkType.swift b/Sources/BraintreeCore/Analytics/LinkType.swift new file mode 100644 index 0000000000..8e1b74c5cc --- /dev/null +++ b/Sources/BraintreeCore/Analytics/LinkType.swift @@ -0,0 +1,6 @@ +/// Used to describe the link type for analytics +/// :nodoc: This class is exposed for internal Braintree use only. Do not use. It is not covered by Semantic Versioning and may change or be removed at any time. +public enum LinkType: String { + case universal = "universal" + case deeplink = "deeplink" +} diff --git a/Sources/BraintreeCore/BTAPIClient.swift b/Sources/BraintreeCore/BTAPIClient.swift index 4479a94a05..3b565a6d4e 100644 --- a/Sources/BraintreeCore/BTAPIClient.swift +++ b/Sources/BraintreeCore/BTAPIClient.swift @@ -310,7 +310,7 @@ import Foundation errorDescription: String? = nil, isConfigFromCache: Bool? = nil, isVaultRequest: Bool? = nil, - linkType: String? = nil, + linkType: LinkType? = nil, payPalContextID: String? = nil ) { analyticsService.sendAnalyticsEvent( @@ -320,7 +320,7 @@ import Foundation eventName: eventName, isConfigFromCache: isConfigFromCache, isVaultRequest: isVaultRequest, - linkType: linkType, + linkType: linkType?.rawValue, payPalContextID: payPalContextID ) ) diff --git a/Sources/BraintreePayPal/BTPayPalApprovalURLParser.swift b/Sources/BraintreePayPal/BTPayPalApprovalURLParser.swift index cf0c28a820..c3b82f7b2d 100644 --- a/Sources/BraintreePayPal/BTPayPalApprovalURLParser.swift +++ b/Sources/BraintreePayPal/BTPayPalApprovalURLParser.swift @@ -45,8 +45,9 @@ struct BTPayPalApprovalURLParser { return nil } - init?(body: BTJSON, linkType: String?) { - if linkType == "universal", let payPalAppRedirectURL = body["agreementSetup"]["paypalAppApprovalUrl"].asURL() { + // TODO: do we need this + init?(body: BTJSON, linkType: LinkType?) { + if linkType == .universal, let payPalAppRedirectURL = body["agreementSetup"]["paypalAppApprovalUrl"].asURL() { redirectType = .payPalApp(url: payPalAppRedirectURL) url = payPalAppRedirectURL } else if let approvalURL = body["paymentResource"]["redirectUrl"].asURL() ?? diff --git a/Sources/BraintreePayPal/BTPayPalClient.swift b/Sources/BraintreePayPal/BTPayPalClient.swift index bfae26aedc..6983bc14a0 100644 --- a/Sources/BraintreePayPal/BTPayPalClient.swift +++ b/Sources/BraintreePayPal/BTPayPalClient.swift @@ -62,7 +62,7 @@ import BraintreeDataCollector private var isConfigFromCache: Bool? /// Used for sending the type of flow, universal vs deeplink to FPTI - private var linkType: String? = nil + private var linkType: LinkType? = nil // MARK: - Initializer @@ -308,7 +308,7 @@ import BraintreeDataCollector request: BTPayPalRequest, completion: @escaping (BTPayPalAccountNonce?, Error?) -> Void ) { - linkType = (request as? BTPayPalVaultRequest)?.enablePayPalAppSwitch == true ? "universal" : "deeplink" + linkType = (request as? BTPayPalVaultRequest)?.enablePayPalAppSwitch == true ? .universal : .deeplink apiClient.sendAnalyticsEvent(BTPayPalAnalytics.tokenizeStarted, isVaultRequest: isVaultRequest, linkType: linkType) apiClient.fetchOrReturnRemoteConfiguration { configuration, error in diff --git a/Sources/BraintreePayPal/BTPayPalReturnURL.swift b/Sources/BraintreePayPal/BTPayPalReturnURL.swift index ab4e964133..9537c41885 100644 --- a/Sources/BraintreePayPal/BTPayPalReturnURL.swift +++ b/Sources/BraintreePayPal/BTPayPalReturnURL.swift @@ -42,7 +42,7 @@ struct BTPayPalReturnURL { url.scheme == "https" && (url.path.contains("cancel") || url.path.contains("success")) } - static func isValidURLAction(url: URL, linkType: String?) -> Bool { + static func isValidURLAction(url: URL, linkType: LinkType?) -> Bool { guard let host = url.host, let scheme = url.scheme, !scheme.isEmpty else { return false } @@ -59,7 +59,7 @@ struct BTPayPalReturnURL { /// If we are using the deeplink/ASWeb based PayPal flow we want to check that the host and path matches /// the static callbackURLHostAndPath. For the universal link flow we do not care about this check. - if hostAndPath != BTPayPalRequest.callbackURLHostAndPath && linkType == "deeplink" { + if hostAndPath != BTPayPalRequest.callbackURLHostAndPath && linkType == .deeplink { return false } diff --git a/Sources/BraintreePayPal/BTPayPalVaultRequest.swift b/Sources/BraintreePayPal/BTPayPalVaultRequest.swift index 8edf6e8ab9..15c4c771f6 100644 --- a/Sources/BraintreePayPal/BTPayPalVaultRequest.swift +++ b/Sources/BraintreePayPal/BTPayPalVaultRequest.swift @@ -19,7 +19,8 @@ import BraintreeCore /// - Warning: This property is currently in beta and may change or be removed in future releases. var enablePayPalAppSwitch: Bool = false - private var application: URLOpener = UIApplication.shared + /// exposed for mocking not installed tests + var application: URLOpener = UIApplication.shared // MARK: - Initializers diff --git a/Sources/BraintreeVenmo/BTVenmoClient.swift b/Sources/BraintreeVenmo/BTVenmoClient.swift index d4ccdd2cf3..479e9fa41d 100644 --- a/Sources/BraintreeVenmo/BTVenmoClient.swift +++ b/Sources/BraintreeVenmo/BTVenmoClient.swift @@ -42,7 +42,7 @@ import BraintreeCore private var payPalContextID: String? = nil /// Used for sending the type of flow, universal vs deeplink to FPTI - private var linkType: String? = nil + private var linkType: LinkType? = nil // MARK: - Initializer @@ -64,7 +64,7 @@ import BraintreeCore /// If the user cancels out of the flow, the error code will be `.canceled`. @objc(tokenizeWithVenmoRequest:completion:) public func tokenize(_ request: BTVenmoRequest, completion: @escaping (BTVenmoAccountNonce?, Error?) -> Void) { - linkType = request.fallbackToWeb ? "universal" : "deeplink" + linkType = request.fallbackToWeb ? .universal : .deeplink apiClient.sendAnalyticsEvent(BTVenmoAnalytics.tokenizeStarted, isVaultRequest: shouldVault, linkType: linkType) let returnURLScheme = BTAppContextSwitcher.sharedInstance.returnURLScheme diff --git a/UnitTests/BraintreeCoreTests/Analytics/FPTIBatchData_Tests.swift b/UnitTests/BraintreeCoreTests/Analytics/FPTIBatchData_Tests.swift index 66a8ffb58f..b6f1cd5ec3 100644 --- a/UnitTests/BraintreeCoreTests/Analytics/FPTIBatchData_Tests.swift +++ b/UnitTests/BraintreeCoreTests/Analytics/FPTIBatchData_Tests.swift @@ -25,7 +25,7 @@ final class FPTIBatchData_Tests: XCTestCase { eventName: "fake-event-1", isConfigFromCache: false, isVaultRequest: false, - linkType: "universal", + linkType: .universal, payPalContextID: "fake-order-id", requestStartTime: 456, startTime: 999888777666 diff --git a/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift b/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift index 2fc9d7e7d6..87dc0d85cb 100644 --- a/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift +++ b/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift @@ -232,7 +232,6 @@ class BTPayPalClient_Tests: XCTestCase { func testTokenize_whenPayPalAppApprovalURLContainsPayPalContextID_sendsPayPalContextIDAndLinkTypeInAnalytics() { let fakeApplication = FakeApplication() payPalClient.application = fakeApplication - payPalClient.payPalAppInstalled = true payPalClient.webAuthenticationSession = MockWebAuthenticationSession() let vaultRequest = BTPayPalVaultRequest( @@ -240,6 +239,8 @@ class BTPayPalClient_Tests: XCTestCase { enablePayPalAppSwitch: true ) + vaultRequest.application.payPalAppInstalled = true + mockAPIClient.cannedResponseBody = BTJSON(value: [ "agreementSetup": [ "paypalAppApprovalUrl": "https://www.paypal.com?ba_token=BA-Random-Value" @@ -252,7 +253,7 @@ class BTPayPalClient_Tests: XCTestCase { payPalClient.handleReturnURL(returnURL) XCTAssertEqual(mockAPIClient.postedPayPalContextID, "BA-Random-Value") - XCTAssertEqual(mockAPIClient.postedLinkType, "universal") + XCTAssertEqual(mockAPIClient.postedLinkType, .universal) XCTAssertNotNil(payPalClient.clientMetadataID) } @@ -282,7 +283,7 @@ class BTPayPalClient_Tests: XCTestCase { payPalClient.tokenize(request) { _, _ in } XCTAssertEqual(mockAPIClient.postedPayPalContextID, "BA-Random-Value") - XCTAssertEqual(mockAPIClient.postedLinkType, "deeplink") + XCTAssertEqual(mockAPIClient.postedLinkType, .deeplink) XCTAssertTrue(mockAPIClient.postedAnalyticsEvents.contains("paypal:tokenize:handle-return:started")) } @@ -301,7 +302,7 @@ class BTPayPalClient_Tests: XCTestCase { payPalClient.tokenize(request) { _, _ in } XCTAssertEqual(mockAPIClient.postedPayPalContextID, "A_FAKE_BA_TOKEN") - XCTAssertEqual(mockAPIClient.postedLinkType, "deeplink") + XCTAssertEqual(mockAPIClient.postedLinkType, .deeplink) XCTAssertTrue(mockAPIClient.postedAnalyticsEvents.contains("paypal:tokenize:handle-return:started")) } @@ -932,13 +933,14 @@ class BTPayPalClient_Tests: XCTestCase { func testIsiOSAppSwitchAvailable_whenApplicationCanOpenPayPalInAppURL_returnsTrueAndSendsAnalytics() { let fakeApplication = FakeApplication() payPalClient.application = fakeApplication - payPalClient.payPalAppInstalled = true let vaultRequest = BTPayPalVaultRequest( userAuthenticationEmail: "fake@gmail.com", enablePayPalAppSwitch: true ) + vaultRequest.application.payPalAppInstalled = true + mockAPIClient.cannedResponseBody = BTJSON(value: [ "agreementSetup": [ "paypalAppApprovalUrl": "https://www.some-url.com/some-path?token=value1" diff --git a/UnitTests/BraintreeTestShared/MockAPIClient.swift b/UnitTests/BraintreeTestShared/MockAPIClient.swift index 7f9e03f9eb..f46932572b 100644 --- a/UnitTests/BraintreeTestShared/MockAPIClient.swift +++ b/UnitTests/BraintreeTestShared/MockAPIClient.swift @@ -13,7 +13,7 @@ public class MockAPIClient: BTAPIClient { public var postedAnalyticsEvents : [String] = [] public var postedPayPalContextID: String? = nil - public var postedLinkType: String? = nil + public var postedLinkType: LinkType? = nil public var postedIsVaultRequest = false @objc public var cannedConfigurationResponseBody : BTJSON? = nil @@ -94,7 +94,7 @@ public class MockAPIClient: BTAPIClient { errorDescription: String? = nil, isConfigFromCache: Bool? = nil, isVaultRequest: Bool? = nil, - linkType: String? = nil, + linkType: LinkType? = nil, payPalContextID: String? = nil ) { postedPayPalContextID = payPalContextID diff --git a/UnitTests/BraintreeVenmoTests/BTVenmoClient_Tests.swift b/UnitTests/BraintreeVenmoTests/BTVenmoClient_Tests.swift index d1889ba17a..16ef497bd4 100644 --- a/UnitTests/BraintreeVenmoTests/BTVenmoClient_Tests.swift +++ b/UnitTests/BraintreeVenmoTests/BTVenmoClient_Tests.swift @@ -584,7 +584,7 @@ class BTVenmoClient_Tests: XCTestCase { XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.last!, BTVenmoAnalytics.tokenizeSucceeded) XCTAssertEqual(mockAPIClient.postedPayPalContextID, "some-resource-id") - XCTAssertEqual(mockAPIClient.postedLinkType, "deeplink") + XCTAssertEqual(mockAPIClient.postedLinkType, .deeplink) } func testTokenizeVenmoAccount_fallbackToWebTrue_sendsSuccessAnalyticsEvent() { @@ -625,7 +625,7 @@ class BTVenmoClient_Tests: XCTestCase { XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.last!, BTVenmoAnalytics.tokenizeSucceeded) XCTAssertEqual(mockAPIClient.postedPayPalContextID, "some-resource-id") - XCTAssertEqual(mockAPIClient.postedLinkType, "universal") + XCTAssertEqual(mockAPIClient.postedLinkType, .universal) } func testTokenizeVenmoAccount_vaultTrue_sendsFailureAnalyticsEvent() { @@ -651,7 +651,7 @@ class BTVenmoClient_Tests: XCTestCase { XCTAssertEqual(mockAPIClient.postedAnalyticsEvents.last!, BTVenmoAnalytics.tokenizeFailed) XCTAssertEqual(mockAPIClient.postedPayPalContextID, "some-resource-id") - XCTAssertEqual(mockAPIClient.postedLinkType, "deeplink") + XCTAssertEqual(mockAPIClient.postedLinkType, .deeplink) } func testTokenizeVenmoAccount_whenAppSwitchCanceled_callsBackWithCancelError() { From 7d21b2ccc576c9cbaf604273972a9466d7265a85 Mon Sep 17 00:00:00 2001 From: Jax DesMarais-Leder Date: Fri, 2 Aug 2024 15:25:58 -0500 Subject: [PATCH 3/8] cleanup unneeded logic --- Sources/BraintreePayPal/BTPayPalApprovalURLParser.swift | 5 ++--- Sources/BraintreePayPal/BTPayPalClient.swift | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Sources/BraintreePayPal/BTPayPalApprovalURLParser.swift b/Sources/BraintreePayPal/BTPayPalApprovalURLParser.swift index c3b82f7b2d..1958702399 100644 --- a/Sources/BraintreePayPal/BTPayPalApprovalURLParser.swift +++ b/Sources/BraintreePayPal/BTPayPalApprovalURLParser.swift @@ -45,9 +45,8 @@ struct BTPayPalApprovalURLParser { return nil } - // TODO: do we need this - init?(body: BTJSON, linkType: LinkType?) { - if linkType == .universal, let payPalAppRedirectURL = body["agreementSetup"]["paypalAppApprovalUrl"].asURL() { + init?(body: BTJSON) { + if let payPalAppRedirectURL = body["agreementSetup"]["paypalAppApprovalUrl"].asURL() { redirectType = .payPalApp(url: payPalAppRedirectURL) url = payPalAppRedirectURL } else if let approvalURL = body["paymentResource"]["redirectUrl"].asURL() ?? diff --git a/Sources/BraintreePayPal/BTPayPalClient.swift b/Sources/BraintreePayPal/BTPayPalClient.swift index 6983bc14a0..a699de6bb1 100644 --- a/Sources/BraintreePayPal/BTPayPalClient.swift +++ b/Sources/BraintreePayPal/BTPayPalClient.swift @@ -347,7 +347,7 @@ import BraintreeDataCollector return } - guard let body, let approvalURL = BTPayPalApprovalURLParser(body: body, linkType: self.linkType) else { + guard let body, let approvalURL = BTPayPalApprovalURLParser(body: body) else { self.notifyFailure(with: BTPayPalError.invalidURL("Missing approval URL in gateway response."), completion: completion) return } From fbd98a8e32cb10a64873e944ad4821c4fae7b68f Mon Sep 17 00:00:00 2001 From: Jax DesMarais-Leder Date: Mon, 5 Aug 2024 09:50:57 -0500 Subject: [PATCH 4/8] update logic; update tests --- Sources/BraintreePayPal/BTPayPalVaultRequest.swift | 6 +++--- .../BraintreeCoreTests/Analytics/FPTIBatchData_Tests.swift | 2 +- UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift | 5 ++--- .../BraintreePayPalTests/BTPayPalVaultRequest_Tests.swift | 5 +++++ 4 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Sources/BraintreePayPal/BTPayPalVaultRequest.swift b/Sources/BraintreePayPal/BTPayPalVaultRequest.swift index 15c4c771f6..b3aea2034b 100644 --- a/Sources/BraintreePayPal/BTPayPalVaultRequest.swift +++ b/Sources/BraintreePayPal/BTPayPalVaultRequest.swift @@ -19,7 +19,7 @@ import BraintreeCore /// - Warning: This property is currently in beta and may change or be removed in future releases. var enablePayPalAppSwitch: Bool = false - /// exposed for mocking not installed tests + /// exposed for mocking in tests var application: URLOpener = UIApplication.shared // MARK: - Initializers @@ -37,7 +37,7 @@ import BraintreeCore offerCredit: Bool = false ) { self.init(offerCredit: offerCredit, userAuthenticationEmail: userAuthenticationEmail) - self.enablePayPalAppSwitch = !application.isPayPalAppInstalled() ? false : enablePayPalAppSwitch + self.enablePayPalAppSwitch = enablePayPalAppSwitch } /// Initializes a PayPal Vault request @@ -56,7 +56,7 @@ import BraintreeCore baseParameters["payer_email"] = userAuthenticationEmail } - if enablePayPalAppSwitch, let universalLink { + if enablePayPalAppSwitch, application.isPayPalAppInstalled(), let universalLink { let appSwitchParameters: [String: Any] = [ "launch_paypal_app": enablePayPalAppSwitch, "os_version": UIDevice.current.systemVersion, diff --git a/UnitTests/BraintreeCoreTests/Analytics/FPTIBatchData_Tests.swift b/UnitTests/BraintreeCoreTests/Analytics/FPTIBatchData_Tests.swift index b6f1cd5ec3..c9b7c0e6ca 100644 --- a/UnitTests/BraintreeCoreTests/Analytics/FPTIBatchData_Tests.swift +++ b/UnitTests/BraintreeCoreTests/Analytics/FPTIBatchData_Tests.swift @@ -25,7 +25,7 @@ final class FPTIBatchData_Tests: XCTestCase { eventName: "fake-event-1", isConfigFromCache: false, isVaultRequest: false, - linkType: .universal, + linkType: LinkType.universal.rawValue, payPalContextID: "fake-order-id", requestStartTime: 456, startTime: 999888777666 diff --git a/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift b/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift index 87dc0d85cb..ef1dffb9c4 100644 --- a/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift +++ b/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift @@ -239,8 +239,6 @@ class BTPayPalClient_Tests: XCTestCase { enablePayPalAppSwitch: true ) - vaultRequest.application.payPalAppInstalled = true - mockAPIClient.cannedResponseBody = BTJSON(value: [ "agreementSetup": [ "paypalAppApprovalUrl": "https://www.paypal.com?ba_token=BA-Random-Value" @@ -932,6 +930,7 @@ class BTPayPalClient_Tests: XCTestCase { func testIsiOSAppSwitchAvailable_whenApplicationCanOpenPayPalInAppURL_returnsTrueAndSendsAnalytics() { let fakeApplication = FakeApplication() + fakeApplication.cannedCanOpenURL = true payPalClient.application = fakeApplication let vaultRequest = BTPayPalVaultRequest( @@ -939,7 +938,7 @@ class BTPayPalClient_Tests: XCTestCase { enablePayPalAppSwitch: true ) - vaultRequest.application.payPalAppInstalled = true + vaultRequest.application = fakeApplication mockAPIClient.cannedResponseBody = BTJSON(value: [ "agreementSetup": [ diff --git a/UnitTests/BraintreePayPalTests/BTPayPalVaultRequest_Tests.swift b/UnitTests/BraintreePayPalTests/BTPayPalVaultRequest_Tests.swift index 2036a75bb0..d4d21bfb63 100644 --- a/UnitTests/BraintreePayPalTests/BTPayPalVaultRequest_Tests.swift +++ b/UnitTests/BraintreePayPalTests/BTPayPalVaultRequest_Tests.swift @@ -1,6 +1,7 @@ import XCTest @testable import BraintreeCore @testable import BraintreePayPal +@testable import BraintreeTestShared class BTPayPalVaultRequest_Tests: XCTestCase { @@ -76,6 +77,10 @@ class BTPayPalVaultRequest_Tests: XCTestCase { enablePayPalAppSwitch: true ) + let fakeApplication = FakeApplication() + fakeApplication.cannedCanOpenURL = true + request.application = fakeApplication + let parameters = request.parameters(with: configuration, universalLink: URL(string: "some-url")!) XCTAssertEqual(parameters["launch_paypal_app"] as? Bool, true) From a3ce3345cea990d1b830112002b345b2973877e0 Mon Sep 17 00:00:00 2001 From: Jax DesMarais-Leder Date: Mon, 5 Aug 2024 10:20:50 -0500 Subject: [PATCH 5/8] remove redundant enum type --- Sources/BraintreeCore/Analytics/LinkType.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/BraintreeCore/Analytics/LinkType.swift b/Sources/BraintreeCore/Analytics/LinkType.swift index 8e1b74c5cc..5bbaac50d4 100644 --- a/Sources/BraintreeCore/Analytics/LinkType.swift +++ b/Sources/BraintreeCore/Analytics/LinkType.swift @@ -1,6 +1,6 @@ /// Used to describe the link type for analytics /// :nodoc: This class is exposed for internal Braintree use only. Do not use. It is not covered by Semantic Versioning and may change or be removed at any time. public enum LinkType: String { - case universal = "universal" - case deeplink = "deeplink" + case universal + case deeplink } From 231e0b0cb360a02908c038124d2b53bc84df9a3b Mon Sep 17 00:00:00 2001 From: Jax DesMarais-Leder Date: Mon, 5 Aug 2024 10:21:02 -0500 Subject: [PATCH 6/8] move constant first in conditional --- Sources/BraintreePayPal/BTPayPalVaultRequest.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/BraintreePayPal/BTPayPalVaultRequest.swift b/Sources/BraintreePayPal/BTPayPalVaultRequest.swift index b3aea2034b..080dd3f419 100644 --- a/Sources/BraintreePayPal/BTPayPalVaultRequest.swift +++ b/Sources/BraintreePayPal/BTPayPalVaultRequest.swift @@ -56,13 +56,14 @@ import BraintreeCore baseParameters["payer_email"] = userAuthenticationEmail } - if enablePayPalAppSwitch, application.isPayPalAppInstalled(), let universalLink { + if let universalLink, enablePayPalAppSwitch, application.isPayPalAppInstalled() { let appSwitchParameters: [String: Any] = [ "launch_paypal_app": enablePayPalAppSwitch, "os_version": UIDevice.current.systemVersion, "os_type": UIDevice.current.systemName, "merchant_app_return_url": universalLink.absoluteString ] + return baseParameters.merging(appSwitchParameters) { $1 } } From e4f4484c3dc96af07a012a74f06efe265a3c0234 Mon Sep 17 00:00:00 2001 From: Jax DesMarais-Leder Date: Mon, 5 Aug 2024 10:27:16 -0500 Subject: [PATCH 7/8] Update Sources/BraintreeCore/Analytics/LinkType.swift Co-authored-by: scannillo <35243507+scannillo@users.noreply.github.com> --- Sources/BraintreeCore/Analytics/LinkType.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Sources/BraintreeCore/Analytics/LinkType.swift b/Sources/BraintreeCore/Analytics/LinkType.swift index 5bbaac50d4..0fb70292ce 100644 --- a/Sources/BraintreeCore/Analytics/LinkType.swift +++ b/Sources/BraintreeCore/Analytics/LinkType.swift @@ -1,5 +1,6 @@ /// Used to describe the link type for analytics /// :nodoc: This class is exposed for internal Braintree use only. Do not use. It is not covered by Semantic Versioning and may change or be removed at any time. +@_documentation(visibility: private) public enum LinkType: String { case universal case deeplink From 893d7363221beaa8711f453415fd6249d22d43bf Mon Sep 17 00:00:00 2001 From: Jax DesMarais-Leder Date: Wed, 7 Aug 2024 16:01:06 -0500 Subject: [PATCH 8/8] pass isPayPalAppInstalled into parameters; update tests; remove application from VaultRequest --- Sources/BraintreePayPal/BTPayPalCheckoutRequest.swift | 2 +- Sources/BraintreePayPal/BTPayPalClient.swift | 6 +++++- Sources/BraintreePayPal/BTPayPalRequest.swift | 2 +- Sources/BraintreePayPal/BTPayPalVaultBaseRequest.swift | 2 +- Sources/BraintreePayPal/BTPayPalVaultRequest.swift | 7 ++----- UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift | 2 -- .../BraintreePayPalTests/BTPayPalVaultRequest_Tests.swift | 6 +----- 7 files changed, 11 insertions(+), 16 deletions(-) diff --git a/Sources/BraintreePayPal/BTPayPalCheckoutRequest.swift b/Sources/BraintreePayPal/BTPayPalCheckoutRequest.swift index adeb355c9b..7032253a99 100644 --- a/Sources/BraintreePayPal/BTPayPalCheckoutRequest.swift +++ b/Sources/BraintreePayPal/BTPayPalCheckoutRequest.swift @@ -120,7 +120,7 @@ import BraintreeCore /// :nodoc: Exposed publicly for use by PayPal Native Checkout module. This method is not covered by semantic versioning. @_documentation(visibility: private) - public override func parameters(with configuration: BTConfiguration, universalLink: URL? = nil) -> [String: Any] { + public override func parameters(with configuration: BTConfiguration, universalLink: URL? = nil, isPayPalAppInstalled: Bool = false) -> [String: Any] { var baseParameters = super.parameters(with: configuration) var checkoutParameters: [String: Any] = [ "intent": intent.stringValue, diff --git a/Sources/BraintreePayPal/BTPayPalClient.swift b/Sources/BraintreePayPal/BTPayPalClient.swift index a699de6bb1..8ea2136b6c 100644 --- a/Sources/BraintreePayPal/BTPayPalClient.swift +++ b/Sources/BraintreePayPal/BTPayPalClient.swift @@ -332,7 +332,11 @@ import BraintreeDataCollector self.payPalRequest = request self.apiClient.post( request.hermesPath, - parameters: request.parameters(with: configuration, universalLink: self.universalLink) + parameters: request.parameters( + with: configuration, + universalLink: self.universalLink, + isPayPalAppInstalled: self.application.isPayPalAppInstalled() + ) ) { body, response, error in if let error = error as? NSError { guard let jsonResponseBody = error.userInfo[BTCoreConstants.jsonResponseBodyKey] as? BTJSON else { diff --git a/Sources/BraintreePayPal/BTPayPalRequest.swift b/Sources/BraintreePayPal/BTPayPalRequest.swift index 2ec43edfdb..a2aefcec84 100644 --- a/Sources/BraintreePayPal/BTPayPalRequest.swift +++ b/Sources/BraintreePayPal/BTPayPalRequest.swift @@ -135,7 +135,7 @@ import BraintreeCore /// :nodoc: Exposed publicly for use by PayPal Native Checkout module. This method is not covered by semantic versioning. @_documentation(visibility: private) - public func parameters(with configuration: BTConfiguration, universalLink: URL? = nil) -> [String: Any] { + public func parameters(with configuration: BTConfiguration, universalLink: URL? = nil, isPayPalAppInstalled: Bool = false) -> [String: Any] { var experienceProfile: [String: Any] = [:] experienceProfile["no_shipping"] = !isShippingAddressRequired diff --git a/Sources/BraintreePayPal/BTPayPalVaultBaseRequest.swift b/Sources/BraintreePayPal/BTPayPalVaultBaseRequest.swift index 7d2fbe7743..4a8f5b58a4 100644 --- a/Sources/BraintreePayPal/BTPayPalVaultBaseRequest.swift +++ b/Sources/BraintreePayPal/BTPayPalVaultBaseRequest.swift @@ -27,7 +27,7 @@ import BraintreeCore /// :nodoc: Exposed publicly for use by PayPal Native Checkout module. This method is not covered by semantic versioning. @_documentation(visibility: private) - public override func parameters(with configuration: BTConfiguration, universalLink: URL? = nil) -> [String: Any] { + public override func parameters(with configuration: BTConfiguration, universalLink: URL? = nil, isPayPalAppInstalled: Bool = false) -> [String: Any] { let baseParameters = super.parameters(with: configuration) var vaultParameters: [String: Any] = ["offer_paypal_credit": offerCredit] diff --git a/Sources/BraintreePayPal/BTPayPalVaultRequest.swift b/Sources/BraintreePayPal/BTPayPalVaultRequest.swift index 080dd3f419..4114ba940e 100644 --- a/Sources/BraintreePayPal/BTPayPalVaultRequest.swift +++ b/Sources/BraintreePayPal/BTPayPalVaultRequest.swift @@ -19,9 +19,6 @@ import BraintreeCore /// - Warning: This property is currently in beta and may change or be removed in future releases. var enablePayPalAppSwitch: Bool = false - /// exposed for mocking in tests - var application: URLOpener = UIApplication.shared - // MARK: - Initializers /// Initializes a PayPal Vault request for the PayPal App Switch flow @@ -49,14 +46,14 @@ import BraintreeCore super.init(offerCredit: offerCredit) } - public override func parameters(with configuration: BTConfiguration, universalLink: URL? = nil) -> [String: Any] { + public override func parameters(with configuration: BTConfiguration, universalLink: URL? = nil, isPayPalAppInstalled: Bool = false) -> [String: Any] { var baseParameters = super.parameters(with: configuration) if let userAuthenticationEmail { baseParameters["payer_email"] = userAuthenticationEmail } - if let universalLink, enablePayPalAppSwitch, application.isPayPalAppInstalled() { + if let universalLink, enablePayPalAppSwitch, isPayPalAppInstalled { let appSwitchParameters: [String: Any] = [ "launch_paypal_app": enablePayPalAppSwitch, "os_version": UIDevice.current.systemVersion, diff --git a/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift b/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift index ef1dffb9c4..7fff4ad489 100644 --- a/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift +++ b/UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift @@ -938,8 +938,6 @@ class BTPayPalClient_Tests: XCTestCase { enablePayPalAppSwitch: true ) - vaultRequest.application = fakeApplication - mockAPIClient.cannedResponseBody = BTJSON(value: [ "agreementSetup": [ "paypalAppApprovalUrl": "https://www.some-url.com/some-path?token=value1" diff --git a/UnitTests/BraintreePayPalTests/BTPayPalVaultRequest_Tests.swift b/UnitTests/BraintreePayPalTests/BTPayPalVaultRequest_Tests.swift index d4d21bfb63..dedae48fee 100644 --- a/UnitTests/BraintreePayPalTests/BTPayPalVaultRequest_Tests.swift +++ b/UnitTests/BraintreePayPalTests/BTPayPalVaultRequest_Tests.swift @@ -77,11 +77,7 @@ class BTPayPalVaultRequest_Tests: XCTestCase { enablePayPalAppSwitch: true ) - let fakeApplication = FakeApplication() - fakeApplication.cannedCanOpenURL = true - request.application = fakeApplication - - let parameters = request.parameters(with: configuration, universalLink: URL(string: "some-url")!) + let parameters = request.parameters(with: configuration, universalLink: URL(string: "some-url")!, isPayPalAppInstalled: true) XCTAssertEqual(parameters["launch_paypal_app"] as? Bool, true) XCTAssertTrue((parameters["os_version"] as! String).matches("\\d+\\.\\d+"))