136 lines
4.0 KiB
TypeScript
136 lines
4.0 KiB
TypeScript
import { NextRequest, NextResponse } from "next/server"
|
|
import prisma from "@/lib/prisma"
|
|
import { createRedirectFlow } from "@/lib/gocardless"
|
|
import { generateReference } from "@/lib/reference"
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
const body = await request.json()
|
|
const { amountPence, donorName, donorEmail, donorPhone, giftAid, eventId, qrSourceId } = body
|
|
|
|
if (!prisma) {
|
|
return NextResponse.json({ error: "Database not configured" }, { status: 503 })
|
|
}
|
|
|
|
// 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 reference
|
|
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++
|
|
}
|
|
|
|
// Create pledge in DB
|
|
const pledge = await prisma.pledge.create({
|
|
data: {
|
|
reference,
|
|
amountPence,
|
|
currency: "GBP",
|
|
rail: "gocardless",
|
|
status: "new",
|
|
donorName: donorName || null,
|
|
donorEmail: donorEmail || null,
|
|
donorPhone: donorPhone || null,
|
|
giftAid: giftAid || false,
|
|
eventId,
|
|
qrSourceId: qrSourceId || null,
|
|
organizationId: org.id,
|
|
},
|
|
})
|
|
|
|
// Create reminder schedule
|
|
const { calculateReminderSchedule } = await import("@/lib/reminders")
|
|
const schedule = calculateReminderSchedule(new Date())
|
|
await prisma.reminder.createMany({
|
|
data: schedule.map((s) => ({
|
|
pledgeId: pledge.id,
|
|
step: s.step,
|
|
channel: s.channel,
|
|
scheduledAt: s.scheduledAt,
|
|
status: "pending",
|
|
payload: { templateKey: s.templateKey, subject: s.subject },
|
|
})),
|
|
})
|
|
|
|
// Track analytics
|
|
await prisma.analyticsEvent.create({
|
|
data: {
|
|
eventType: "pledge_completed",
|
|
pledgeId: pledge.id,
|
|
eventId,
|
|
qrSourceId: qrSourceId || null,
|
|
metadata: { amountPence, rail: "gocardless" },
|
|
},
|
|
})
|
|
|
|
// Try real GoCardless flow
|
|
// GoCardless live mode requires HTTPS redirect URLs
|
|
const baseUrl = process.env.BASE_URL || "http://localhost:3000"
|
|
const isHttps = baseUrl.startsWith("https://")
|
|
const redirectUrl = `${baseUrl}/api/gocardless/callback?pledge_id=${pledge.id}`
|
|
|
|
if (!isHttps && process.env.GOCARDLESS_ENVIRONMENT === "live") {
|
|
// Can't use GC live with HTTP — return simulated mode
|
|
// Set BASE_URL to your HTTPS domain to enable live GoCardless
|
|
console.warn("GoCardless live mode requires HTTPS BASE_URL. Falling back to simulated.")
|
|
return NextResponse.json({
|
|
mode: "simulated",
|
|
pledgeId: pledge.id,
|
|
reference,
|
|
id: pledge.id,
|
|
})
|
|
}
|
|
|
|
const flow = await createRedirectFlow({
|
|
description: `${event.name} — ${reference}`,
|
|
reference,
|
|
pledgeId: pledge.id,
|
|
successRedirectUrl: redirectUrl,
|
|
})
|
|
|
|
if (flow) {
|
|
// Save the redirect flow ID for completion
|
|
await prisma.paymentInstruction.create({
|
|
data: {
|
|
pledgeId: pledge.id,
|
|
bankReference: reference,
|
|
bankDetails: {},
|
|
gcMandateUrl: flow.redirectUrl,
|
|
},
|
|
})
|
|
|
|
return NextResponse.json({
|
|
mode: "live",
|
|
pledgeId: pledge.id,
|
|
reference,
|
|
redirectUrl: flow.redirectUrl,
|
|
redirectFlowId: flow.redirectFlowId,
|
|
})
|
|
}
|
|
|
|
// Fallback: no GoCardless configured — return pledge for simulated flow
|
|
return NextResponse.json({
|
|
mode: "simulated",
|
|
pledgeId: pledge.id,
|
|
reference,
|
|
id: pledge.id,
|
|
})
|
|
} catch (error) {
|
|
console.error("GoCardless create flow error:", error)
|
|
return NextResponse.json({ error: "Internal error" }, { status: 500 })
|
|
}
|
|
}
|