Files
pagodera/app/api/webhooks/abacatepay/route.ts
T
Evandro Fonseca Junior 8271d1fa31 feat(thank-you-page): convert to client component with confetti and UI enhancements
- Changed from server to client component to enable interactive features like confetti animation
- Removed server-side translations and hardcoded Portuguese text for simplicity
- Added confetti effect on page load and improved success icon with animations
- Updated buttons and share functionality for better user experience
- This enhances the donation thank you page with celebratory elements and client-side interactivity
2025-10-26 22:12:43 -03:00

108 lines
3.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { NextRequest, NextResponse } from "next/server";
import { abacatePay } from "@/lib/abacatepay";
import { markAsPaid } from "@/lib/payment-cache";
export async function POST(request: NextRequest) {
try {
const payload = await request.text();
// AbacatePay envia webhookSecret como query param
const { searchParams } = new URL(request.url);
const webhookSecret = searchParams.get("webhookSecret");
const expectedSecret = process.env.ABACATEPAY_WEBHOOK_SECRET;
// Validar secret
if (!webhookSecret || !expectedSecret || webhookSecret !== expectedSecret) {
console.error("🔒 Webhook rejeitado: secret inválido ou ausente");
return NextResponse.json({ error: "Secret inválido" }, { status: 401 });
}
console.log("✅ Webhook autenticado com sucesso");
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);
// Salvar no cache para frontend detectar via polling
markAsPaid(pixQrCode.id, donationRecord);
// 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 }
);
}
}