Skip to main content
Sandbox não é mock. Suas cobranças saem pelo Infi, um provedor nosso, que por baixo fala com uma conta de homologação do Asaas — o mesmo adapter, os mesmos formatos de webhook, os mesmos modos de falha que produção. O que muda é a conta, não o código. Na prática: o campo provider volta "infi", e tudo abaixo descreve o comportamento do Asaas, porque é ele que está por baixo. Isso é ótimo pra fidelidade, e a parte chata dele acabou: você fecha o loop sozinho, sem a chave de ninguém. O QR que a gente devolve em sandbox é uma página nossa de confirmação — escaneia com o celular, aperta um botão, a fatura vira paga.

O que vem na cobrança pix

  • pixPayload — renderize como QR, igual em produção. Em sandbox ele é a URL da nossa página de confirmação; em produção é o copia-e-cola EMV. Seu código de renderização é o mesmo nos dois: é um QR.
  • pixQrImage — PNG em base64 já pronto, gerado do mesmo pixPayload. Se vier, use em vez de gerar o QR você mesmo.
  • sandboxConfirmUrl — só existe em sandbox, e é o marcador explícito. Se você quer um botão “confirmar como se eu tivesse pagado” na sua tela de teste, cheque este campo. Nunca farejando se o pixPayload parece uma URL: em produção ele é EMV, e esse tipo de checagem é como uma coisa de teste vaza pro checkout de verdade.
  • providerPixPayload — o EMV do Asaas, pra você ver o que produção devolveria. Não é pagável em app de banco: é conta de homologação.
  • Se pixPayload vier vazio, a cobrança existe mas não tem como ser paga. Não invente um QR a partir de id ou providerId: o banco do pagador recusa, e a tela mente pra ele. Mostre erro e ofereça outro método.
Pix exige CPF/CNPJ. O Asaas recusa criar o pagador sem CPF/CNPJ — sem ele a cobrança para em 422 customer_tax_id_required. Colete e passe o taxId junto do cliente.

Fechar o loop: confirmar o pagamento

Duas formas, e nenhuma precisa de chave de provedor. Escaneando, que é o teste que vale — é o fluxo do seu comprador de verdade: renderize o pixPayload como QR, aponte a câmera do celular, e abra. Cai numa página nossa com o valor e um botão. Ou por HTTP, pro seu CI:
$SANDBOX_CONFIRM_URL é o sandboxConfirmUrl que a cobrança devolveu. No SDK:
Confirmar duas vezes é seguro: a segunda responde 200 e não faz nada — é uma página que as pessoas recarregam. Medido de ponta a ponta: confirmação → paid em ~3s, com o grant de download emitido e a entrega disparada.
Por baixo é o Asaas confirmando, não a gente inventando. A nossa rota não escreve “pago” no banco. Ela chama o confirm de sandbox do provedor que criou a cobrança, e o webhook real dele volta pelo mesmo caminho que produção usa. É por isso que você vê pixTransaction, taxa descontada e data de crédito — a cobrança é real, só a confirmação é sua.
Isso não existe em produção. A rota de confirmação não é montada num deployment live — não é protegida, é ausente. E o sandboxConfirmUrl não vem na resposta. Se o seu código chama ela, ele não tem o que chamar em produção; se ele checa o campo, se comporta certo nos dois. É por isso que o marcador é um campo e não o formato do pixPayload.

Como saber que pagou

Duas formas, e em sandbox só uma funciona hoje: Registrar webhook em sandbox responde 503 secret_store_unavailable — o cofre de segredos não está disponível pra tenant de sandbox. Então, em sandbox, polling é o caminho:
Os eventos, a verificação de assinatura e o que fazer em produção estão em webhooks.

O que NÃO existe em sandbox

Coisas que respondem erro e não são bug seu:
  • providers.* → 404. Conectar PSP (BYOP) é superfície só de produção. Em sandbox quem processa é o Infi — um provedor nosso, e é o que aparece em provider nas suas cobranças. Ele roda sobre o Asaas por baixo, o que explica os formatos e os erros que você vê, mas a conta é nossa e não há o que conectar.
  • webhooks.create → 503, como acima.
  • Cartão pode vir desabilitado. A resposta pública do link traz cardEnabled; se for false, só Pix e boleto estão disponíveis naquele tenant.