Fluxo recomendado — checkout próprio
Sequência mínima para um parceiro ou cliente entregar checkout custom sem fork do core.
Passo 1
Catálogo e preço
GET /products e GET /prices/resolve com Bearer server-side.
API ExplorerPasso 2
Sessão checkout
POST /checkout/sessions → salvar session_token (httpOnly).
Ver passo a passoPasso 3
Place-order
PATCH estado → POST place-order com Idempotency-Key → redirect checkout_url.
Ver erros HTTP
Princípios
Checkout headless = regras no core, UX substituível. SSOT humano: docs/headless-checkout.md
| Regra | Detalhe |
|---|---|
| API-first | Validação, pedido e pagamento autoritativos no Go — não no React |
| UX substituível | app/(compra)/* é referência; destino apps/checkout no repo cliente |
| Sem PAN/CVV | Gateway tokenizado — redirect ou hosted checkout (PCI SAQ A) |
| Dois atores auth | Comprador (X-Checkout-Session-Token) ≠ integrador (Bearer mc_live_…) |
Dois caminhos
Preferir checkout sessions (A). Caminho B para bots, ERP ou MCP conversacional.
| Caminho A — sessions | Caminho B — granular | |
|---|---|---|
| Quando usar | Web, app, BFF custom | ERP, automação, MCP direto |
| Estado | Server-side JSON (TTL 30 min) | Monta OrderCreate no integrador |
| Endpoints | POST/GET/PATCH /checkout/sessions + place-order | POST /orders → submit → payments/initiate |
| SDK | createCheckoutSession, patchCheckoutSession, placeCheckoutOrder | createOrder, submitOrder, initiatePayment |
Autenticação
| Superfície | Header | Quem |
|---|---|---|
| Checkout sessions | X-Checkout-Session-Token (cks_tok_…) | Comprador — rotas públicas |
| Catálogo / preços | Authorization: Bearer mc_live_… | Backend integrador (server-side) |
| MCP | X-API-Key (kind: mcp) | Agente / bot |
| Conta pós-compra | Bearer mc_sess_… | POST /auth/customer/* |
Rate limits checkout (comprador) são distintos do tier integrador — ver checkout-anti-fraud.md.
1. Criar sessão
POST /v1/checkout/sessions
Content-Type: application/json
{
"member_id": "550e8400-e29b-41d4-a716-446655440000",
"warehouse_id": "660e8400-e29b-41d4-a716-446655440001",
"channel": "web",
"state": { "cart": [], "identity_verified": false }
}
→ 201: id, session_token (uma vez), expires_at2. Atualizar estado (PATCH merge)
PATCH /v1/checkout/sessions/{session_id}
X-Checkout-Session-Token: cks_tok_…
{
"state": {
"cart": [
{ "sku": "SKU-001", "qty": 2, "selected": true, "unit_price": 19990 }
],
"customer_name": "Maria Silva",
"customer_email": "[email protected]",
"identity_verified": true,
"payment_method_id": "pix",
"delivery_mode": "shipping"
}
}3. Place-order
POST /v1/checkout/sessions/{session_id}/place-order
X-Checkout-Session-Token: cks_tok_…
Idempotency-Key: 7c9e6679-7425-40de-944b-e07fc1f90ae7
→ 201: { order, session_id, payment_status?, checkout_url? }
→ Redireccionar comprador para checkout_url quando existirValidação server-side (place-order)
Implementação: internal/checkout/validate.go · state_to_order.go
| Campo / regra | Obrigatório | Notas |
|---|---|---|
| identity_verified | Sim (true) | Identidade confirmada antes de fechar |
| cart[].selected + qty > 0 | Sim | Pelo menos um item selecionado |
| placed_order_id | Vazio | Sessão single-use após pedido |
| member_id no state | Se presente = sessão | Anti cross-tenant |
| warehouse_id | Sim | Na sessão ou no state |
Estado da sessão (referência)
| Campo | Uso |
|---|---|
| cart | Itens com sku, qty, selected, unit_price |
| customer_* | Nome, email, whatsapp |
| identity_verified | Gate obrigatório para place-order |
| payment_method_id | pix | boleto | card | wallet |
| delivery_mode, address_id, shipping_speed | Entrega |
| schedule_slot_id, scheduled_for | Serviços agendados |
| coupon_code + coupon_applied | Desconto |
| channel | web | whatsapp → origin do pedido |
Tipo TS: lib/compra/types.ts (CompraSessionState). OpenAPI: additionalProperties — evolui com o core pin LTS.
Códigos HTTP — place-order
| Status | Significado | Ação UX |
|---|---|---|
| 201 | Pedido criado | Confirmar + redirect se checkout_url |
| 401 | Token inválido / expirado | Reiniciar checkout |
| 402 | Pagamento recusado | Mensagem genérica — nunca detalhe de cartão |
| 403 | checkout.validate bloqueou | Mostrar message do core |
| 409 | Incompleto ou sessão já usada | Revisar passos |
| 429 | Velocity | Respeitar Retry-After |
SDK TypeScript
import {
createCommerceClient,
createCheckoutSession,
patchCheckoutSession,
placeCheckoutOrder,
} from "@mira/commerce-client-sdk"
const client = createCommerceClient({
baseUrl: process.env.COMMERCE_API_URL,
getAccessToken: () => process.env.MC_API_TOKEN, // só server-side
})
const { data: session } = await createCheckoutSession({
client,
body: { member_id, warehouse_id, channel: "web" },
})
await placeCheckoutOrder({
client,
path: { session_id: session.id },
headers: {
"X-Checkout-Session-Token": sessionToken,
"Idempotency-Key": crypto.randomUUID(),
},
})Regenerar: npm run sdk:generate · Catálogo completo: /dev/experience/sdk · Ver sdk-wiring.md
Padrão BFF (browser)
| Rota Next.js (protótipo) | Core API |
|---|---|
| POST /api/checkout/session | POST /checkout/sessions |
| PATCH /api/checkout/session | PATCH /checkout/sessions/{id} |
| POST /api/compra/place-order | POST /checkout/sessions/{id}/place-order |
lib/compra/checkout-api.ts
Cliente BFF no browser
app/api/checkout/session/route.ts
Proxy sessão + cookie httpOnly
app/(compra)/*
Implementação referência UX
Repo cliente (Fase 4)
{cliente}-commerce/
├── apps/checkout/ ← UX custom (este guia)
├── apps/storefront/
├── manifest.yaml ← pin core LTS
└── extensions/ ← plugins Go (Mirá)Anti-patterns
| Evitar | Preferir |
|---|---|
| Regra de desconto só no front | GET /prices/resolve + PATCH sessão + hook |
| Bearer mc_live_ no browser | BFF server-side |
| Formulário PAN/CVV na app | checkout_url do gateway |
| Fork mira-commerce-core | apps/checkout + pin LTS |
| Retry place-order sem Idempotency-Key | UUID estável por tentativa UX |
Checklist integrador
- Ler OpenAPI tags checkout, catalog, pricing
- Escolher caminho A (sessions) ou B (orders direto)
- BFF se front browser; Bearer só server-side
- Idempotency-Key em place-order e initiate payment
- Mensagens genéricas em 402 (ADR 0013)
- Testar com mc_test_… antes de produção