Skip to main content
TL;DR: Same order_reference, same amount and currency, within the session TTL returns the same session. A different amount or currency on a still-payable session is rejected with 409, not silently applied. After the session is no longer payable (TTL elapsed, declined, or cancelled), a new session is possible. Always check your own order state before creating one.
order_reference is your idempotency key for session creation, and the hosted page itself guards against a shopper submitting payment twice for the same session. But that protection is time-boxed (it lasts only the session’s TTL) and doesn’t compare the request body — so it prevents a duplicate session, not necessarily a duplicate charge for an already-paid order.

When this happens

  • Your server retries a create call after a timeout or network error, using the same order_reference.
  • Your server retries with the same order_reference but a changed field (a different amount, for example).
  • Your server reuses an order_reference after its original session’s TTL has already elapsed.
  • Two create calls for the same order_reference race each other (rare — only a genuine concurrent race, not a normal sequential retry).
  • A shopper double-clicks pay, or your page double-submits, on the hosted page itself.

What you see

What to do

1

Reuse order_reference per order attempt, not per HTTP call

Generate one order_reference per order attempt and reuse it for every retry of that same attempt (API reference). A changed amount or currency on a retry gets rejected with 409 rather than silently applied while the original session is still live — don’t send a different total expecting it to update an in-flight order.
2

Check your order isn't already paid before creating a session

The order_reference guard only lasts the session’s TTL (5–60 minutes) — after that, the same reference happily creates a brand-new session. If your original order already completed (for example, its webhook arrived after your client gave up waiting), a later retry with the same or a fresh order_reference will charge the shopper again. Check your own order state before every create call — see Prevent duplicate payments for the full pattern.
3

Handle a 409, either cause

409 session:idempotency_conflict covers two distinct causes with different detail text: a concurrent create for the same reference still in flight (“still being created; retry shortly” — the losing call has no checkout_id to look up, so retry the create after a short delay instead of fetching one), or a changed amount or currency against a still-payable session (“already in use for a different amount or currency” — fix the request or use a new order_reference).
4

A new order_reference isn't required after a decline or cancellation, but keeps things clean

Reusing the order_reference from a failed, cancelled, or expired session now creates a new session automatically — the reference is released as soon as the original stops being payable, so you don’t strictly need a new value. Many merchants still mint a fresh order_reference per attempt for their own tracking. See Handle declined hosted checkout payments.

Prevent duplicate payments

The full guide to guarding your own order state.

Redirect integration

Where the create-session retry guidance fits into the full flow.

Session lifecycle

Replay semantics in full, including the session-states diagram.

Handle failures

All ten failure scenarios, symptom → page.
Last modified on September 15, 2026