Failed and pending ramps
Handle 0Gate attempts that are delayed, abandoned, failed, expired, cancelled, or waiting on webhook delivery.
Payment and ramp flows are asynchronous. A user can close the hosted surface, return before a webhook arrives, hit an unavailable path, fail a hosted step, or retry after an expired session. Your product should make those states explicit instead of guessing from the browser.
State decision tree
Pending cases
| Case | User experience | Backend handling |
|---|---|---|
| Return URL reached before webhook | Show pending or "processing". | Wait for signed event or perform a server-side read. |
| Browser closed | Let the user resume or start a new attempt. | Keep session open until terminal state or expiry. |
| Webhook delayed | Show pending with support-safe reference. | Inspect delivery log and retry if dead-lettered. |
| Capability unavailable | Show unavailable path. | Do not create a session or force hidden routes. |
| Payment/ramp step failed | Show retry guidance. | Record failure event and create a new session for retry. |
Retry rules
- Reuse an idempotency key only for the same create attempt and same body.
- Create a new session for a new user attempt after expiry, cancellation, or meaningful input change.
- Never mark an order completed from
return_urlalone. - Store failure reasons as safe machine codes where possible.
- Escalate with session id, event id, delivery id, and request id, not secrets or raw identity data.
User messaging
| Backend state | Suggested copy |
|---|---|
| Waiting for event | "Your payment is still processing. We will update this page when confirmation is received." |
| Failed | "This attempt could not be completed. Start a new attempt or contact support if funds appear to have moved." |
| Cancelled | "This checkout was cancelled. You can start again when ready." |
| Expired | "This session expired. Start a new checkout to continue." |
| Unavailable | "This path is not available for the selected region, asset, method, or account configuration." |