Skip to content

Commit df99366

Browse files
baubrey91Anderson Kloss Maia
authored andcommitted
PR Comments
1 parent 1634a5f commit df99366

File tree

9 files changed

+53
-39
lines changed

9 files changed

+53
-39
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Braintree iOS SDK Release Notes
22

3+
## unreleased
4+
* PayPal
5+
* Add optional `fallbackURLScheme` parameter to support custom URL scheme fallback in PayPal App Switch
6+
37
## 6.40.0 (2025-11-20)
48
* PayPal
59
* Add `offerCredit` property to `PayPalCheckoutRequest`. Enables merchants to offer PayPal Credit for Checkout requests.
@@ -12,7 +16,6 @@
1216
* Add `userAction` property to `BTPayPalVaultRequest`
1317
* Add `paypal:tokenize:default-browser-switch:started` event for when the appSwitch fails for universalLink and `openURLInDefaultBrowser` is triggered.
1418
* Add `paypal:tokenize:default-browser-switch:succeeded` and `paypal:tokenize:default-browser-switch:failed` events for when the browser switch succeeds or fails.
15-
* Add `fallbackUrlScheme` to client initializer for merchants to have fallback return url if https url fails
1619

1720
## 6.38.0 (2025-09-09)
1821
* BraintreeShopperInsights (BETA)

Sources/BraintreePayPal/BTPayPalCheckoutRequest.swift

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,11 @@ import BraintreeCore
168168
public override func parameters(
169169
with configuration: BTConfiguration,
170170
universalLink: URL? = nil,
171-
fallbackUrlScheme: String? = nil,
171+
fallbackURLScheme: String? = nil,
172172
isPayPalAppInstalled: Bool = false
173173
) -> [String: Any] {
174-
var baseParameters = super.parameters(
175-
with: configuration,
176-
universalLink: universalLink,
177-
fallbackUrlScheme: fallbackUrlScheme,
178-
isPayPalAppInstalled: isPayPalAppInstalled
179-
)
174+
var baseParameters = super.parameters(with: configuration, universalLink: universalLink, isPayPalAppInstalled: isPayPalAppInstalled)
175+
180176
var checkoutParameters: [String: Any] = [
181177
"intent": intent.stringValue,
182178
"amount": amount,

Sources/BraintreePayPal/BTPayPalClient.swift

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ import BraintreeDataCollector
5959

6060
private var universalLink: URL?
6161

62-
private var fallbackUrlScheme: String?
62+
private var fallbackURLScheme: String?
6363

6464
/// Indicates if the user returned back to the merchant app from the `BTWebAuthenticationSession`
6565
/// Will only be `true` if the user proceed through the `UIAlertController`
@@ -70,7 +70,7 @@ import BraintreeDataCollector
7070
private var contextID: String?
7171

7272
/// Used to determine whether or not to render the WAS popup.
73-
/// If the experiement is enabled, set the `prefersEphemeralWebBrowserSession` flag to true.
73+
/// If the experiment is enabled, set the `prefersEphemeralWebBrowserSession` flag to true.
7474
private var experiment: String?
7575

7676
/// Used for analytics purposes, to determine if browser-presentation event is associated with a locally cached, or remotely fetched `BTConfiguration`
@@ -103,15 +103,16 @@ import BraintreeDataCollector
103103
/// - Parameters:
104104
/// - apiClient: The API Client
105105
/// - universalLink: The URL to use for the PayPal app switch flow. Must be a valid HTTPS URL dedicated to Braintree app switch returns. This URL must be allow-listed in your Braintree Control Panel.
106+
/// - fallbackURLScheme: Optional: A custom URL scheme to use as a fallback if the universal link fails. This scheme must be registered in your app's Info.plist and allow-listed in the Braintree Control Panel.
106107
/// - Warning: This initializer should be used for merchants using the PayPal App Switch flow. This feature is currently in beta and may change or be removed in future releases.
107-
@objc(initWithAPIClient:universalLink:fallbackUrlScheme:)
108-
public convenience init(apiClient: BTAPIClient, universalLink: URL, fallbackUrlScheme: String? = nil) {
108+
@objc(initWithAPIClient:universalLink:fallbackURLScheme:)
109+
public convenience init(apiClient: BTAPIClient, universalLink: URL, fallbackURLScheme: String? = nil) {
109110
self.init(apiClient: apiClient)
110111

111112
/// appending a PayPal app switch specific path to verify we are in the correct flow when
112113
/// `canHandleReturnURL` is called
113114
self.universalLink = universalLink.appendingPathComponent("braintreeAppSwitchPayPal")
114-
self.fallbackUrlScheme = fallbackUrlScheme
115+
self.fallbackURLScheme = fallbackURLScheme
115116
}
116117

117118
// MARK: - Public Methods
@@ -461,7 +462,7 @@ import BraintreeDataCollector
461462
parameters: request.parameters(
462463
with: configuration,
463464
universalLink: self.universalLink,
464-
fallbackUrlScheme: self.fallbackUrlScheme,
465+
fallbackURLScheme: self.fallbackURLScheme,
465466
isPayPalAppInstalled: self.application.isPayPalAppInstalled()
466467
)
467468
) { body, _, error in
@@ -769,6 +770,6 @@ extension BTPayPalClient: BTAppContextSwitchClient {
769770
/// :nodoc:
770771
@_documentation(visibility: private)
771772
@objc public static func canHandleReturnURL(_ url: URL) -> Bool {
772-
BTPayPalReturnURL.isValid(url, fallbackUrlScheme: payPalClient?.fallbackUrlScheme)
773+
BTPayPalReturnURL.isValid(url, fallbackURLScheme: payPalClient?.fallbackURLScheme)
773774
}
774775
}

Sources/BraintreePayPal/BTPayPalRequest.swift

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ import BraintreeCore
181181
public func parameters(
182182
with configuration: BTConfiguration,
183183
universalLink: URL? = nil,
184-
fallbackUrlScheme: String? = nil,
184+
fallbackURLScheme: String? = nil,
185185
isPayPalAppInstalled: Bool = false
186186
) -> [String: Any] {
187187
var experienceProfile: [String: Any] = [:]
@@ -243,15 +243,12 @@ import BraintreeCore
243243
}
244244

245245
if let universalLink, enablePayPalAppSwitch, isPayPalAppInstalled {
246-
var appSwitchParameters: [String: Any] = [
246+
let appSwitchParameters: [String: Any] = [
247247
"launch_paypal_app": enablePayPalAppSwitch,
248248
"os_version": UIDevice.current.systemVersion,
249249
"os_type": UIDevice.current.systemName,
250250
"merchant_app_return_url": universalLink.absoluteString
251251
]
252-
if let fallbackUrlScheme {
253-
appSwitchParameters["fallback_url_scheme"] = fallbackUrlScheme
254-
}
255252

256253
return parameters.merging(appSwitchParameters) { $1 }
257254
}

Sources/BraintreePayPal/BTPayPalReturnURL.swift

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,23 @@ struct BTPayPalReturnURL {
3636
// MARK: - Static Methods
3737

3838
/// Evaluates whether the url represents a valid PayPal return URL.
39-
/// - Parameter url: an app switch or ASWebAuthenticationSession return URL
39+
/// - Parameters:
40+
/// - url: an app switch or ASWebAuthenticationSession return URL
41+
/// - fallbackURLScheme: Optional fallback URL scheme to check against if universal link check fails
4042
/// - Returns: `true` if the url represents a valid PayPal app switch return
41-
static func isValid(_ url: URL, fallbackUrlScheme: String?) -> Bool {
42-
let isValidScheme = url.scheme == "https" || url.scheme == fallbackUrlScheme
43+
static func isValid(_ url: URL, fallbackURLScheme: String? = nil) -> Bool {
44+
let containsExpectedAction = url.path.contains("cancel") || url.path.contains("success")
4345
let containsAppSwitchPath = url.path.contains("braintreeAppSwitchPayPal")
44-
let containsExpectedPath = url.path.contains("cancel") || url.path.contains("success")
45-
let isValidAppSwitchURL = isValidScheme && containsAppSwitchPath && containsExpectedPath
4646

47-
return isValidAppSwitchURL
47+
// Check for valid HTTPS universal link
48+
let isHTTPSScheme = url.scheme == "https"
49+
let isValidUniversalLink = isHTTPSScheme && containsAppSwitchPath && containsExpectedAction
50+
51+
// Check for valid fallback URL scheme
52+
let isFallbackScheme = fallbackURLScheme != nil && url.scheme == fallbackURLScheme
53+
let isValidFallbackURL = isFallbackScheme && containsAppSwitchPath && containsExpectedAction
54+
55+
return isValidUniversalLink || isValidFallbackURL
4856
}
4957

5058
static func isValidURLAction(url: URL, didPayPalServerAttemptAppSwitch: Bool) -> Bool {

Sources/BraintreePayPal/BTPayPalVaultBaseRequest.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,15 @@ import BraintreeCore
4444
public override func parameters(
4545
with configuration: BTConfiguration,
4646
universalLink: URL? = nil,
47-
fallbackUrlScheme: String? = nil,
47+
fallbackURLScheme: String? = nil,
4848
isPayPalAppInstalled: Bool = false
4949
) -> [String: Any] {
50-
let baseParameters = super.parameters(with: configuration, universalLink: universalLink, isPayPalAppInstalled: isPayPalAppInstalled)
50+
let baseParameters = super.parameters(
51+
with: configuration,
52+
universalLink: universalLink,
53+
fallbackURLScheme: fallbackURLScheme,
54+
isPayPalAppInstalled: isPayPalAppInstalled
55+
)
5156
var vaultParameters: [String: Any] = ["offer_paypal_credit": offerCredit]
5257

5358
if let billingAgreementDescription {
@@ -67,6 +72,10 @@ import BraintreeCore
6772

6873
vaultParameters["shipping_address"] = shippingAddressParameters
6974
}
75+
76+
if let fallbackURLScheme {
77+
vaultParameters["merchant_app_fallback_url_scheme"] = fallbackURLScheme
78+
}
7079

7180
return baseParameters.merging(vaultParameters) { $1 }
7281
}

Sources/BraintreePayPal/BTPayPalVaultRequest.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,13 @@ import BraintreeCore
5454
public override func parameters(
5555
with configuration: BTConfiguration,
5656
universalLink: URL? = nil,
57-
fallbackUrlScheme: String? = nil,
57+
fallbackURLScheme: String? = nil,
5858
isPayPalAppInstalled: Bool = false
5959
) -> [String: Any] {
6060
super.parameters(
6161
with: configuration,
6262
universalLink: universalLink,
63-
fallbackUrlScheme: fallbackUrlScheme,
63+
fallbackURLScheme: fallbackURLScheme,
6464
isPayPalAppInstalled: isPayPalAppInstalled
6565
)
6666
}

UnitTests/BraintreePayPalTests/BTPayPalCheckoutRequest_Tests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,7 @@ class BTPayPalCheckoutRequest_Tests: XCTestCase {
276276
func testParametersWithConfiguration_setsAppSwitchParameters_WithoutUserAuthenticationEmail() {
277277
let request = BTPayPalCheckoutRequest(enablePayPalAppSwitch: true, amount: "1")
278278

279-
let parameters = request.parameters(with: configuration, universalLink: URL(string: "some-url")!, isPayPalAppInstalled: true)
279+
let parameters = request.parameters(with: configuration, universalLink: URL(string: "some-url")!, fallbackURLScheme: "paypal", isPayPalAppInstalled: true)
280280

281281
XCTAssertNil(parameters["payer_email"])
282282
XCTAssertEqual(parameters["launch_paypal_app"] as? Bool, true)

UnitTests/BraintreePayPalTests/BTPayPalClient_Tests.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class BTPayPalClient_Tests: XCTestCase {
2020
mockAPIClient.cannedResponseBody = BTJSON(value: [
2121
"paymentResource": ["redirectUrl": "http://fakeURL.com"]
2222
])
23-
payPalClient = BTPayPalClient(apiClient: mockAPIClient, universalLink: URL(string: "https://www.paypal.com")!, fallbackUrlScheme: "paypal")
23+
payPalClient = BTPayPalClient(apiClient: mockAPIClient, universalLink: URL(string: "https://www.paypal.com")!, fallbackURLScheme: "paypal")
2424
mockWebAuthenticationSession = MockWebAuthenticationSession()
2525
payPalClient.webAuthenticationSession = mockWebAuthenticationSession
2626
}
@@ -840,20 +840,20 @@ class BTPayPalClient_Tests: XCTestCase {
840840
XCTAssertNil(BTPayPalClient.payPalClient)
841841
}
842842

843-
func testHandleReturnURL_whenFallBack_withPath() {
844-
let url = URL(string: "paypal://path")!
843+
func testHandleReturnURL_withFallBack() {
844+
let url = URL(string: "paypal://braintree-payments/braintreeAppSwitchPayPal/success")!
845845
BTPayPalClient.payPalClient = self.payPalClient
846846
XCTAssertTrue(BTPayPalClient.canHandleReturnURL(url))
847847
}
848848

849-
func testHandleReturnURL_whenFallBack_withoutPath() {
850-
let url = URL(string: "paypal://")!
849+
func testHandleReturnURL_withFallBack_noSuccess() {
850+
let url = URL(string: "paypal://braintree-payment/braintreeAppSwitchPayPal")!
851851
BTPayPalClient.payPalClient = self.payPalClient
852-
XCTAssertTrue(BTPayPalClient.canHandleReturnURL(url))
852+
XCTAssertFalse(BTPayPalClient.canHandleReturnURL(url))
853853
}
854-
854+
855855
func testHandleReturnURL_whenFallBack_wrongScheme() {
856-
let url = URL(string: "palpay://")!
856+
let url = URL(string: "incorrect-scheme://braintree-payment/braintreeAppSwitchPayPal/success")!
857857
BTPayPalClient.payPalClient = self.payPalClient
858858
XCTAssertFalse(BTPayPalClient.canHandleReturnURL(url))
859859
}

0 commit comments

Comments
 (0)