crypto-commons-gather.ing-w.../app/api/webhook/route.ts

97 lines
3.2 KiB
TypeScript

import { type NextRequest, NextResponse } from "next/server"
import createMollieClient from "@mollie/api-client"
import { updatePaymentStatus } from "@/lib/google-sheets"
import { sendPaymentConfirmation } from "@/lib/email"
import { addToListmonk } from "@/lib/listmonk"
// Lazy initialization
let mollieClient: ReturnType<typeof createMollieClient> | null = null
function getMollie() {
if (!mollieClient) {
mollieClient = createMollieClient({ apiKey: process.env.MOLLIE_API_KEY! })
}
return mollieClient
}
export async function POST(request: NextRequest) {
try {
// Mollie sends payment ID in the body as form data
const formData = await request.formData()
const paymentId = formData.get("id") as string
if (!paymentId) {
console.error("[Webhook] No payment ID received")
return NextResponse.json({ error: "Missing payment ID" }, { status: 400 })
}
// Fetch the full payment from Mollie API (this is how you verify — no signature needed)
const payment = await getMollie().payments.get(paymentId)
const metadata = (payment.metadata || {}) as Record<string, string>
console.log(`[Webhook] Payment ${paymentId} status: ${payment.status}`)
if (payment.status === "paid") {
const customerEmail = payment.billingAddress?.email || ""
const amountPaid = `${payment.amount.value}`
// Update Google Sheet
const updated = await updatePaymentStatus({
name: metadata.name || "",
email: customerEmail,
paymentSessionId: paymentId,
paymentStatus: "Paid",
paymentMethod: payment.method || "unknown",
amountPaid,
paymentDate: new Date().toISOString(),
})
if (updated) {
console.log(`[Webhook] Google Sheet updated for ${metadata.name}`)
} else {
console.error(`[Webhook] Failed to update Google Sheet for ${metadata.name}`)
}
// Send confirmation email
if (customerEmail) {
await sendPaymentConfirmation({
name: metadata.name || "",
email: customerEmail,
amountPaid,
paymentMethod: payment.method || "card",
contributions: metadata.contributions || "",
dietary: metadata.dietary || "",
})
// Add to Listmonk newsletter
addToListmonk({
email: customerEmail,
name: metadata.name || "",
attribs: {
contact: metadata.contact,
contributions: metadata.contributions,
expectations: metadata.expectations,
},
}).catch((err) => console.error("[Webhook] Listmonk sync failed:", err))
}
} else if (payment.status === "failed" || payment.status === "canceled" || payment.status === "expired") {
console.log(`[Webhook] Payment ${payment.status}: ${paymentId}`)
if (metadata.name) {
await updatePaymentStatus({
name: metadata.name,
paymentSessionId: paymentId,
paymentStatus: "Failed",
paymentDate: new Date().toISOString(),
})
}
}
// Mollie expects 200 OK
return NextResponse.json({ received: true })
} catch (err) {
console.error("[Webhook] Error:", err)
return NextResponse.json({ error: "Webhook error" }, { status: 500 })
}
}