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
Payment cancelled
No worries — your basket is still saved. Ready when you are.
err.name === 'AbortError' → true
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 → 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 → OperationError
second 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