Skip to main content
Prepare a browser measurement while the visitor browses. At checkout, apply your policy before creating the order. Existing signed-in visitors reuse a current measurement; an early submission waits only when a required check is still pending. The Next.js and PostgreSQL example includes a checkout page at /checkout. It creates an order and a payment task in PostgreSQL. It does not collect card details or call a payment provider.

Submit the cart

The example has a single-product cart. The browser sends a product ID and quantity:
The server derives the account from the authenticated session. It reads the price and available stock from the product table. Client-supplied prices or account IDs cannot change the order. app/checkout/page.jsx contains the complete form, catalog loading, error handling, retry controls and receipt display. lib/actions.mjs contains the corresponding server action. Both ship in the archive.

Check policy and create the order

The checkout action requires an authenticated account and a quantity between one and five. It uses checkout:<sku> as the shared resource key, then locks and checks that product’s stock. All contenders for the same product use the same key, across application instances. Its sample policy requests a ceremony when the measurement is not uncontradicted. A passed ceremony permits the policy to continue, but stock and account checks still apply. Stock is checked again after a ceremony; submitting earlier does not reserve an item. Once policy allows the action, commit performs three writes with the same PostgreSQL transaction client: The SDK saves the completion receipt in that transaction too. If any write fails, the stock change, order and outbox entry roll back together. Retrying the same attempt returns the saved order when it already completed. Intentional repeat purchases are allowed. The example does not apply redemption’s one-per-account or identifier-match rule to checkout.

Deliver payment separately

The reference ends with example_payment_outbox. Connect it to your existing payment worker. Each row contains a stable idempotency_key of checkout:<attemptId> and the order’s stored amount and currency. Your worker should claim pending work durably, use that key for repeated provider requests, and record the provider’s result. Reconcile provider webhooks or status reads with the order in your own database. A lost response is not proof that payment failed. Database completion alone does not guarantee exactly-once execution by an external provider. The order is awaiting-payment until your payment flow updates it. Choose an explicit payment deadline, cancellation policy and stock-release transaction. Preserve payment/order receipts for the retry and reconciliation window. The local example does not run an outbox worker or expire orders automatically. For a cart with several products, replace the single-product transaction with your existing cart transaction and lock stock rows in a consistent order. Preserve your checks for discounts, tax, delivery, currency and payment eligibility. Use the same protected write path for every endpoint that can create the order.

Exercise the example

  1. Select Account one, choose Notebook and place an order. The receipt shows the server-calculated total and payment-pending state.
  2. Retry that attempt. The order and outbox tables must still contain one row for its ID.
  3. Select Start another order to create an intentional second purchase.
  4. Use two browser sessions to compete for Last notebook. Only one order can allocate its final unit.
  5. Enable the contradicted fixture and use a fresh browser session. The order must remain unwritten until the ceremony completes and policy allows it.
The shared action contract covers authenticated context, waiting, challenges, retries and database retention. The same browser client can protect registration and other actions in your application.