Skip to content

Commit

Permalink
Merge pull request #1335 from braintree/paypal-app-switch-feature
Browse files Browse the repository at this point in the history
Merge PayPal App Switch feature branch
  • Loading branch information
scannillo authored Jun 12, 2024
2 parents b5dea15 + ec3488a commit 666fa3b
Show file tree
Hide file tree
Showing 29 changed files with 1,006 additions and 209 deletions.
16 changes: 16 additions & 0 deletions Braintree.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
800E78C429E0DD5300D1B0FC /* FPTIBatchData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 800E78C329E0DD5300D1B0FC /* FPTIBatchData.swift */; };
800ED7832B4F5B66007D8A30 /* BTEligiblePaymentsRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 800ED7822B4F5B66007D8A30 /* BTEligiblePaymentsRequest.swift */; };
800FC544257FDC5100DEE132 /* BTApplePayCardNonce_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 800FC543257FDC5100DEE132 /* BTApplePayCardNonce_Tests.swift */; };
8014221C2BAE935B009F9999 /* BTPayPalApprovalURLParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8014221B2BAE935B009F9999 /* BTPayPalApprovalURLParser.swift */; };
8037BFB02B2CCC130017072C /* BTShopperInsightsAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8037BFAF2B2CCC130017072C /* BTShopperInsightsAnalytics.swift */; };
804326BF2B1A5C5B0044E90B /* BTApplePaymentTokensRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 804326BE2B1A5C5B0044E90B /* BTApplePaymentTokensRequest.swift */; };
804698372B27C5390090878E /* BTShopperInsightsClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8064F38E2B1E492F0059C4CB /* BTShopperInsightsClient.swift */; };
Expand Down Expand Up @@ -230,6 +231,8 @@
BE349111294B77E100D2CF68 /* BTPayPalVaultRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE349110294B77E100D2CF68 /* BTPayPalVaultRequest.swift */; };
BE349113294B798300D2CF68 /* BTPayPalRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE349112294B798300D2CF68 /* BTPayPalRequest.swift */; };
BE48CE4829D5DDA600F0825C /* BTThreeDSecureV2TextBoxCustomization.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE82E74029C4A1330059FE97 /* BTThreeDSecureV2TextBoxCustomization.swift */; };
BE549F112BF5445F00B6F441 /* BTPayPalReturnURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE6BC22D2BA9CFFC00C3E321 /* BTPayPalReturnURL.swift */; };
BE549F122BF5449E00B6F441 /* BTPayPalVaultBaseRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE6BC22B2BA9C67600C3E321 /* BTPayPalVaultBaseRequest.swift */; };
BE549F142BF6576300B6F441 /* BTAnalyticsEventsStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE549F132BF6576300B6F441 /* BTAnalyticsEventsStorage.swift */; };
BE54C0332912B68E009C6CEE /* BTHTTP_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE54C0322912B68E009C6CEE /* BTHTTP_Tests.swift */; };
BE54C0352912B6BC009C6CEE /* BTHTTPTestProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE54C0342912B6BC009C6CEE /* BTHTTPTestProtocol.swift */; };
Expand Down Expand Up @@ -297,6 +300,7 @@
BEA0F9282B23741900C21EFA /* BTPayPalMessagingRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA0F9272B23741900C21EFA /* BTPayPalMessagingRequest.swift */; };
BEA0F92A2B23768600C21EFA /* BTPayPalMessagingError.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEA0F9292B23768600C21EFA /* BTPayPalMessagingError.swift */; };
BEB9BF532A26872B00A3673E /* BTWebAuthenticationSessionClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEB9BF522A26872B00A3673E /* BTWebAuthenticationSessionClient.swift */; };
BEBA590F2BB1B5B9005FA8A2 /* BTPayPalReturnURL_Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BEBA590E2BB1B5B9005FA8A2 /* BTPayPalReturnURL_Tests.swift */; };
BEBC222728D25BB400D83186 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 80DBE69423A931A600373230 /* Helpers.swift */; };
BEBC6E4B29258FD4004E25A0 /* BraintreeCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 570B93AC285397520041BAFE /* BraintreeCore.framework */; };
BEBC6E5E2927CF59004E25A0 /* Braintree.h in Headers */ = {isa = PBXBuildFile; fileRef = BEBC6E5D2927CF59004E25A0 /* Braintree.h */; settings = {ATTRIBUTES = (Public, ); }; };
Expand Down Expand Up @@ -810,6 +814,7 @@
800E78C329E0DD5300D1B0FC /* FPTIBatchData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FPTIBatchData.swift; sourceTree = "<group>"; };
800ED7822B4F5B66007D8A30 /* BTEligiblePaymentsRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTEligiblePaymentsRequest.swift; sourceTree = "<group>"; };
800FC543257FDC5100DEE132 /* BTApplePayCardNonce_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTApplePayCardNonce_Tests.swift; sourceTree = "<group>"; };
8014221B2BAE935B009F9999 /* BTPayPalApprovalURLParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPayPalApprovalURLParser.swift; sourceTree = "<group>"; };
8037BFAF2B2CCC130017072C /* BTShopperInsightsAnalytics.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTShopperInsightsAnalytics.swift; sourceTree = "<group>"; };
804326BE2B1A5C5B0044E90B /* BTApplePaymentTokensRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTApplePaymentTokensRequest.swift; sourceTree = "<group>"; };
804698302B27C5340090878E /* BraintreeShopperInsights.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = BraintreeShopperInsights.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -975,6 +980,8 @@
BE698EA328AD2C10001D9B10 /* BTCoreConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTCoreConstants.swift; sourceTree = "<group>"; };
BE698EA528B3CDAD001D9B10 /* ConfigurationCache_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationCache_Tests.swift; sourceTree = "<group>"; };
BE698EAA28B50F41001D9B10 /* BTClientToken_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTClientToken_Tests.swift; sourceTree = "<group>"; };
BE6BC22B2BA9C67600C3E321 /* BTPayPalVaultBaseRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPayPalVaultBaseRequest.swift; sourceTree = "<group>"; };
BE6BC22D2BA9CFFC00C3E321 /* BTPayPalReturnURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPayPalReturnURL.swift; sourceTree = "<group>"; };
BE70A962284FA3F000F6D3F7 /* BTDataCollectorError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTDataCollectorError.swift; sourceTree = "<group>"; };
BE70A964284FA9DE00F6D3F7 /* MockBTDataCollector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockBTDataCollector.swift; sourceTree = "<group>"; };
BE7A9643299FC5DE009AB920 /* BTConfiguration+ApplePay.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "BTConfiguration+ApplePay.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1014,6 +1021,7 @@
BEA0F9272B23741900C21EFA /* BTPayPalMessagingRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPayPalMessagingRequest.swift; sourceTree = "<group>"; };
BEA0F9292B23768600C21EFA /* BTPayPalMessagingError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPayPalMessagingError.swift; sourceTree = "<group>"; };
BEB9BF522A26872B00A3673E /* BTWebAuthenticationSessionClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTWebAuthenticationSessionClient.swift; sourceTree = "<group>"; };
BEBA590E2BB1B5B9005FA8A2 /* BTPayPalReturnURL_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTPayPalReturnURL_Tests.swift; sourceTree = "<group>"; };
BEBC6E5D2927CF59004E25A0 /* Braintree.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Braintree.h; sourceTree = "<group>"; };
BEBC6F252937A510004E25A0 /* BTClientMetadata_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTClientMetadata_Tests.swift; sourceTree = "<group>"; };
BEBC6F272937BD1F004E25A0 /* BTGraphQLHTTP_Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BTGraphQLHTTP_Tests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1351,6 +1359,8 @@
57544F5B295254A500DEB7B0 /* BTJSON+PayPal.swift */,
57544F572952298900DEB7B0 /* BTPayPalAccountNonce.swift */,
3B7A261029C0CAA40087059D /* BTPayPalAnalytics.swift */,
8014221B2BAE935B009F9999 /* BTPayPalApprovalURLParser.swift */,
BE6BC22D2BA9CFFC00C3E321 /* BTPayPalReturnURL.swift */,
BE8E5CEE294B6937001BF017 /* BTPayPalCheckoutRequest.swift */,
57544F5929524E4D00DEB7B0 /* BTPayPalClient.swift */,
5754481F294A2EBE00DEB7B0 /* BTPayPalCreditFinancing.swift */,
Expand All @@ -1359,6 +1369,7 @@
BEF5D2E5294A18B300FFD56D /* BTPayPalLineItem.swift */,
57D9436D2968A8080079EAB1 /* BTPayPalLocaleCode.swift */,
BE349112294B798300D2CF68 /* BTPayPalRequest.swift */,
BE6BC22B2BA9C67600C3E321 /* BTPayPalVaultBaseRequest.swift */,
BE349110294B77E100D2CF68 /* BTPayPalVaultRequest.swift */,
62A659A32B98CB23008DFD67 /* PrivacyInfo.xcprivacy */,
);
Expand Down Expand Up @@ -1925,6 +1936,7 @@
427F32DF25D1D62D00435294 /* BTPayPalClient_Tests.swift */,
BECB10C52B5999EE008D398E /* BTPayPalLineItem_Tests.swift */,
42FC218A25CDE0290047C49A /* BTPayPalRequest_Tests.swift */,
BEBA590E2BB1B5B9005FA8A2 /* BTPayPalReturnURL_Tests.swift */,
427F328F25D1A7B900435294 /* BTPayPalVaultRequest_Tests.swift */,
A9E5C1E424FD665D00EE691F /* Info.plist */,
);
Expand Down Expand Up @@ -3175,12 +3187,15 @@
57544F5E295258AC00DEB7B0 /* BTPayPalError.swift in Sources */,
BEF5D2E6294A18B300FFD56D /* BTPayPalLineItem.swift in Sources */,
BE349113294B798300D2CF68 /* BTPayPalRequest.swift in Sources */,
BE549F112BF5445F00B6F441 /* BTPayPalReturnURL.swift in Sources */,
57544F5C295254A500DEB7B0 /* BTJSON+PayPal.swift in Sources */,
BE549F122BF5449E00B6F441 /* BTPayPalVaultBaseRequest.swift in Sources */,
3B7A261129C0CAA40087059D /* BTPayPalAnalytics.swift in Sources */,
BE8E5CEF294B6937001BF017 /* BTPayPalCheckoutRequest.swift in Sources */,
5754481E294A2A1D00DEB7B0 /* BTPayPalCreditFinancingAmount.swift in Sources */,
57D9436E2968A8080079EAB1 /* BTPayPalLocaleCode.swift in Sources */,
57544F582952298900DEB7B0 /* BTPayPalAccountNonce.swift in Sources */,
8014221C2BAE935B009F9999 /* BTPayPalApprovalURLParser.swift in Sources */,
BE349111294B77E100D2CF68 /* BTPayPalVaultRequest.swift in Sources */,
57544820294A2EBE00DEB7B0 /* BTPayPalCreditFinancing.swift in Sources */,
57544F5A29524E4D00DEB7B0 /* BTPayPalClient.swift in Sources */,
Expand Down Expand Up @@ -3526,6 +3541,7 @@
BECB10C62B5999EE008D398E /* BTPayPalLineItem_Tests.swift in Sources */,
3B7A261429C35BD00087059D /* BTPayPalAnalytics_Tests.swift in Sources */,
A95229C724FD949D006F7D25 /* BTConfiguration+PayPal_Tests.swift in Sources */,
BEBA590F2BB1B5B9005FA8A2 /* BTPayPalReturnURL_Tests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
12 changes: 8 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Braintree iOS SDK Release Notes

## unreleased
* BraintreePayPal
* Add PayPal App Switch vault flow (BETA)
* Add `BTPayPalVaultRequest(userAuthenticationEmail:enablePayPalAppSwitch:offerCredit:)`
* Add `BTPayPalClient(apiClient:universalLink:)`
* Send `link_type` and `paypal_installed` in `event_params` when available to PayPal's analytics service (FPTI)
* **Note:** This feature is currently in beta and may change or be removed in future releases.

## 6.20.0 (2024-06-06)
* Re-use existing URLSession instance for `v1/configuration` and subsequent BT GW API calls
* BraintreeShopperInsights (BETA)
Expand All @@ -13,10 +21,6 @@
* Batch analytics events to FPTI
* Send `start_time`, `end_time`, and `endpoint` to FPTI for tracking API request latency
* Send `isVaultRequest` to FPTI for tracking in Venmo and PayPal flows
* BraintreePayPalMessaging (BETA)
* Add `BTPayPalMessagingRequest`, `BTPayPalMessagingColor`, `BTPayPalMessagingLogoType`, `BTPayPalMessagingOfferType`, `BTPayPalMessagingPageType`, `BTPayPalMessagingTextAlignment`, and `BTPayPalMessagingDelegate`
* Add `BTPayPalMessagingView` to display PayPal messages to promote offers such as Pay Later and PayPal Credit to customers.
* To get started create a `BTPayPalMessagingView` and call `start(_:)` with a `BTPayPalMessagingRequest`

## 6.18.2 (2024-05-15)
* BraintreePayPal
Expand Down
4 changes: 2 additions & 2 deletions Demo/Application/Base/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ import BraintreeCore
registerDefaultsFromSettings()
persistDemoSettings()
BTAppContextSwitcher.sharedInstance.returnURLScheme = returnURLScheme

userDefaults.setValue(true, forKey: "magnes.debug.mode")

return true
}

func registerDefaultsFromSettings() {
if processInfoArgs.contains("-EnvironmentSandbox") {
userDefaults.set(BraintreeDemoEnvironment.sandbox.rawValue, forKey: BraintreeDemoSettings.EnvironmentDefaultsKey)
Expand Down
9 changes: 8 additions & 1 deletion Demo/Application/Base/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,15 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate {
URLContexts.forEach { urlContext in
let url = urlContext.url
if url.scheme?.localizedCaseInsensitiveCompare("com.braintreepayments.Demo.payments") == .orderedSame {
_ = BTAppContextSwitcher.sharedInstance.handleOpenURL(context: urlContext)
BTAppContextSwitcher.sharedInstance.handleOpenURL(context: urlContext)
}
}
}

func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
if let returnURL = userActivity.webpageURL, returnURL.path.contains("braintree-payments") {
print("Returned to Demo app via universal link: \(returnURL)")
BTAppContextSwitcher.sharedInstance.handleOpen(returnURL)
}
}
}
38 changes: 35 additions & 3 deletions Demo/Application/Features/PayPalWebCheckoutViewController.swift
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import Foundation
import UIKit
import BraintreePayPal
import BraintreeCore

class PayPalWebCheckoutViewController: PaymentButtonBaseViewController {

lazy var payPalClient = BTPayPalClient(apiClient: apiClient)
lazy var payPalClient = BTPayPalClient(
apiClient: apiClient,
universalLink: URL(string: "https://mobile-sdk-demo-site-838cead5d3ab.herokuapp.com/braintree-payments")!
)

lazy var emailLabel: UILabel = {
let label = UILabel()
Expand Down Expand Up @@ -40,6 +44,7 @@ class PayPalWebCheckoutViewController: PaymentButtonBaseViewController {
override func createPaymentButton() -> UIView {
let payPalCheckoutButton = createButton(title: "PayPal Checkout", action: #selector(tappedPayPalCheckout))
let payPalVaultButton = createButton(title: "PayPal Vault", action: #selector(tappedPayPalVault))
let payPalAppSwitchButton = createButton(title: "PayPal App Switch", action: #selector(tappedPayPalAppSwitch))

let stackView = UIStackView(arrangedSubviews: [
UIStackView(arrangedSubviews: [emailLabel, emailTextField]),
Expand All @@ -48,7 +53,7 @@ class PayPalWebCheckoutViewController: PaymentButtonBaseViewController {
UIStackView(arrangedSubviews: [newPayPalCheckoutToggleLabel, newPayPalCheckoutToggle]),
payPalCheckoutButton
]),
buttonsStackView(label: "Vault",views: [payPalVaultButton])
buttonsStackView(label: "Vault",views: [payPalVaultButton, payPalAppSwitchButton])
])

stackView.axis = .vertical
Expand Down Expand Up @@ -110,13 +115,40 @@ class PayPalWebCheckoutViewController: PaymentButtonBaseViewController {
self.completionBlock(nonce)
}
}

@objc func tappedPayPalAppSwitch(_ sender: UIButton) {
sender.setTitle("Processing...", for: .disabled)
sender.isEnabled = false

guard let userEmail = emailTextField.text, !userEmail.isEmpty else {
self.progressBlock("Email cannot be nil for App Switch flow")
sender.isEnabled = true
return
}

let request = BTPayPalVaultRequest(
userAuthenticationEmail: userEmail,
enablePayPalAppSwitch: true
)

payPalClient.tokenize(request) { nonce, error in
sender.isEnabled = true

guard let nonce else {
self.progressBlock(error?.localizedDescription)
return

}

self.completionBlock(nonce)
}
}

// MARK: - Helpers

private func buttonsStackView(label: String, views: [UIView]) -> UIStackView {
let titleLabel = UILabel()
titleLabel.text = label
titleLabel.font = .preferredFont(forTextStyle: .title3)

let buttonsStackView = UIStackView(arrangedSubviews: [titleLabel] + views)
buttonsStackView.axis = .vertical
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
<array>
<string>com.braintreepayments.Demo.payments</string>
<string>com.venmo.touch.v2</string>
<string>paypal</string>
<string>paypal-app-switch-checkout</string>
</array>
<key>LSRequiresIPhoneOS</key>
<true/>
Expand Down
8 changes: 4 additions & 4 deletions IntegrationTests/BraintreePayPal_IntegrationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class BraintreePayPal_IntegrationTests: XCTestCase {
let tokenizationExpectation = expectation(description: "Tokenize one-time payment")
let returnURL = URL(string: oneTouchCoreAppSwitchSuccessURLFixture)

payPalClient.handleBrowserSwitchReturn(returnURL, paymentType: .checkout) { tokenizedPayPalAccount, error in
payPalClient.handleReturn(returnURL, paymentType: .checkout) { tokenizedPayPalAccount, error in
guard let nonce = tokenizedPayPalAccount?.nonce else {
XCTFail("Failed to tokenize account.")
return
Expand All @@ -42,7 +42,7 @@ class BraintreePayPal_IntegrationTests: XCTestCase {
let tokenizationExpectation = expectation(description: "Tokenize one-time payment")
let returnURL = URL(string: oneTouchCoreAppSwitchSuccessURLFixture)

payPalClient.handleBrowserSwitchReturn(returnURL,paymentType: .checkout) { tokenizedPayPalAccount, error in
payPalClient.handleReturn(returnURL,paymentType: .checkout) { tokenizedPayPalAccount, error in
guard let nonce = tokenizedPayPalAccount?.nonce else {
XCTFail("Failed to tokenize account.")
return
Expand All @@ -68,7 +68,7 @@ class BraintreePayPal_IntegrationTests: XCTestCase {
let tokenizationExpectation = expectation(description: "Tokenize billing agreement payment")
let returnURL = URL(string: oneTouchCoreAppSwitchSuccessURLFixture)

payPalClient.handleBrowserSwitchReturn(returnURL, paymentType: .vault) { tokenizedPayPalAccount, error in
payPalClient.handleReturn(returnURL, paymentType: .vault) { tokenizedPayPalAccount, error in
guard let nonce = tokenizedPayPalAccount?.nonce else {
XCTFail("Failed to tokenize account.")
return
Expand All @@ -92,7 +92,7 @@ class BraintreePayPal_IntegrationTests: XCTestCase {
let tokenizationExpectation = expectation(description: "Tokenize billing agreement payment")
let returnURL = URL(string: oneTouchCoreAppSwitchSuccessURLFixture)

payPalClient.handleBrowserSwitchReturn(returnURL, paymentType: .vault) { tokenizedPayPalAccount, error in
payPalClient.handleReturn(returnURL, paymentType: .vault) { tokenizedPayPalAccount, error in
guard let nonce = tokenizedPayPalAccount?.nonce else {
XCTFail("Failed to tokenize account.")
return
Expand Down
Loading

0 comments on commit 666fa3b

Please sign in to comment.