103 lines
3.4 KiB
TypeScript
103 lines
3.4 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server";
|
||
import { abacatePay } from "@/lib/abacatepay";
|
||
|
||
export async function POST(request: NextRequest) {
|
||
try {
|
||
const payload = await request.text();
|
||
const signature = request.headers.get("x-abacatepay-signature");
|
||
|
||
if (!signature || !abacatePay.verifyWebhookSignature(payload, signature)) {
|
||
return NextResponse.json(
|
||
{ error: "Assinatura inválida" },
|
||
{ status: 401 }
|
||
);
|
||
}
|
||
|
||
const event = JSON.parse(payload);
|
||
|
||
console.log("🔔 Webhook recebido:", JSON.stringify(event, null, 2));
|
||
|
||
// Verificar se é evento de pagamento confirmado
|
||
const isPaidEvent =
|
||
event.event === "billing.paid" ||
|
||
event.event === "pix.paid" ||
|
||
event.data?.pixQrCode?.status === "PAID";
|
||
|
||
if (isPaidEvent) {
|
||
// Extrair dados do PIX
|
||
const pixQrCode = event.data.pixQrCode;
|
||
const payment = event.data.payment;
|
||
|
||
if (!pixQrCode || pixQrCode.status !== "PAID") {
|
||
console.log("⚠️ Webhook recebido mas status não é PAID");
|
||
return NextResponse.json({ success: true });
|
||
}
|
||
|
||
console.log("✅ Doação confirmada via webhook:", pixQrCode.id);
|
||
|
||
// Detectar se é doação anônima ou identificada
|
||
const isAnonymous = !pixQrCode.customer;
|
||
|
||
// Log estruturado para futura persistência em banco de dados
|
||
const donationRecord: any = {
|
||
type: "donation_paid",
|
||
pixId: pixQrCode.id,
|
||
amount: pixQrCode.amount,
|
||
fee: payment?.fee,
|
||
method: payment?.method,
|
||
anonymous: isAnonymous,
|
||
paidAt: new Date().toISOString(),
|
||
createdAt: new Date().toISOString(),
|
||
devMode: event.devMode || false,
|
||
};
|
||
|
||
// Adicionar dados do customer se disponível (doação identificada)
|
||
if (!isAnonymous && pixQrCode.customer?.metadata) {
|
||
donationRecord.customer = {
|
||
id: pixQrCode.customer.id,
|
||
name: pixQrCode.customer.metadata.name,
|
||
email: pixQrCode.customer.metadata.email,
|
||
cellphone: pixQrCode.customer.metadata.cellphone,
|
||
taxId: pixQrCode.customer.metadata.taxId,
|
||
};
|
||
|
||
console.log(
|
||
"📧 Email disponível para agradecimento:",
|
||
pixQrCode.customer.metadata.email
|
||
);
|
||
}
|
||
|
||
console.log("📝 Registro de pagamento:", donationRecord);
|
||
|
||
// Registar pixId confirmado (para frontend detectar via localStorage)
|
||
// O frontend salva pixIds confirmados em localStorage via evento do servidor
|
||
// Isso permite detecção em tempo real sem polling na API
|
||
|
||
// TODO: Quando implementar banco de dados, salvar aqui:
|
||
// await db.donations.create(donationRecord);
|
||
|
||
// TODO: Quando implementar email, enviar aqui:
|
||
// if (!isAnonymous && donationRecord.customer?.email) {
|
||
// await sendThankYouEmail({
|
||
// to: donationRecord.customer.email,
|
||
// donorName: donationRecord.customer.name,
|
||
// amount: donationRecord.amount,
|
||
// });
|
||
// }
|
||
|
||
// TODO: Atualizar estatísticas
|
||
// await updateDonationStats(donationRecord.amount);
|
||
} else {
|
||
console.log("ℹ️ Evento de webhook (não é pagamento):", event.event);
|
||
}
|
||
|
||
return NextResponse.json({ success: true });
|
||
} catch (error) {
|
||
console.error("Webhook error:", error);
|
||
return NextResponse.json(
|
||
{ error: "Erro ao processar webhook" },
|
||
{ status: 500 }
|
||
);
|
||
}
|
||
}
|