Build a 0Gate payment flow that survives real users, retries, and settlement delays.
Use the hosted 0Gate path for public integrations: create the session on your server, present the hosted flow in the browser, verify signed webhooks, and reconcile from trusted state when delivery is delayed.
Create your business record first, then bind the hosted 0Gate session to it.
Send only browser-safe values to the client and mount or redirect to the hosted flow.
0Gate owns the hosted UX, compliance steps, quote confirmation, and payment collection.
Verify signed backend events and update your ledger through an idempotent worker.
Use 0Gate for public payment flows. Your app owns the commercial record, 0Gate owns the hosted payment experience, and your backend treats signed webhooks as the durable source of truth.
Browser callbacks are not settlement
onSuccess, return URLs, and redirect arrivals can improve the user experience, but they do not prove value moved. Fulfill only after a verified backend event or a trusted server-side reconciliation read.
Workflow contract
Payment lifecycle map
The browser path is temporary. The durable path is your server, the 0Gate session, signed webhooks, and your own ledger.
Store the cart, wallet action, top-up, invoice, or account intent before 0Gate is called.
Create the session with server-side credentials, a stable reference, return URLs, and an idempotency key.
The browser opens the 0Gate experience with browser-safe values only. Client callbacks stay UX-only.
Your backend verifies the signature, dedupes the event, and queues durable fulfillment.
A terminal state is written once, with reconciliation available for delayed or dead-lettered events.
Can show loading, success, failure, or return screens. It never grants value by itself.
Runs the hosted compliance, quote, payment, and confirmation experience inside the approved product boundary.
Owns order state, signature verification, event dedupe, fulfillment, support, and reconciliation.
The production contract is small: create one business record, bind one 0Gate session to it, present the hosted flow, and close your ledger from signed backend state.
| Step | System of record | Partner responsibility | What not to do |
|---|---|---|---|
| Intent | Your database | Create an order, wallet action, deposit, invoice, or top-up before starting 0Gate. | Let the browser invent the amount or settlement reference. |
| Session | 0Gate | Create the session from your server and store the 0Gate session id beside your own reference. | Put secret credentials or session creation logic in client code. |
| Hosted UX | 0Gate iframe or redirect | Send only browser-safe values to the page and show a processing state after client success. | Treat a callback, iframe message, or return URL as final settlement. |
| Webhook | Your backend | Verify the raw-body signature, dedupe by event id, and route known events to workers. | Parse or log unverified payloads. |
| Fulfillment | Your ledger | Move internal value exactly once, then expose status to the browser or support team. | Re-run fulfillment on retries or support refreshes. |
End-to-end sequence
State you own
Keep your own state model explicit. 0Gate state explains the hosted session; your ledger state explains what your product has granted.
| Record | Owner | Required fields | Why it exists |
|---|---|---|---|
| Payment attempt | You | Your id, customer/account id, amount intent, 0Gate session id, current status. | Lets support and reconciliation find one attempt without reading browser history. |
| Webhook event | You | Event id, event type, received time, processing result. | Makes retries harmless and gives you an audit trail. |
| Fulfillment action | You | Fulfillment id, source event id, ledger mutation id, terminal status. | Prevents duplicate credits, releases, mints, shipments, or account updates. |
| Reconciliation task | You | Attempt id, reason, last checked at, result. | Recovers from delayed webhooks, dead letters, and support escalations. |
type PaymentAttemptStatus =
| 'pending_session'
| 'requires_action'
| 'processing'
| 'fulfilled'
| 'failed'
| 'expired'
| 'cancelled';
type PaymentAttempt = {
id: string;
gateSessionId: string | null;
userReference: string;
status: PaymentAttemptStatus;
fulfilledAt: Date | null;
};Server flow
The server flow should read like one transaction boundary even when the API call is remote.
async function startPaymentFlow(input: PaymentInput) {
const attempt = await paymentAttempts.create({
amount: input.amount,
currency: input.currency,
userReference: input.orderId,
status: 'pending_session',
});
const session = await gateSessions.createForAttempt({
attempt,
flow: input.flow,
returnUrl: input.returnUrl,
cancelUrl: input.cancelUrl,
idempotencyKey: `payment-attempt:${attempt.id}`,
});
await paymentAttempts.attachGateSession(attempt.id, {
gateSessionId: session.id,
status: 'requires_action',
});
return {
attemptId: attempt.id,
clientSecret: session.clientSecret,
};
}Keep endpoint mechanics in API reference
This guide intentionally describes the production workflow. Request bodies, response schemas, generated examples, error codes, and SDK-specific signatures belong in the 0Gate API and SDK reference pages.
Browser flow
The browser has one job: open the hosted experience and keep the customer informed. It can display a processing state, poll your own order endpoint, or wait for your app push channel after the backend fulfills.
| Browser signal | Use it for | Do not use it for |
|---|---|---|
| Hosted widget ready | Enable UI and measure load health. | Mark the payment as started in your ledger. |
| Success callback | Move the page to a processing or return screen. | Fulfill, credit, or ship. |
| Close callback | Offer retry, support, or save-for-later UX. | Cancel a server-side attempt without checking trusted state. |
| Return URL visit | Show a status page. | Trust query-string status values. |
Webhook worker
Your webhook worker is the settlement path. It should be small, boring, and idempotent.
async function handleGateEvent(rawBody: string, headers: Headers) {
const event = verifyGateWebhook(rawBody, headers);
const inserted = await webhookEvents.insertOnce({
id: event.id,
type: event.type,
gateSessionId: event.data.id,
});
if (!inserted) return { received: true };
await paymentEventQueue.enqueue({
eventId: event.id,
gateSessionId: event.data.id,
type: event.type,
});
return { received: true };
}Make retries safe
Idempotency
Use one stable key per logical payment attempt and reuse it for retries.
Raw-body signatures
Verify Gate-Signature before parsing or trusting webhook JSON.
Dedupe
Use the webhook event id as the idempotency key for fulfillment.
Reconciliation
Read trusted server state only as a fallback recovery path.
Redaction
Never log secret keys, webhook secrets, browser session secrets, raw PII, or full webhook payloads.
State machine
Represent pending, processing, fulfilled, failed, expired, and cancelled explicitly.
Retry policy
| Edge | Retry behavior | Your guardrail |
|---|---|---|
| Session creation | Retry with the same logical idempotency key. | Reuse one key per order or payment attempt. |
| Browser mount or redirect | User can refresh, remount, or return from mobile webview. | Session remains scoped to one hosted flow. |
| Webhook delivery | 0Bit delivery is at least once. | Unique event id plus idempotent fulfillment. |
| Reconciliation | Support or scheduled jobs can read trusted state later. | Compare terminal state before moving internal value. |
Reconciliation path
Reconciliation is recovery, not the primary payment path. Use it for delayed webhook delivery, support tickets, dead-lettered events, and status pages that need a trusted refresh.
async function reconcilePaymentAttempt(attempt: PaymentAttempt) {
const session = await gateSessions.retrieve(attempt.gateSessionId);
if (session.status === 'completed') {
await fulfillOnce({
attemptId: attempt.id,
gateSessionId: session.id,
source: 'reconciliation',
});
}
if (['failed', 'expired', 'cancelled'].includes(session.status)) {
await paymentAttempts.close(attempt.id, session.status);
}
}Production readiness checklist
Production readiness checklist
Related pages
0Gate quickstart
Follow the product-specific 0Gate setup path.
Embed the 0Gate widget
Mount the hosted iframe or use hosted redirect.
Webhook events
Handle signed backend events as the durable source of truth.
Prepare production launch
Confirm credentials, domains, webhooks, reconciliation, monitoring, and support.