Payments
Take payment for orders, with the server in charge of the amount.
Paying offline, at pickup or on delivery, always works, so an app is never blocked on a payment provider. Connect Stripe and checkout also offers card through Stripe's own payment page. The server totals the order and asks Stripe whether it was paid; the browser never decides.
- Lines
- 1 × Stoneware mug2 × Linen napkin
- Total
- $64.00computed on the server from catalogue prices
- Methods
- card (Stripe Checkout), offline
- Status
- ✓ paid, confirmed with Stripe
- Stripe Checkout
- On your own Stripe account
- Server-side totals
- A screen never sends an amount
- Offline fallback
- Orders are never lost
- Payments ledger
- Every checkout recorded
How checkout works
Declare a basket in the contract: the order table, its lines, the line's item reference, quantity and the item's price field.
Create the order and its lines, then call app.pay.checkout. With Stripe connected the shopper goes to Stripe Checkout; otherwise the order is recorded as due offline.
When the shopper returns, call app.pay.confirm, which asks Stripe. Each checkout is an entry in the payments ledger under Backend, Logs, Payments.
const order = await app.create("order", { shopper_id: app.me("shopper") });
for (const l of lines) {
await app.create("order_item",
{ order_id: order.id, product_id: l.id, quantity: l.qty });
}
const methods = await app.pay.methods(); // ["offline", "card"]
await app.pay.checkout("order", String(order.id), "card");
// back from Stripe: confirm asks Stripe, not the URL
const back = app.pay.returned();
if (back?.status === "success") await app.pay.confirm("order", back.orderId);
Payments that can't be tampered with
Stripe Checkout
Card payments go through Stripe's hosted page, to your own Stripe account, once you connect it.
Amounts from the catalogue
The server totals quantity times the item's catalogue price over the order's lines. A component never sends an amount.
Confirmed with Stripe
A return to your page is not proof of payment; app.pay.confirm asks Stripe before anything is marked paid.
Offline always works
If Stripe can't be reached, the order stands as an offline payment with a note for the shopper.
Subscriptions and refunds
Write a server function that calls the Stripe API with your key, or use the Stripe integration's actions for subscriptions, refunds and payment links.
Razorpay and others
The Razorpay integration creates payment links, and provider callbacks arrive as signed webhooks that a workflow records.
Atelier records payments from a signed callback
In Atelier a buyer places an order, a server rule checks stock, and a signed payment-callback webhook records the provider's answer. Workflows then mark the order paid, tell the maker and track it through to shipped.

Works with
Payments: common questions
Do payments go to my own Stripe account?
Yes. You connect your own Stripe account in the builder's integrations, and checkout sends shoppers to Stripe Checkout for that account.
Can a shopper change the price in the browser?
No. A component never sends an amount. The server totals quantity times the item's catalogue price over the order's lines.
What if Stripe is down?
The order is never lost. If Stripe can't be reached, it stands as an offline payment with a note to show the shopper.
Can I take subscriptions or issue refunds?
Yes, from a server function that calls the Stripe API with your secret key, or with the Stripe integration's actions such as create_subscription and create_refund.