Avoid possible race condition issues with loading google and apple pa…#3130
Avoid possible race condition issues with loading google and apple pa…#3130Crabcyborg wants to merge 3 commits into
Conversation
📝 WalkthroughWalkthroughPayPal payment method initialization now supports deferred eligibility discovery for Google Pay and Apple Pay. The init flow discovers base methods, awaits a new parallel eligibility-check step using SDK-readiness polling, and then builds the selector UI. Radio UI construction was refactored with a centralized helper function. ChangesPayPal Deferred Discovery & UI Refactor
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
|
Overall Grade |
Security Reliability Complexity Hygiene |
Code Review Summary
| Analyzer | Status | Updated (UTC) | Details |
|---|---|---|---|
| PHP | Jun 8, 2026 5:20p.m. | Review ↗ | |
| JavaScript | Jun 8, 2026 5:20p.m. | Review ↗ |
Important
AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@paypal/js/frontend.js`:
- Around line 134-150: The async interleaving in paypalInit() can let stale runs
mutate module-scoped state (paymentMethods, thisForm, cardFieldsValid, SDK
caches) after a newer run has already reset things; add a monotonically
increasing init token (e.g., module-scoped initCounter and local runToken) and
increment/init it before any awaits, reset per-run globals
(paymentMethods.clear(), selectedMethod=null, thisForm=null,
cardFieldsValid=false, clear relevant SDK caches) immediately before the first
await, then in any async continuations (after await discoverPaymentMethods and
await discoverDeferredPaymentMethods and the other affected block at 294-308)
check that the current module initCounter still equals the runToken and bail out
early if not, ensuring only the latest paypalInit run mutates shared state.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9d84e0ae-7480-46bc-a0a5-351c95d7f7b5
📒 Files selected for processing (1)
paypal/js/frontend.js
| // Reset state so each run rediscovers cleanly (the DOM above was cleared). | ||
| paymentMethods.clear(); | ||
| selectedMethod = null; | ||
|
|
||
| // 1. Discover synchronous methods (Card, PayPal, alternative funding). | ||
| await discoverPaymentMethods( { | ||
| cardFieldsAreSupported, | ||
| buttonsAreEnabled, | ||
| isRecurring | ||
| } ); | ||
|
|
||
| // 1b. Discover Google Pay / Apple Pay. Their SDKs (pay.js / PayPal applepay | ||
| // component) load asynchronously and are frequently not ready during the first | ||
| // run, which previously left these buttons missing on first load. We wait | ||
| // (bounded) for them here so every eligible method appears together in one | ||
| // render instead of popping in afterward. | ||
| await discoverDeferredPaymentMethods( { buttonsAreEnabled, isRecurring } ); |
There was a problem hiding this comment.
Guard paypalInit() against stale async reruns.
These new deferred awaits create a real interleaving window where an older paypalInit() can resume after a later run has already cleared and rebuilt the shared module state. Because paymentMethods, thisForm, cardFieldsValid, and the SDK caches are all module-scoped, the stale run can register methods into the new registry, append UI against the wrong form state, or re-enable card submit using validity from the previous render. Add a monotonically increasing init token and reset the rest of the per-run globals before the first await.
Suggested direction
+ let initRunId = 0;
+
async function paypalInit() {
+ const runId = ++initRunId;
const cardElement = document.querySelector( '.frm-card-element' );
if ( ! cardElement ) {
return;
}
@@
// Reset state so each run rediscovers cleanly (the DOM above was cleared).
paymentMethods.clear();
selectedMethod = null;
+ cardFieldsValid = false;
+ submitEvent = null;
+ googlePayConfig = null;
+ applePayConfig = null;
+ applePayInstance = null;
// 1. Discover synchronous methods (Card, PayPal, alternative funding).
await discoverPaymentMethods( {
cardFieldsAreSupported,
buttonsAreEnabled,
isRecurring
} );
+ if ( runId !== initRunId ) {
+ return;
+ }
@@
await discoverDeferredPaymentMethods( { buttonsAreEnabled, isRecurring } );
+ if ( runId !== initRunId ) {
+ return;
+ }
+
+ // Apply the same stale-run guard after later awaited init steps that mutate DOM/state.Also applies to: 294-308
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@paypal/js/frontend.js` around lines 134 - 150, The async interleaving in
paypalInit() can let stale runs mutate module-scoped state (paymentMethods,
thisForm, cardFieldsValid, SDK caches) after a newer run has already reset
things; add a monotonically increasing init token (e.g., module-scoped
initCounter and local runToken) and increment/init it before any awaits, reset
per-run globals (paymentMethods.clear(), selectedMethod=null, thisForm=null,
cardFieldsValid=false, clear relevant SDK caches) immediately before the first
await, then in any async continuations (after await discoverPaymentMethods and
await discoverDeferredPaymentMethods and the other affected block at 294-308)
check that the current module initCounter still equals the runToken and bail out
early if not, ensuring only the latest paypalInit run mutates shared state.
…y sdks
Related ticket https://secure.helpscout.net/conversation/3348528710/253332
This update waits for up to 3 seconds for Google Pay and Apple Pay to load so we're less likely to run into cases where the options never appear because PayPal loaded first.
Pre-release
formidable-6.31.1b.zip
Summary by CodeRabbit