- 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
108 lines
3.6 KiB
TypeScript
108 lines
3.6 KiB
TypeScript
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 }
|
||
);
|
||
}
|
||
}
|