-
Notifications
You must be signed in to change notification settings - Fork 294
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[QL] Consolidate Return URL Logic #1271
Changes from 8 commits
510053e
ddf266e
f894f8a
7d5e65e
3262216
b81d4bb
63ffc97
e79b416
2131792
58403e1
b3543f5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -48,6 +48,9 @@ import BraintreeDataCollector | |
|
||
// MARK: - Private Properties | ||
|
||
/// URL Scheme for PayPal In-App Checkout | ||
private let payPalInAppScheme: String = "paypal-in-app-checkout://" | ||
|
||
/// Indicates if the user returned back to the merchant app from the `BTWebAuthenticationSession` | ||
/// Will only be `true` if the user proceed through the `UIAlertController` | ||
private var webSessionReturned: Bool = false | ||
|
@@ -59,9 +62,6 @@ import BraintreeDataCollector | |
/// Used for sending the type of flow, universal vs deeplink to FPTI | ||
private var linkType: String? = nil | ||
|
||
/// URL Scheme for PayPal In-App Checkout | ||
private let payPalInAppScheme: String = "paypal-in-app-checkout://" | ||
|
||
// MARK: - Initializer | ||
|
||
/// Initialize a new PayPal client instance. | ||
|
@@ -182,17 +182,30 @@ import BraintreeDataCollector | |
payPalContextID: payPalContextID, | ||
payPalInstalled: payPalAppInstalled | ||
) | ||
guard let url, isValidURLAction(url: url) else { | ||
|
||
guard let url, BTPayPalReturnURL.isValidURLAction(url: url, linkType: linkType) else { | ||
notifyFailure(with: BTPayPalError.invalidURLAction, completion: completion) | ||
return | ||
} | ||
|
||
guard let response = responseDictionary(from: url) else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
guard !url.absoluteString.contains("cancel") else { | ||
notifyCancel(completion: completion) | ||
return | ||
} | ||
|
||
var account: [String: Any] = response | ||
|
||
let clientDictionary: [String: String] = [ | ||
"platform": "iOS", | ||
"product_name": "PayPal", | ||
"paypal_sdk_version": "version" | ||
] | ||
|
||
let responseDictionary: [String: String] = ["webURL": url.absoluteString] | ||
|
||
var account: [String: Any] = [ | ||
"client": clientDictionary, | ||
"response": responseDictionary, | ||
"response_type": "web" | ||
] | ||
|
||
if paymentType == .checkout { | ||
account["options"] = ["validate": false] | ||
|
@@ -256,14 +269,16 @@ import BraintreeDataCollector | |
// MARK: - App Switch Methods | ||
|
||
func handleReturnURL(_ url: URL) { | ||
guard let returnURL = BTPayPalAppSwitchReturnURL(url: url) else { | ||
guard let returnURL = BTPayPalReturnURL(.payPalApp(url: url)) else { | ||
notifyFailure(with: BTPayPalError.invalidURL("App Switch return URL cannot be nil"), completion: appSwitchCompletion) | ||
return | ||
} | ||
|
||
switch returnURL.state { | ||
case .succeeded, .canceled: | ||
case .succeeded: | ||
handleReturn(url, paymentType: .vault, completion: appSwitchCompletion) | ||
case .canceled: | ||
notifyCancel(completion: appSwitchCompletion) | ||
case .unknownPath: | ||
notifyFailure(with: BTPayPalError.appSwitchReturnURLPathInvalid, completion: appSwitchCompletion) | ||
} | ||
|
@@ -314,7 +329,7 @@ import BraintreeDataCollector | |
return | ||
} | ||
|
||
guard let body, let approvalURL = BTPayPalApprovalURLParser(body: body) else { | ||
guard let body, let approvalURL = BTPayPalApprovalURLParser(body: body, linkType: self.linkType) else { | ||
self.notifyFailure(with: BTPayPalError.invalidURL("Missing approval URL in gateway response."), completion: completion) | ||
return | ||
} | ||
|
@@ -409,7 +424,19 @@ import BraintreeDataCollector | |
return | ||
} | ||
|
||
handleReturn(url, paymentType: paymentType, completion: completion) | ||
guard let url, let returnURL = BTPayPalReturnURL(.webBrowser(url: url)) else { | ||
notifyFailure(with: BTPayPalError.invalidURL("ASWebAuthenticationSession return URL cannot be nil"), completion: completion) | ||
return | ||
} | ||
|
||
switch returnURL.state { | ||
case .succeeded: | ||
handleReturn(url, paymentType: .vault, completion: completion) | ||
case .canceled: | ||
notifyCancel(completion: completion) | ||
case .unknownPath: | ||
notifyFailure(with: BTPayPalError.asWebAuthenticationSessionURLInvalid(url.absoluteString), completion: completion) | ||
} | ||
} sessionDidAppear: { [self] didAppear in | ||
if didAppear { | ||
apiClient.sendAnalyticsEvent( | ||
|
@@ -443,65 +470,6 @@ import BraintreeDataCollector | |
return | ||
} | ||
} | ||
|
||
private func isValidURLAction(url: URL) -> Bool { | ||
guard let host = url.host, let scheme = url.scheme, !scheme.isEmpty else { | ||
return false | ||
} | ||
|
||
var hostAndPath = host | ||
.appending(url.path) | ||
.components(separatedBy: "/") | ||
.dropLast(1) // remove the action (`success`, `cancel`, etc) | ||
.joined(separator: "/") | ||
|
||
if hostAndPath.count > 0 { | ||
hostAndPath.append("/") | ||
} | ||
|
||
if hostAndPath != BTPayPalRequest.callbackURLHostAndPath && (payPalRequest as? BTPayPalVaultRequest)?.universalLink == nil { | ||
return false | ||
} | ||
|
||
guard let action = action(from: url), | ||
let query = url.query, | ||
query.count > 0, | ||
action.count >= 0, | ||
["success", "cancel", "authenticate"].contains(action) else { | ||
return false | ||
} | ||
|
||
return true | ||
} | ||
|
||
private func responseDictionary(from url: URL) -> [String : Any]? { | ||
if let action = action(from: url), action == "cancel" { | ||
return nil | ||
} | ||
|
||
let clientDictionary: [String: String] = [ | ||
"platform": "iOS", | ||
"product_name": "PayPal", | ||
"paypal_sdk_version": "version" | ||
] | ||
|
||
let responseDictionary: [String: String] = ["webURL": url.absoluteString] | ||
|
||
return [ | ||
"client": clientDictionary, | ||
"response": responseDictionary, | ||
"response_type": "web" | ||
] | ||
} | ||
|
||
private func action(from url: URL) -> String? { | ||
guard let action = url.lastPathComponent.components(separatedBy: "?").first, | ||
!action.isEmpty else { | ||
return url.host | ||
} | ||
|
||
return action | ||
} | ||
|
||
// MARK: - Analytics Helper Methods | ||
|
||
|
@@ -554,6 +522,6 @@ extension BTPayPalClient: BTAppContextSwitchClient { | |
/// :nodoc: | ||
@_documentation(visibility: private) | ||
@objc public static func canHandleReturnURL(_ url: URL) -> Bool { | ||
BTPayPalAppSwitchReturnURL.isValid(url) | ||
BTPayPalReturnURL.isValid(url) | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit for another time - maybe another indicator we should move this to an enum
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I don't think the
BTPayPalApprovalURLParser
class should care if the merchant has opted into universal links or not. It's only determining which URL type it got back from the GW response.The flow logic for falling back to the web flow already is taken care of in
BTPayPalClient
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As of Thursday, we were still getting the
paypalAppApprovalUrl
from the Gateway regardless of theBTPayPalClient
. This may have been because we were working with a mock and an update may have been deployed to just allow our client side handling. Worth digging into but I was still getting this URL with thePPBeta
app uninstalled.