Lock an on-ramp flow
Build a hosted 0Gate buy flow that locks the user into on-ramp without exposing headless rails or API-reference details.
Use an on-ramp flow when the customer wants to buy crypto with fiat inside the hosted 0Gate experience. Your server creates the intent, the session is locked to buy, and fulfillment still waits for a verified backend event.
Kit blocks are still the hosted 0Gate iframe
An on-ramp kit block is not a custom ramp UI builder. KYC, payment-method collection, quote confirmation, and compliance screens remain inside the 0Gate hosted experience.
Flow boundary
| Boundary | Owned by | What to lock |
|---|---|---|
| Buy intent | Your app | Account, amount intent, target asset, wallet destination if you already collected it. |
| Session | 0Gate | Flow set to on-ramp, return URLs, reference ids, and any allowed constraints. |
| Browser | Your app + 0Gate iframe | Browser-safe session values and the on-ramp presentation only. |
| Settlement record | Your backend | Terminal status from verified backend state. |
Implementation shape
- Create a buy intent before you start 0Gate.
- Create a server-side 0Gate session with the flow locked to on-ramp.
- Store the 0Gate session id next to your buy intent.
- Mount the on-ramp kit block or the full hosted widget with the session-bound flow.
- Treat browser success as processing, then fulfill from signed backend state.
async function startOnRamp(input: OnRampIntent) {
const intent = await buyIntents.create({
accountId: input.accountId,
fiatAmount: input.fiatAmount,
fiatCurrency: input.fiatCurrency,
targetAsset: input.targetAsset,
walletAddress: input.walletAddress,
status: 'pending_session',
});
const session = await gateSessions.createForAttempt({
attemptId: intent.id,
flow: 'on_ramp',
userReference: intent.id,
idempotencyKey: `on-ramp:${intent.id}`,
});
await buyIntents.attachGateSession(intent.id, session.id);
return { intentId: intent.id, clientSecret: session.clientSecret };
}State model
| State | Meaning | User-facing copy |
|---|---|---|
| pending_session | Your app has a buy intent but no 0Gate session id yet. | Preparing checkout. |
| requires_action | The hosted buy flow is ready. | Continue in 0Gate. |
| processing | The customer finished the hosted flow, but your backend has not fulfilled yet. | Confirming payment. |
| fulfilled | A verified terminal event closed the ledger update. | Crypto purchase complete. |
| failed | The hosted buy flow failed or could not complete. | Payment failed. Try again or contact support. |
| expired | The session timed out before completion. | Session expired. Start again. |
| cancelled | The customer or backend cancelled the attempt. | Checkout cancelled. |
Browser behavior
Keep the browser path deliberately thin.
| Browser event | Recommended action | Reason |
|---|---|---|
| Mount ready | Enable the checkout area and stop loading placeholders. | The iframe can accept user input. |
| Success callback | Switch to a processing view and read your own attempt status. | Settlement is not proven client-side. |
| Close callback | Show retry, continue later, or support options. | The attempt may still be open or processing. |
| Return URL | Render a status page backed by your server. | Query strings and redirects are not authoritative. |
Webhook and reconciliation
The on-ramp terminal path is the same as the generic payment flow: verify the signature, dedupe by event id, and fulfill once. Reconciliation should read trusted server state only when webhook delivery is delayed, dead-lettered, or a support agent needs to resolve a stuck attempt.
async function applyOnRampEvent(event: GateSessionEvent) {
const intent = await buyIntents.findByGateSession(event.data.id);
if (!intent) return;
if (event.type === 'gate_session.completed') {
await fulfillBuyIntentOnce(intent.id, { sourceEventId: event.id });
}
if (event.type === 'gate_session.failed') {
await buyIntents.markFailed(intent.id, { sourceEventId: event.id });
}
}Production checklist
- Lock the flow on the server, not only in browser UI.
- Store your intent id, 0Gate session id, and event ids.
- Redact browser session values and webhook payloads from logs.
- Keep support screens generic: do not expose provider, treasury, or compliance internals.
- Test refreshes, double-clicks, closed iframes, delayed webhook delivery, and duplicate events.
Related pages
Build a 0Gate payment flow
Use the shared architecture, state, webhook, and reconciliation pattern.
Embed a single flow with kit blocks
Mount the hosted iframe with one flow pre-selected.
Preview quotes before checkout
Show indicative pricing before the hosted flow starts.
Handle failed or cancelled flows
Build retry and support paths for terminal and interrupted sessions.