Tratamento de Erros
O Toktus API utiliza códigos HTTP padrão e retorna erros em formato JSON consistente.
Formato Padrão de Erro
Todas as respostas de erro seguem este formato:
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Descrição legível do problema"
}
}| Campo | Descrição |
|---|---|
success | Sempre false para erros |
error.code | Código programático do erro (use para lógica condicional) |
error.message | Mensagem descritiva (pode exibir ao usuário ou logar) |
[!NOTE]
O campoerror.detailsexiste apenas em ambiente de desenvolvimento (NODE_ENV=development). Em produção, os erros retornam somentecodeemessage.
Códigos HTTP
| HTTP | Significado | Ação |
|---|---|---|
| 200 | Sucesso | Processar resposta normalmente |
| 201 | Criado com sucesso | Recurso foi criado |
| 400 | Requisição inválida | Corrigir dados enviados |
| 401 | Não autenticado | Verificar API Key |
| 403 | Conta inativa | Contatar suporte |
| 404 | Não encontrado | Recurso não existe |
| 400 | Credenciais não cadastradas | Cadastrar via POST /api/v1/credentials |
| 422 | Operação não suportada | Gateway não suporta esta operação |
| 429 | Rate limit excedido | Aguardar antes de tentar novamente |
| 502 | Erro do gateway | Problema no gateway, verificar status |
| 502 | Gateway inacessível | Gateway offline ou sem conexão |
| 504 | Timeout do gateway | Gateway demorou muito, tentar novamente |
Erros por Categoria
Autenticação (HTTP 401 / 403)
```json
{
"success": false,
"error": {
"code": "MISSING_API_KEY",
"message": "Header x-api-key is required"
}
}
```
**Solução:**
```javascript
// Incorrect — missing x-api-key header
fetch('https://api.toktus.com/api/v1/customers', { method: 'POST' });
// Correct — include x-api-key in every authenticated request
fetch('https://api.toktus.com/api/v1/customers', {
method: 'POST',
headers: { 'x-api-key': process.env.TOKTUS_API_API_KEY }
});
```
```json
{
"success": false,
"error": {
"code": "INVALID_API_KEY",
"message": "The provided API key is invalid"
}
}
```
**Causas:** API Key incorreta ou revogada.
```json
{
"success": false,
"error": {
"code": "CLIENT_INACTIVE",
"message": "Your account has been deactivated"
}
}
```
**Solução:** Entre em contato com o suporte para reativar sua conta.
```json
{
"success": false,
"error": {
"code": "GATEWAY_AUTH_ERROR",
"message": "Credenciais inválidas para o gateway \"pagarme\". Verifique a apiKey/secretKey cadastrada.",
"gateway": "pagarme"
}
}
```
**Solução:** Atualize as credenciais via `POST /api/v1/credentials`.
[!NOTE]
O HTTP status reflete o código retornado pelo gateway:401ou403dependendo do provedor.
Validação (HTTP 400)
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "Dados inválidos na requisição",
"details": {
"errors": [
{ "field": "amount", "message": "Valor mínimo é 100 centavos (R$ 1,00)" },
{ "field": "email", "message": "Formato de e-mail inválido" }
]
}
}
}Causas comuns: Campos obrigatórios ausentes, tipos de dados incorretos, valores fora dos limites, formato inválido.
Exemplo de validação preventiva:
const Joi = require('joi');
const chargeSchema = Joi.object({
gateway: Joi.string().valid('stripe', 'pagarme', 'asaas', 'mercadopago', 'pagseguro').required(),
paymentMethod: Joi.string().valid('credit_card', 'boleto', 'pix').required(),
amount: Joi.number().integer().min(100).required(),
customer: Joi.object({
name: Joi.string().min(3).max(100).required(),
email: Joi.string().email().required(),
document: Joi.string().pattern(/^\d{11}$|^\d{14}$/).required()
})
});Recurso Não Encontrado (HTTP 404)
```json
{
"success": false,
"error": {
"code": "NOT_FOUND",
"message": "Cliente 'cus_abc123' não encontrado no gateway Stripe"
}
}
```
**Causas:** ID incorreto, recurso deletado, recurso pertence a outra conta, gateway errado.
```json
{
"success": false,
"error": {
"code": "MISSING_CREDENTIALS",
"message": "Credenciais do gateway \"stripe\" não cadastradas. Faça POST /api/v1/credentials com as chaves desse gateway antes de usar esse endpoint."
}
}
```
**Solução:** Cadastre as credenciais via `POST /api/v1/credentials` antes de operar.
Timeout (HTTP 504)
{
"success": false,
"error": {
"code": "GATEWAY_TIMEOUT",
"message": "O gateway Stripe não respondeu dentro do tempo limite (30 segundos)."
}
}Estratégia de retry com exponential backoff:
async function createChargeWithRetry(data, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch('https://api.toktus.com/api/v1/charges', {
method: 'POST',
headers: {
'x-api-key': API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
if (response.status === 504 && attempt < maxRetries) {
await sleep(Math.pow(2, attempt) * 1000); // 2s, 4s, 8s
continue;
}
return await response.json();
} catch (error) {
if (attempt === maxRetries) throw error;
await sleep(2000);
}
}
}Operação Não Suportada (HTTP 422)
{
"success": false,
"error": {
"code": "GATEWAY_UNSUPPORTED_OPERATION",
"message": "Asaas exige \"customerId\" em toda cobrança. Crie um customer antes (POST /api/v1/customers) e envie o externalId retornado."
}
}Rate Limit (HTTP 429)
{
"success": false,
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Limite de requisições excedido."
}
}Headers incluídos:
Retry-After: 45(segundos)X-RateLimit-Limit: 100X-RateLimit-Remaining: 0X-RateLimit-Reset: 60(segundos até reset)
async function makeRequest(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
await sleep(retryAfter * 1000);
continue;
}
return response;
}
}Erro do Gateway (HTTP 502)
{
"success": false,
"error": {
"code": "GATEWAY_ERROR",
"message": "Erro retornado pelo gateway Stripe: Card declined",
"gateway": "stripe"
}
}Erros comuns de gateway:
| Gateway Code | Significado | Ação |
|---|---|---|
card_declined | Cartão recusado | Solicitar outro cartão |
insufficient_funds | Saldo insuficiente | Solicitar outro método |
expired_card | Cartão vencido | Atualizar dados do cartão |
incorrect_cvc | CVV incorreto | Verificar CVV |
processing_error | Erro de processamento | Tentar novamente |
Tratamento com mensagens amigáveis:
const ERROR_MESSAGES = {
card_declined: 'Seu cartão foi recusado. Por favor, tente outro cartão.',
insufficient_funds: 'Saldo insuficiente. Por favor, use outro cartão.',
expired_card: 'Seu cartão está vencido. Por favor, atualize os dados.',
incorrect_cvc: 'Código de segurança (CVV) incorreto.',
processing_error: 'Erro no processamento. Por favor, tente novamente.'
};Gateway Inacessível (HTTP 502)
{
"success": false,
"error": {
"code": "GATEWAY_UNREACHABLE",
"message": "Não foi possível estabelecer conexão com o gateway Stripe."
}
}Solução: Implemente circuit breaker ou fallback para gateways alternativos.
Estratégias de Tratamento
Fallback para Gateways Alternativos
const GATEWAY_PRIORITY = ['stripe', 'pagarme', 'asaas'];
async function createChargeWithFallback(data) {
for (const gateway of GATEWAY_PRIORITY) {
try {
data.gateway = gateway;
return await createCharge(data);
} catch (error) {
console.warn(`Falha em ${gateway}:`, error.message);
if (gateway === GATEWAY_PRIORITY.at(-1)) throw error;
}
}
}Referência Rápida
| Código | HTTP | Retry? | Ação |
|---|---|---|---|
VALIDATION_ERROR | 400 | Não | Corrigir dados enviados |
MISSING_API_KEY | 401 | Não | Adicionar header x-api-key |
INVALID_API_KEY | 401 | Não | Verificar API Key |
GATEWAY_AUTH_ERROR | 401 / 403 | Não | Atualizar credenciais do gateway |
CLIENT_INACTIVE | 403 | Não | Contatar suporte |
NOT_FOUND | 404 | Não | Verificar ID do recurso |
MISSING_CREDENTIALS | 400 | Não | Cadastrar credenciais via POST /api/v1/credentials |
GATEWAY_UNSUPPORTED_OPERATION | 422 | Não | Usar método alternativo |
RATE_LIMIT_EXCEEDED | 429 | Sim | Aguardar Retry-After |
GATEWAY_ERROR | 502 | Condicional | Depende do erro retornado pelo gateway |
GATEWAY_UNREACHABLE | 502 | Sim | Circuit breaker / fallback |
GATEWAY_TIMEOUT | 504 | Sim | Retry com exponential backoff |
Updated about 2 hours ago