Endpoint de preço
Um produto de preço variável não tem valor de tabela: quem sabe quanto cobrar em cada ciclo é o seu sistema. Você cadastra uma URL no produto e, na virada de cada ciclo, o motor faz um POST nela perguntando o valor daquele assinante naquele período.
O que enviamos
Seção intitulada “O que enviamos”POST https://seusistema.com.br/pricingContent-Type: application/jsonX-Revtriever-Signature: t=1756500000,v1=1fa9c2…{ "version": 1, "mode": "live", "pricingRequestId": "prq_9f8e77", "product": { "externalRef": "garrafas-extras" }, "subscription": { "externalRef": "clube-4512" }, "customer": { "externalRef": "cliente-4512" }, "period": { "start": "2026-09-01", "end": "2027-02-28" }, "cycle": 2, "currency": "BRL"}Os externalRef são os seus IDs — o cálculo do seu lado é um lookup no seu próprio banco. mode é "test" quando a chamada vem do botão de teste ou de uma simulação.
O que esperamos de volta
Seção intitulada “O que esperamos de volta”Em até 10 segundos, HTTP 200 com:
{ "amount": 18760, "description": "Garrafas extras — 4 un." }amount: centavos, inteiro, sempre. Nada de float, nada de vírgula.description(opcional): vira a linha da fatura que o seu assinante lê.- Para não cobrar o item neste ciclo:
{ "skip": true }. (Diferente deamount: 0, que é uma linha grátis na fatura.)
Verifique a assinatura
Seção intitulada “Verifique a assinatura”Toda chamada nossa leva X-Revtriever-Signature: t=<timestamp>,v1=<hmac>. Recalcule o HMAC-SHA256 sobre t + "." + corpo cru com o seu secret de integração e rejeite timestamps com mais de 5 minutos:
const crypto = require('crypto');
function verificar(corpoCru, header, secret) { const { t, v1 } = Object.fromEntries(header.split(',').map((p) => p.split('='))); if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false; const esperado = crypto.createHmac('sha256', secret).update(`${t}.${corpoCru}`).digest('hex'); return crypto.timingSafeEqual(Buffer.from(esperado), Buffer.from(v1));}Os dois erros clássicos que esse snippet evita:
- Verificar sobre o JSON re-serializado em vez dos bytes crus do corpo — re-serializar muda a ordem das chaves e a assinatura nunca bate. Use o corpo antes de qualquer parse.
- Comparar com
===em vez de comparação constant-time.
O secret é um por conta (o mesmo dos webhooks) e vive em Cobrança → Integração, com rotação sem downtime: ao gerar um novo, o anterior continua aceito por 24 horas.
Regras que protegem vocês dois
Seção intitulada “Regras que protegem vocês dois”- Perguntamos uma vez por ciclo. A resposta é congelada na fatura com request e response crus — retentativas e reemissões não chamam de novo. Seu endpoint não precisa ser idempotente.
- Teto por cobrança. No cadastro do produto você define um valor máximo; resposta acima dele é tratada como falha. É a proteção contra um bug seu virar uma cobrança absurda no seu cliente.
- Se o endpoint falhar, retentamos com intervalos crescentes por pelo menos 12 horas. Você recebe um aviso rápido (~15 min, com o erro cru) enquanto ainda retentamos; se esgotar, a fatura fica retida — atrasar é melhor que cobrar errado — e sai o alerta crítico. Destrave pelo painel ou pela API:
POST /v1/invoices/{id}/retry-pricingou informe o valor manualmente. - A fatura é gerada com folga antes do vencimento, então uma retenção curta é invisível para o seu assinante.
Teste antes do primeiro ciclo
Seção intitulada “Teste antes do primeiro ciclo”No cadastro do produto (ou via POST /v1/products/{id}/pricing-test) disparamos uma chamada real com mode: "test" e mostramos o resultado cru — status, latência e validação do formato. Ninguém descobre integração quebrada na primeira fatura de verdade.