import { NextRequest, NextResponse } from "next/server" import prisma from "@/lib/prisma" import { createPledgeSchema } from "@/lib/validators" import { generateReference } from "@/lib/reference" import { calculateReminderSchedule } from "@/lib/reminders" import { sendPledgeReceipt } from "@/lib/whatsapp" export async function POST(request: NextRequest) { try { const body = await request.json() if (!prisma) { return NextResponse.json({ error: "Database not configured" }, { status: 503 }) } const parsed = createPledgeSchema.safeParse(body) if (!parsed.success) { return NextResponse.json( { error: "Invalid data", details: parsed.error.flatten() }, { status: 400 } ) } const { amountPence, rail, donorName, donorEmail, donorPhone, giftAid, eventId, qrSourceId } = parsed.data // Get event + org const event = await prisma.event.findUnique({ where: { id: eventId }, include: { organization: true }, }) if (!event) { return NextResponse.json({ error: "Event not found" }, { status: 404 }) } const org = event.organization // Generate unique reference (retry on collision) let reference = "" let attempts = 0 while (attempts < 10) { reference = generateReference(org.refPrefix || "PNPL", amountPence) const exists = await prisma.pledge.findUnique({ where: { reference } }) if (!exists) break attempts++ } if (attempts >= 10) { return NextResponse.json({ error: "Could not generate unique reference" }, { status: 500 }) } // Create pledge + payment instruction + reminder schedule in transaction // eslint-disable-next-line @typescript-eslint/no-explicit-any const pledge = await prisma.$transaction(async (tx: any) => { const p = await tx.pledge.create({ data: { reference, amountPence, currency: "GBP", rail, status: "new", donorName: donorName || null, donorEmail: donorEmail || null, donorPhone: donorPhone || null, giftAid, eventId, qrSourceId: qrSourceId || null, organizationId: org.id, }, }) // Create payment instruction for bank transfers if (rail === "bank" && org.bankSortCode && org.bankAccountNo) { await tx.paymentInstruction.create({ data: { pledgeId: p.id, bankReference: reference, bankDetails: { bankName: org.bankName || "", sortCode: org.bankSortCode, accountNo: org.bankAccountNo, accountName: org.bankAccountName || org.name, }, }, }) } // Create reminder schedule const schedule = calculateReminderSchedule(new Date()) await tx.reminder.createMany({ data: schedule.map((s) => ({ pledgeId: p.id, step: s.step, channel: s.channel, scheduledAt: s.scheduledAt, status: "pending", payload: { templateKey: s.templateKey, subject: s.subject }, })), }) // Track analytics await tx.analyticsEvent.create({ data: { eventType: "pledge_completed", pledgeId: p.id, eventId, qrSourceId: qrSourceId || null, metadata: { amountPence, rail }, }, }) return p }) // Build response const response: Record = { id: pledge.id, reference: pledge.reference, } if (rail === "bank" && org.bankSortCode) { response.bankDetails = { bankName: org.bankName || "", sortCode: org.bankSortCode, accountNo: org.bankAccountNo || "", accountName: org.bankAccountName || org.name, } } // Async: Send WhatsApp receipt to donor (non-blocking) if (donorPhone) { sendPledgeReceipt(donorPhone, { donorName: donorName || undefined, amountPounds: (amountPence / 100).toFixed(0), eventName: event.name, reference: pledge.reference, rail, bankDetails: rail === "bank" && org.bankSortCode ? { sortCode: org.bankSortCode, accountNo: org.bankAccountNo || "", accountName: org.bankAccountName || org.name, } : undefined, orgName: org.name, }).catch(err => console.error("[WAHA] Receipt send failed:", err)) } // Async: Notify volunteer if QR source has volunteer info if (qrSourceId) { prisma?.qrSource.findUnique({ where: { id: qrSourceId }, select: { volunteerName: true, label: true, pledges: { select: { amountPence: true } } }, }).then(qr => { // In future: if volunteer has a phone number stored, send WhatsApp notification // For now, this is a no-op unless volunteer phone is added to schema if (qr) { console.log(`[PLEDGE] ${qr.volunteerName || qr.label}: +1 pledge (£${(amountPence / 100).toFixed(0)})`) } }).catch(() => {}) } return NextResponse.json(response, { status: 201 }) } catch (error) { console.error("Pledge creation error:", error) return NextResponse.json({ error: "Internal error" }, { status: 500 }) } }