# Checkout

URL: /systems/finance/checkout · Id: `capability:checkout`

> Checkout takes payment on a Startup’s own Domain through a hosted checkout page, payment links, a pricing table and an embedded payment form.

Checkout takes payment for an [Offer](/business/nouns/offer) on the [Startup](/business/nouns/startup)’s own [Domain](/business/nouns/domain), from a hosted page, a payment link, a pricing table or an embedded form. Each one places an [Order](/business/nouns/order) or starts a [Subscription](/business/nouns/subscription), and Stripe moves the money.

## Contract
[Offer.publish](/business/nouns/offer/publish) puts one Offer on all of them at once. Each shows the same [Price](/business/nouns/price), terms, tax and renewal date, under the Startup’s [Brand](/business/nouns/brand).

| Way to buy | Where it runs | Buyer’s call |
| --- | --- | --- |
| Hosted checkout | A page on the Startup’s Domain, behind every buy button | [Order.place](/business/nouns/order/place) or [`Offer.subscribe`](/business/nouns/offer/subscribe) |
| Payment link | One shareable address per Offer, for an email, a chat or an invoice reminder | `Order.place` or `Offer.subscribe` |
| Pricing table | The [`PriceTable`](/code/ui/components/price-table) component on any [Page](/business/nouns/page) of the [Site](/business/nouns/site) | `Order.place` or `Offer.subscribe` |
| Embedded form | Inside the Startup’s own [App](/business/nouns/app), for an upgrade without leaving it | `Offer.subscribe` |
| [Agent](/business/nouns/agent) buyers | A priced call that answers 402 with the Price, paid under a [Mandate](/business/nouns/mandate) | [Mandate.spend](/business/nouns/mandate/spend) |

Hosted checkout, payment links, the pricing table and the embedded form run the same four steps, so a buyer pays the same way everywhere.

| Step | What happens | Call |
| --- | --- | --- |
| 1. Place | The buyer sees the Price, terms, tax and any trial or renewal date before paying. | `Order.place` |
| 2. Screen | Stripe scores the payment, and the risk [Policy](/business/nouns/policy) allows, authenticates, holds or blocks it. | [Order.screen](/business/nouns/order/screen) |
| 3. Pay | The charge runs when the buyer confirms, on the Startup’s own Stripe account as seller of record. | [Order.pay](/business/nouns/order/pay) |
| 4. Grant | The Offer’s [Entitlements](/business/nouns/entitlement) turn on, and the buyer, now a [Customer](/business/nouns/customer), can sign in to the [Portal](/business/nouns/portal). | [Order.fulfill](/business/nouns/order/fulfill) |

| Rule | Detail |
| --- | --- |
| Payment details | Stripe’s embedded form takes card and bank details directly, so the Startup never holds a card number; [`PaymentSource.add`](/business/nouns/payment-source/add) saves one as a [Payment Source](/business/nouns/payment-source). |
| Tax | Stripe Tax computes tax at `Order.place` from `Customer.billingAddress` and `Customer.taxIds`, on the Startup’s own account; see [Tax calculation](/systems/finance/tax-calculation). |
| Promotion codes | `Order.place` and `Offer.subscribe` take a `promotionCode`, which applies its [Coupon](/business/nouns/coupon) when the Coupon covers the Offer. |
| Trials | [Offer.trial](/business/nouns/offer/trial) starts a free period of `days`, with or without a card up front. |
| Quantity | `Offer.subscribe` takes a `quantity`, such as 12 members, and bills the Price per unit. |
| Prepaid credit | An Offer with `Offer.credit` issues a paid [Credit Grant](/business/nouns/credit-grant) when `Order.pay` succeeds. |
| Terms | `Offer.terms`, refund rights included, show before payment and stay attached to the Order. |
| Test environment | In `test`, every way to buy takes Stripe’s test cards through `Order.pay`, and no money moves. |

## Verbs
| Verb | Object | Event |
| --- | --- | --- |
| [Offer.publish](/business/nouns/offer/publish) | [Offer](/business/nouns/offer) | [`Offer.published`](/business/events/Offer.published) |
| [Order.place](/business/nouns/order/place) | [Order](/business/nouns/order) | [`Order.placed`](/business/events/Order.placed) |
| [Order.pay](/business/nouns/order/pay) | [Order](/business/nouns/order) | [`Order.paid`](/business/events/Order.paid) |
| [Offer.subscribe](/business/nouns/offer/subscribe) | [Offer](/business/nouns/offer) | [`Offer.subscribed`](/business/events/Offer.subscribed) |
| [Offer.trial](/business/nouns/offer/trial) | [Offer](/business/nouns/offer) | [`Offer.trialed`](/business/events/Offer.trialed) |

## Interfaces
- [API](/code/api/reference)
- [SDK](/code/sdk/reference)
- [CLI](/code/cli/reference)
- [MCP](/code/mcp/reference)
- Webhooks

## Example
```ts
import { $ } from 'startup-builder'

// shiftnote.app sells a $99 setup pack once. `try` returns the surfaces publishing creates, and changes nothing.
await $.Offer.publish({ offer: 'offer_setup_pack' }, { mode: 'try' })
await $.Offer.publish({ offer: 'offer_setup_pack' })

// A clinic manager opens the payment link on shiftnote.app and types CLINIC20: $79.20 plus tax.
// Each paid Order turns on the pack's Entitlements.
$.on('Order.paid', async (event) => {
  await $.Order.fulfill({ order: event.data.order.$id })
})

// End of the day: every Order checkout took, largest first.
const orders = await $.Order.list({ where: "status = 'paid'", sort: '-total' })
```