Skip to content

feat(PayPal): add PendingPaymentStore for auto-link session persistence#1640

Draft
pedrofsn wants to merge 2 commits into
braintree:feature/paypal-autolink-manual-returnfrom
pedrofsn:paypal-autolink-pending-payment-store
Draft

feat(PayPal): add PendingPaymentStore for auto-link session persistence#1640
pedrofsn wants to merge 2 commits into
braintree:feature/paypal-autolink-manual-returnfrom
pedrofsn:paypal-autolink-pending-payment-store

Conversation

@pedrofsn

@pedrofsn pedrofsn commented Jul 14, 2026

Copy link
Copy Markdown

Summary of changes

Adds PendingPaymentStore, the in-memory session store used by the PayPal auto-link on manual return feature to carry a pending billing agreement token across the App Switch round trip.

Background

When a user approves a PayPal Billing Agreement (BA) via App Switch and returns to the merchant app, the return can fail to convert into a tokenized payment method in two cases:

  • The universal link fails to reopen the merchant app (~5% of returns).
  • The user manually switches back to the merchant app before the universal link launches.

Today, when handleOpen(url) is never called, the SDK has no reference to the approved BA token and cannot tokenize it — the BA is approved on PayPal's side but the merchant never receives a nonce.

This PR is PR 1 of a multi-PR series that adds "auto-link on manual return": the SDK persists the BA token before app switch and retries tokenization when the user returns, regardless of how they returned. This PR introduces only the storage primitive; no tokenization logic and no PayPalClient wiring are included here, so each PR in the series stays independently reviewable.

Changes:

  • PendingPaymentStore (internal, in-memory, process-level singleton): holds a PendingSession (BA token + correlation ID + merchant account ID + intent + payment type + timestamp/TTL) and a CompletableDeferred<PayPalAccountNonce> used by later PRs to guarantee exactly one BTGW tokenization call across the multiple entry points (return-handling and re-click) that can race to trigger it.

Steps to Test

This PR only adds a storage primitive with no wiring into the checkout flow yet, so it has no end-user-visible behavior to exercise manually. Verification is via the included unit tests:

  1. Run ./gradlew :PayPal:testDebugUnitTest --tests "*.PendingPaymentStoreUnitTest"
  2. Confirm all tests pass: initial state is null, PendingSession.isExpired() correctly reflects TTL boundaries, getOrCreateDeferred() returns the same deferred to a second caller while the first remains the initiator, and clear() resets all fields and cancels any in-flight deferred.

AI Usage

Which AI Agent Was Used?

  • Claude

How was AI used?
Code generation and unit test authoring for PendingPaymentStore, based on an existing HLD/LLD design and a prior reference implementation for this feature.

Estimated AI Code Contribution

  • less than 30%
  • 30 - 60%
  • 60 - 100%

Checklist

  • Added a changelog entry
  • Tested and confirmed payment flows affected by this change are functioning as expected (no flow wiring yet in this PR; unit-tested in isolation)

Authors

Inner Source Process

Internal to PayPal contributors should fill out this section. All others can delete.

PR should follow these steps before codeowners review will begin:

  1. Comment /inner source on this PR — this will automatically add the inner source and tech lead review required labels. Open the PR in a draft state.
  2. PR should be reviewed by and approved by your team's technical lead, we do not allow LGTM reviews, there should be comments and feedback provided on all PR reviews
  3. Once the above steps are completed, comment /ready on this PR — this will automatically remove the tech lead review required label. Move the PR to ready to review.
  4. PR comments must be addressed within 24 hours, if you are unable to address within this timeframe, move the PR back to a draft state so our team knows not to review

Inner Source Checklist

PR series

Part of a 5-PR series re-splitting the original "auto-link on manual return" feature (previously opened as #1633 and #1634, closed for being too large to review):

  1. This PR - PendingPaymentStore (session persistence)
  2. AutoLinkTokenizeUseCase (tokenization logic) - stacked on this PR
  3. PayPalClient wiring (handleReturnToApp + re-click) - stacked on PR 2
  4. PayPalLauncher/PayPalPaymentAuthResult/Demo wiring - stacked on PR 3
  5. AppForegroundDetector merchant-independent foreground trigger - stacked on PR 4

Please review/merge in order 1 -> 5.

Introduces PendingPaymentStore, the in-memory session store used by the
PayPal auto-link on manual return feature to track a pending billing
agreement token across the app-switch round trip.

Part 1 of a multi-PR split of the original PayPal auto-link on manual
return feature (previously braintree#1633).
@pedrofsn
pedrofsn requested a review from a team July 14, 2026 18:26
@pedrofsn
pedrofsn requested a review from a team as a code owner July 14, 2026 18:26
@saralvasquez

Copy link
Copy Markdown
Contributor

I see this PR doesn't follow our standard template. Please take a look at this PR to see our inner sourcing process and follow the steps as listed. Thank you!

@jaxdesmarais

Copy link
Copy Markdown
Contributor

To add to @saralvasquez comment, this also is pointing to the main branch instead of a feature branch. Based on the PR description I am guessing claude nuked the template. Please open following the correct template and update the base branch to reflect that this will go into a feature branch before main.

@pedrofsn

Copy link
Copy Markdown
Author

Thanks for the feedback! Quick clarification on the base branch: I'm contributing via a fork (pedrofsn/braintree_android), so I only have push access to my own fork. I can't create a shared branch directly on this repo, since GitHub only lets a fork-based PR's base point to a branch that already exists upstream, and the only one available to me is main.

For reference, the original #1633 /#1634 split used the same approach: both targeted main, with each subsequent PR's head branch stacked on the previous one, so the diff naturally shrinks to just the new commit once the earlier PR merges.

If you'd prefer this series (5 PRs total) to target a dedicated integration branch instead of main directly, I'm happy to follow that but someone with write access would need to create it first (e.g. feature/paypal-autolink-manual-return), since I can't push branches here myself. Otherwise I'll keep following the main + stacked-head-branches pattern from the original split. Let me know which you'd prefer.

CC @jaxdesmarais @noguier

@jaxdesmarais

Copy link
Copy Markdown
Contributor

That's fair @pedrofsn - I have gone ahead and created feature/paypal-autolink-manual-return for you and pushed it up so you can use that as the base branch.

@pedrofsn
pedrofsn changed the base branch from main to feature/paypal-autolink-manual-return July 14, 2026 20:11
@pedrofsn

Copy link
Copy Markdown
Author

That's fair @pedrofsn - I have gone ahead and created feature/paypal-autolink-manual-return for you and pushed it up so you can use that as the base branch.

Thanks @jaxdesmarais, appreciate you setting that up! Rebased this PR onto feature/paypal-autolink-manual-return.

Kicking off the inner source process now:

/inner source

PRs 2-5 in this series will also target this branch going forward.

@github-actions github-actions Bot added inner source This PR is internal to PP but external to the mobile SDK team tech lead review required labels Jul 14, 2026
@pedrofsn
pedrofsn marked this pull request as draft July 14, 2026 20:15
@pedrofsn

Copy link
Copy Markdown
Author

Marking this as draft while it's pending tech lead review, per the inner source process.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

inner source This PR is internal to PP but external to the mobile SDK team tech lead review required

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants