Recover purchases
Resume an interrupted purchase without asking the buyer to pay twice.
Alex closes the page just after approving a payment. When they return, recover the existing checkout before showing another Pay button. Browser storage alone cannot recover a purchase across devices or after storage is cleared.
Save before the next side effect
| Before | Save |
|---|---|
| Creating the checkout | Stable buyer reference, raffle, quantity and purchase idempotency key on your server |
| Opening the wallet | Returned checkout ID, amount and deadline, linked to that purchase |
| Broadcasting a signed ticket payment | Signature, exact signed bytes, wallet/network and blockhash validity information |
| Registering the receipt | Keep the original checkout and signature until the outcome is resolved |
Authenticate every read and write to your purchase records. A client-supplied external_user_ref is not proof of identity. Scope records by environment, partner and buyer, and avoid putting signed transaction bytes in URLs, analytics or logs.
For a public paid storefront, use a durable server ledger and coordinate concurrent requests for the same buyer. A browser lock prevents two tabs in that browser from opening two prompts; it does not prevent concurrent requests from different devices.
Resume algorithm
- Load the purchase from your server. If creation timed out before an ID was saved, repeat checkout creation with the same idempotency key and body.
- Fetch the current checkout. Display confirmed tickets only for
completed. - If a signature is saved but the outcome is unknown, register that same signature and query status. Do not construct or sign another transfer.
- If the transaction was signed but broadcasting timed out, inspect chain status as well as checkout status. A missing RPC signature status alone is not proof of failure. If rebroadcasting is appropriate and the original transaction is still valid, send the identical saved bytes.
- Offer a new approval only after the previous attempt is definitively failed or expired, the API permits a retry, and the payment deadline still allows it.
refund_onlyforbids another purchase attempt.
Keep an extended-wait state with Check status and a purchase reference for support. Do not automatically turn a network timeout into another wallet prompt.
Restore history across devices
Call GET /users/{externalUserRef}/checkouts?limit=20&offset=0 from your server. Read data.items and data.has_more; advance the offset to load older purchases. This restores reservation-first purchases and tickets, but not wallet approvals or signed transaction bytes.
An unresolved checkout with no locally saved signature still needs reconciliation. Do not infer “unpaid” from missing browser storage. Keep your own purchase journal and process webhooks even while the buyer is offline.
Reconcile refunds separately
If refund_operation_id is returned, query GET /payments/{operationId}. kind: "refund" with completed means the refund completed, not that tickets were issued. Show refund pending and refund completed distinctly.
See checkout states and the payment sample for the ordering around signing and broadcast.