import { NextRequest, NextResponse } from "next/server" import prisma from "@/lib/prisma" import { sendWhatsAppMessage } from "@/lib/whatsapp" import { setQrValue } from "@/lib/qr-store" /** * WAHA webhook — receives incoming WhatsApp messages + session status events * Handles: PAID, HELP, CANCEL commands from donors * Also captures QR code value for the pairing flow */ export async function POST(request: NextRequest) { try { const body = await request.json() const { event, payload } = body // Handle session.status events (for QR code capture) if (event === "session.status") { console.log("[WAHA] Session status:", JSON.stringify({ status: payload?.status, hasQr: !!payload?.qr, keys: Object.keys(payload || {}) })) // WAHA sends QR in different formats depending on version const qrVal = payload?.qr?.value || payload?.qr || payload?.qrCode if (payload?.status === "SCAN_QR_CODE" && qrVal && typeof qrVal === "string") { setQrValue(qrVal) console.log("[WAHA] QR value stored, length:", qrVal.length) } return NextResponse.json({ ok: true }) } // WAHA sends message events if (!payload?.body || !payload?.from) { return NextResponse.json({ ok: true }) } const text = payload.body.trim().toUpperCase() const fromPhone = payload.from.replace("@c.us", "") // Only handle known commands if (!["PAID", "HELP", "CANCEL", "STATUS"].includes(text)) { return NextResponse.json({ ok: true }) } if (!prisma) return NextResponse.json({ ok: true }) // Find pledges by this phone number // Normalize: try with and without country code const phoneVariants = [fromPhone] if (fromPhone.startsWith("44")) { phoneVariants.push("0" + fromPhone.slice(2)) phoneVariants.push("+" + fromPhone) } const pledges = await prisma.pledge.findMany({ where: { donorPhone: { in: phoneVariants }, status: { in: ["new", "initiated"] }, }, include: { event: { select: { name: true } }, paymentInstruction: true, }, orderBy: { createdAt: "desc" }, take: 5, }) if (pledges.length === 0) { await sendWhatsAppMessage(fromPhone, `We couldn't find any pending pledges for this number. If you need help, please contact the charity directly.`) return NextResponse.json({ ok: true }) } const pledge = pledges[0] // Most recent const amount = (pledge.amountPence / 100).toFixed(0) switch (text) { case "PAID": { await prisma.pledge.update({ where: { id: pledge.id }, data: { status: "initiated", iPaidClickedAt: new Date() }, }) await sendWhatsAppMessage(fromPhone, `āœ… Thanks! We've noted that you've paid your *Ā£${amount}* pledge to ${pledge.event.name}.\n\nWe'll confirm once the payment is matched. Ref: \`${pledge.reference}\`` ) break } case "HELP": { const bankDetails = pledge.paymentInstruction?.bankDetails as Record | null if (bankDetails) { await sendWhatsAppMessage(fromPhone, `šŸ¦ *Bank Details for your Ā£${amount} pledge:*\n\nSort Code: \`${bankDetails.sortCode}\`\nAccount: \`${bankDetails.accountNo}\`\nName: ${bankDetails.accountName}\nReference: \`${pledge.reference}\`\n\nāš ļø _Use the exact reference above_` ) } else { await sendWhatsAppMessage(fromPhone, `Your pledge ref is \`${pledge.reference}\` for Ā£${amount} to ${pledge.event.name}.\n\nContact the charity for payment details.` ) } break } case "CANCEL": { await prisma.pledge.update({ where: { id: pledge.id }, data: { status: "cancelled", cancelledAt: new Date() }, }) await sendWhatsAppMessage(fromPhone, `Your Ā£${amount} pledge to ${pledge.event.name} has been cancelled. No worries — thank you for considering! šŸ™` ) break } case "STATUS": { const statusText = pledges.map(p => `• Ā£${(p.amountPence / 100).toFixed(0)} → ${p.event.name} (${p.status}) [${p.reference}]` ).join("\n") await sendWhatsAppMessage(fromPhone, `šŸ“‹ *Your pledges:*\n\n${statusText}`) break } } return NextResponse.json({ ok: true }) } catch (error) { console.error("WhatsApp webhook error:", error) return NextResponse.json({ ok: true }) } }