0Bit Documentation
Production 0Gate payment flow

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
Server boundary

Create your business record first, then bind the hosted 0Gate session to it.

Present
Browser boundary

Send only browser-safe values to the client and mount or redirect to the hosted flow.

Verify
0Gate boundary

0Gate owns the hosted UX, compliance steps, quote confirmation, and payment collection.

Fulfill
Webhook boundary

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.

Flat architecture diagram
Intent recorded
Your app

Store the cart, wallet action, top-up, invoice, or account intent before 0Gate is called.

Session bound
Your server

Create the session with server-side credentials, a stable reference, return URLs, and an idempotency key.

Hosted flow
0Gate + browser

The browser opens the 0Gate experience with browser-safe values only. Client callbacks stay UX-only.

Signed event
Webhook worker

Your backend verifies the signature, dedupes the event, and queues durable fulfillment.

Ledger closed
Your records

A terminal state is written once, with reconciliation available for delayed or dead-lettered events.

Browser UX lane

Can show loading, success, failure, or return screens. It never grants value by itself.

0Gate hosted lane

Runs the hosted compliance, quote, payment, and confirmation experience inside the approved product boundary.

Durable backend lane

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.

StepSystem of recordPartner responsibilityWhat not to do
IntentYour databaseCreate an order, wallet action, deposit, invoice, or top-up before starting 0Gate.Let the browser invent the amount or settlement reference.
Session0GateCreate 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 UX0Gate iframe or redirectSend 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.
WebhookYour backendVerify the raw-body signature, dedupe by event id, and route known events to workers.Parse or log unverified payloads.
FulfillmentYour ledgerMove 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.

RecordOwnerRequired fieldsWhy it exists
Payment attemptYouYour id, customer/account id, amount intent, 0Gate session id, current status.Lets support and reconciliation find one attempt without reading browser history.
Webhook eventYouEvent id, event type, received time, processing result.Makes retries harmless and gives you an audit trail.
Fulfillment actionYouFulfillment id, source event id, ledger mutation id, terminal status.Prevents duplicate credits, releases, mints, shipments, or account updates.
Reconciliation taskYouAttempt 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 signalUse it forDo not use it for
Hosted widget readyEnable UI and measure load health.Mark the payment as started in your ledger.
Success callbackMove the page to a processing or return screen.Fulfill, credit, or ship.
Close callbackOffer retry, support, or save-for-later UX.Cancel a server-side attempt without checking trusted state.
Return URL visitShow 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

EdgeRetry behaviorYour guardrail
Session creationRetry with the same logical idempotency key.Reuse one key per order or payment attempt.
Browser mount or redirectUser can refresh, remount, or return from mobile webview.Session remains scoped to one hosted flow.
Webhook delivery0Bit delivery is at least once.Unique event id plus idempotent fulfillment.
ReconciliationSupport 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

Secret credentials never leave the server.
Session creation retries share the same idempotency key.
Your order row stores the 0Gate session id and your own reference id.
Browser success moves UI to processing, not fulfilled.
Webhook route verifies Gate-Signature against the raw body.
Fulfillment dedupes by webhook event id and session id.
Dead-letter and replay paths are monitored.
Reconciliation can recover delayed or missed webhook delivery.