Webhook de Cobrança
Quando o status de uma cobrança muda, a A55 envia um POST com um corpo JSON para a sua webhook_url.
Campos do payload
| Campo | Tipo | Descrição |
|---|---|---|
charge_uuid | string (UUID) | Identificador da cobrança |
status | string | Status atual: confirmed, error, refunded, chargeback_requested, chargeback_refunded |
transaction_reference | string | Sua referência externa (se fornecida na criação) |
subscription_uuid | string | Identificador da assinatura (se aplicável) |
amount | number | Valor da cobrança |
currency | string | Código de moeda ISO |
created_at | string | Timestamp de criação (ISO 8601) |
updated_at | string | Timestamp da última atualização (ISO 8601) |
Exemplos de payload por status
- Confirmado
- Erro / Recusado
- Estornado
- Chargeback
{
"charge_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "confirmed",
"transaction_reference": "ord_001",
"amount": 100.50,
"currency": "BRL",
"created_at": "2026-03-20T10:00:00Z",
"updated_at": "2026-03-20T10:00:15Z"
}
{
"charge_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "error",
"transaction_reference": "ord_001",
"amount": 100.50,
"currency": "BRL",
"created_at": "2026-03-20T10:00:00Z",
"updated_at": "2026-03-20T10:00:20Z"
}
{
"charge_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "refunded",
"transaction_reference": "ord_001",
"amount": 100.50,
"currency": "BRL",
"created_at": "2026-03-20T10:00:00Z",
"updated_at": "2026-03-21T14:30:00Z"
}
{
"charge_uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"status": "chargeback_requested",
"transaction_reference": "ord_001",
"amount": 100.50,
"currency": "BRL",
"created_at": "2026-03-20T10:00:00Z",
"updated_at": "2026-03-25T09:00:00Z"
}
Código de tratamento
- Python (Flask)
- JavaScript (Express)
import json, os
from flask import Flask, request, Response
app = Flask(__name__)
@app.route("/webhooks/a55", methods=["POST"])
def handle_charge_webhook():
raw = request.get_data()
sig = request.headers.get("X-Webhook-Signature", "")
ts = request.headers.get("X-Webhook-Timestamp", "")
if not verify_signature(raw, sig, ts, os.environ["WEBHOOK_SECRET"]):
return Response(status=401)
event = json.loads(raw)
charge_uuid = event["charge_uuid"]
status = event["status"]
if already_processed(charge_uuid, status):
return Response(status=200)
if status == "confirmed":
fulfill_order(charge_uuid)
elif status == "error":
notify_payment_failed(charge_uuid)
elif status == "refunded":
process_refund(charge_uuid)
elif status == "chargeback_requested":
flag_chargeback(charge_uuid)
mark_processed(charge_uuid, status)
return Response(status=200)
const express = require('express');
const crypto = require('crypto');
const app = express();
app.post('/webhooks/a55', express.raw({ type: 'application/json' }), (req, res) => {
const sig = req.headers['x-webhook-signature'] || '';
const ts = req.headers['x-webhook-timestamp'] || '';
if (!verifySignature(req.body, sig, ts, process.env.WEBHOOK_SECRET)) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body);
const { charge_uuid, status } = event;
switch (status) {
case 'confirmed':
fulfillOrder(charge_uuid);
break;
case 'error':
notifyPaymentFailed(charge_uuid);
break;
case 'refunded':
processRefund(charge_uuid);
break;
case 'chargeback_requested':
flagChargeback(charge_uuid);
break;
}
res.sendStatus(200);
});
Boas práticas
| Prática | Por quê |
|---|---|
| Verifique a assinatura HMAC primeiro | Previna eventos forjados |
| Retorne 200 imediatamente | Evite timeouts e retentativas |
| Processe em fila de background | Trabalho pesado não deve bloquear o handler HTTP |
Deduplicar por charge_uuid + status | Entrega at-least-once significa que duplicatas são possíveis |
| Registre payloads brutos | Trilha de auditoria para debugging |
| Nunca confie no status vindo apenas da URL de redirect | Sempre confirme via webhook |