Skip to main content

Webhooks e Postbacks

O NextPay Gateway oferece duas formas de receber notificações sobre mudanças de status das transações:
  1. Webhook Permanente: Configurado no painel do usuário, recebe notificações de todas as transações
  2. Postback por Transação: URL específica configurada no campo postbackUrl ao criar uma venda

Diferenças entre Webhook e Postback

Webhook Permanente

  • Configurado no painel do usuário
  • Recebe notificações de todas as transações
  • Inclui campos adicionais: fee, transactionId, type
  • Sem assinatura HMAC

Postback por Transação

  • Configurado no campo postbackUrl da venda
  • Recebe notificação apenas da transação específica
  • Inclui externalTransactionId
  • Com assinatura HMAC no header X-Signature

Webhook Permanente

Webhooks permanentes são configurados no painel do usuário e recebem notificações de todas as transações do usuário.

Configuração

Configure seu webhook permanente no painel administrativo do NextPay Gateway. O webhook será chamado sempre que uma transação mudar de status.

Formato da Notificação

O webhook permanente envia um payload com os seguintes campos:
{
  "id": 789,
  "userId": 1,
  "amount": "29900",
  "date": "2025-02-10T15:30:00.000Z",
  "status": "PAGO",
  "paymentMethod": "PIX",
  "customerDocument": "12345678901",
  "customerEmail": "[email protected]",
  "customerName": "Maria Silva Santos",
  "customerPhone": "11999887766",
  "pixKey": "00020126870014br.gov.bcb.pix2565pix.creditag.com.br/qr/v3/at/abc123...",
  "boletoCode": null,
  "shipping": null,
  "fee": 0,
  "saleItems": [
    {
      "id": 789,
      "saleId": 789,
      "title": "Curso de Programação",
      "unitPrice": "29900",
      "quantity": 1,
      "tangible": false
    }
  ],
  "transactionId": 789,
  "type": "TRANSACTION"
}

Campos do Webhook Permanente

id
integer
required
ID da transação (Sale ID)
userId
integer
required
ID do usuário (vendedor)
amount
string
required
Valor da transação em centavos (formato string)
date
string
required
Data de criação da transação (ISO 8601)
status
string
required
Status atual da transação. Valores possíveis: PENDENTE, EM_PROCESSAMENTO, PAGO, CANCELADO, RECUSADO, ESTORNADO, FALHA, CHARGEBACK, MED
paymentMethod
string
required
Método de pagamento. Valores possíveis: PIX, CREDIT_CARD, DEBIT_CARD, BOLETO
customerDocument
string
Documento do cliente (CPF ou CNPJ)
customerEmail
string
Email do cliente
customerName
string
Nome completo do cliente
customerPhone
string
Telefone do cliente (apenas dígitos)
pixKey
string
Chave PIX gerada (apenas para pagamentos PIX)
boletoCode
string
Código do boleto (apenas para pagamentos via boleto)
shipping
string
Informações de entrega em formato JSON string (apenas para produtos físicos). null para produtos digitais.
fee
number
required
Taxa cobrada na transação em centavos. Apenas no webhook permanente.
saleItems
array
required
Lista de itens da transação
saleItems[].id
integer
required
ID do item
saleItems[].saleId
integer
required
ID da venda (mesmo que id)
saleItems[].title
string
required
Título do produto
saleItems[].unitPrice
string
required
Preço unitário em centavos (formato string)
saleItems[].quantity
integer
required
Quantidade do item
saleItems[].tangible
boolean
required
Se o item é físico (true) ou digital (false)
transactionId
integer
required
ID da transação (mesmo que id). Apenas no webhook permanente.
type
string
required
Tipo da notificação. Sempre "TRANSACTION" para webhooks de transações. Apenas no webhook permanente.

Postback por Transação

Postbacks são URLs específicas configuradas no campo postbackUrl ao criar uma venda. Recebem notificações apenas daquela transação específica.

Configuração

Configure o postback incluindo o campo postbackUrl ao criar a transação:
{
  "amount": 29900,
  "paymentMethod": "PIX",
  "customer": {
    "name": "Maria Silva Santos",
    "email": "[email protected]"
  },
  "items": [...],
  "postbackUrl": "https://seu-servidor.com/webhook/transacao"
}

Segurança

Postbacks incluem assinatura HMAC-SHA256 no header X-Signature para validação de segurança. A assinatura é gerada usando a chave secreta POSTBACK_SECRET_KEY configurada no ambiente do NextPay Gateway.
Importante: Sempre valide a assinatura HMAC antes de processar a notificação. Isso garante que a requisição realmente veio do NextPay Gateway e não foi alterada durante a transmissão.
Como funciona a assinatura:
  1. O NextPay Gateway gera uma assinatura HMAC-SHA256 do payload JSON usando a chave secreta POSTBACK_SECRET_KEY
  2. A assinatura é enviada no header HTTP X-Signature
  3. Você deve validar a assinatura comparando com a assinatura esperada gerada localmente
Exemplo de requisição com assinatura:
POST https://seupostbackurl.com.br/webhook/transacao
Content-Type: application/json
X-Signature: a1b2c3d4e5f6789012345678901234567890abcdef1234567890abcdef123456
Validação da Assinatura em Node.js:
const crypto = require('crypto');

function validateSignature(payload, signature, secretKey) {
  // Gerar assinatura esperada usando a mesma chave secreta
  const hmac = crypto.createHmac('sha256', secretKey);
  hmac.update(JSON.stringify(payload));
  const expectedSignature = hmac.digest('hex');
  
  // Comparar assinaturas de forma segura (timing-safe)
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}

// Uso no endpoint
app.post('/webhook/transacao', (req, res) => {
  const signature = req.headers['x-signature'];
  const payload = req.body;
  const secretKey = process.env.POSTBACK_SECRET_KEY;
  
  if (!validateSignature(payload, signature, secretKey)) {
    return res.status(401).json({ error: 'Assinatura inválida' });
  }
  
  // Processar notificação...
});
Validação da Assinatura em Python:
import hmac
import hashlib
import json
import os

def validate_signature(payload, signature, secret_key):
    """
    Valida a assinatura HMAC-SHA256 do postback.
    
    IMPORTANTE: O JSON deve ser stringificado SEM ordenação de chaves,
    exatamente como enviado pelo NextPay Gateway.
    """
    # Gerar assinatura esperada
    payload_json = json.dumps(payload, separators=(',', ':'))
    expected_signature = hmac.new(
        secret_key.encode('utf-8'),
        payload_json.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    
    # Comparar assinaturas de forma segura
    return hmac.compare_digest(signature, expected_signature)

# Uso no endpoint Flask
@app.route('/webhook/transacao', methods=['POST'])
def webhook_transacao():
    signature = request.headers.get('X-Signature')
    payload = request.json
    secret_key = os.environ.get('POSTBACK_SECRET_KEY')
    
    if not validate_signature(payload, signature, secret_key):
        return jsonify({'error': 'Assinatura inválida'}), 401
    
    # Processar notificação...
    return jsonify({'success': True}), 200
Sobre a chave secreta: A chave POSTBACK_SECRET_KEY é configurada no ambiente do NextPay Gateway. Esta é a mesma chave que você deve usar para validar as assinaturas dos postbacks. Entre em contato com o suporte técnico para obter ou configurar sua chave secreta de postback.Nota importante: O JSON do payload é stringificado sem ordenação de chaves (JSON.stringify padrão). Não use sort_keys=True no Python ou qualquer ordenação de chaves, pois isso resultará em assinaturas diferentes e a validação falhará.

Formato da Notificação

O postback por transação envia um payload com os seguintes campos:
{
  "id": 789,
  "userId": 1,
  "amount": "29900",
  "date": "2025-02-10T15:30:00.000Z",
  "status": "PAGO",
  "paymentMethod": "PIX",
  "customerDocument": "12345678901",
  "customerEmail": "[email protected]",
  "customerName": "Maria Silva Santos",
  "customerPhone": "11999887766",
  "pixKey": "00020126870014br.gov.bcb.pix2565pix.creditag.com.br/qr/v3/at/abc123...",
  "boletoCode": null,
  "shipping": null,
  "saleItems": [
    {
      "id": 789,
      "saleId": 789,
      "title": "Curso de Programação",
      "unitPrice": "29900",
      "quantity": 1,
      "tangible": false
    }
  ],
  "externalTransactionId": "103693454"
}

Campos do Postback por Transação

id
integer
required
ID da transação (Sale ID)
userId
integer
required
ID do usuário (vendedor)
amount
string
required
Valor da transação em centavos (formato string)
date
string
required
Data de criação da transação (ISO 8601)
status
string
required
Status atual da transação. Valores possíveis: PENDENTE, EM_PROCESSAMENTO, PAGO, CANCELADO, RECUSADO, ESTORNADO, FALHA, CHARGEBACK, MED
paymentMethod
string
required
Método de pagamento. Valores possíveis: PIX, CREDIT_CARD, DEBIT_CARD, BOLETO
customerDocument
string
Documento do cliente (CPF ou CNPJ)
customerEmail
string
Email do cliente
customerName
string
Nome completo do cliente
customerPhone
string
Telefone do cliente (apenas dígitos)
pixKey
string
Chave PIX gerada (apenas para pagamentos PIX)
boletoCode
string
Código do boleto (apenas para pagamentos via boleto)
shipping
string
Informações de entrega em formato JSON string (apenas para produtos físicos). null para produtos digitais.
saleItems
array
required
Lista de itens da transação
externalTransactionId
string
required
ID da transação no gateway de pagamento externo (adquirente). Apenas no postback por transação.

Comparação dos Formatos

CampoWebhook PermanentePostback por Transação
id
userId
amount
date
status
paymentMethod
customerDocument
customerEmail
customerName
customerPhone
pixKey
boletoCode
shipping
saleItems
fee
transactionId
type✅ ("TRANSACTION")
externalTransactionId
Assinatura HMAC✅ (X-Signature header)

Quando as Notificações São Enviadas

As notificações são enviadas quando a transação muda para um status final:
  • PAGO - Pagamento confirmado
  • CANCELADO - Transação cancelada
  • RECUSADO - Pagamento recusado
  • ESTORNADO - Valor estornado
  • FALHA - Falha no processamento
  • CHARGEBACK - Chargeback identificado (cartão)
  • MED - Mediação PIX iniciada
Nota: Status intermediários como PENDENTE e EM_PROCESSAMENTO não geram notificações.

Exemplo de Implementação

Node.js/Express

const express = require('express');
const crypto = require('crypto');
const app = express();

app.use(express.json());

// Postback por Transação (com validação HMAC)
app.post('/webhook/transacao', (req, res) => {
  const signature = req.headers['x-signature'];
  const payload = req.body;
  const secretKey = process.env.POSTBACK_SECRET_KEY;
  
  // Validar assinatura
  const hmac = crypto.createHmac('sha256', secretKey);
  hmac.update(JSON.stringify(payload));
  const expectedSignature = hmac.digest('hex');
  
  // Comparação segura contra timing attacks
  if (!crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  )) {
    return res.status(401).json({ error: 'Assinatura inválida' });
  }
  
  // Processar notificação
  console.log('Transação atualizada:', {
    id: payload.id,
    status: payload.status,
    amount: payload.amount
  });
  
  res.status(200).json({ success: true });
});

// Webhook Permanente (sem validação HMAC)
app.post('/webhook/permanente', (req, res) => {
  const payload = req.body;
  
  // Processar notificação
  console.log('Transação atualizada:', {
    id: payload.id,
    status: payload.status,
    fee: payload.fee,
    type: payload.type
  });
  
  res.status(200).json({ success: true });
});

app.listen(3000);

Python/Flask

from flask import Flask, request, jsonify
import hmac
import hashlib
import json
import os

app = Flask(__name__)

def validate_signature(payload, signature, secret_key):
    """
    Valida a assinatura HMAC-SHA256.
    IMPORTANTE: Não use sort_keys=True - o JSON deve ser stringificado
    exatamente como enviado pelo NextPay Gateway.
    """
    payload_json = json.dumps(payload, separators=(',', ':'))
    expected_signature = hmac.new(
        secret_key.encode('utf-8'),
        payload_json.encode('utf-8'),
        hashlib.sha256
    ).hexdigest()
    
    return hmac.compare_digest(signature, expected_signature)

# Postback por Transação (com validação HMAC)
@app.route('/webhook/transacao', methods=['POST'])
def webhook_transacao():
    signature = request.headers.get('X-Signature')
    payload = request.json
    secret_key = os.environ.get('POSTBACK_SECRET_KEY')
    
    # Validar assinatura
    if not validate_signature(payload, signature, secret_key):
        return jsonify({'error': 'Assinatura inválida'}), 401
    
    # Processar notificação
    print(f"Transação atualizada: {payload['id']} - {payload['status']}")
    
    return jsonify({'success': True}), 200

# Webhook Permanente (sem validação HMAC)
@app.route('/webhook/permanente', methods=['POST'])
def webhook_permanente():
    payload = request.json
    
    # Processar notificação
    print(f"Transação atualizada: {payload['id']} - {payload['status']} - Fee: {payload['fee']}")
    
    return jsonify({'success': True}), 200

if __name__ == '__main__':
    app.run(port=3000)

Boas Práticas

Importante:
  • Sempre retorne HTTP 200 para indicar que a notificação foi recebida com sucesso
  • Implemente idempotência para evitar processamento duplicado
  • Sempre valide a assinatura HMAC em postbacks por transação antes de processar
  • Use comparação timing-safe para validar assinaturas (evita timing attacks)
  • Use HTTPS para proteger os dados em trânsito
  • Implemente retry logic no seu servidor caso a notificação falhe
  • Mantenha a chave POSTBACK_SECRET_KEY segura e nunca a exponha em código frontend
Dica: Para testar webhooks localmente, use ferramentas como ngrok ou localtunnel para expor seu servidor local.