Erros de Integração¶
Erros comuns ao integrar com a API Saipos.
Erro 401 - Unauthorized¶
Sintoma¶
Causas¶
- Credenciais Saipos inválidas
- Token expirado
- Headers incorretos
Solução¶
-- Verificar credenciais do restaurante
SELECT
id,
name,
"codeStoreSaipos",
"saiposIdPartner",
"saiposSecret"
FROM "Restaurant"
WHERE id = 'restaurant_id';
-- Atualizar credenciais
UPDATE "Restaurant"
SET
"saiposIdPartner" = 'novo_id_partner',
"saiposSecret" = 'novo_secret'
WHERE id = 'restaurant_id';
-- Invalidar cache de token
DELETE FROM "TokenCache" WHERE provider = 'SAIPOS';
Erro 901/902 - Credenciais Inválidas¶
Sintoma¶
ou
Solução¶
O sistema já invalida o cache automaticamente quando recebe 901/902:
Ação manual: Atualizar credenciais no banco
UPDATE "Restaurant"
SET
"saiposIdPartner" = 'novo_id',
"saiposSecret" = 'novo_secret'
WHERE "codeStoreSaipos" = 'LOJA_001';
Erro 400 - Bad Request¶
order_id já existe¶
Sintoma:
Causa: Em testes, o mesmo transactionKey está sendo reutilizado.
Solução: Use isTestTransaction: true no evento de teste:
O sistema adiciona timestamp automaticamente:
if (isTest) {
const shortTs = Math.floor(Date.now() / 1000);
order_id = `${baseOrderId}-${shortTs}`;
}
Campo obrigatório faltando¶
Sintoma:
Solução: Verificar mapeamento do restaurante
SELECT
"codeStoreNayax",
"codeStoreSaipos"
FROM "Restaurant"
WHERE "codeStoreNayax" = 'LOJA001';
-- Se null, configurar
UPDATE "Restaurant"
SET "codeStoreSaipos" = 'LOJA_SAIPOS_001'
WHERE "codeStoreNayax" = 'LOJA001';
Formato inválido¶
Sintoma:
Solução: Verificar evento no banco e mapper
Erro 422 - Unprocessable Entity¶
integration_code não encontrado¶
Sintoma:
Causa: Produto não cadastrado no Saipos
Soluções:
- Cadastrar produto no Saipos (recomendado)
- Mapear para produto existente
- Usar produto genérico
cod_store inválido¶
Sintoma:
Solução: Verificar código correto no Saipos
-- Ver código atual
SELECT "codeStoreSaipos" FROM "Restaurant"
WHERE "codeStoreNayax" = 'LOJA001';
-- Atualizar
UPDATE "Restaurant"
SET "codeStoreSaipos" = 'codigo_correto_saipos'
WHERE "codeStoreNayax" = 'LOJA001';
payment_types inválido¶
Sintoma:
Solução: Verificar mapeamento de tenderType
// Verificar TENDER_MAP em nayax-to-saipos.mapper.ts
const TENDER_MAP = {
1: { code: 'DIN', type: 'OFFLINE' },
50: { code: 'PARTNER_PAYMENT', type: 'ONLINE', complement: 'pix' },
// ...
};
Erro 500 - Internal Server Error¶
Sintoma¶
Solução¶
Erro temporário do Saipos. O job será retentado automaticamente com backoff:
- Tentativa 1: ~30s
- Tentativa 2: ~2min
- Tentativa 3: ~5min
- Tentativa 4: ~15min
- Tentativa 5: ~30min
- Tentativa 6+: ~60min
Se persistir: Contatar suporte Saipos
Erro 503 - Service Unavailable¶
Sintoma¶
Solução¶
API Saipos fora do ar temporariamente. Sistema retenta automaticamente.
Verificar: https://status.saipos.com (se existir)
Timeout¶
Sintoma¶
Causas¶
- API Saipos lenta
- Rede lenta
- Payload muito grande
Solução¶
// Aumentar timeout (em src/webhooks/saipos/saipos.client.ts)
const response = await axios.post(url, body, {
timeout: 60000 // 60 segundos
});
Network Error¶
Sintoma¶
Soluções¶
-
Verificar conectividade:
-
Verificar DNS:
-
Verificar firewall:
-
Verificar proxy (se usar):
SSL Error¶
Sintoma¶
Solução (apenas development)¶
Em produção: Instalar certificados corretos
Erros de Validação de Totais¶
Soma não fecha¶
Sintoma: Job vai para DLQ com erro de validação
Debug:
SELECT
"transactionKey",
"totalAmount",
(SELECT SUM(amount) FROM "Payment" WHERE "nayaxEventId" = "transactionKey") as payments_sum,
"totalAmount" - (SELECT SUM(amount) FROM "Payment" WHERE "nayaxEventId" = "transactionKey") as diff
FROM "NayaxEvent"
WHERE "transactionKey" = 'event_key';
Solução: O mapper já ajusta automaticamente, mas se persistir:
// Verificar to2() function no mapper
function to2(n: number) {
return Math.round((n + Number.EPSILON) * 100) / 100;
}
Debug de Erro Específico¶
Ver Erro Completo¶
Parsear JSON do Erro¶
SELECT
id,
"eventKey",
"lastError"::json->>'msg' as message,
("lastError"::json->>'status')::int as http_status,
"lastError"::json->'data' as response_data
FROM "OutboxJob"
WHERE status IN ('FAILED', 'DLQ')
ORDER BY "createdAt" DESC
LIMIT 10;
Reprocessar após Correção¶
Depois de corrigir o problema:
-- Resetar job específico
UPDATE "OutboxJob"
SET
status = 'PENDING',
"nextRunAt" = NOW(),
attempts = 0,
"lastError" = NULL
WHERE id = 'job_abc123';
-- Reprocessar todos DLQ de uma loja
UPDATE "OutboxJob" o
SET
status = 'PENDING',
"nextRunAt" = NOW(),
attempts = 0,
"lastError" = NULL
FROM "NayaxEvent" e
WHERE o."eventKey" = e."transactionKey"
AND e."storeCode" = 'LOJA001'
AND o.status = 'DLQ';
Estatísticas de Erros¶
-- Erros mais comuns
SELECT
"lastError"::json->>'status' as http_status,
COUNT(*) as occurrences
FROM "OutboxJob"
WHERE status = 'DLQ'
AND "lastError" IS NOT NULL
GROUP BY "lastError"::json->>'status'
ORDER BY occurrences DESC;
-- Taxa de erro por loja
SELECT
e."storeCode",
COUNT(*) FILTER (WHERE o.status = 'SENT') as success,
COUNT(*) FILTER (WHERE o.status = 'DLQ') as failed,
ROUND(
COUNT(*) FILTER (WHERE o.status = 'DLQ')::numeric /
COUNT(*)::numeric * 100,
2
) as error_rate
FROM "OutboxJob" o
JOIN "NayaxEvent" e ON o."eventKey" = e."transactionKey"
GROUP BY e."storeCode"
ORDER BY error_rate DESC;
Próximos Passos¶
- Debug - Debug detalhado
- Problemas Comuns - Outros problemas
- FAQ - Perguntas frequentes