Criar um cliente e cobrar no PIX
Nesta recipe
- Aponte para o sandbox
- Cadastre o cliente
- Crie a cobrança PIX
- Use o id e o uuid
Checkout embutido (ninepay.js)
Coloque o pagamento de uma fatura dentro da sua página, com o seu botão de pagar e o iframe da NinePay para cartão, PIX e boleto.
6 passos
Abrir recipe: Checkout embutido (ninepay.js)Cobrança no cartão de crédito
Gere um card hash com um cartão de teste do sandbox, cobre no cartão em parcelas e confira o status.
4 passos
Abrir recipe: Cobrança no cartão de créditoCriar um cliente e cobrar no PIX
Cadastre o cliente com POST /customers e gere uma cobrança PIX para ele com POST /payments.
4 passos
Abrir recipe: Criar um cliente e cobrar no PIXCriar plano e assinatura recorrente
Crie um plano mensal, assine um cliente nele e mande o cliente para o checkout ativar a assinatura.
3 passos
Abrir recipe: Criar plano e assinatura recorrenteEstornar um pagamento
Devolva ao cliente um pagamento pago no cartão ou no PIX e acompanhe o reembolso até ser concluído.
3 passos
Abrir recipe: Estornar um pagamentoReceber e validar webhooks
Receba os eventos da NinePay, responda rápido, valide a assinatura, ignore entregas repetidas e confirme o pagamento na API antes de liberar o acesso.
6 passos
Abrir recipe: Receber e validar webhooks
Receber e validar webhooks
Receba os eventos da NinePay, responda rápido, valide a assinatura, ignore entregas repetidas e confirme o pagamento na API antes de liberar o acesso.
Pressione Enter para destacar o código deste passo1. Exponha um endereço público
Cadastre a URL do seu servidor em Integrações → Webhooks no dashboard ou com
POST /automations(veja Webhooks). O endereço precisa ser público; usehttps. O formulário do webhook tem um envio de teste para cada evento.2. Leia o corpo cru e o evento
Cada entrega é um
POSTcom JSON:id(o da entrega),type(o evento, comopayment.approvedousubscription.updated),data(o recurso no mesmo formato da API) ecreated_at. Leia o corpo como bytes, sem fazer parse antes de validar: a assinatura é calculada sobre o corpo exatamente como chegou.3. Valide a assinatura
Cada entrega de um webhook com segredo traz
X-Ninepay-TimestampeX-Ninepay-Signature. Calculesha256=+ HMAC-SHA256 de"{timestamp}.{corpo cru}", com o segredo do webhook (nep_whs_…) como chave, e compare com o header em tempo constante. Recuse com 401 se não bater ou se o timestamp tiver mais de 5 minutos de diferença para o seu relógio. A assinatura muda a cada tentativa, então valide todas.O segredo aparece uma vez só, ao criar o webhook ou ao clicar em Gerar novo segredo. Webhooks criados antes da assinatura não têm segredo, e não vêm assinados, até você clicar em Gerar segredo em Integrações → Webhooks.
4. Responda em até 10 segundos
Qualquer status fora de 2xx, ou a falta de resposta, conta como falha, e a entrega é repetida até 10 vezes, com intervalos crescentes. Responda primeiro e deixe o processamento pesado para depois (numa fila, por exemplo).
5. Ignore entregas repetidas
O
idé o mesmo em todas as tentativas de uma entrega. Guarde os ids já processados para não liberar nada duas vezes. Os eventos também podem chegar fora de ordem.6. Confirme na API antes de liberar
A assinatura prova que foi a NinePay quem enviou, mas não substitui a consulta. Trate o evento como um aviso: consulte
GET /payments/{id}com o seu token e libere só se ostatusforpaid. Assim, umPOSTforjado ou repetido para o seu endereço não libera nada.
import { createServer } from 'node:http';
import { createHmac, timingSafeEqual } from 'node:crypto';
const BASE_URL = 'https://api.sandbox.cefispay.com.br/api/v1';
const processed = new Set(); // em produção, guarde no seu banco
createServer(async (req, res) => {
if (req.method !== 'POST' || req.url !== '/webhooks/ninepay') {
res.writeHead(404).end();
return;
}
const chunks = [];
for await (const chunk of req) chunks.push(chunk);
const rawBody = Buffer.concat(chunks); // corpo cru, antes de qualquer parse
// Valide a assinatura antes de confiar no evento
const timestamp = req.headers['x-ninepay-timestamp'] ?? '';
const signature = req.headers['x-ninepay-signature'] ?? '';
const expected = 'sha256=' + createHmac('sha256', process.env.NINEPAY_WEBHOOK_SECRET)
.update(`${timestamp}.`)
.update(rawBody)
.digest('hex');
const a = Buffer.from(signature);
const b = Buffer.from(expected);
const fresh = /^\d+$/.test(timestamp) && Math.abs(Date.now() / 1000 - Number(timestamp)) <= 300;
if (!fresh || a.length !== b.length || !timingSafeEqual(a, b)) {
res.writeHead(401).end();
return;
}
const event = JSON.parse(rawBody.toString('utf8'));
// Responda 2xx logo (em até 10 s) e processe depois
res.writeHead(200).end();
if (processed.has(event.id)) return; // a mesma entrega pode chegar de novo
processed.add(event.id);
if (event.type === 'payment.approved') {
// Mesmo com a assinatura válida, confirme o estado na API
const apiRes = await fetch(`${BASE_URL}/payments/${event.data.id}`, {
headers: {
Authorization: `Bearer ${process.env.NINEPAY_TOKEN}`,
Accept: 'application/json',
},
});
const { data: payment } = await apiRes.json();
if (payment.status === 'paid') {
// libere o acesso do cliente aqui
}
}
}).listen(3000);