Erros, timeouts e retentativas
Todas as respostas de erro são JSON. O código HTTP diz a classe do problema; o campo error diz o detalhe.
Formato
{ "error": "Forma de pagamento não habilitada.", "code": "opcional" }{ "error": { "amount": ["The amount must be greater than 0."], "due_date": ["The due date field is required."] } }{ "error": "Internal error. Please retry later or contact support.", "code": "internal_error" }Detalhes internos (SQL, caminhos, exceções) nunca são enviados ao cliente; ficam apenas no log do servidor.
Códigos HTTP
| HTTP | Causa | O que fazer |
|---|---|---|
| 200 / 202 | Sucesso (202: evento enfileirado). | — |
| 400 | Regra de negócio: método não habilitado, saldo insuficiente, recusa do emissor/banco, Idempotency-Key ausente/inválida. | Não repita igual. Leia error. |
| 401 | Token ausente, inválido ou expirado; credenciais incorretas em /api/auth. | Obtenha novo token. |
| 404 | Recurso inexistente ou de outra conta. | Confira o identificador. |
| 409 | Mesma Idempotency-Key ainda em processamento. | Aguarde 1–2 s e repita. |
| 422 | Campos inválidos, ou Idempotency-Key reutilizada com outro corpo. | Corrija o corpo. |
| 429 | Limite de requisições. Cabeçalho Retry-After. | Espere Retry-After e use backoff. |
| 500 / 5xx | Falha inesperada. | Repita com a mesma Idempotency-Key. |
Valores de code
idempotency_key_required, idempotency_key_invalid, idempotency_key_reused, idempotency_in_progress, internal_error. Os demais erros trazem só error; não faça lógica com o texto da mensagem, que pode mudar.
Retentativas
| Resposta | Repetir? |
|---|---|
| Timeout / erro de rede / 5xx | Sim, com a MESMA Idempotency-Key, backoff exponencial com jitter (ex.: 1, 2, 4, 8 s; máx. 5 tentativas). |
| 429 | Sim, após Retry-After. |
| 409 | Sim, após 1–2 s. |
| 400 / 401 / 404 / 422 | Não repita igual. 401: renove o token e repita uma vez. |
async function comRetentativa(fn, max = 5) {
for (let i = 0; i < max; i++) {
try {
const r = await fn();
if (r.status < 500 && r.status !== 429 && r.status !== 409) return r;
const espera = Number(r.headers.get('retry-after')) * 1000 || Math.min(1000 * 2 ** i, 30000);
await new Promise(ok => setTimeout(ok, espera + Math.random() * 250));
} catch (e) {
await new Promise(ok => setTimeout(ok, Math.min(1000 * 2 ** i, 30000)));
}
}
throw new Error('Falha após ' + max + ' tentativas');
}Timeouts
Defina timeout de conexão (~5 s) e de resposta (~30 s) no seu cliente. Cartão e saque podem demorar mais por dependerem de terceiros. Se estourar o tempo, não assuma falha: repita com a mesma Idempotency-Key ou consulte o estado.
Respostas inesperadas
- Ignore campos novos desconhecidos (a API pode acrescentar campos sem mudar de versão).
- Se o corpo não for JSON (ex.: página de erro de proxy), trate como falha transitória.
- Valores desconhecidos de status: trate como pendente e consulte depois.