Por que não só o sandbox
O sandbox do Asaas não entrega webhook no localhost. Isso obriga a um túnel, um deploy de homologação ou payloads forjados na mão, e nenhum deles testa o que importa: o prazo de crédito, os juros de atraso, o split, a retentativa do webhook. E no sandbox, um pagamento no cartão cai em 32 dias reais.
Este simulador entrega webhooks na sua máquina e deixa você mover o tempo. Diferente de um mock de respostas fixas, ele guarda estado: saldos, extratos, filas e agendamentos.
As regras que segue
- Uma máquina de estados por forma de pagamento. Pix vai direto a
RECEIVED; boleto cai em D+1 útil; cartão de crédito em D+32; débito em D+3. Uma cobrança não paga passa antes porOVERDUE. - Dinheiro de verdade.
netValue = value − taxa, a taxa é debitada no razão, e o extrato fecha com o saldo (um invariante testado). - Juros e multa. A multa uma vez, no primeiro dia de atraso; os juros pro rata die sobre o mês comercial de 30 dias.
- Split entre contas. Cada chave de API é uma conta, com
walletIde saldo próprios. Um split move dinheiro e lança nos dois extratos. - Webhooks como o Asaas manda. O header
asaas-access-token; só HTTP 200 conta; 15 tentativas na tabela do Asaas;SEQUENTIALLYcom bloqueio real da fila;interruptedno fim. - Cartão. Tokenização (o número do cartão nunca é guardado), cartões de teste, pré-autorização e captura, chargeback. O parcelamento no cartão cobra o cartão.
- Cobrança recorrente no cartão. No vencimento o cartão salvo da assinatura é tentado às 8h, 14h e 20h, e mais três vezes no dia seguinte (o relato do próprio Asaas sobre o cronograma, blog.asaas.com, 2024). Cada recusa é um
PAYMENT_CREDIT_CARD_CAPTURE_REFUSEDcom a cobrança aindaPENDING; depoisOVERDUE, sem novas tentativas. Um cartão novo na assinatura vale para as cobranças pendentes. Um cartão salvo pode aprovar mais N cobranças e depois recusar. - Recorrência. A assinatura cria a primeira cobrança na criação; as seguintes nascem 40 dias antes do vencimento. No parcelamento, a sobra do arredondamento vai para a última parcela, e no cartão a parcela
ncai em D+32×n. - Pix que paga.
GET /v3/payments/{id}/pixQrCodedevolve um BR Code EMV de verdade, com CRC16 e a imagem do QR: a câmera do celular lê. - Onboarding de subconta. Criar a subconta, listar os documentos pendentes, enviar um arquivo, aprovar.
Notificações ao pagador
O que o Asaas avisa a quem paga, entregue de verdade na sua máquina.
- Todo evento de cobrança (criada, alterada, vencendo, vencida, recebida) manda ao pagador um e-mail no Mailpit, um template de WhatsApp pela Cloud API do mock do WhatsApp e um SMS no mock de SMS, com o remetente
ASAAS. - O SMS segue o interruptor
smsEnabledForCustomerda notificação e só sai para quem tem celular cadastrado. - Um parcelamento é avisado uma vez: a criação da primeira parcela conta o plano inteiro (R$ 3.000,00 em 5x de R$ 600,00); lembretes e avisos de pagamento seguem por parcela.
- Nada para ligar, do jeito que você rodar (bun,
docker run, o seu compose,make setup): o Mailpit, o mock do WhatsApp e o mock de SMS são achados sozinhos, pelo nome do serviço no compose, nesta máquina ou no host do Docker, na subida e a cada 10 s, o mais próximo primeiro. O mock de SMS é procurado na porta8899, pelo serviçosmsousms-mockdo compose, e só é aceito quando respondeservice: "sms"noGET /health. O endereço em que o mock do WhatsApp responde de volta é achado do mesmo jeito: fica o primeiro que ele verifica. - O gerador também é achado sozinho na porta
45450(no boot e a cada 10 s, no serviçogeneratorougenerator-mockdo compose, nesta máquina ou no host do Docker): aceita quem responderservice: "generator"noGET /health, liberando a aba Integrações › Gerador no painel para popular a conta selecionada com clientes reais e cobranças. - Tudo sobe configurado: no boot e a cada tick, o número remetente recebe o webhook deste servidor e os templates do pagador são criados no mock do WhatsApp, pelas chamadas da própria Meta.
- Todo cliente nasce com as oito notificações padrão do Asaas (
GET /v3/customers/{id}/notifications, alteradas comPUT /v3/notifications/batch), aqui com e-mail, SMS e WhatsApp ligados (o Asaas começa com o WhatsApp desligado). O aviso de vencimento sai N dias antes (10 por padrão) e no dia do vencimento; a cobrança vencida é avisada de novo a cada N dias (7 por padrão) enquanto não é paga; a cobrança de assinatura não é avisada na criação. - O
notificationDisableddo cliente e cada configuração são respeitados, um canal com falha nunca trava a cobrança, e o painel reenvia quando você pede.
O painel
Abra http://localhost:45445. Não é o painel do Asaas: é o do simulador, e mostra o que o Asaas não deixa ver.
Zoom
Início
Cobranças por status com os totais, os cartões de teste e a carteira e a chave da conta.
Zoom
Clientes
Gestão de pagadores, CPF/CNPJ, contatos e histórico de cobranças.
Zoom
Criar Cliente
Cadastro rápido de clientes com validação de campos em tempo real.
Zoom
Cobranças
Avulsas, parceladas e assinaturas; acompanhamento de status em tempo real.
Zoom
Criar Cobrança
Configuração de cliente, valor, vencimento e forma de pagamento.
Zoom
Link da Fatura
Tela em invoiceUrl com opções de simulação de pagamento.
Zoom
Fatura com Pix
QR Code dinâmico EMV e copia-e-cola com confirmação instantânea.
Zoom
Boleto Bancário
Layout com linha digitável de 47 dígitos e código de barras.
Zoom
Webhooks
Endpoints, token de autenticação e controle de fila de eventos.
Zoom
Criar Webhook
Inscrição em grupos de eventos de cobrança, transferência e clientes.
Zoom
Logs de Webhooks
Histórico de entregas, payloads e respostas do seu endpoint.
Zoom
Logs de Requisições
Chamadas à /v3 com parâmetros enviados e status de resposta.
Zoom
Relógio Virtual
Avance dias ou meses para observar a máquina de estados agir.
Zoom
Cartões de Teste
Números específicos para forçar aprovação, recusa ou saldo insuficiente.
Zoom
Extrato Financeiro
Lançamentos de crédito, débito de taxas e saldo conciliado.
- Clientes: criar, editar, remover e restaurar; abrir um para ver tudo o que foi cobrado dele.
- Uma cobrança: o link da fatura, simular o pagamento, confirmar recebimento em dinheiro, receber, vencer, estornar, apagar, restaurar, e as notificações dela.
- Integrações, por conta: chaves de API, webhooks com a lista de eventos, o log de requisições (cada chamada à
/v3com o que foi enviado e o que voltou, erros incluídos), o log de webhooks com a resposta do seu endpoint e os eventos que nenhum webhook levou, os canais de e-mail e WhatsApp, e o gerador (Gerador). - Cartões de teste: aceitar qualquer cartão bem formado ou só os da lista; adicionar um cartão com o desfecho que ele força.
- Notas fiscais (NFS-e): agendar a nota de uma cobrança, emitir, cancelar e cadastrar as informações fiscais da conta. A prefeitura responde no próximo tick (
SYNCHRONIZED→AUTHORIZED, ouERRORsem informações fiscais), ou na hora com os botões do simulador; opdfUrle oxmlUrlde uma nota autorizada abrem o documento. Uma assinatura cominvoiceSettingsganha a nota de cada cobrança. - Perfis: uma conta raiz por tenant da sua aplicação, criada e apagada sem reiniciar. Zerar esvazia um perfil e mantém o id e as chaves: tudo, ou só as cobranças, as notas e os logs, mantendo clientes e subcontas; cancelar antes envia
PAYMENT_DELETED,SUBSCRIPTION_DELETEDeINVOICE_CANCELEDaos seus webhooks antes da limpeza.
Configuração
| Variável | Padrão | Significado |
|---|---|---|
PORT | 45445 | Porta HTTP |
ASAAS_API_KEY | gerada, impressa no boot | A chave da conta principal |
WEBHOOK_LOCALHOST_REWRITE | (vazio) | Host que substitui localhost nas URLs de webhook; use host.docker.internal |
CLOCK_MODE | REAL | REAL (segue a máquina; o relógio não deixa pular), VIRTUAL_FLOWING ou VIRTUAL_FROZEN |
ADMIN_ENABLED | true | As rotas /_admin/* e o painel |
INVOICE_BASE_URL | http://localhost:<PORT> | Para onde apontam invoiceUrl e bankSlipUrl, como o navegador alcança este servidor |
MAILPIT_URL | (sem valor: achado sozinho) | O Mailpit dos e-mails ao pagador; vazio, desativa o e-mail |
WHATSAPP_MOCK_URL | (sem valor: achado sozinho) | O mock do WhatsApp dos templates ao pagador; vazio, desativa o WhatsApp |
SMS_MOCK_URL | (sem valor: achado sozinho) | O mock de SMS das mensagens ao pagador; vazio, desativa o SMS |
GENERATOR_URL | (sem valor: achado sozinho) | O gerador para popular o mock; vazio, desativa o gerador |
WHATSAPP_PHONE_NUMBER_ID | 5511400000000 | O número remetente no mock do WhatsApp |
WHATSAPP_WABA_ID | 100000000000001 | A WABA cuja biblioteca guarda os templates do pagador |
NOTIFY_CALLBACK_BASE_URL | (sem valor: achado sozinho) | Este servidor como o mock do WhatsApp o alcança |
MAILPIT_UI_URL, WHATSAPP_UI_URL | http://localhost:8025, http://localhost:3010 | As duas telas, como o navegador as abre a partir de Integrações |
SMS_UI_URL | http://localhost:8899 | A tela do mock de SMS, onde as mensagens ao pagador são lidas. O painel do Asaas não tem aba de SMS |
GENERATOR_UI_URL | http://localhost:45450 | A tela do gerador, como o navegador a abre a partir de Integrações |
NOTIFY_FROM_EMAIL | notificacoes@asaas.localhost | Remetente dos e-mails |
CARD_CAPTURE_SCHEDULE | 0@08:00,0@14:00,0@20:00,1@08:00,1@14:00,1@20:00 | Quando a cobrança recorrente no cartão é tentada: dias depois do vencimento @ hora local. O painel troca em tempo de execução |
DATABASE_PATH | /data/asaas.db | Monte um volume em /data para persistir |
TZ | America/Sao_Paulo | Fuso horário |
Fidelidade: provada, não prometida
As regras de negócio foram capturadas do sandbox real do Asaas e são conferidas campo a campo por uma suíte de paridade. tools/capture.ts roda os cenários contra o Asaas e grava as respostas em tests/golden/; a suíte as reproduz contra o simulador e compara tudo o que não é legitimamente volátil, até o conjunto exato de chaves de cada objeto.
A captura derrubou 20 regras deduzidas da documentação, entre elas: a assinatura cria a primeira cobrança na hora; a taxa do parcelamento no cartão sai do total e a divisão trunca; a parcela n cai em D+32×n; o campo do desconto é limitDate, que a especificação não declara.
bun run test:parity
ASAAS_SANDBOX_API_KEY='$aact_hmlg_…' bun run capture # capture de novo com a sua chave do sandboxO script recusa qualquer chave que não comece com $aact_hmlg_. Detalhes em progress.md.
Desenvolvimento
bun install
bun run codegen # spec OpenAPI → TypeBox e o manifesto de operações
bun run db:generate # migrations a partir do schema Drizzle
bun run dev
bun test
bun run e2e # sobe o servidor, paga um Pix, espera o PAYMENT_RECEIVED
bun run spec:diff # o Asaas mudou a API?Leia o AGENTS.md antes de contribuir: domínio puro, nada de Date.now(), dinheiro em centavos inteiros.
Licença
MIT, veja a LICENSE.
Sem vínculo com o Asaas. Um simulador independente da API pública deles, para desenvolvimento local e testes. "Asaas" é marca de seus donos. Não use para mover dinheiro de verdade: nenhum pagamento aqui é real.