PagnovoPagnovodocs

Documentação

Ambiente de testes (Sandbox)

Antes de processar pagamento real, você quer testar sua integração — gerar PIX, confirmar webhook, fazer saque, lidar com rejeição. O Sandbox da Pagnovo é um ambiente paralelo onde tudo funciona como em produção, mas nenhum dinheiro real é movimentado.

A ideia é simples: você cria uma conta de testes em 20 segundos (sem KYC, sem cartão, sem CNPJ), recebe uma chave sk_test_* e usa os mesmos endpoints da API real. As transações são processadas por um acquirer interno que segue regras determinísticas baseadas no valor — você controla se quer simular um pagamento aprovado, rejeitado, chargeback ou estorno só mudando os centavos.

Quando estiver tudo funcionando, solicite produção pelo dashboard, complete o KYC e use a chave LIVE (sem prefixo) nos mesmos endpoints. Zero código pra trocar — só a chave muda.

Criar sua conta sandbox

  1. Acesse https://portal.pagnovo.com/auth/signup
  2. Preencha 6 campos (nome, e-mail, CPF, senha, tipo de uso, categoria) — sem CNPJ, sem upload de documento, sem aprovação manual
  3. Confirme seu e-mail pelo link que chega na caixa de entrada (válido por 48h)
  4. Faça login → o dashboard abre direto em modo TEST, com banner amarelo no topo avisando "Modo desenvolvedor"
  5. Sua chave sk_test_* já está disponível em Configurações → Credenciais

Pronto. Daqui você pode chamar qualquer endpoint da API real.

Como autenticar

Mesma autenticação HTTP Basic da API normal — só o formato da chave muda:

AmbienteFormato da chaveExemplo
TEST (Sandbox)sk_test_<uuid>sk_test_6c94c548-4bf6-4c05-919c-bf6d490f1eff
LIVE (Produção)UUID puro (sem prefixo)6c94c548-4bf6-4c05-919c-bf6d490f1eff

O prefixo sk_test_ é só pra você reconhecer visualmente que está usando a chave de teste — o backend identifica o ambiente pelo registro da chave no banco.

bash
# Sandbox
curl -X POST "https://api.pagnovo.com/transactions/v2/purchase" \
  -H "Authorization: Basic $(echo -n 'secret:sk_test_6c94c548-4bf6-4c05-919c-bf6d490f1eff' | base64)" \
  -H "Content-Type: application/json" \
  -d '{ "amount": 1000, "paymentMethod": "PIX", ... }'

Não pode usar a chave LIVE em sandbox e vice-versa — a API rejeita com 401.

Regras determinísticas — PIX (cash-in)

O acquirer do sandbox decide o status da transação baseado nos 2 últimos dígitos do valor em centavos. Isso permite simular qualquer cenário sem depender de timing real:

Valor (centavos)FinalStatus retornadoWebhook V2 disparado
1000 (R$ 10,00).00APPROVEDcashin.paid
1001 (R$ 10,01).01REJECTEDnenhum evento V2 (só response HTTP)
1002 (R$ 10,02).02INCOSISTENTcashin.paid com status: "INCOSISTENT" no payload
1004 (R$ 10,04).04CHARGEBACKinfraction.updated
1005 (R$ 10,05).05REFUNDEDcashin.refunded
1006 (R$ 10,06).06BLOCKEDinfraction.updated
Outros (.03, .07, .08, .09 etc.)qualquerAPPROVED (default)cashin.paid

Os dois últimos dígitos do valor controlam o outcome. R$ 50,00, R$ 1.000,00 e R$ 0,03 todos retornam APPROVED.

⚠️ CHARGEBACK e BLOCKED disparam o evento infraction.updated em V2 (não eventos próprios). Os campos da disputa vêm no payload do mesmo evento — veja a seção de webhooks V2 pra detalhes.

⚠️ REJECTED não dispara nenhum webhook V2 — só retorna no response HTTP. Se você usa V1 (postbackUrl per-tx), o POST sai com status: "REJECTED" no payload V1 normal.

bash
# Simula PIX rejeitado (final .01):
curl -X POST "https://api.pagnovo.com/transactions/v2/purchase" \
  -H "Authorization: Basic $(echo -n 'secret:sk_test_xxx' | base64)" \
  -d '{ "amount": 5001, "paymentMethod": "PIX", "name": "...", "email": "...", "cpf": "..." }'
# → response HTTP retorna status: REJECTED
# → webhook V2: nenhum disparo (REJECTED não tem mapping V2)
# → webhook V1: POST para postbackUrl com { type: "TRANSACTION", status: "REJECTED", ... }

Regras determinísticas — Withdraw (cash-out)

Pra saques, o outcome é decidido pelo CPF do recebedor (campo document no DTO de saque). Use CPFs especiais pra simular cada cenário:

CPF do recebedorStatus retornado
11111111111WITHDRAW_APPROVED
99999999999WITHDRAW_REJECTED
22222222222WITHDRAW_RETURNED
Qualquer outro CPF válidoWITHDRAW_APPROVED (default)
bash
# Simula saque rejeitado:
curl -X POST "https://api.pagnovo.com/withdraws/cash-out" \
  -H "Authorization: Basic $(echo -n 'secret:sk_test_xxx' | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 5000,
    "pixKey": "99999999999",
    "pixType": "CPF",
    "document": "99999999999",
    "externalId": "saque-teste-1"
  }'
# → status: WITHDRAW_REJECTED no response + webhook cashout.failed

O pixType é obrigatório e indica o tipo da chave PIX do recebedor: CPF, CNPJ, PHONE, EMAIL ou RANDOM. O campo document carrega o CPF/CNPJ do recebedor pra validação anti-fraude (independente da pixKey).

Webhooks no Sandbox

Você tem dois modelos de webhook que rodam em paralelo (e funcionam igual em Sandbox e em produção):

  1. V2 (recomendado) — endpoints globais. Cadastre uma URL na aba Webhooks do dashboard (ou via POST /v2/webhooks) escolhendo os eventos. Você recebe notificação de todos os eventos daquele tipo, sem precisar passar postbackUrl em cada request. O payload vem com envelope { event, environment, payload }.

  2. V1 (legado) — postbackUrl por transação. Você passa o campo postbackUrl no body de cada POST /transactions/v2/purchase ou POST /withdraws/cash-out. Quando a transação muda de status, fazemos um POST naquela URL com o payload (camelCase plano, sem envelope).

Se você configurar os dois (postbackUrl numa transação + um endpoint V2 ativo no mesmo evento), seu servidor recebe 2 POSTs — um de cada modelo. Garanta que seu handler seja idempotente. Pra distinguir: o payload V2 tem os campos event e environment.

bash
curl -X POST "https://api.pagnovo.com/transactions/v2/purchase" \
  -H "Authorization: Basic $(echo -n 'secret:sk_test_xxx' | base64)" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "João Silva",
    "email": "joao@exemplo.com",
    "cpf": "12345678901",
    "phone": "9876543210",
    "amount": 1001,
    "description": "Teste sandbox",
    "responsibleDocument": "12345678901",
    "responsibleExternalId": "user-1",
    "externalId": "tx-test-1",
    "paymentMethod": "PIX",
    "postbackUrl": "https://webhook.site/<seu-uuid>"
  }'

Em Sandbox, o webhook chega no mesmo formato que produção — você consegue testar parsing, validação de assinatura HMAC e handlers reais antes de subir.

Como diferenciar TEST de LIVE no seu lado:

  1. Pela chave — toda request HTTP traz a chave que originou a transação: sk_test_* indica teste, UUID puro indica produção. No seu handler de webhook, inspecione qual chave criou aquela Transaction (pode salvar no seu DB local).

  2. externalId — você passa um ID do seu banco aqui. Em ambiente de teste use prefixos diferentes (test-pedido-123 vs pedido-123) pra evitar conflito.

  3. Saldo isolado — Sandbox tem saldo fictício separado (R$ 100.000 inicial). Não confunde com produção.

Dica: a aba Webhooks do dashboard já tem CRUD de endpoints V2 com seleção de eventos, histórico de entregas e reenvio. Para novas integrações, prefira V2 — o postbackUrl por transação (V1) segue suportado para quem já usa.

Pra testar sem expor URL público, use webhook.site ou ngrok durante o desenvolvimento.

Saldo e isenção de taxas

Toda conta sandbox começa com saldo fictício (geralmente R$ 100.000) — não tem fluxo de "carregar saldo de teste". O acquirer mock retorna saldo simulado quando você consulta:

bash
curl "https://api.pagnovo.com/accounts/balance" \
  -H "Authorization: Basic $(echo -n 'secret:sk_test_xxx' | base64)"
# {
#   "available": 10000000,    // R$ 100.000 em centavos
#   "blocked": 0,
#   "totalBalance": 10000000
# }

Taxas zeradas — Sandbox roda com taxPix=0, taxWithdraw=0, taxRefund=0 e fees fixos = 0. Permite calcular fluxo bruto sem precisar normalizar os valores recebidos.

Quando você solicitar produção e o KYC for aprovado, o User LIVE herda as taxas comerciais que você negociar com a Pagnovo.

Resetar sandbox

Quer começar do zero? Configurações → Sandbox → Resetar sandbox apaga tudo que você criou em testes:

  • ✅ Apaga: Transactions, Withdraws, Refunds, Customers, Invoices, Subscriptions, Products, Offers, Links de pagamento, Cupons
  • Mantém: sua chave sk_test_*, configurações da conta (módulos, webhook URL), e o registro do User TEST

A confirmação exige digitar RESETAR pra liberar o botão. Limite de 1 reset por 30 segundos — evita spam.

Também disponível via API:

bash
curl -X POST "https://api.pagnovo.com/v2/sandbox/reset" \
  -H "Authorization: Bearer <seu-jwt-do-dashboard>"
# {
#   "ok": true,
#   "deletedCounts": {
#     "transactions": 12,
#     "withdraws": 3,
#     "invoices": 5,
#     ...
#   }
# }

Esse endpoint exige autenticação JWT do dashboard + 2FA (não pode chamar com sk_test_) — é destrutivo demais pra ser API-key only.

Solicitar produção (LIVE)

Quando sua integração estiver funcionando em Sandbox, é hora de pedir o ambiente LIVE. O dashboard tem um botão "Solicitar produção" no banner amarelo do topo (ou em qualquer página dentro do modo TEST).

Fluxo:

  1. Clica em "Solicitar produção" → abre o wizard /auth/upgrade-to-live em 5 passos
  2. Preenche dados completos: endereço da empresa, dados do sócio, uploads (contrato social, cartão CNPJ, comprovantes)
  3. Submete pra análise → conta entra em status ANALYZING no LIVE
  4. Equipe Pagnovo revisa (1-3 dias úteis) e aprova
  5. Após aprovação: você recebe uma nova chave LIVE (UUID puro, sem prefixo) e suas configurações de TEST são copiadas pro LIVE (categoria, módulos, webhook URL, etc)
  6. Toggle TEST↔LIVE no dashboard libera

Importante: catálogo, transações, customers e demais dados do TEST não são migrados pro LIVE. Sandbox é descartável — produção começa virgem.

Você pode continuar usando TEST em paralelo com LIVE — é útil pra testar mudanças antes de subir, ou pra reproduzir um bug específico.

Diferenças entre TEST e LIVE

AspectoTEST (Sandbox)LIVE (Produção)
Chave da APIsk_test_<uuid>UUID puro
KYC obrigatório❌ Não✅ Sim (análise 1-3 dias)
Dinheiro real❌ Não✅ Sim
Saldo inicialR$ 100.000 fictícioR$ 0 — depende dos cash-ins
TaxasZeradasConforme contrato
AcquirerSandboxAcquiringProvider (mock)PSPs reais (PIX da SPI, etc)
WebhooksDisparados normalmenteDisparados normalmente
Limite de transaçõesSem limite específicoConforme acordo comercial
Reset✅ Botão no dashboard❌ Impossível
Banner amarelo no dashboard✅ "Modo desenvolvedor"

A regra geral: se você consegue fazer em TEST, vai funcionar igual em LIVE. O único cuidado é não esquecer de trocar a chave no .env quando subir pra produção.

Checklist de migração TEST → LIVE

Antes de virar a chave (literalmente), confere:

  • Webhook URL acessível publicamente (não localhost) e respondendo HTTP 200 em < 5s
  • Verificação de assinatura HMAC implementada — ver Verificação de assinatura
  • Idempotência no seu handler — você pode receber o mesmo webhook 2-3x em casos de retry
  • externalId preenchido em todos os requests pra rastrear via dashboard depois
  • Logs capturando x-trace-id de toda response da API — ver Trace ID e debug
  • Retry exponential backoff se você fizer polling de status (não recomendado — prefira webhook)
  • Cenários de erro testados: .02 (rejected), .04 (chargeback), .05 (refunded)
  • Saque rejeitado testado com document: '99999999999' (mesma chave PIX como destino)
  • .env separado entre staging/produção (não hardcoda chave em código)

Tudo OK? Solicita produção pelo dashboard e bora.