# Checkout and orders

> Quote, place the order, take the payment with Stripe, and follow the order until the seller is paid.

One order buys one listing. Checkout is three calls and Stripe's payment form; after that, the order moves on by transitions until the buyer has it and the seller is paid. Every call sends the buyer's session.

## 1. Quote

Show what the buyer will pay before they pay it:

```http
POST /v1/me/orders/quote
{
  "listing_id": "8f3c…",
  "currency": "EUR",
  "delivery": {
    "method": "shipping",
    "option": "standard",
    "address": { "name": "Mia Holm", "line1": "Hallvägen 29", "postal_code": "121 62", "city": "Johanneshov", "country": "SE", "phone": "+46…" }
  },
  "voucher_code": "SPRING10"
}
```

```json
{
  "currency": "EUR",
  "total": 61900,
  "line_items": [
    { "code": "item", "label": "Eames lounge chair", "unit_price": 59000, "quantity": 1, "amount": 59000 },
    { "code": "shipping", "label": "Shipping", "unit_price": 8800, "quantity": 1, "amount": 8800 },
    { "code": "voucher", "label": "Voucher SPRING10", "unit_price": -5900, "quantity": 1, "amount": -5900 }
  ]
}
```

- Show `line_items` as they come, by `label`, and `total` as the amount to pay. Don't add things up yourself.
- Quote again whenever the delivery, address or voucher changes. Placing the order prices it the same way.
- `currency` is what the buyer pays in: one of the marketplace's, the listing's when left out.

Line item codes:

| Code | Meaning |
|---|---|
| `item` | The listing. |
| `shipping` | Delivery, priced. |
| `collect` | Collected at the showroom or warehouse where it's stored. Free. |
| `shipping_address_needed` | Ask for the address; the price comes with it. Placing without one fails. |
| `shipping_quote` | Delivery can't be priced here: the marketplace sends a quote after the order. |
| `voucher` | A discount (negative). |

A voucher that doesn't apply gives `422` with the reason on `body.voucher_code`. Show it next to the voucher field.

## 2. Place the order

```http
POST /v1/me/orders
Idempotency-Key: 6f1d3c5e-…
{ the same body as the quote }
```

```json
{
  "order": { "id": "1a2b…", "state": "pending_payment", "total": 61900, "currency": "EUR", "available_transitions": [] },
  "payment": { "client_secret": "pi_3Q…_secret_…" }
}
```

- This reserves the listing and starts the payment. The buyer has **15 minutes** to pay; then the order becomes `payment_expired` and the listing is released.
- Send an `Idempotency-Key` (a new UUID for each press of "Pay"), so a retried request can't place two orders.
- `409` means it can't be bought right now: sold, reserved by someone else, or the delivery isn't possible. Show the `detail` and go back to the listing.

## 3. Take the payment with Stripe

Payments run on the marketplace's Stripe account. Use Stripe's [Payment Element](https://docs.stripe.com/payments/payment-element) with the marketplace's Stripe **publishable** key (the team gives it to you; it's public) and the order's `client_secret`. Cards, Klarna and the other methods the marketplace has turned on in Stripe appear by themselves.

```ts

const stripe = await loadStripe(STRIPE_PUBLISHABLE_KEY)
const elements = stripe.elements({ clientSecret: payment.client_secret })
elements.create('payment').mount('#payment')

// On "Pay":
const { error } = await stripe.confirmPayment({
  elements,
  confirmParams: { return_url: `${location.origin}/orders/${order.id}` },
})
if (error) showError(error.message) // otherwise Stripe sends the buyer to return_url
```

When Stripe confirms the payment, mongrl moves the order to `purchased`. That happens in the background, a moment after the buyer lands on your order page: read the order (`GET /v1/me/orders/{id}`) and, while it's still `pending_payment`, check again every couple of seconds.

In the test environment, pay with Stripe's test card `4242 4242 4242 4242`, any future date and any CVC.

## Order states

```text
(new) ─request-payment─▶ pending_payment ─confirm-payment─▶ purchased
                             └─expire-payment (15 min)─▶ payment_expired
purchased ─mark-delivered─▶ delivered ─mark-received / auto (14 days)─▶ received
purchased ─mark-received-early─▶ received
purchased ─cancel / auto-cancel (14 days)─▶ canceled
delivered ─dispute─▶ disputed ─resolve-received─▶ received
                              └─resolve-canceled / auto (60 days)─▶ canceled
```

`payment_expired`, `received` and `canceled` are final. Each order has `history`: every transition with when it happened, for a timeline on the order page.

## Moving an order on

Each order has `available_transitions`: what **this user** may do now. Show a button for each and nothing else.

```http
POST /v1/me/orders/{id}/transitions/mark-delivered
{ "data": { "tracking_number": "JJSE00012345", "carrier": "PostNord" } }
```

| Transition | Who | When |
|---|---|---|
| `mark-delivered` | the seller | It's sent or handed over. `data` can carry `tracking_number` and `carrier`; they appear in `order.delivery`. |
| `mark-received` | the buyer | They have it. The seller is paid. |
| `mark-received-early` | the buyer | They have it before it was marked sent (e.g. picked up). The seller is paid. |
| `dispute` | the buyer | Something's wrong after delivery. The marketplace's team resolves it. |

When the marketplace delivers a listing itself (`fulfilment: marketplace`, e.g. stored listings), its team marks the order sent in the console. The timers keep orders moving if nobody acts: an order that's never sent is canceled and refunded after 14 days; a delivered order counts as received after 14 days.

## Listing orders

- `GET /v1/me/orders` lists the buyer's purchases; `?as=seller` lists their sales. Filter with `state`; page with `limit` and `offset`.
- Each order has `line_items`, `total`, `delivery` and, for the seller, `payout`: `subtotal`, `commission`, `storage_fee`, `amount` and its `status` (`pending` until the buyer has it, then `paid`; `waiting_for_payout_details` until the seller sets up [payouts](https://dev.mongrl.com/guides/payouts.md)).

## Emails mongrl sends

Buyers and sellers get emails when an order is paid, sent and received, each linking to `https://<your shop>/<lang>/orders/<id>`. Build that page. See [Pages your shop needs](https://dev.mongrl.com/guides/storefront-pages.md).

## Checklist

- [ ] Checkout: delivery choice, address, voucher, live quote, "Pay" with the Payment Element.
- [ ] `Idempotency-Key` on `placeOrder`.
- [ ] The order page waits for `purchased` after Stripe returns, then shows the timeline and the buttons from `available_transitions`.
- [ ] A delivery-time picker when the order offers one. See [Shipping and delivery](https://dev.mongrl.com/guides/delivery.md#booking-a-delivery-time).
