- Início
- API /v1
- Conceitos da API
Conceitos da API
Autenticação, idempotência, Turnstile, formato de erro, paginação, valores e limites da API /v1.
A API /v1 é a mesma em homologação e produção, cada uma no seu endereço (veja Ambientes e chaves). As requisições e as respostas são JSON.
Autenticação
Toda requisição leva a chave no cabeçalho Authorization:
curl https://paystation.quero.space/v1/me -H "Authorization: Bearer sk_live_…"- A chave pública (
pk_…) pode ir para o navegador e alcança só o que o checkout precisa. - A chave secreta (
sk_…) fica no seu servidor e alcança todas as rotas. - Cada rota da referência diz qual chave aceita.
Valores em centavos
Todo valor de dinheiro entra e sai em centavos inteiros: R$ 99,90 é 9990.
Idempotência
POST /v1/charges exige o cabeçalho Idempotency-Key, um UUID v4 gerado por você para cada tentativa de compra. Se a resposta se perder, repita a requisição com a mesma chave e a mesma compra: a compra não é cobrada duas vezes. Só payment.cards pode mudar entre um envio e outro, para levar tokens de cartão ainda válidos.
curl -X POST https://paystation.quero.space/v1/charges \
-H "Authorization: Bearer sk_live_…" \
-H "Idempotency-Key: 6b1f6d7e-3c2a-4e0b-9f4d-8a5c2e7b1d90" \
-H "Content-Type: application/json" \
-d '{ "offer_id": "…", "payment": { "method": "pix" }, "customer": { … } }'- A partir do momento em que a cobrança começa, a resposta fica guardada na chave,
402 card_declinede422 payment_rejectedinclusive, e a repetição a devolve comIdempotent-Replayed: true. Por isso, outro cartão depois de uma recusa é outra tentativa, com chave nova. A exceção é o402 acquirer_disabled, que libera a chave: no primeiro envio ele quer dizer que nada foi cobrado; num reenvio, não diz nada sobre o envio anterior. - Um
5xx,503 api_unavailableinclusive, é sempre resultado desconhecido: a cobrança pode ter sido feita. Repita a mesma chave. - Uma recusa antes de a cobrança começar (validação do corpo, Turnstile, oferta inexistente, limite de requisições) não guarda nada: a mesma chave pode ser enviada de novo, mas só dentro do limite de reenvio da regra de tentativa. Depois dele, uma chave livre ainda cobraria.
- Enquanto a primeira requisição está rodando, a repetição recebe
409 idempotency_key_in_use. Se ela terminou sem resposta guardada, a repetição devolve o estado atual da cobrança, semIdempotent-Replayed.
| Situação | Resposta |
|---|---|
| Sem a chave, ou fora do formato UUID v4 | 422 missing_idempotency_key ou invalid_idempotency_key |
| A mesma chave ainda em processamento | 409 idempotency_key_in_use: repita mais tarde |
A mesma chave com outro corpo: algo além de payment.cards mudou, inclusive a ordem das chaves do JSON | 409 idempotency_key_reuse: reenvie o corpo guardado; sem ele, procure a cobrança. Nunca troque a chave |
| Uma resposta guardada há mais de 24 horas | 409 idempotency_key_expired: a tentativa pode ter cobrado |
| Uma tentativa que parou no meio e não tem resposta a dar | 409 idempotency_key_unresolved: a tentativa pode ter cobrado |
Nos dois últimos casos, não abra uma tentativa nova antes de procurar a cobrança pela chave. A regra completa, com a busca, está em Checkout próprio.
Turnstile
POST /v1/charges pode pedir o token do Cloudflare Turnstile resolvido pelo comprador, no cabeçalho cf-turnstile-token:
- numa cobrança de cartão com a chave pública, sempre;
- em qualquer cobrança, com qualquer chave, quando o cliente ativou o Turnstile.
Sem o token, a resposta é 403 turnstile_required; com um token recusado, 403 turnstile_invalid; com um token já usado vezes demais, 403 turnstile_budget_exhausted. Nos três casos, o comprador resolve o desafio de novo. O checkout embutido cuida disso sozinho.
Erros
Todo erro vem no mesmo envelope:
{
"error": {
"type": "invalid_request_error",
"code": "invalid_param",
"message": "offer_id must be a uuid.",
"param": "offer_id",
"request_id": "req_2f9c0e4a-…"
}
}type | Status | Significa |
|---|---|---|
invalid_request_error | 422; 404 para o que não existe; 409 para um conflito, como upsell_already_purchased; 400 malformed_path para um caminho que não decodifica | A requisição tem um problema. param diz onde. |
authentication_error | 401 | Chave ausente, inválida ou do tipo errado. |
permission_error | 403 | Falta o Turnstile, ou ele foi recusado. |
rate_limit_error | 429 | Limite de requisições atingido. |
idempotency_error | 409 | Conflito de Idempotency-Key. |
card_error | 402 | O cartão foi recusado. |
api_error | 500, ou 503 api_unavailable | Erro do lado do Paystation. Num 503, repita com a mesma Idempotency-Key: se a cobrança já tiver sido feita, a repetição devolve o resultado dela em vez de cobrar de novo. |
Decida pelo code, não pela message, que pode mudar. As respostas da API trazem o cabeçalho X-Request-Id, o mesmo valor de request_id. Mande-o ao suporte para localizar a requisição.
Duas recusas vêm antes da API e não têm envelope nem X-Request-Id: 400 INVALID_PAYLOAD, quando o corpo não é um JSON válido, e 400 INVALID_QUERY, quando limit não é um número inteiro ou vem repetido.
Paginação
As listagens, como GET /v1/charges e GET /v1/offers, devolvem { data, has_more }, do mais novo para o mais antigo. Para a próxima página, passe o id do último item em starting_after:
curl "https://paystation.quero.space/v1/charges?limit=100&starting_after=6f1c2b1e-…" \
-H "Authorization: Bearer sk_live_…"limit vai de 1 a 100 (padrão 20). Um parâmetro desconhecido ou repetido é recusado com 422, em vez de ser ignorado.
Limites
| Limite | Valor |
|---|---|
| Por chave | 600 requisições por minuto, somando as rotas |
| Por endereço IP | 2.000 requisições por minuto |
GET /v1/charges/{id} | 1.200 por minuto por chave, à parte do limite por chave |
POST /v1/charges com a chave pública | 10 a cada 15 minutos por chave e IP do comprador |
POST /v1/charges com a chave secreta | 120 por minuto por chave |
Ao passar de um limite, a resposta é 429 rate_limit_error. Espere e tente de novo com intervalo crescente. Para acompanhar um Pix ou boleto até o pagamento, consulte GET /v1/charges/{id} ou, melhor, espere o webhook.