Checkout pela API
Monte o catálogo, o carrinho e o pagamento no seu próprio site, usando os gateways conectados na loja.
Com a API, o seu site mostra os produtos, monta o carrinho e fecha o pedido. A cobrança é criada no gateway conectado na loja e o dinheiro cai direto na sua conta do gateway, como numa venda pela vitrine. Entregas, e-mails ao cliente, cupons, ofertas e proteção de checkout funcionam do mesmo jeito.
Antes de começar
- Um gateway conectado e uma rota para cada forma de pagamento que você vai oferecer, em Configurações › Gateways. Teste primeiro com o gateway em Sandbox. Veja Gateways de pagamento.
- Pelo menos um produto publicado, com preço ativo.
- Uma chave publicável com os escopos Catálogo (leitura) e Carrinho e checkout (
catalog:readecheckout:write). Veja Autenticação.
Chame direto do navegador
Todas as rotas deste guia aceitam a chave publicável. Chamá-las do navegador do comprador é o caminho recomendado: os limites de carrinho e checkout contam por IP, e chamadas que saem todas do seu servidor dividem o mesmo limite. Veja Limites de requisição.
Visão geral do fluxo
GET /productseGET /products/{productId}: mostre os produtos e escolha o preço (priceId).POST /carts: crie o carrinho e guarde oide otoken./carts/{cartId}/**: adicione itens, altere quantidades e aplique cupom, sempre com o cabeçalhoX-Cart-Token.GET /payment-methods: mostre as formas de pagamento que a loja aceita e carregue o SDK do gateway de cartão com a configuração pública.POST /checkout: feche o pedido com os dados do comprador e a forma de pagamento.- Mostre o Pix ou o boleto, conclua a etapa do cartão quando o gateway pedir e leve o comprador à página do pedido. Se o pagamento for recusado, pague de novo com
POST /orders/{orderId}/payments. - No seu servidor, receba
order.paidpor webhook para saber que o pagamento foi confirmado.
Liste os produtos
GET /products traz os produtos publicados, na ordem do catálogo, com o preço em destaque de cada um. Filtre por categoria com category=<slug> e busque pelo nome com q.
const MINEPAG_PUBLISHABLE_KEY = 'pk_…';
const api = (path, init = {}) =>
fetch(`https://minepag.com.br/api/v1${path}`, {
...init,
headers: { Authorization: `Bearer ${MINEPAG_PUBLISHABLE_KEY}`, 'Content-Type': 'application/json', ...init.headers }
}).then(async response => {
const body = await response.json();
if (!response.ok) throw Object.assign(new Error(body.error.message), { code: body.error.code, issues: body.error.issues });
return body;
});
const { data: products } = await api('/products?limit=24');Para a página do produto, use GET /products/{productId} com o id ou o endereço (slug) do produto. A resposta traz todos os preços em prices (com variações em attributes e estoque em stock), as imagens, os benefícios e os campos de checkout em checkoutFields.
Cada preço tem um id: é ele que vai para o carrinho. amount já vem com o desconto de promoções ativas; compareAtAmount é o valor "de" para riscar. Preços do tipo "pague quanto quiser" trazem minimumAmount.
Crie o carrinho
const { data: cart } = await api('/carts', {
method: 'POST',
body: JSON.stringify({
items: [{ priceId: '0193b7c2-3c11-7a44-9b1e-0f1a2b3c4d05', quantity: 1 }],
context: { utmSource: 'instagram', utmCampaign: 'lancamento' }
})
});
localStorage.setItem('minepag:cart', JSON.stringify({ id: cart.id, token: cart.token }));A resposta (201) traz o carrinho completo e o token, que também vem no cabeçalho X-Cart-Token. Guarde os dois: o id sozinho não dá acesso ao carrinho. O token vale por 30 dias, o mesmo prazo do carrinho (expiresAt).
Campos úteis do carrinho:
| Campo | O que mostrar |
|---|---|
items[] | Produto, preço, quantidade, unitAmount, descontos e total de cada linha. available: false indica item que não pode mais ser comprado. |
totals | subtotal, offerDiscountTotal, couponDiscount, discountTotal e total, em centavos. |
coupon | Código, se foi aplicado (applied) e message quando deixou de valer. |
issues | Pendências por linha (ex.: produto indisponível). Resolva antes do checkout. |
suggestedOffers | Ofertas que o lojista configurou para o carrinho (versão superior, venda complementar, combo). |
Altere o carrinho
Todas as rotas de carrinho exigem o cabeçalho X-Cart-Token. Sem ele, ou com o token de outro carrinho, a resposta é 404 cart_not_found. Cada rota devolve o carrinho atualizado.
| Ação | Rota |
|---|---|
| Consultar | GET /carts/{cartId} |
| Adicionar item (soma a quantidade se o preço já estiver no carrinho) | POST /carts/{cartId}/items |
| Alterar quantidade, valor livre ou campos de checkout | PATCH /carts/{cartId}/items/{itemId} |
| Remover item | DELETE /carts/{cartId}/items/{itemId} |
| Aplicar cupom | PUT /carts/{cartId}/coupon |
| Remover cupom | DELETE /carts/{cartId}/coupon |
| Informar o e-mail (para a recuperação de carrinho abandonado) | PATCH /carts/{cartId} |
const saved = JSON.parse(localStorage.getItem('minepag:cart'));
const { data: updated } = await api(`/carts/${saved.id}/coupon`, {
method: 'PUT',
headers: { 'X-Cart-Token': saved.token },
body: JSON.stringify({ code: 'BEMVINDO10' })
});Se o produto tem campos de checkout (ex.: nick do Minecraft), envie os valores em checkoutFields ao adicionar ou alterar o item, no formato { "nick": "Steve" }. Os campos obrigatórios que faltarem impedem o checkout com 422 validation_failed, e error.issues aponta o item em checkoutFields.<itemId>.
Feche o pedido
POST /checkout recebe o carrinho, os dados do comprador e a forma de pagamento. Envie o X-Cart-Token e um Idempotency-Key gerado uma vez por tentativa de compra.
const idempotencyKey = crypto.randomUUID();
// Guarde o momento em que o comprador marcou "Li e aceito os termos" no seu checkout.
const termsAcceptedAt = new Date();
const { data: checkout } = await api('/checkout', {
method: 'POST',
headers: { 'X-Cart-Token': saved.token, 'Idempotency-Key': idempotencyKey },
body: JSON.stringify({
cartId: saved.id,
buyer: { name: 'Steve Silva', email: 'steve@exemplo.com.br', phone: '11987654321', document: '12345678909' },
payment: { method: 'pix' },
marketingConsent: false,
termsAcceptedAt: termsAcceptedAt.toISOString()
})
});| Campo | Regra |
|---|---|
buyer.name | De 2 a 120 caracteres. |
buyer.email | E-mail válido. É por ele que o cliente é identificado na loja. |
buyer.phone | Opcional. Com DDD, de 10 a 15 dígitos. |
buyer.document | CPF ou CNPJ. Obrigatório no boleto e no cartão. |
payment.method | pix, boleto ou card. |
payment.environment | production (padrão) ou sandbox. Sandbox só com a chave secreta, para testar. |
payment.installments | De 1 a 12. Padrão: 1. |
payment.billingAddress | Obrigatório no boleto: street, number, complement (opcional), district, city, state (UF, ex.: SP), postalCode (8 dígitos) e country: "BR". |
payment.card | Obrigatório no cartão: o token gerado pelo gateway. Veja Pagamento com cartão. |
marketingConsent | Se o comprador aceitou receber comunicações da loja. Padrão: false. |
termsAcceptedAt | Obrigatório. Data e hora ISO 8601 em que o comprador aceitou os termos da loja, nas últimas 24 horas. |
buyer.ip e buyer.userAgent | Só com a chave secreta, quando o pedido sai do seu servidor: IP e navegador do comprador. Com a chave publicável, a MinePag usa os da própria requisição. |
checkoutFields | Opcional. Valores dos campos de checkout por item: { "<itemId>": { "<chave>": "<valor>" } }. |
Mostre no seu checkout os links dos termos de uso e da política de privacidade cadastrados em Site › Vitrine, e a política de reembolso. O pedido guarda o momento do aceite, os links e a versão da política vigentes, e o IP e o navegador do comprador. Tudo isso entra no dossiê de contestação: sem eles, a defesa de um chargeback perde força.
A resposta (201) traz:
{
"data": {
"order": {
"id": "0193b7c2-5b2f-7d66-8e30-2b3c4d5e6f02",
"number": 1042,
"status": "pending",
"paymentStatus": "pending",
"subtotal": 4990,
"discountTotal": 0,
"total": 4990,
"expiresAt": "2026-10-12T15:15:00.000Z"
},
"payment": {
"id": "0193b7c2-5b40-7d66-8e30-2b3c4d5e6f07",
"method": "pix",
"status": "pending",
"amount": 4990,
"instructions": {
"method": "pix",
"copyPaste": "00020101021226…6304ABCD",
"qrCodeImage": "data:image/png;base64,iVBORw0KGgo…",
"expiresAt": "2026-10-12T15:00:00.000Z"
},
"clientAction": { "kind": "none" },
"failureReason": null
},
"accessToken": "eyJvcmRlcklkIjoi…",
"orderUrl": "https://minha-loja.minepag.app/order/0193b7c2-5b2f-7d66-8e30-2b3c4d5e6f02?token=…"
},
"meta": { "requestId": "5b0f7a52-0d8e-4b51-9a57-1f0c6b7e2d10" }
}O preço é sempre recalculado no servidor: o total do pedido pode ser diferente do que o seu site mostrou se uma promoção acabou ou o estoque mudou. Mostre order.total na confirmação.
Pedidos de valor zero são confirmados na hora: payment vem null e o pedido já sai pago.
Mostre o pagamento
Siga payment.instructions e payment.clientAction:
| Forma | O que fazer |
|---|---|
| Pix | Mostre instructions.copyPaste com um botão de copiar e o QR Code. qrCodeImage pode ser uma imagem em data:, um endereço https:// ou null; quando vier null, gere o QR Code a partir de copyPaste. O Pix vence em 30 minutos (expiresAt). |
| Boleto | Mostre instructions.digitableLine (linha digitável), o link instructions.pdfUrl e instructions.dueDate. Alguns gateways também devolvem pixCopyPaste para pagar o boleto por Pix. O boleto vence em 3 dias. |
| Cartão | Veja clientAction abaixo. Com payment.status igual a confirmed, o pagamento já foi aprovado. |
clientAction.kind diz se falta alguma etapa no navegador:
none: nada a fazer.stripe_confirm: a Stripe pediu autenticação do cartão (3D Secure). Chamestripe.handleNextAction({ clientSecret })com oclientAction.clientSecretrecebido.redirect: leve o comprador paraclientAction.url.
Acompanhe a confirmação
O jeito mais simples é levar o comprador para orderUrl: a página do pedido na vitrine (no domínio próprio da loja, quando houver) mostra o Pix ou o boleto, atualiza sozinha quando o pagamento é confirmado, lista as entregas e deixa o comprador tentar outra forma de pagamento se o cartão for recusado. O link vale por 90 dias.
Se você quer manter o comprador no seu site, confirme o pagamento no seu servidor:
- Receba o webhook
order.paid(recomendado). Veja Webhooks. - Ou consulte
GET /orders/{orderId}com uma chave secreta, a partir do seu servidor, atépaymentStatusvirarpaid.
Pagamento com cartão
O número do cartão nunca passa pela MinePag nem pelo seu servidor. O formulário do cartão roda no navegador com o SDK do gateway, que devolve um token. Esse token vai em payment.card, com o provider do gateway que atende cartão na loja (veja em Configurações › Gateways). Token de outro gateway é recusado com 422 validation_failed em payment.card.
As chaves públicas do gateway (Public Key, chave publicável, identificador de conta) ficam na sua conta do gateway. Use as do mesmo ambiente (Sandbox ou Produção) da conexão na MinePag.
Monte o Payment Element com a sua chave publicável da Stripe e, no envio, gere um token de confirmação com stripe.createConfirmationToken({ elements }).
{ "provider": "stripe", "confirmationTokenId": "ctoken_1Q…" }Se a resposta trouxer clientAction.kind: "stripe_confirm", conclua a autenticação com stripe.handleNextAction({ clientSecret }).
Erros comuns no checkout
| Código | O que aconteceu | O que fazer |
|---|---|---|
cart_not_found | Carrinho expirado, ou X-Cart-Token ausente ou de outro carrinho. | Crie outro carrinho. |
cart_empty | O carrinho não tem itens. | Volte para o catálogo. |
cart_already_checked_out | Este carrinho já virou um pedido com outra Idempotency-Key. | Consulte o pedido gerado ou crie outro carrinho. |
validation_failed | Dados inválidos. error.issues aponta cada campo (ex.: buyer.document). | Mostre a mensagem ao lado do campo. |
product_unavailable, out_of_stock | Um item saiu de venda ou ficou sem estoque. | Atualize o carrinho e mostre issues. |
coupon_invalid, coupon_not_applicable | O cupom deixou de valer. | Remova o cupom e mostre o novo total. |
payment_method_unavailable | A loja não tem gateway para essa forma de pagamento. | Ofereça só as formas de GET /payment-methods. |
payment_declined (402) | O gateway recusou o pagamento. O pedido foi criado e continua aguardando pagamento. | Pague de novo com outra forma (veja abaixo). |
payment_provider_unavailable (502) | O gateway não respondeu. O pedido foi criado sem cobrança: error.details traz order, payment, accessToken e orderUrl. | Repita com a mesma Idempotency-Key e o mesmo corpo: a MinePag devolve o pedido e cria a cobrança que faltou (veja abaixo). |
checkout_blocked | A proteção de checkout da loja barrou a compra. | Mostre a mensagem. Não repita automaticamente. |
checkout_paused | O checkout da loja está pausado por pendência de faturamento. | Mostre a mensagem e tente mais tarde. |
rate_limited | Muitas tentativas do mesmo IP. | Espere o tempo de Retry-After. |
Pagamento recusado e nova tentativa
Quando o gateway recusa o pagamento, POST /checkout responde 402 payment_declined. O pedido já existe e continua aguardando pagamento: error.details traz o mesmo objeto da resposta de sucesso, com order, payment (status: "failed" e o motivo em failureReason), accessToken e orderUrl.
Para pagar de novo, peça outro cartão ou outra forma de pagamento e chame POST /orders/{orderId}/payments com o accessToken no cabeçalho X-Order-Token:
curl -X POST https://minepag.com.br/api/v1/orders/$ORDER_ID/payments \
-H "Authorization: Bearer $MINEPAG_PUBLISHABLE_KEY" \
-H "X-Order-Token: $ACCESS_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "buyer": { "name": "Ana Souza", "email": "ana@example.com" }, "payment": { "method": "pix" } }'A resposta tem o mesmo formato do checkout. Cada tentativa é um novo pagamento do mesmo pedido; outra recusa responde 402 de novo. A rota responde 409 conflict se ainda houver um pagamento em andamento e 409 order_not_payable se o pedido já foi pago, cancelado ou expirou. Se preferir, leve o comprador para orderUrl: a página do pedido também oferece a nova tentativa.
Gateway fora do ar e nova tentativa
Quando o gateway não responde, POST /checkout responde 502 payment_provider_unavailable. O carrinho já virou pedido, mas a cobrança não foi criada: error.details traz order, payment (status: "pending" e instructions: null), accessToken e orderUrl.
{
"data": null,
"error": {
"code": "payment_provider_unavailable",
"message": "O gateway de pagamento não respondeu. O pedido #1042 foi criado e continua aguardando pagamento: abra o pedido para tentar de novo.",
"details": {
"order": { "id": "0193b7c2-5b2f-7d66-8e30-2b3c4d5e6f02", "number": 1042, "status": "pending", "paymentStatus": "pending", "total": 4990 },
"payment": { "id": "0193b7c2-5b40-7d66-8e30-2b3c4d5e6f07", "method": "pix", "status": "pending", "instructions": null },
"accessToken": "eyJvcmRlcklkIjoi…",
"orderUrl": "https://minha-loja.minepag.app/order/0193b7c2-5b2f-7d66-8e30-2b3c4d5e6f02?token=…"
},
"requestId": "5b0f7a52-0d8e-4b51-9a57-1f0c6b7e2d10"
}
}Para tentar de novo, escolha um caminho:
- Mesma forma de pagamento: repita
POST /checkoutcom a mesmaIdempotency-Keye o mesmo corpo, com espera crescente (ex.: 1 s, 2 s, 4 s). A resposta de erro 5xx não fica guardada, então a repetição executa de verdade: a MinePag encontra o pedido já criado e cria a cobrança que faltou. A resposta é200com o pedido e as instruções de pagamento. A cobrança vai ao gateway com a mesma chave de idempotência, sem risco de cobrar duas vezes. - Outra forma de pagamento, ou mais tarde: chame
POST /orders/{orderId}/paymentscom oaccessTokenno cabeçalhoX-Order-Token, como na recusa acima. Com a mesma forma de pagamento, a cobrança é criada no pagamento que ficou pendente; com outra, esse pagamento é cancelado e uma nova tentativa começa. Também dá para levar o comprador paraorderUrl.
Não monte outro carrinho nem use outra Idempotency-Key no mesmo carrinho: a resposta seria 409 cart_already_checked_out. Se o gateway continuar fora do ar, as duas rotas respondem 502 de novo, com os mesmos details.
A lista completa está em Erros da API.
Testar
- Conecte o gateway em Sandbox. As formas que ele aceita entram na coluna Equipe testando · Sandbox da seção Checkout, sem mexer nas rotas de Produção.
- Faça uma compra completa com os dados de teste do gateway, usando a chave secreta no seu servidor e
payment.environment: "sandbox". Com a chave publicável, as formas de pagamento em Sandbox não aparecem emGET /payment-methodse o checkout em Sandbox respondepayment_method_unavailable, para nenhum cliente cair num gateway de teste. - Confira o pedido em Vendas › Pedidos e as entregas em Entregas.
- Pedidos em Sandbox não contam na sua fatura da MinePag.