Validar e fechar
Valida uma sessão de checkout sem criar nada e fecha o pedido, com chave de idempotência e os códigos de conflito do fechamento.
Duas chamadas fecham o funil. validate roda as conferências do fechamento (estoque, preço, cupom, pagamento) e responde se a sessão está em condições, sem criar nada: serve para avisar o cliente antes do clique final. place cria o pedido de verdade: reserva estoque, registra o pedido e inicia o pagamento conforme a forma escolhida.
Validar a sessão
/api/storefront/checkout/{sessionId}/validateClienteValidar a sessão sem fechar o pedido
Devolve valid: true, ou valid: false com a lista de problemas corrigíveis. É seguro repetir.
Parâmetros de caminho
sessionIduuidobrigatórioFechar o pedido
/api/storefront/checkout/{sessionId}/placeClienteFechar o pedido a partir da sessão
Transforma a sessão em pedido. A sessão precisa ter endereço, frete e pagamento. A resposta é o pedido criado. Se o carrinho tinha produtos de mais de um seller, vêm também os sub-pedidos, um por seller.
Mande sempre um Idempotency-Key (um UUID novo por tentativa de compra): se a conexão cair e você repetir o place com a mesma chave, recebe o mesmo pedido em vez de um duplicado.
Parâmetros de caminho
sessionIduuidobrigatórioCabeçalhos
Idempotency-KeystringCorpo da requisição
O corpo é opcional.
share_ref_codestringvenddor_ref.Exemplo de requisição
curl -X POST "$BASE_URL/api/storefront/checkout/$SESSION_ID/validate" \
-H "X-Session-ID: $VISITOR_ID"
curl -X POST "$BASE_URL/api/storefront/checkout/$SESSION_ID/place" \
-H "X-Session-ID: $VISITOR_ID" \
-H "Idempotency-Key: $(uuidgen)"Resposta
{ "valid": true }{
"valid": false,
"errors": [
{ "field": "cart", "code": "empty_cart", "message": "Cart is empty" }
]
}{
"order": {
"id": "18566b6e-7857-4f00-9e62-52f644c2e78e",
"order_number": "408316782",
"customer_id": "d4613dde-ac7a-431c-9232-50d8cd6ad5fc",
"status": "O",
"subtotal": 30000,
"shipping_total": 0,
"tax_total": 0,
"discount_total": 8625,
"total": 21375,
"currency": "BRL",
"payment_method": "pix"
},
"items": [],
"addresses": [],
"status_history": []
}Campos da resposta
validbooleantrue se a sessão passa nas conferências.errors[]arrayfield, code (estável, para o seu código) e message (texto livre, pode vir em inglês; não dependa dele). Os códigos estão na tabela abaixo.orderobjectid, order_number, status (letra do cadastro de status da loja), subtotal, shipping_total, tax_total, discount_total e total em centavos, currency e payment_method.itemsarrayaddressesarraystatus_historyarraysub_ordersarrayCódigos de validação
code | O que aconteceu |
|---|---|
empty_cart | O carrinho está vazio. |
price_changed | O preço de um item mudou desde que a sessão foi montada. No place, vira HTTP 409. |
insufficient_stock | Um item não tem saldo suficiente. |
product_not_found, product_inactive, product_not_available_for_user | O produto não existe, foi desativado ou não é visível para este cliente. |
zero_price_purchase_disabled, zero_price_quote_required | Produto sem preço que a loja não vende direto (exige cotação). |
qty_below_minimum, qty_above_maximum, qty_not_multiple_of_step | Quantidade fora da regra do produto (mínimo, máximo ou múltiplo). |
shipping_inactive, shipping_not_found | A forma de entrega escolhida não está mais disponível. |
payment_not_eligible, payment_eligibility_unavailable | A forma de pagamento não vale para este pedido. |
min_order_not_met, vendor_min_order_not_met, vendor_minimum_unavailable | Pedido abaixo do mínimo da loja ou de um seller. |
coupon_invalid, coupon_validation_error | O cupom deixou de ser válido. |
Erros
| Status | Quando | Corpo |
|---|---|---|
| 200 | validate com problemas | {"valid": false, "errors": [...]} (não é erro HTTP) |
| 400 | place recusado por validação | {"error": "checkout_validation_failed", "message": "...", "errors": [{"field", "code", "message"}]} |
| 400 | place sem endereço, frete ou pagamento | {"error": "shipping address is required"} e similares |
| 403 | Chave sfk_ ou token vat_ sem o escopo | {"error": "missing_scope", "required": "..."} |
| 403 | Compra de visitante desligada depois que a sessão começou | {"error": "guest checkout is not enabled"} |
| 404 | Sessão inexistente ou de outra pessoa | {"error": "checkout session not found"} |
| 409 | O preço mudou desde que a sessão foi montada (errors traz price_changed) | {"error": "checkout_validation_failed", ...}: refaça o resumo e peça confirmação |
| 409 | Outra tentativa com a mesma chave ainda está em andamento | {"error": "in_progress", "retry_after_ms": 1500} |
| 409 | O carrinho já virou pedido | {"error": "cart_already_ordered", "message": "..."} |
| 409 | Pedido sendo processado | {"error": "order_in_progress", "message": "...", "retry_after_ms": 1500} |
| 409 | Pedido idêntico feito há poucos minutos | {"error": "duplicate_order", "message": "...", "minutes_remaining": 3, "order_number": "..."} (order_number só quando existe) |
| 409 | O frete precisa ser cotado de novo antes de fechar | {"error": "shipping_requote_required", "message": "..."}, às vezes com reason: refaça o passo de frete |
| 409 | Seller restringe compras por IP | {"error": "vendor_ip_restricted", "message": "...", "vendor_name": "..."} |
| 409 | Moeda estrangeira sem gateway internacional | {"error": "no_international_gateway", "currency": "...", "fallback": "BRL"} |
| 412 | Verificação de e-mail (OTP) exigida | {"error": "otp_required", "scope": "checkout"} |
| 412 | Limite de crédito B2B excedido | {"error": "credit_limit_exceeded", "limit": N, "available": N} |
| 412 | Cliente B2B inadimplente | {"error": "finance_delinquent"} |
| 422 | Idempotency-Key já usada com outra sessão | {"error": "idempotency key reused with a different checkout session"} |