v149 · Payments · demo
Smart Retry Flow
A checkout that reacts to real PaymentRequest.show() outcomes from a same-origin payment handler: user cancellation gets a gentle nudge to continue shopping, while an internal app error opens a fallback payment path.
Payment handler setup
Install the handler before pressing Pay. The live payment request is launched from the Pay button.
Checkout
Premium Widget£29.00
Shipping£3.50
Total£32.50
Basket state will be saved before the first payment attempt.
Payment confirmed
Your order #CHR-147 is on its way.
err.name → (no error)
PaymentResponse.complete('success') called
→ show confirmation page
PaymentResponse.complete('success') called
→ show confirmation page
Payment cancelled
No worries — your basket is still saved. Ready when you are.
err.name === 'AbortError' → true
err.message →
→ user made a deliberate choice
→ do NOT retry automatically
→ show "continue shopping" message
err.message →
User cancelled in the payment app.→ user made a deliberate choice
→ do NOT retry automatically
→ show "continue shopping" message
Payment app error
Fallback method is available.
err.name === 'OperationError' → true
err.message →
→ internal handler failure, not user decision
→ reveal fallback method
→ wait for a new user gesture before reopening payment UI
err.message →
Waiting for handler payload.→ internal handler failure, not user decision
→ reveal fallback method
→ wait for a new user gesture before reopening payment UI
Fallback stopped
Fallback also failed. Stop automatic retries and show a manual payment option.
second request err.name →
second request err.message →
→ cap retries after one fallback attempt
→ offer support, invoice, or another payment method
OperationErrorsecond request err.message →
Waiting for handler payload.→ cap retries after one fallback attempt
→ offer support, invoice, or another payment method
// merchant page — Chrome 149+
async function pay(method) {
const request = new PaymentRequest([{ supportedMethods: method }], details);
try {
const response = await request.show();
await response.complete('success');
showOrderConfirmation(); // payment worked
} catch (err) {
if (err.name === 'AbortError') {
// User pressed cancel — respect it, don't retry
showCancelMessage('Your basket is saved for later.');
} else if (err.name === 'OperationError') {
// Payment app bug — user didn't cancel, safe to offer fallback
showFallbackPaymentMethod();
} else {
showGenericError(err.message);
}
}
}
see also
- Error Signal Tester — inspect each error name
- Back to feature index
- ChromeStatus entry