PaybytokenDocs
Checkout
Guide · Checkout

Model orders and payment attempts

Keep merchant orders, Checkout Sessions and Payment Intents separate so retries remain safe.

A reliable integration starts by deciding which system owns each piece of state. Your application owns the commercial order. Paybytoken owns each checkout interaction and funds-movement attempt. Do not make one resource serve all three purposes.

Resource relationship

ResourceWhat it representsNormal lifetime
Merchant orderWhat your customer is buying and what you may fulfillYour business lifecycle
Checkout SessionOne customer checkout interaction with a snapshot of the amount and token choicesUntil completed, canceled or expired
Payment IntentOne selected payment method, chain and asset attemptUntil it succeeds or reaches a terminal failure state
EventAn immutable business transition used for fulfillment and reconciliationRetained for audit

One merchant order can create another Checkout Session after an earlier Session expires or is canceled. It must not create parallel Sessions accidentally because the customer double-clicked a button or your server retried a timed-out request.

Build the order on your server

Load products, quantities, discounts and prices from your own trusted records. Convert the final order into Checkout line items only after authorization and inventory checks.

const order = await orders.getForCheckout(orderId, authenticatedCustomer.id)

const session = await paybytoken.checkoutSessions.create(
  {
    currency: 'usd',
    line_items: order.items.map((item) => ({
      quantity: item.quantity,
      unit_amount: item.unitPrice,
      product_data: {
        name: item.name,
        description: item.variantName,
        images: item.imageUrl ? [item.imageUrl] : [],
      },
    })),
    supported_tokens: order.allowedStablecoins,
    customer_id: order.paybytokenCustomerId,
    customer_email: order.receiptEmail,
    success_url: `https://shop.example.com/orders/${order.id}/return`,
    cancel_url: 'https://shop.example.com/cart',
    metadata: { order_id: order.id },
  },
  { idempotencyKey: `checkout-session:${order.id}:${order.checkoutVersion}` },
)

currency is the order display currency and currently accepts USD. unit_amount is a positive decimal string, while each quantity is a positive integer. Paybytoken calculates subtotal and total from the complete line-item snapshot.

For a customer-entered top-up, validate the submitted amount against your own minimum and maximum on the server, then create the line item from that accepted amount. For a reusable public page with built-in variable-amount rules, use a Payment Link.

Treat supported tokens as an allowlist

supported_tokens limits what this order may accept. Include only combinations currently returned by token discovery and allowed by your product. Do not send a contract address, decimals or chain ID from browser input; Paybytoken resolves those values from its enabled token configuration.

Changing the allowlist requires a new Checkout Session. Never change token meaning after a customer has received payment instructions.

Make Session creation retry-safe

Use a stable Idempotency-Key for retries of the same logical Session creation request. The same key must never represent a different amount, order version or customer.

A practical key contains:

  • the merchant order ID;
  • a checkout version that increments when the payable order changes; and
  • optionally the presentation mode when your application deliberately creates distinct flows.

Store the returned Session ID on the order. If your server loses the response, retry the exact request with the same key or retrieve the stored Session instead of creating an unrelated attempt.

Decide when to create another Session

Reuse the existing open Session while the customer is continuing the same checkout interaction. Create a new Session when:

  • the previous Session expired or was canceled;
  • price, quantity, customer or allowed tokens changed;
  • the customer deliberately starts a replacement attempt; or
  • your support workflow has established that no usable active Session remains.

Do not reuse a completed Session, a one-time destination address or an embedded client_secret. Do not cancel an order merely because the browser closed; retrieve canonical state first.

Map state without collapsing it

Keep at least these identifiers on your order record:

merchant_order_id
paybytoken_checkout_session_id
paybytoken_payment_intent_id
paybytoken_last_event_id

The Checkout Session describes the customer interaction. The Payment Intent describes payment. Your order may remain pending_payment while the browser says complete and only become paid after a verified payment_intent.succeeded event.

Use metadata for correlation, not authorization. A webhook payload containing your order_id is still untrusted until its signature is verified.

Handle late and duplicate signals

  • Deduplicate webhook processing by Event ID.
  • Lock the merchant order before applying a terminal payment transition.
  • Compare the Payment Intent amount, currency and stored Session relationship before fulfillment.
  • Treat redirects and embedded completion events as navigation signals only.
  • If a canceled or expired interaction later has on-chain activity, let Paybytoken's canonical resource and support workflow decide the financial outcome.

Continue with hosted checkout, embedded checkout, or custom checkout. Every mode uses the same resource model.

On this page

API Workbench

Full Explorer

Open in new tab