PayZuDocs
Boas práticas

Tratamento de erros

Explica quais erros permitem nova tentativa, quais não, e como tratar timeout.

A PayZu retorna os códigos HTTP padrão. Sua estratégia depende da categoria.

A tabela completa de códigos HTTP e o catálogo de errorCode, com o que fazer em cada um, estão em Códigos de erro; esta página cobre a estratégia: quando retentar, como fazer backoff e o que logar.

Helper de retry

Retry em 429, 5xx e 424. Nos demais 4xx, nunca.

ATTEMPTS=4
DELAY=1

for i in $(seq 1 $ATTEMPTS); do
  STATUS=$(curl -s -o /tmp/resp.json -w "%{http_code}" \
    -X POST https://api.payzu.processamento.com/v1/pix \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"amount":99.90,"clientReference":"order-1234"}')

  case $STATUS in
    2*) cat /tmp/resp.json; exit 0 ;;
    424|429|5*) sleep $DELAY; DELAY=$((DELAY*2)) ;;
    *) echo "Erro $STATUS"; cat /tmp/resp.json; exit 1 ;;
  esac
done
echo "Max retries excedido"
exit 1
async function withRetry<T>(
  fn: () => Promise<Response>,
  attempts = 4,
): Promise<T> {
  let lastErr: unknown;
  for (let i = 0; i < attempts; i++) {
    try {
      const res = await fn();
      if (res.ok) return res.json();

      if (res.status >= 400 && res.status < 500 && res.status !== 429 && res.status !== 424) {
        const body = await res.text();
        throw new Error(`Client error ${res.status}: ${body}`);
      }
    } catch (err) {
      lastErr = err;
    }

    const delay = Math.min(8000, 1000 * 2 ** i) + Math.random() * 250;
    await new Promise((r) => setTimeout(r, delay));
  }
  throw lastErr ?? new Error('Max retries exceeded');
}

Timeout: a armadilha do "pode ter dado certo"

Timeout não é equivalente a falha. A PayZu pode ter recebido, processado e gravado a transação, e a resposta apenas não voltou. Sua app não sabe.

Solução: use clientReference único e consulte antes de retentar.

async function createOrRetry(orderId: string, amount: number) {
  const ref = `order-${orderId}`;
  try {
    return await withRetry(() => postPix({ amount, clientReference: ref }));
  } catch (err) {
    // pode ter dado certo apesar do erro/timeout
    const existing = await fetch(
      `https://api.payzu.processamento.com/v1/pix?clientReference=${ref}`,
      { headers },
    ).then((r) => (r.ok ? r.json() : null));
    if (existing) return existing;
    throw err;
  }
}

Observabilidade do erro

Sempre logue, no mínimo:

CampoPor quê
requestIdVem nas respostas de erro PayZu. Suporte rastreia direto.
id localSeu identificador (pedido, pagamento Pix).
id PayZuSe já houver.
endToEndIdÚtil para rastrear no Bacen em disputa.
clientReferenceA chave de correlação universal.
HTTP status + messageA causa raiz quase sempre está em message.
Tentativa N de MDiferencia primeira tentativa de retry.
log.error('PayZu /pix falhou', {
  requestId: body.requestId,
  status: res.status,
  message: body.message,
  clientReference: ref,
  attempt: i + 1,
  attempts,
});

Mensagens de erro úteis para o usuário final

Não exponha message cru. Case pelo errorCode, que é estável, e não pelo texto: a message vem em português e pode mudar. Falha de validação de schema chega sempre como PZV001, com o campo responsável em details[].

errorCodeHTTPMensagem para o usuário
PZA100401"Erro de configuração. Contate o suporte com o código requestId."
PZV001400Monte a mensagem a partir de details[].field, campo a campo.
PZD600400"Valor abaixo do mínimo aceito para esta operação."
PZC200422"Saldo insuficiente para concluir a operação."
PZI110 / PZI111424"Instituição financeira instável no momento. Tente em instantes."
PZF500424"Instituição financeira indisponível no momento. Tente em instantes."
PZG429429"Estamos com muitas requisições. Tente em instantes."
PZI100500"Sistema temporariamente indisponível. Já estamos olhando."

O catálogo completo está em Códigos de erro.

Armadilhas comuns

ArmadilhaSintoma
Retentar em 400Spam contra a API, mesmo erro N vezes
Retentar em 401 sem rotacionar tokenToken vaza ainda mais no log
Sem backoff (retry imediato em loop)Vira rate limit, depois fica banido
Sem jitter no backoffN clientes batem ao mesmo tempo, "thundering herd"
Tratar timeout como falha definitivaCliente cobra 2x do usuário
Não logar requestIdSuporte não consegue investigar

Abrir suporte com o requestId

Tem o requestId salvo? Manda direto pro time.

Nesta página