Documentação reorganizada
- O site ganhou duas abas — Guias e Referência da API — e páginas novas: Aprovação de produção, Cobranças, Links e checkout, Assinaturas, Modo de recebimento, Erros e códigos e Limites
- Os grupos da referência foram renomeados em português e ordenados por importância; as rotas internas do painel ficaram num grupo final Interno, documentadas por transparência
- Textos desatualizados foram corrigidos contra a API atual (modo de recebimento, aprovação de produção, ambiente de testes, validade do token). A página Ambiente de testes mudou de endereço (
/devmoderedireciona)
Aprovação de produção do lojista
- Todo lojista nasce operando só em modo teste. Para emitir em produção, o cadastro precisa ser aprovado pela equipe V4 Pay: dados da empresa completos + 3 documentos (CNPJ ou contrato social, documento do responsável, comprovante de endereço), enviados em
/producao(novo). Enquanto não aprovado: sessão do painel sempre em teste,POST /auth/environmentcomliveePOST /api-keyscomenvironment: liverespondem409, chavev4pay_live_existente responde403, e toda emissão em produção (Pix, checkout, link de pagamento, assinatura, cobrança manual e saque) é barrada — tudo comcodigo: PRODUCAO_NAO_LIBERADAe o blocoproducaodizendo em que pé está - O modo teste não muda em nada; pagamento de cobrança já emitida sempre confirma (a trava é só na emissão). No checkout público, o pagador de um link indisponível vê mensagem neutra (
codigo: LINK_INDISPONIVEL) — a situação cadastral do lojista não é exposta - Novo:
GET /admin/lojistas(e detalhe, download de documento por stream, aprovar e recusar) — a fila de análise, exclusiva da equipe. O usuário devolvido pelo login e pelo perfil ganhaproduction_status PUT /auth/profilepassa a aceitaraddress,address_number,complement,province,postal_codeecompany_type
Painel em domínio próprio
- O CORS da API aceita uma lista de origens (
FRONTEND_URL+FRONTEND_URLS_EXTRAS) — o domínio novopay.v4companyamaral.come o antigo convivem durante a migração. Integrações sem navegador (curl, n8n, webhooks) seguem como sempre
Roadmap público do produto
- Novo:
GET /roadmap— o quadro do produto em 4 colunas (backlog, faremos, estamos cozinhando, pronto), com curtidas (1 por lojista, alterna emPOST /roadmap/{id}/vote) e comentários (GET/POST /roadmap/{id}/comments; o autor ou a equipe apagam). Criar, editar, mover e apagar cartões é exclusivo da equipe V4 Pay (403comcodigo: SOMENTE_ADMINpara os demais; chave de API nunca é admin) - O usuário devolvido pelo login e pelo perfil ganha
is_admin
Identificador fim a fim do Pix
- O
end_to_end_idda cobrança passa a ser preenchido quando o pagamento real é confirmado: o aviso do Asaas não traz o E2E — ele mora na transação Pix, que a API agora consulta na confirmação (quando a chave tem a permissão necessária). Pagamentos antigos sem o campo podem ser completados pela operação
Modo conta única pronto para produção
- No modo conta única (cobranças na conta principal, sem subconta): o
walletIddeixa de ser exigido (não há split);GET /receiving-accountrespondemodo: "conta_unica"com status aprovado — não é preciso cadastrar empresa;POST /withdrawalsresponde409comcodigo: REPASSE_MANUAL(o repasse é feito manualmente pelo V4 Pay enquanto o modo estiver ligado). Fora do modo, nada muda
Servidor mais resiliente e log sem dado pessoal
- Erro inesperado em qualquer rota passa a responder
500controlado com um código de correlação (error.id), em vez de poder derrubar o processo; a mensagem interna do erro não é mais ecoada ao cliente — erros de negócio (com status e mensagem próprios) continuam como sempre - Os logs do servidor deixam de registrar dados pessoais do pagador (documento, e-mail, telefone); passam a registrar identificadores (txid, lojista, modo, valor)
Webhook com secret informado
POST /webhook-endpointsaceitasecretopcional — para o painel gerar o segredo no navegador e mostrá-lo ao lojista antes de salvar. Só no formato exatowhsec_+ 32 hexadecimais (a mesma entropia do gerado pela API); fora disso,400. Omitido, a API gera como sempre
Cliente com CPF ou CNPJ
- Cliente, assinatura e cobrança aceitam CPF ou CNPJ no campo de documento (
cpf/customer_cpf), com os dígitos verificadores conferidos — documento inválido é recusado com400e mensagem clara (antes o cliente gravava qualquer coisa sem validar). A API guarda só os dígitos - O comprovante público mascara CNPJ também:
12.***.***/0001-**
Cobrança a partir de produto
POST /pixganha o modo produto: comproduct_id, o valor é calculado no servidor (preço do produto, menoscoupon_codeopcional — mesmas regras do checkout), a descrição padrão é o nome do produto e a validade padrão é 24 h. O pagador vem decustomer_idou dos campos inline (novo_cliente: truetambém o cadastra em clientes).success_urlecancel_url(http(s) validadas) definem o retorno pós-pagamento, eGET /checkout/{txid}passa a devolvê-las com oproduct_id. O modo manual continua igual
Campos que limpam de verdade
- Apagar a URL da imagem na edição do produto passa a limpar o campo:
PUT /products/{id}comimage_urlvazio ou nulo grava nulo. URL preenchida agora precisa ser http(s) válida — texto solto ou outro esquema é recusado com400 - O mesmo
''limpa também:descriptionde produto, cupom, assinatura e link de pagamento;customer_emailda assinatura;product_iddo link (desvincula o produto)
Produto com sku
POST /productsePUT /products/{id}aceitamsku(opcional, até 64 caracteres; vazio limpa). O campo era descartado em silêncio. Sku repetido entre os seus produtos do mesmo ambiente é recusado com409ecodigo: SKU_DUPLICADO
Assinatura nunca fica sem data de cobrança
- Uma assinatura ativa sem
next_billing_atsumia do cron de cobrança em silêncio. Agora:PUT /subscriptions/{id}aceitanext_billing_atmas recusa data nula ou inválida (400); reativar uma assinatura que ficou sem data recalcula a partir de agora pelointerval; e o cron corrige sozinho as que encontrar, com registro emsem_datano resumo da execução
Cupom sem validade volta a validar
- Correção em
GET /coupons/validate: cupom semexpires_at(sem validade — válido para sempre) era recusado como “não encontrado ou expirado”. O checkout público não tinha o problema
Correções da revisão de 06/09
- A exclusão de cupom, produto e assinatura inverteu a ordem: apaga primeiro e desvincula as referências depois. Falha no meio deixa referências órfãs — recuperáveis, avisadas no campo
avisoda resposta — em vez de perder o registro com as referências já zeradas. Acima de 500 referências a exclusão é recusada (409,codigo: MUITAS_REFERENCIAS) com a sugestão de desativar em vez de apagar - Reenvio de webhook do Asaas não pode mais duplicar o e-mail de comprovante: o envio passou para depois de o evento ser marcado como concluído, e o timeout padrão do e-mail caiu de 10 s para 5 s
GET /checkout/{txid}ganha limite por IP (300 consultas por 10 minutos; o polling normal do checkout usa ~120) e responde429acima disso- Cupom que deixaria o total abaixo do mínimo de R$ 5,00 é recusado já na validação (
400,codigo: VALOR_MINIMO), em vez de prometer um total que a emissão recusaria em seguida - Desativar cupom passou a existir de verdade:
PUT /coupons/{id}aceitais_active, e cupom desativado é recusado no checkout e no/coupons/validate— é a alternativa que o409da exclusão sugere
QR gerado na hora
- A imagem do QR (
qr_code_url) deixa de ser guardada na cobrança e passa a ser gerada a cada resposta a partir depix_code. Continua vindo emPOST /pix,GET /pix/status,GET /checkout/{txid}e no eventocharge.created, mas só em cobrançapending; nas demais vemnull. Motivo: era o maior campo da cobrança e deixava o banco sem espaço para campos novos
Saldo sem teto silencioso
- O resumo de saldo (
GET /withdrawals/summary) passa a somar todas as cobranças pagas e saques. Antes, uma conta com mais de 5 mil registros tinha o saldo disponível calculado para menos, sem erro. Listagens sem paginação continuam limitadas a 5 mil, mas agora avisam quando cortam
Exclusões sem órfãos
DELETE /coupons/{id},DELETE /products/{id}eDELETE /subscriptions/{id}passam a zerar as referências antes de apagar: cobranças e links perdem ocoupon_id, links perdem oproduct_id, cobranças perdem osubscription_id. A resposta trazdesvinculadoscom a contagem portabela.campo. Se a limpeza falhar, nada é apagado
Comprovante por e-mail ao pagador
- Quando a cobrança vira paga, o pagador recebe um e-mail com valor, recebedor, data, identificador fim a fim e o link do comprovante. Vale para o pagamento real e para a simulação no Dev Mode. Cobrança de teste não gera e-mail, salvo
EMAIL_ENVIAR_EM_TESTE=true. Depende deRESEND_API_KEYeEMAIL_REMETENTEno servidor; sem eles nada é enviado
Recebedor congelado no comprovante
- Quando a cobrança vira paga, a API grava nela o nome e o CNPJ do lojista (
store_name,store_document). O comprovante público (GET /checkout/{txid}) passa a mostrar quem recebeu naquele dia, mesmo que o cadastro mude depois. Cobranças pagas antes desta versão continuam lendo do cadastro atual
Valor mínimo de cobrança
POST /pix, o checkout de link,POST /payment-linksePOST /subscriptions(criar e editar) passam a recusar valor abaixo de R$ 5,00 com400ecodigo: VALOR_MINIMO(o mínimo vem emvalor_minimo), antes de chamar o motor de pagamentos. Antes o erro chegava cru, depois de o cliente já ter sido criado. A regra vale igual no Dev Mode, para a recusa não aparecer só em produção
Evento charge.created
- Novo evento de webhook
charge.created: disparado sempre que uma cobrança nasce (POST /pix, checkout de link, cron de assinatura,POST /charges), compix_code,qr_code_urlecheckout_url. Serve para automações entregarem o QR ao cliente na hora - O catálogo de eventos (
GET /webhook-endpoints/eventos) passa a listar todos os que a API emite:charge.created,charge.paid,subscription.charged,withdrawal.completed,withdrawal.rejected
Checkout em modo teste, de ponta a ponta
- O checkout público (
/checkout/link/{slug}/pay) passa a emitir a cobrança no ambiente do link: link criado em modo teste gera cobrança de teste. Antes, sem sessão, ele assumia produção e falhava com “lojista sem subconta” GET /checkout/link/{slug}eGET /checkout/{txid}devolvemenvironment- Nova rota
POST /checkout/{txid}/simular-pagamento, só para cobrança de teste: o ambiente de testes do motor de pagamentos dá a cobrança como paga e o resto do fluxo (webhook,paid, aviso ao lojista) acontece de verdade. É o botão “Simular pagamento” do checkout - Correção: o campo
qr_code_urlda cobrança tinha 2048 caracteres e a imagem do QR tem ~4,4 mil; a gravação falhava. O campo cresceu para 8192 e o provisionador passa a crescer campos quando o schema pede
Webhooks com nome e eventos
- Endpoints de webhook ganham
nameeevents(lista; vazia = todos). O despachante só entrega a um endpoint os eventos que ele assinou.GET /webhook-endpoints/eventoslista os eventos disponíveis. As respostas passam a trazerversion: v1
Assistente de primeiro acesso
- Nova rota
POST /auth/onboarding: grava as respostas do assistente do painel (origem, responsável, loja, CNPJ, endereço, segmento, faturamento) e marcaonboarding_completed_at. O usuário devolvido por/auth/mee pelo login passa a trazer esses campos PUT /auth/profileaceitaaccept_production_terms: true, que registraproduction_terms_accepted_at(aceite dos termos no diálogo “Ir para produção”)
Erro de configuração sem detalhe interno
- Quando o motor de pagamentos do ambiente não está configurado no servidor, o erro devolvido por
POST /receiving-accountePOST /pixpassa a ser uma frase curta em português, sem citar configuração interna. O diagnóstico completo continua no log do servidor
Aviso de ambiente sem motor de pagamentos configurado
GET /receiving-account, quando o lojista ainda não tem subconta, passa a dizer se o motor de pagamentos do ambiente está configurado no servidor (provedor_configurado). O painel usa isso para avisar antes do formulário, em vez de falhar no fim
Aprovação da conta de recebimento (KYC)
A API passa a acompanhar a aprovação da subconta no motor de pagamentos e a barrar cobrança em conta não aprovada.
GET /receiving-accountganha o blocoaprovacao: as quatro frentes (dados comerciais, conta bancária, documentos, geral), a data da última conferência e olink_documentospara o lojista enviar documentos e selfie no motor de pagamentosstatusda subconta passa a sercreating | pending | approved | rejected(eraactive, que só dizia que a subconta existia)- Nova rota
POST /receiving-account/approvalconfere no motor de pagamentos na hora POST /pixdevolve 409 comcodigo: SUBCONTA_NAO_APROVADA(ouSUBCONTA_INEXISTENTE) em vez de 500 com o erro cru do motor de pagamentos- O webhook da subconta passa a escutar os 18 eventos
ACCOUNT_STATUS_*; subcontas antigas são completadas
Validação da subconta no sandbox
Subconta e split validados de ponta a ponta no sandbox (conta PJ com autoaprovação).
- O onboarding passa a garantir uma chave Pix ativa na subconta (
pix_key,pix_key_statusemGET /receiving-account) — sem ela o motor de pagamentos não gera QR code - Endereço público de webhook documentado para quem opera o servidor: sem ele a conta de recebimento nascia sem aviso de pagamento
fee_amountpassa a gravar o valor exato do split que o motor de pagamentos devolve (split[].totalValue), calculado sobre o líquido- Correção: a trava de conta única respeitava o ambiente da requisição, não o da cobrança — no webhook (sem requisição) ela disparava para pagamento de teste
Modo teste com porta de entrada
O modo teste ficou acessível. Até aqui ele existia no servidor sem jeito de ligar.
- Novo:
POST /auth/environment— troca a sessão de ambiente;GET /auth/medevolveenvironment;POST /auth/refreshpreserva o ambiente POST /api-keysaceitaenvironment; a chave nascev4pay_test_…ouv4pay_live_…GET /api-keysmostra as chaves dos dois ambientes, com o ambiente de cada uma- Painel: interruptor Modo teste, faixa de aviso, seletor de ambiente ao criar chave, e o cadastro da empresa que abre a subconta
- O interruptor Dev Mode passou a se chamar Dados de exemplo, para não se confundir com o modo teste
motor de pagamentos e travas
motor de pagamentos passa a ser o único provedor de Pix. Cada lojista tem uma subconta no CNPJ
dele, e a cobrança nasce lá com um split da taxa do V4 Pay. O Banco Inter foi removido.
- Novo:
POST /receiving-accounteGET /receiving-account— cadastro da empresa e abertura da subconta - Novo: recepção do aviso de pagamento do motor de pagamentos, com validação de origem e idempotência
- Novo:
POST /pix/simulate-payment— marca uma cobrança de teste como paga - Novo:
POST /auth/google— acesso com a conta Google - Novo:
GET /checkout/{txid}— dados do comprovante para a página pública - Separação entre ambiente de teste e de produção por requisição (coluna
environmentem 11 coleções) - Correção: avisos ao lojista disparados sem
awaitnão completavam em serverless — agora completam - Correção: reenvio de webhook retoma evento que não concluiu, em vez de descartá-lo
- Removido:
POST /webhook/pixe/admin/webhook(eram do Inter)
Faxina e segurança
- Removidas as sobras do Supabase; a camada de dados é 100% Appwrite
.envprotegido no.gitignoree credenciais expostas auditadas- Guia de produção e serviço de Pix reescritos
Perfil e auditoria
- Novo:
PUT /auth/profile— nome da loja e CNPJ - Novo:
GET /audit— trilha de auditoria das ações na loja