Skip to content

[Feature] PopupBridge++#90

Closed
jtanya17 wants to merge 20 commits into
braintree:mainfrom
jtanya17:feature/enhanced-popup-bridge
Closed

[Feature] PopupBridge++#90
jtanya17 wants to merge 20 commits into
braintree:mainfrom
jtanya17:feature/enhanced-popup-bridge

Conversation

@jtanya17
Copy link
Copy Markdown

@jtanya17 jtanya17 commented Mar 20, 2026

Summary of changes

  • Add PayPal app switch support to PopupBridge
  • Introduces an optional enablePopupBridgeAppSwitch flag to POPPopupBridge that, when enabled, allows the SDK to launch the native PayPal app for checkout instead of opening a browser session.
  • When the app launch succeeds, a NotificationCenter observer handles the deep link return URL and injects the result back into the WebView. If the app launch fails, the flow falls back to the existing WebAuthenticationSession behavior.
  • Exposes isPayPalInstalled, launchApp(), and getDeepLinkReturnUrlPrefix() to JavaScript, and fires analytics events for app detection and launch outcomes.

Checklist

  • Added a changelog entry

Authors

List GitHub usernames for everyone who contributed to this pull request.

@jtanya17 jtanya17 changed the title Feature/enhanced popup bridge [Feature] PopupBridge++ Mar 23, 2026
Comment thread Sources/PopupBridge/Analytics/PopupBridgeAnalytics.swift Outdated
/// instead of opening a browser. Defaults to false for backward compatibility.
/// This is specific to the popup bridge flow and is separate from the JS SDK's
/// appSwitchWhenAvailable which controls non-webview mobile browser app switch.
public init(webView: WKWebView, prefersEphemeralWebBrowserSession: Bool = true, enablePopupBridgeAppSwitch: Bool = false) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does this take a different approach vs venmo?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR follows the existing Popup Bridge pattern and only adds PayPal-specific app-switch handling behind the feature flag. I kept Venmo unchanged here to avoid expanding scope, since the HLD and QA for this work were focused on the PayPal path.

Comment thread Sources/PopupBridge/POPPopupBridge.swift Outdated
Comment thread Sources/PopupBridge/POPPopupBridge.swift Outdated
Comment on lines +119 to +120
guard let self,
let url = notification.userInfo?["url"] as? URL else {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
guard let self,
let url = notification.userInfo?["url"] as? URL else {
guard let self, let url = notification.userInfo?["url"] as? URL else {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if self is nil here?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Jax, if self is nil, the bridge has already been deallocated and there’s nothing left to handle, so returning early is the correct behavior. I split the guards to make that explicit.


if let returnUrlScheme {
let deepLinkReturnUrlPrefix = "\(returnUrlScheme)://\(host)/"
deepLinkJs = """
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does deepLinkJs mean/do?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deepLinkJs injects the getDeepLinkReturnUrlPrefix() JavaScript function onto window.popupBridge

return '\(deepLinkReturnUrlPrefix)';
};
"""
deepLinkProperty = """
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does deepLinkProperty mean/do?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

deepLinkProperty injects the deepLinkReturnUrlPrefix JavaScript property onto window.popupBridge

Comment thread Sources/PopupBridge/UIApplication+URLOpener.swift Outdated
Comment thread UnitTests/MockAnalyticsService.swift Outdated
Comment thread UnitTests/MockURLOpener.swift Outdated
@jtanya17 jtanya17 marked this pull request as ready for review April 28, 2026 16:01
@jtanya17 jtanya17 requested a review from a team April 28, 2026 16:01
@jtanya17 jtanya17 requested a review from a team as a code owner April 28, 2026 16:01
Copy link
Copy Markdown
Contributor

@buzzamus buzzamus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't appear this PR builds. Looks like there are issues around launchAppReturnObserver. Once this builds and has been thoroughly tested please let us know when this is ready for review

Self.analyticsService.sendAnalyticsEvent(PopupBridgeAnalytics.succeeded, sessionID: sessionID)

let script = """
;(function() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am assuming there is a specific reason this starts with a semicolon, but I am just curious what the reason is

vkspune and others added 5 commits May 7, 2026 09:47
- Add missing convenience init
- Fix  compiler error
- Fix failing test
- Add code comment explaining the leading semicolon
- Add `## unreleased` CHANGELOG entry for `enablePopupBridgeAppSwitch` and new JS APIs
* docs: create SECURITY.md

* docs: update SECURITY.md
* Add integration test coverage
Copy link
Copy Markdown
Contributor

@buzzamus buzzamus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the git history got messed up somewhere since it has previous PR changes as part of this PR's commit history and also left a few other comments.

> List GitHub usernames for everyone who contributed to this pull request.

-
-
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this file is from this PR. I see several files committed from previous PRs, so I think your git history is screwed up somehow.

codeCoverageEnabled = "YES">
<Testables>
<TestableReference
skipped = "NO">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. I don't think this change is meant for this PR as it was already merged into main

get { stubBody }
set { stubBody = newValue }
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same with this file. This is already merged into main

startWasCalled = true
capturedURL = url
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

}

override func stopLoading() {}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

@@ -0,0 +1,145 @@
import AuthenticationServices
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

@@ -0,0 +1,53 @@
<?xml version="1.0" encoding="UTF-8"?>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

085C05B32F9A78E000460CD9 /* MockWebAuthSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 085C05B22F9A78E000460CD9 /* MockWebAuthSession.swift */; };
085C05B82F9A79DA00460CD9 /* MockScriptMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 085C05B72F9A79DA00460CD9 /* MockScriptMessage.swift */; };
085C05BA2F9A7AB900460CD9 /* StubURLProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 085C05B92F9A7AB900460CD9 /* StubURLProtocol.swift */; };
451348272D8CAD7C009265C9 /* WebViewScriptHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 451348262D8CAD6D009265C9 /* WebViewScriptHandler.swift */; };
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This project file has all of the changes from recent PRs in it, so this will also need to be resolved. Not sure if this is from a bad merge or a rebase screwed up the history, but these need to be removed. The best way will be to instead of manually removing see if you can fix the git history

Comment thread SECURITY.md
- Avoid accessing, modifying, or deleting data that does not belong to you
- Make a good faith effort to avoid disruption to production systems

We appreciate responsible disclosure and your efforts to keep Braintree SDK users safe.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another previously merged change


/// Exposed for testing
convenience init(
webView: WKWebView,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are quite a few initializers now in this file, which to me is a big code smell. I think we should try and find a different way to handle. If we are using default values this wouldn't be a breaking change so, I am not sure if we need so many new ones created

@jaxdesmarais
Copy link
Copy Markdown
Contributor

@jtanya17 I am moving this PR to a draft state based on @buzzamus comments as this code is not ready to be reviewed by my team. Please resolve our issues and take a look through your diff before reopening.

@jaxdesmarais jaxdesmarais marked this pull request as draft May 7, 2026 18:17
@jtanya17
Copy link
Copy Markdown
Author

Closing in favor of #95

@jtanya17 jtanya17 closed this May 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants