Receber por cartão
Há dois caminhos:
| Caminho | Para quem | O número do cartão passa pelo seu servidor? |
|---|---|---|
Página hospedada (POST /charges com method: card) | Todo mundo | Não. Você manda o portador para a nossa página |
Cartão cru (POST /charges/card-raw) | Só conta liberada para isso, com certificação PCI | Sim, e o escopo de PCI passa a ser seu |
Comece pela página hospedada. É ela que mantém o seu escopo de PCI onde ele está.
Página hospedada
Criar
POST /integration/v1/charges
{
"amount": "250.00",
"method": "card",
"description": "Pedido 1234",
"returnUrl": "https://loja.exemplo.com.br/pedido/1234/obrigado",
"callbackUrl": "https://loja.exemplo.com.br/webhooks/pedido/1234"
}
Campos opcionais que mudam a cobrança:
| Campo | O que faz |
|---|---|
currency | BRL (padrão), ou USD e EUR no cartão internacional, quando a sua conta tem essas moedas ligadas. Fora disso, 422 card_currency_not_supported com a lista aceita em error.details.supported |
wallet | apple_pay ou google_pay: a página mostra só essa carteira, sem campo de cartão. Nome, e-mail, telefone e país vêm da carteira e completam os payer* que você não mandou. Uma recusa encerra a cobrança (declined) |
payerCountry | O país de quem paga (ISO 3166-1, duas letras). Muda o valor cobrado quando há preço por país; sem ele, vale a regra geral |
returnUrl é obrigatória no cartão. É para onde o portador volta quando
termina. Sem ela, ele fica preso na página de pagamento com uma compra concluída
e nenhum caminho de volta — e o seu suporte recebe a ligação.
A resposta traz checkoutUrl
Redirecione o portador para lá. A página hospedada trata os dados do cartão e o 3-D Secure.
{
"id": "c41a8f02-5d6e-4b7a-8c9d-0e1f2a3b4c5d",
"method": "card",
"status": "pending",
"amount": "250.00",
"netAmount": "238.75",
"currency": "BRL",
"wallet": null,
"checkoutUrl": "https://minhaloja-pay.connectionpay.io/a1b2c3d4..."
}
Quando a compra é aprovada, a resposta de consulta passa a trazer cardBrand,
cardLast4 e installments.
returnUrl não é confirmação
O portador voltando para a sua returnUrl significa que ele terminou o
fluxo, não que o pagamento foi aprovado. Ele pode ter voltado de uma recusa,
ou ter fechado e reaberto a aba.
Confirme pelo webhook. Liberar mercadoria no retorno do navegador é o erro mais comum — e o mais caro — de integração de cartão.
Cartão cru
Só para conta liberada para cartão cru. Peça a liberação ao suporte; sem ela, a rota recusa. Com ela, o número e o CVV passam pelo seu servidor e o escopo de PCI é seu: guarde só o que a norma permite (nunca o CVV).
POST /integration/v1/charges/card-raw
Idempotency-Key: 7b6f0c8e-pedido-1234
{
"amount": "120.00",
"currency": "USD",
"card": { "number": "4242424242424242", "expMonth": 12, "expYear": 2028, "cvc": "123" },
"payerName": "Jane Doe",
"payerEmail": "jane@example.com",
"returnUrl": "https://loja.exemplo.com.br/pedido/1234/obrigado",
"callbackUrl": "https://loja.exemplo.com.br/webhooks/pedido/1234"
}
O cartão cru é do cartão internacional: currency é USD ou EUR. O número e o CVV vão ao processador do
cartão e não ficam guardados aqui. A resposta é a cobrança com cardStatus:
cardStatus | O que fazer |
|---|---|
ok | Nada: a captura chega pelo webhook charge.paid |
requires_action | O banco pediu 3-D Secure. Conclua no navegador do comprador com action.clientSecret e action.publishableKey, e depois chame POST /integration/v1/charges/{id}/card-raw/confirm |
processing | Espere o webhook |
Recusa: 422, com o código do emissor em error.code e em error.details.declineCode. Aceita Idempotency-Key:
repita a mesma chave numa nova tentativa do mesmo pedido.
Dois eventos, dois fatos diferentes
| Evento | O que aconteceu |
|---|---|
charge.paid | a compra foi aprovada — pode liberar a mercadoria |
charge.settled | o dinheiro ficou disponível no saldo |
Entre um e outro passam cerca de 30 dias. Para você são dois fatos distintos: um libera a entrega, o outro entra no caixa.
Até charge.settled, o valor aparece em receivable no
saldo, não em available.
Reserva
Uma parcela do líquido fica retida como reserva contra contestação, e é liberada
depois do prazo. Os campos reserveAmount e reserveReleaseDate na cobrança
dizem quanto e até quando.
Ela aparece em reserve no saldo. Não é uma taxa: é o seu dinheiro, com data
para ficar disponível.
Devolver
POST /integration/v1/charges/{id}/refund
{ "amount": "50.00" }
Sem amount, devolve o que resta. Devoluções parciais somam: o que ainda pode
ser devolvido é amount − refundedAmount.
Contestação
Uma contestação chega como charge.refunded com
data.refund.isChargeback: true.
Ela pode deixar o seu saldo negativo se não houver lastro. Recusá-la não faria a contestação deixar de existir — ela já aconteceu do lado do emissor.