Skip to content

fix(checkout): P1 — redirect_requested treated as success before 3DS authentication #4437

Description

@koala73

Summary

Our checkout.redirect_requested handler runs terminal success side-effects before the payment is authenticated. With manualRedirect: true, SDK 1.8.0 routes all redirects to the merchant via this event — including the 3DS bank-escape hop. So when a card needs a full-page 3DS redirect, we fire onSuccess and clear the retry context before the user has even reached the bank.

Evidence

  • src/services/checkout.ts:379-399case 'checkout.redirect_requested': if (!successFired) runTerminalSuccessSideEffects('event-redirect') runs first, then window.location.href = redirectTo.
  • runTerminalSuccessSideEffects fires onSuccessCallback, clearCheckoutAttempt('success'), clearPendingCheckoutIntent(), markPostCheckout() — i.e. it tells the app the user is Pro and wipes the retry intent.
  • SDK 1.8.0 (index.esm.js, handler v): the parent SDK auto-navigates only on checkout.redirect; checkout.redirect_requested is forwarded untouched. With manualRedirect:true forwarded into the iframe URL, the iframe emits redirect_requested for redirects it would otherwise perform — which includes the 3DS escape, not just the final return-URL hop.

Fix direction (small)

Only treat redirect_requested as terminal success when redirect_to resolves to our return_url origin (the final hop). If redirect_to is an external/bank origin (intermediate 3DS), navigate without marking success or clearing retry context. Better still: don't infer payment success from a redirect at all — rely on the return_url status params + the payment.succeeded/subscription.active webhook.

Test first

Emit redirect_requested with redirect_to = an external bank origin and assert success side-effects do NOT fire (retry context preserved); emit it with redirect_to = our return_url and assert they do.


Part of the DodoPayments 3DS stuck-payments incident.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1High priority, fix soonbugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions