3-D Secure & redirects
Handling authentication redirects and return URLs.
Some payments cannot complete in one synchronous step. A 3-D Secure challenge
sends the buyer to their bank's authentication page; redirect-based methods
(instant EFT, some wallets) send them to a bank or app. In both cases the buyer
leaves the page and must come back. ShiftxPay handles this with a returnUrl, a
return marker, a poll-confirm loop, and a webhook backstop.
returnUrl
Set returnUrl on mountCheckout to the page the buyer should land on after an
external authentication or redirect step:
await mountCheckout({
baseUrl: "https://api.lite.shiftxpay.com",
sessionId: "cs_...",
sessionSecret: "cs_secret_...",
container: "#payment-element",
returnUrl: "https://shop.example.com/checkout/return",
});- Peach embedded widget — straight-through card payments complete in place and
never leave the page. A 3-D Secure challenge or a redirect-based method (instant
EFT, some wallets) runs inside the widget's own flow and returns the buyer to your
returnUrl.
For the hosted checkout page you do not
set this yourself: the gateway threads its own return URL of the form
{checkout-origin}/{id}?pp_return=1#{session_secret} into the PSP. The session
secret rides in the fragment so it never reaches server logs or the Referer
header.
The pp_return marker
The hosted page distinguishes a fresh open from a redirect return using the
pp_return=1 query marker on the return URL:
- No marker → a fresh open: mount the PSP surface and wait for the buyer.
pp_return=1→ the buyer is returning from an external step: do not re-mount the widget, go straight to confirming the outcome.
The poll-confirm loop
On return, the page confirms the session against the gateway:
POST /v1/checkout/{id}/confirm?secret={session_secret}The gateway verifies the intent server-side at the PSP (it never trusts the browser) and responds with the session status:
| Status | Meaning | Page behaviour |
|---|---|---|
completed | The payment is recorded. | Show success; stop polling. |
processing | The PSP is still settling. | Keep polling, backing off. |
requires_action | Further authentication is pending. | Keep polling, backing off. |
failed | The payment failed. | Show failure; stop polling. |
The confirm call is idempotent — a completed session always returns its recorded
payment — and is safe to retry, which is exactly what the poll loop does until the
status resolves to completed or failed.
The webhook backstop
A buyer might close the tab and never return — yet the payment still settles at
the PSP. ShiftxPay does not depend on the buyer coming back: the Peach inbound
webhook drives the session to completion independently. The
browser-confirm path and the webhook path both finalize through the same row-
locked transition, so whichever arrives first records the payment and the other
observes completed — the payment is never double-booked.
This is why you should treat your outbound webhook as the source of truth for
the final payment state. The SDK's onSuccess is a fast path for the common case;
the webhook is the guarantee. See Webhooks.