Começando
Aqui você encontra toda a documentação da API PayAlfa v2. Abaixo seguem algumas orientações gerais sobre a utilização dos nossos serviços.
https://api.payalfa.app/v2/
Autenticação
Toda requisição leva as suas credenciais client_id e client_secret como campos do corpo (POST) ou da query string (GET). Não existe token separado: as credenciais são a própria autenticação.
Credenciais de produção
Para obter as credenciais de produção (client_id e client_secret), entre no painel e abra Credenciais e IPs em https://payalfa.app/keys.
Obtendo suas credenciais de acesso ao sandbox
Para obter suas credenciais, basta solicitá-las ao nosso time de suporte através do seu gerente.
Exemplo de integração
Veja abaixo exemplos de como realizar uma requisição para gerar um QR Code Pix:
$ch = curl_init('https://api.payalfa.app/v2/pix/qrcode.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'client_id' => getenv('PAYALFA_CLIENT_ID'),
'client_secret' => getenv('PAYALFA_CLIENT_SECRET'),
'nome' => 'João Silva',
'cpf' => '12345678901',
'valor' => 100.00,
'descricao' => 'Pagamento de serviço',
'urlnoty' => 'https://seusite.com/webhook.php'
])
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($response, true);
if ($httpCode === 200 && isset($data['qrcode'])) {
echo "QR Code gerado com sucesso!";
echo "QR Code: " . $data['qrcode'];
} else {
echo "Erro: " . ($data['message'] ?? 'Desconhecido');
}
import os
import requests
url = 'https://api.payalfa.app/v2/pix/qrcode.php'
payload = {
'client_id': os.getenv('PAYALFA_CLIENT_ID'),
'client_secret': os.getenv('PAYALFA_CLIENT_SECRET'),
'nome': 'João Silva',
'cpf': '12345678901',
'valor': 100.00,
'descricao': 'Pagamento de serviço',
'urlnoty': 'https://seusite.com/webhook.py'
}
response = requests.post(url, data=payload)
data = response.json()
if response.status_code == 200 and 'qrcode' in data:
print("QR Code gerado com sucesso!")
print(f"QR Code: {data['qrcode']}")
else:
print(f"Erro: {data.get('message', 'Desconhecido')}")
require 'net/http'
require 'json'
uri = URI('https://api.payalfa.app/v2/pix/qrcode.php')
params = {
client_id: ENV['PAYALFA_CLIENT_ID'],
client_secret: ENV['PAYALFA_CLIENT_SECRET'],
nome: 'João Silva',
cpf: '12345678901',
valor: 100.00,
descricao: 'Pagamento de serviço',
urlnoty: 'https://seusite.com/webhook.rb'
}
response = Net::HTTP.post_form(uri, params)
data = JSON.parse(response.body)
if response.code == '200' && data['qrcode']
puts "QR Code gerado com sucesso!"
puts "QR Code: #{data['qrcode']}"
else
puts "Erro: #{data['message'] || 'Desconhecido'}"
end
const axios = require('axios');
const payload = {
client_id: process.env.PAYALFA_CLIENT_ID,
client_secret: process.env.PAYALFA_CLIENT_SECRET,
nome: 'João Silva',
cpf: '12345678901',
valor: 100.00,
descricao: 'Pagamento de serviço',
urlnoty: 'https://seusite.com/webhook.js'
};
axios.post('https://api.payalfa.app/v2/pix/qrcode.php', payload)
.then(response => {
console.log("QR Code gerado com sucesso!");
console.log(`QR Code: ${response.data.qrcode}`);
})
.catch(error => {
console.error(`Erro: ${error.response?.data?.message || 'Desconhecido'}`);
});
Próximos passos
Agora que você já sabe como começar, explore os endpoints disponíveis:
- Gerar QRCode Pix - Crie QR codes para recebimento
- Fazer um pagamento - Realize transferências Pix
- Consultar status - Verifique o status de transações
- Webhooks - Configure notificações automáticas
Respostas HTTP
A API utiliza códigos de status HTTP padrão para indicar o sucesso ou falha de uma requisição.
Códigos de sucesso
| Código | Descrição |
|---|---|
200 |
OK - A requisição foi processada com sucesso |
Códigos de erro
| Código | Descrição |
|---|---|
400 |
Bad request - parâmetros inválidos ou ausentes |
401 |
Unauthorized - credenciais de autenticação inválidas |
403 |
Forbidden - IP não autorizado ou acesso negado |
404 |
Not found - recurso não encontrado |
405 |
Method not allowed - método HTTP diferente do aceito pelo endpoint |
500 |
Internal server error - erro interno do servidor |
Exemplo de resposta de erro
{
"statusCode": 400,
"message": "CPF inválido. Por favor, forneça um CPF válido."
}
Credenciais de acesso
Gerencie suas credenciais de acesso à API PayAlfa.
Nunca exponha seu client_secret em código client-side (JavaScript do navegador, aplicativos mobile, etc). Use apenas em requisições server-side.
Obtendo credenciais
- Acesse o painel de gerenciamento em https://payalfa.app/keys
- Copie seu
client_ideclient_secret - Armazene as credenciais de forma segura em variáveis de ambiente
- Use as credenciais em todas as requisições à API
Parâmetros de autenticação
| Parâmetro | Tipo | Descrição |
|---|---|---|
client_id |
string | Identificador único do cliente na API |
client_secret |
string | Chave secreta para autenticação (mantenha privada) |
Gerar QRCode
Cria um QR Code Pix para recebimento de pagamentos.
Parâmetros da requisição
| Parâmetro | Tipo | Descrição |
|---|---|---|
client_id |
string | Seu client ID (obrigatório) |
client_secret |
string | Sua chave secreta (obrigatório) |
nome |
string | Nome completo do pagador (obrigatório) |
cpf |
string | CPF do pagador - apenas números (obrigatório) |
valor |
float | Valor em reais - formato decimal (obrigatório) |
descricao |
string | Descrição do pagamento (opcional) |
urlnoty |
string | URL para receber notificações webhook (opcional) |
Exemplo de requisição
$ch = curl_init('https://api.payalfa.app/v2/pix/qrcode.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'client_id' => getenv('PAYALFA_CLIENT_ID'),
'client_secret' => getenv('PAYALFA_CLIENT_SECRET'),
'nome' => 'João Silva Santos',
'cpf' => '12345678901',
'valor' => 150.99,
'descricao' => 'Pagamento de serviço premium',
'urlnoty' => 'https://seusite.com/webhook/payalfa'
])
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($response, true);
if ($httpCode === 200 && isset($data['qrcode'])) {
echo "QR Code: " . $data['qrcode'] . PHP_EOL;
echo "Transaction ID: " . $data['transactionId'] . PHP_EOL;
} else {
echo "Erro: " . ($data['message'] ?? 'Erro desconhecido');
}
import os
import requests
url = 'https://api.payalfa.app/v2/pix/qrcode.php'
payload = {
'client_id': os.getenv('PAYALFA_CLIENT_ID'),
'client_secret': os.getenv('PAYALFA_CLIENT_SECRET'),
'nome': 'João Silva Santos',
'cpf': '12345678901',
'valor': 150.99,
'descricao': 'Pagamento de serviço premium',
'urlnoty': 'https://seusite.com/webhook/payalfa'
}
response = requests.post(url, data=payload)
data = response.json()
if response.status_code == 200 and 'qrcode' in data:
print(f"QR Code: {data['qrcode']}")
print(f"Transaction ID: {data['transactionId']}")
else:
print(f"Erro: {data.get('message', 'Erro desconhecido')}")
curl -X POST https://api.payalfa.app/v2/pix/qrcode.php \ -d "client_id=SEU_CLIENT_ID" \ -d "client_secret=SEU_CLIENT_SECRET" \ -d "nome=João Silva Santos" \ -d "cpf=12345678901" \ -d "valor=150.99" \ -d "descricao=Pagamento de serviço premium" \ -d "urlnoty=https://seusite.com/webhook/payalfa"
Resposta de sucesso
{
"statusCode": 200,
"message": "QR Code gerado com sucesso via PayAlfa",
"qrcode": "00020126850014br.gov.bcb.pix...",
"transactionId": "4392d1d7e408d3cec04fm1zf3gv7vkq1",
"amount": 150.99,
"reference_code": "4392d1d7e408d3cec04fm1zf3gv7vkq1",
"gateway": "payalfa"
}
Use o transactionId retornado para consultar o status do pagamento através do endpoint Consultar status.
Fazer um pagamento
Realiza transferências Pix para chaves Pix de terceiros.
Para realizar transferências Pix via API, é obrigatório fazer a liberação do endereço IP do servidor de onde serão feitas as requisições. Sem a liberação do IP, todas as requisições de transferência serão bloqueadas automaticamente.
Descubra o IP do seu servidor em: https://payalfa.app/meuip
Adicione o IP na lista de IPs liberados em: https://payalfa.app/keys
Parâmetros da requisição
| Parâmetro | Tipo | Descrição |
|---|---|---|
client_id |
string | Seu client ID (obrigatório) |
client_secret |
string | Sua chave secreta (obrigatório) |
nome |
string | Nome do beneficiário (obrigatório) |
cpf |
string | CPF do beneficiário - apenas números (obrigatório) |
valor |
float | Valor da transferência em reais (obrigatório) |
chave_pix |
string | Chave Pix do destinatário (obrigatório) |
descricao |
string | Descrição da transferência (opcional) |
urlnoty |
string | URL para notificações webhook (opcional) |
Exemplo de requisição
$ch = curl_init('https://api.payalfa.app/v2/pix/payment.php');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query([
'client_id' => getenv('PAYALFA_CLIENT_ID'),
'client_secret' => getenv('PAYALFA_CLIENT_SECRET'),
'nome' => 'Maria Silva',
'cpf' => '98765432100',
'valor' => 250.75,
'chave_pix' => '11970142332',
'descricao' => 'Pagamento de fornecedor',
'urlnoty' => 'https://seusite.com/webhook/payalfa'
])
]);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($response, true);
if ($httpCode === 200) {
echo "Status: " . $data['status'] . PHP_EOL;
echo "Transaction ID: " . $data['transactionId'];
} else {
echo "Erro: " . ($data['message'] ?? 'Erro desconhecido');
}
import os
import requests
url = 'https://api.payalfa.app/v2/pix/payment.php'
payload = {
'client_id': os.getenv('PAYALFA_CLIENT_ID'),
'client_secret': os.getenv('PAYALFA_CLIENT_SECRET'),
'nome': 'Maria Silva',
'cpf': '98765432100',
'valor': 250.75,
'chave_pix': '11970142332',
'descricao': 'Pagamento de fornecedor',
'urlnoty': 'https://seusite.com/webhook/payalfa'
}
response = requests.post(url, data=payload)
data = response.json()
if response.status_code == 200:
print(f"Status: {data['status']}")
print(f"Transaction ID: {data['transactionId']}")
else:
print(f"Erro: {data.get('message', 'Erro desconhecido')}")
curl -X POST https://api.payalfa.app/v2/pix/payment.php \ -d "client_id=SEU_CLIENT_ID" \ -d "client_secret=SEU_CLIENT_SECRET" \ -d "nome=Maria Silva" \ -d "cpf=98765432100" \ -d "valor=250.75" \ -d "chave_pix=11970142332" \ -d "descricao=Pagamento de fornecedor" \ -d "urlnoty=https://seusite.com/webhook/payalfa"
Resposta de sucesso
{
"statusCode": 200,
"message": "Pagamento processado com sucesso",
"transactionId": "e7f8a9b3c4d5e6f7g8h9i0j1k2l3m4n5",
"external_id": "e7f8a9b3c4d5e6f7g8h9i0j1k2l3m4n5",
"status": "PAID",
"gateway": "payalfa"
}
Alguns pagamentos podem retornar com status: "PENDING", indicando que estão em processamento. Use o endpoint Consultar status para verificar a confirmação.
Consultar status
Verifica o status atual de uma transação Pix (depósito ou saque).
Use este endpoint para verificar se um QR Code foi pago ou se uma transferência foi confirmada. Ideal para polling ou validação manual de transações.
Parâmetros da requisição
| Parâmetro | Tipo | Descrição |
|---|---|---|
client_id |
string | Seu client ID (obrigatório) |
client_secret |
string | Sua chave secreta (obrigatório) |
transaction_id |
string | ID da transação retornado ao gerar QRCode/pagamento (obrigatório*) |
reference_code |
string | Código de referência alternativo (obrigatório*) |
* Informe pelo menos um dos dois: transaction_id ou reference_code. A consulta também aceita POST com os mesmos campos.
Exemplo de requisição
$transactionId = '0eaf56ba401c9bfa5d61mkm3ch551oyt';
$url = 'https://api.payalfa.app/v2/pix/status.php?' . http_build_query([
'client_id' => getenv('PAYALFA_CLIENT_ID'),
'client_secret' => getenv('PAYALFA_CLIENT_SECRET'),
'transaction_id' => $transactionId
]);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($response, true);
if ($httpCode === 200) {
echo "Status: " . $data['transaction']['status'] . PHP_EOL;
echo "Tipo: " . $data['transaction']['type'] . PHP_EOL;
echo "Valor: R$ " . $data['transaction']['amount'];
if ($data['transaction']['status'] === 'PAID') {
echo PHP_EOL . "Pago em: " . $data['transaction']['paid_at'];
}
} else {
echo "Erro: " . ($data['message'] ?? 'Erro desconhecido');
}
import os
import requests
transaction_id = '0eaf56ba401c9bfa5d61mkm3ch551oyt'
url = 'https://api.payalfa.app/v2/pix/status.php'
params = {
'client_id': os.getenv('PAYALFA_CLIENT_ID'),
'client_secret': os.getenv('PAYALFA_CLIENT_SECRET'),
'transaction_id': transaction_id
}
response = requests.get(url, params=params)
data = response.json()
if response.status_code == 200:
print(f"Status: {data['transaction']['status']}")
print(f"Tipo: {data['transaction']['type']}")
print(f"Valor: R$ {data['transaction']['amount']}")
if data['transaction']['status'] == 'PAID':
print(f"Pago em: {data['transaction']['paid_at']}")
else:
print(f"Erro: {data.get('message', 'Erro desconhecido')}")
curl -X GET "https://api.payalfa.app/v2/pix/status.php?client_id=SEU_CLIENT_ID&client_secret=SEU_CLIENT_SECRET&transaction_id=0eaf56ba401c9bfa5d61mkm3ch551oyt"
Resposta - transação PAGA
{
"statusCode": 200,
"message": "Transação confirmada com sucesso",
"transaction": {
"transactionId": "0eaf56ba401c9bfa5d61mkm3ch551oyt",
"id": "0eaf56ba401c9bfa5d61mkm3ch551oyt",
"end2end": "E18236120202601200426s0123456789",
"status": "PAID",
"type": "DEPOSIT",
"amount": 10.00,
"tax": 0.50,
"total": 10.50,
"nome": "João Silva",
"document": "12345678909",
"descricao": "Pedido 1234",
"created_at": "2026-01-20 01:25:36",
"confirmed_date": "2026-01-20 01:26:15",
"paid_at": "2026-01-20 01:26:15"
},
"gateway": "payalfa"
}
Resposta - transação PENDENTE
{
"statusCode": 200,
"message": "Transação aguardando confirmação",
"transaction": {
"transactionId": "0eaf56ba401c9bfa5d61mkm3ch551oyt",
"id": "0eaf56ba401c9bfa5d61mkm3ch551oyt",
"end2end": "",
"status": "PENDING",
"type": "DEPOSIT",
"amount": 10.00,
"tax": 0.50,
"total": 10.50,
"nome": "João Silva",
"document": "12345678909",
"descricao": "Pedido 1234",
"created_at": "2026-01-20 01:25:36",
"confirmed_date": null
},
"gateway": "payalfa"
}
Status possíveis
| Status | Descrição |
|---|---|
PENDING |
Aguardando confirmação do pagamento |
PAID |
Pagamento confirmado e processado |
FAILED |
Transação falhou no processamento |
CANCELLED |
Transação foi cancelada |
Tipos de transação
| Tipo | Descrição |
|---|---|
DEPOSIT |
Depósito via QR Code Pix |
WITHDRAW |
Saque ou transferência Pix |
Se você estiver fazendo polling (consultas periódicas), recomendamos um intervalo de 5 a 10 segundos entre cada consulta para evitar sobrecarga.
Consultar saldo
Consulte o saldo disponível e bloqueado da sua conta via API.
Parâmetros da requisição
| Parâmetro | Tipo | Obrigatório | Descrição |
|---|---|---|---|
client_id |
string | Sim | ID do cliente (fornecido pela PayAlfa) |
client_secret |
string | Sim | Chave secreta do cliente |
Exemplo de requisição
<?php
$client_id = 'seu_client_id';
$client_secret = 'seu_client_secret';
$url = 'https://api.payalfa.app/v2/account/balance.php?' . http_build_query([
'client_id' => $client_id,
'client_secret' => $client_secret
]);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
$data = json_decode($response, true);
echo "Saldo disponível: R$ " . $data['balance']['available'];
?>
import requests
client_id = 'seu_client_id'
client_secret = 'seu_client_secret'
url = 'https://api.payalfa.app/v2/account/balance.php'
params = {
'client_id': client_id,
'client_secret': client_secret
}
response = requests.get(url, params=params)
data = response.json()
print(f"Saldo disponível: R$ {data['balance']['available']}")
curl -X GET "https://api.payalfa.app/v2/account/balance.php?client_id=seu_client_id&client_secret=seu_client_secret"
const axios = require('axios');
const client_id = 'seu_client_id';
const client_secret = 'seu_client_secret';
const url = 'https://api.payalfa.app/v2/account/balance.php';
axios.get(url, {
params: {
client_id: client_id,
client_secret: client_secret
}
})
.then(response => {
console.log(`Saldo disponível: R$ ${response.data.balance.available}`);
})
.catch(error => {
console.error('Erro:', error.response.data);
});
Resposta de sucesso (200)
{
"statusCode": 200,
"message": "Saldo consultado com sucesso",
"balance": {
"available": 1250.50,
"blocked": 0.00,
"total": 1250.50
},
"user": {
"username": "exemplo_usuario",
"name": "João Silva"
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|---|---|
balance.available |
number | Saldo disponível para saque/uso |
balance.blocked |
number | Saldo bloqueado (em análise ou cautelar) |
balance.total |
number | Saldo total (disponível + bloqueado) |
user.username |
string | Nome de usuário da conta |
user.name |
string | Nome completo do titular |
Use este endpoint para exibir o saldo em tempo real no seu sistema, permitindo que seus usuários acompanhem suas transações e disponibilidade de recursos.
Mantenha o client_secret seguro e nunca o exponha em código front-end (JavaScript do navegador). Sempre faça requisições através do back-end da sua aplicação.
Evento de pagamento
Webhook enviado automaticamente para a urlnoty quando um pagamento Pix é recebido e confirmado.
Seu endpoint de webhook deve retornar HTTP 200 para confirmar o recebimento da notificação. Qualquer outro código HTTP fará a API reenviar a notificação.
Payload do Webhook
{
"transactionType": "RECEIVEPIX",
"transactionId": "a502e53d7e7d7c8afd0fmenrr80g57h0",
"amount": 150.99,
"paymentType": "PIX",
"status": "PAID",
"dateApproval": "2025-08-23 04:38:39"
}
Processamento do Webhook
<?php
// Receber dados do webhook
$input = file_get_contents('php://input');
$data = json_decode($input, true);
if (!$data) {
http_response_code(400);
die('Invalid JSON');
}
// Log para auditoria
file_put_contents('webhook.log',
date('Y-m-d H:i:s') . " - " . $input . PHP_EOL,
FILE_APPEND
);
// Processar pagamento recebido
if ($data['transactionType'] === 'RECEIVEPIX' && $data['status'] === 'PAID') {
$transactionId = $data['transactionId'];
$amount = $data['amount'];
// Sua lógica de negócio aqui
// Exemplo: liberar produto/serviço ao cliente
liberarProduto($transactionId, $amount);
// Confirmar recebimento
http_response_code(200);
echo "OK";
} else {
// Recebeu mas não processou
http_response_code(200);
echo "Received";
}
?>
from flask import Flask, request
import json
from datetime import datetime
app = Flask(__name__)
@app.route('/webhook/payalfa', methods=['POST'])
def webhook():
data = request.get_json()
if not data:
return 'Invalid JSON', 400
# Log para auditoria
with open('webhook.log', 'a') as f:
f.write(f"{datetime.now()} - {json.dumps(data)}\n")
# Processar pagamento recebido
if data.get('transactionType') == 'RECEIVEPIX' and data.get('status') == 'PAID':
transaction_id = data['transactionId']
amount = data['amount']
# Sua lógica de negócio aqui
liberar_produto(transaction_id, amount)
return 'OK', 200
return 'Received', 200
if __name__ == '__main__':
app.run()
const express = require('express');
const fs = require('fs');
const app = express();
app.use(express.json());
app.post('/webhook/payalfa', (req, res) => {
const data = req.body;
if (!data) {
return res.status(400).send('Invalid JSON');
}
// Log para auditoria
fs.appendFileSync('webhook.log',
`${new Date().toISOString()} - ${JSON.stringify(data)}\n`
);
// Processar pagamento recebido
if (data.transactionType === 'RECEIVEPIX' && data.status === 'PAID') {
const { transactionId, amount } = data;
// Sua lógica de negócio aqui
liberarProduto(transactionId, amount);
return res.status(200).send('OK');
}
res.status(200).send('Received');
});
app.listen(3000);
Use o transactionId retornado no webhook para rastrear e validar pagamentos. Este é o mesmo ID retornado na criação do QR Code.
Evento de transferência
Webhook enviado automaticamente para a urlnoty quando uma transferência Pix é concluída.
Payload do Webhook
{
"transactionType": "PAYMENT",
"transactionId": "e7f8a9b3c4d5e6f7g8h9i0j1k2l3m4n5",
"amount": 250.75,
"paymentType": "PIX",
"dateApproval": "2025-08-23 15:45:22",
"statusCode": {
"statusId": 1,
"description": "Transferência concluída com sucesso"
}
}
Campos do payload
| Campo | Descrição |
|---|---|
transactionId |
O mesmo ID devolvido pelo endpoint Fazer um pagamento |
amount |
Valor transferido, em reais |
statusCode.statusId |
1 = transferência concluída com sucesso |
dateApproval |
Data e hora da confirmação |
Este webhook é enviado quando a transferência é concluída. Para saber se uma transferência ficou pendente ou falhou, consulte o endpoint Consultar status com o transactionId.
Assinatura de Webhook (HMAC-SHA256)
Valide a autenticidade dos webhooks recebidos da PayAlfa usando assinatura HMAC-SHA256.
A assinatura HMAC é opcional. Se você não ativar no painel, os webhooks chegam normalmente sem o header de assinatura, e nenhuma integração existente é afetada.
Como funciona
- Acesse payalfa.app/keys e clique em Ativar assinatura de Webhook
- Confirme com seu PIN e copie o
webhook_secretgerado. Ele é exibido uma única vez. - A partir desse momento, todo webhook enviado pela PayAlfa incluirá o header:
X-PayAlfa-Signature: sha256=b94d27b9934d3e08a52e52d7da7dabfac484efe04b959a508a20e1aa9e8e5fa3
Como validar
No seu endpoint de webhook, recalcule a assinatura com HMAC-SHA256 usando o corpo bruto do request e compare com o header recebido. Use comparação segura para evitar timing attacks.
<?php
// Seu webhook_secret copiado do painel
$webhookSecret = getenv('PAYALFA_WEBHOOK_SECRET');
// Corpo bruto do request (antes de json_decode)
$body = file_get_contents('php://input');
// Header enviado pela PayAlfa
$signatureHeader = $_SERVER['HTTP_X_PAYALFA_SIGNATURE'] ?? '';
// Recalcula a assinatura esperada
$expected = 'sha256=' . hash_hmac('sha256', $body, $webhookSecret);
// Comparação segura (evita timing attack)
if (!hash_equals($expected, $signatureHeader)) {
http_response_code(401);
exit('Assinatura invalida');
}
// Assinatura valida - processa o payload
$data = json_decode($body, true);
if ($data['transactionType'] === 'RECEIVEPIX' && $data['status'] === 'PAID') {
liberarProduto($data['transactionId'], $data['amount']);
}
http_response_code(200);
echo 'OK';
import hmac
import hashlib
import os
from flask import Flask, request, abort
app = Flask(__name__)
WEBHOOK_SECRET = os.getenv('PAYALFA_WEBHOOK_SECRET')
@app.route('/webhook', methods=['POST'])
def webhook():
body = request.get_data()
signature_header = request.headers.get('X-PayAlfa-Signature', '')
expected = 'sha256=' + hmac.new(
WEBHOOK_SECRET.encode(),
body,
hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, signature_header):
abort(401, 'Assinatura invalida')
data = request.get_json()
if data.get('transactionType') == 'RECEIVEPIX' and data.get('status') == 'PAID':
liberar_produto(data['transactionId'], data['amount'])
return 'OK', 200
const express = require('express');
const crypto = require('crypto');
const app = express();
const WEBHOOK_SECRET = process.env.PAYALFA_WEBHOOK_SECRET;
// Importante: usar raw body para calcular a assinatura
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
const signatureHeader = req.headers['x-payalfa-signature'] || '';
const expected = 'sha256=' + crypto
.createHmac('sha256', WEBHOOK_SECRET)
.update(req.body)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signatureHeader))) {
return res.status(401).send('Assinatura invalida');
}
const data = JSON.parse(req.body);
if (data.transactionType === 'RECEIVEPIX' && data.status === 'PAID') {
liberarProduto(data.transactionId, data.amount);
}
res.status(200).send('OK');
});
app.listen(3000);
Use sempre o corpo bruto do request para calcular a assinatura, nunca o JSON já parseado. Qualquer alteração na ordem dos campos ou espaçamento invalida a assinatura.
Sem assinatura configurada
Se o usuário não ativou a assinatura no painel, o webhook chega sem o header X-PayAlfa-Signature. Nesse caso, o comportamento legado é mantido e nenhum código existente precisa ser alterado.
| Situação | Header presente? | Ação recomendada |
|---|---|---|
| HMAC ativado no painel | Sim |
Validar a assinatura antes de processar |
| HMAC não configurado | Não |
Processar normalmente (comportamento legado) |