Como integrar a API PIX da Pagnovo: guia para desenvolvedores
Guia prático da API Pagnovo: autenticação, criação de cobrança PIX, brCode, webhooks com HMAC, sandbox e os erros mais comuns de quem está integrando.
Equipe Pagnovo · 2026-08-04
Este guia cobre o caminho completo de uma integração PIX com a Pagnovo — da primeira chamada autenticada ao webhook de confirmação — com o código que realmente funciona na nossa API.
Referência completa e sempre atualizada: portal.pagnovo.com/docs.
Antes de começar: como o PIX funciona por trás
O PIX é o sistema de pagamentos instantâneos do Banco Central. O pagador inicia o pagamento, a instituição dele envia a ordem ao SPI, que valida, debita e credita — tudo em segundos, 24/7.
Você não se conecta ao Banco Central diretamente: quem tem acesso ao SPI são as instituições autorizadas. Sua aplicação conversa com a Pagnovo, que expõe uma API REST por cima disso.
Passo 1 — Autenticação
A Pagnovo usa autenticação por chave secreta via header Authorization, no formato Basic:
curl -X POST https://api.pagnovo.com/transactions/v2/purchase \
-H "Authorization: Basic $(echo -n 'secret:sk_test_SUA_CHAVE' | base64)" \
-H "Content-Type: application/json" \
-d '{ "amount": 18990, "description": "Pedido #1042", "externalId": "order-1042" }'
Dois detalhes importantes:
- O ambiente vem da chave, não da URL.
sk_test_*opera em teste;sk_live_*em produção (após aprovação do KYC). A base é semprehttps://api.pagnovo.com. - Cada chave carrega escopos. Leitura exige
view<Recurso>; escrita exigemanage<Recurso>. Sem o escopo, a resposta é 403 "Unauthorized Permissions" — diferente de 401, que é credencial inválida ou ausente.
Opcionalmente, você pode configurar uma allowlist de IP para createWithdraw e createRefund.
Com ela ativa, chamadas de outro IP retornam 400 "IP unauthorized".
Passo 2 — Valores em centavos
Esse é o erro nº 1 de quem começa: todos os valores monetários são inteiros em centavos.
| Valor real | Campo amount |
|---|---|
| R$ 1,00 | 100 |
| R$ 189,90 | 18990 |
| R$ 1.500,00 | 150000 |
Cupons percentuais e juros usam basis points (1.000 = 10%); multas fixas, centavos.
Passo 3 — Criar a cobrança
POST /transactions/v2/purchase
{
"amount": 18990,
"description": "Pedido #1042",
"externalId": "order-1042",
"postbackUrl": "https://sua-app.com/webhooks/pagnovo",
"restrictPayerDocument": true
}
Campos que valem atenção:
externalId— o identificador do pedido no seu sistema. Use sempre: é ele que permite buscar a transação depois sem guardar o ID da Pagnovo.restrictPayerDocument— quandotrue, só o CPF/CNPJ informado consegue pagar aquele QR. Excelente contra pagamento de terceiro e útil em antifraude.postbackUrl— webhook por transação (modelo V1). Para produção, prefira webhooks V2 registrados uma vez (passo 5).
A resposta traz:
{
"id": "...",
"status": "PENDING",
"amount": 18990,
"brCode": "00020126...",
"qrCode": "..."
}
O brCode é a string EMV do copia-e-cola; o qrCode, a representação para exibir.
Passo 4 — Exibir para o cliente
Ofereça as duas formas:
- Copia-e-cola com botão de copiar — essencial no mobile, onde o usuário está no mesmo aparelho e não consegue escanear a própria tela
- QR Code gerado a partir do
brCode
Forçar só o QR em quem está no celular é uma das maiores causas de abandono.
Passo 5 — Webhooks
Registre o endpoint uma vez e escolha os eventos:
POST /v2/webhooks
{
"url": "https://sua-app.com/webhooks/pagnovo",
"description": "Produção",
"events": ["cashin.paid", "cashin.refunded", "cashout.success"]
}
A resposta devolve um secret em texto puro uma única vez — guarde-o com segurança. Se perder,
use POST /v2/webhooks/:id/rotate-secret.
Eventos disponíveis incluem cashin.paid, cashin.refunded, cashout.success, cashout.failed,
cashout.returned, infraction.updated (substitui os antigos CHARGEBACK/BLOCKED) e o ciclo de
assinaturas (subscription.created, .activated, .paused, .canceled, .past_due, .expired).
O envelope V2 é padronizado:
{
"event": "cashin.paid",
"environment": "TEST",
"payload": { "id": "...", "status": "APPROVED", "amount": 18990 }
}
Passo 6 — Validar a assinatura (HMAC)
Nunca processe um webhook sem validar a assinatura. Um endpoint aberto é um convite para alguém marcar pedidos como pagos sem pagar.
A Pagnovo assina com HMAC-SHA256 sobre o payload com as chaves ordenadas:
import crypto from 'crypto';
function sortObjectKeys(obj: any): any {
if (Array.isArray(obj)) return obj.map(sortObjectKeys);
if (obj !== null && typeof obj === 'object') {
return Object.keys(obj).sort().reduce((acc, key) => {
acc[key] = sortObjectKeys(obj[key]);
return acc;
}, {} as any);
}
return obj;
}
const expected = crypto
.createHmac('sha256', secret)
.update(JSON.stringify(sortObjectKeys(payload)))
.digest('hex');
// Compare em tempo constante
const ok = crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(recebida));
As três causas clássicas de assinatura inválida:
- Reserialização diferente — espaços em branco ou ordem de chaves alterada
- Comparação não segura — usar
===em vez de comparação em tempo constante - Secret errado — confundir a chave de teste com a de produção
Passo 7 — Conciliar
Webhook é o caminho principal, mas sua aplicação pode estar fora do ar na hora da chamada. Mantenha uma consulta de fallback para pedidos pendentes:
GET /transactions/:id
Esse endpoint aceita o ID da Pagnovo, o seu externalId ou o end2End do PIX — por isso
vale sempre enviar externalId na criação.
Toda resposta traz o header x-trace-id; guarde-o nos logs. É com ele que o suporte encontra a
requisição exata nos nossos servidores.
Passo 8 — Testar no sandbox
Use uma chave sk_test_*. O sandbox tem resultados determinísticos por valor, o que permite
testar todos os caminhos sem depender de sorte:
| Valor | Resultado |
|---|---|
| R$ 10,00 | Aprovado |
| R$ 10,01 | Rejeitado |
| R$ 10,02 | Inconsistente |
| R$ 10,04 | Chargeback |
| R$ 10,05 | Estorno |
| R$ 10,06 | Bloqueado |
O ambiente de teste tem limite de 30 operações por dia (transações + saques + estornos).
Erros mais comuns
- Enviar valor em reais.
189.90vira R$ 1,89. Sempre centavos. - Confirmar o pedido na criação. Cobrança criada ≠ pago.
statusnascePENDING. - Não validar HMAC. Risco direto de fraude.
- Ignorar idempotência. O mesmo webhook pode chegar mais de uma vez — use o
idda transação como chave e responda 200 se já processou. - Demorar para responder. Responda 200 rápido e processe em fila; demora é tratada como falha e gera reenvio.
- Confundir 401 com 403. 401 = credencial inválida. 403 = chave válida, sem o escopo necessário.
- Não guardar
externalId. Sem ele, conciliar depois fica muito mais difícil.
Checklist antes de produção
- Chave
sk_live_*com os escopos mínimos necessários - Valores sempre em centavos (teste com
18990, não189.90) - Webhook V2 registrado, com secret armazenado com segurança
- Validação HMAC com comparação em tempo constante
- Idempotência por
idda transação - Resposta 200 imediata + processamento assíncrono
- Fallback com
GET /transactions/:id -
x-trace-idregistrado nos logs - Fluxos de rejeição e estorno testados no sandbox
Comece pela documentação oficial, crie sua conta em portal.pagnovo.com e conheça nossa API PIX. Dúvidas na integração? Fale com o time.