import { type NextRequest, NextResponse } from "next/server" import createMollieClient from "@mollie/api-client" // Lazy initialization to avoid build-time errors let mollieClient: ReturnType | null = null function getMollie() { if (!mollieClient) { mollieClient = createMollieClient({ apiKey: process.env.MOLLIE_API_KEY! }) } return mollieClient } // Dynamic pricing tiers (in EUR) const PRICING_TIERS = [ { label: "Early bird", price: 80, cutoff: "2026-03-31" }, { label: "Regular", price: 120, cutoff: "2026-07-01" }, { label: "Late", price: 150, cutoff: "2099-12-31" }, ] // Promo codes — map code → tier label const PROMO_CODES: Record = { "earlybird-friends": "Early bird", } function getCurrentTicketPrice(): number { const now = new Date().toISOString().slice(0, 10) const tier = PRICING_TIERS.find((t) => now < t.cutoff) ?? PRICING_TIERS[PRICING_TIERS.length - 1] return tier.price } function getTicketPriceForPromo(code: string): number | null { const tierLabel = PROMO_CODES[code] if (!tierLabel) return null const tier = PRICING_TIERS.find((t) => t.label === tierLabel) return tier?.price ?? null } const PROCESSING_FEE_PERCENT = 0.02 // 2% to cover Mollie payment processing fees // Accommodation prices per person for 7 nights const ACCOMMODATION_PRICES: Record = { "ch-multi": { label: "Bed in shared room (Commons Hub)", price: 279.30 }, "ch-double": { label: "Bed in double room (Commons Hub)", price: 356.30 }, "hh-single": { label: "Single room (Herrnhof)", price: 665 }, "hh-double-separate": { label: "Double room, separate beds (Herrnhof)", price: 420 }, "hh-double-shared": { label: "Double room, shared double bed (Herrnhof)", price: 350 }, "hh-triple": { label: "Triple room (Herrnhof)", price: 350 }, "hh-daybed": { label: "Daybed or extra bed in living room (Herrnhof)", price: 280 }, } // Public base URL const BASE_URL = process.env.NEXT_PUBLIC_BASE_URL || "https://cryptocommonsgather.ing" export async function POST(request: NextRequest) { try { const formData = await request.formData() const registrationDataStr = formData.get("registrationData") as string const includeAccommodation = formData.get("includeAccommodation") === "true" const accommodationType = (formData.get("accommodationType") as string) || "ch-multi" const registrationData = registrationDataStr ? JSON.parse(registrationDataStr) : null // Calculate subtotal — check for valid promo code override const promoCode = (formData.get("promoCode") as string) || "" const promoPrice = promoCode ? getTicketPriceForPromo(promoCode) : null const ticketPrice = promoPrice ?? getCurrentTicketPrice() let subtotal = ticketPrice const descriptionParts = [`CCG 2026 Ticket (€${ticketPrice})`] if (includeAccommodation) { const accom = ACCOMMODATION_PRICES[accommodationType] if (accom) { subtotal += accom.price descriptionParts.push(`${accom.label} (€${accom.price.toFixed(2)})`) } } // Add processing fee on top const processingFee = Math.round(subtotal * PROCESSING_FEE_PERCENT * 100) / 100 const total = subtotal + processingFee descriptionParts.push(`Processing fee (€${processingFee.toFixed(2)})`) // Build metadata for webhook const metadata: Record = {} if (registrationData) { metadata.name = registrationData.name || "" metadata.email = registrationData.email || "" metadata.contact = registrationData.contact || "" metadata.contributions = (registrationData.contributions || "").substring(0, 500) metadata.expectations = (registrationData.expectations || "").substring(0, 500) metadata.howHeard = registrationData.howHeard || "" metadata.dietary = (registrationData.dietary || []).join(", ") + (registrationData.dietaryOther ? `, ${registrationData.dietaryOther}` : "") metadata.crewConsent = registrationData.crewConsent || "" metadata.accommodation = includeAccommodation ? accommodationType : "none" } const payment = await getMollie().payments.create({ amount: { value: total.toFixed(2), currency: "EUR", }, description: `CCG 2026 Registration — ${descriptionParts.join(" + ")}`, redirectUrl: `${BASE_URL}/success`, webhookUrl: `${BASE_URL}/api/webhook`, metadata, }) // Redirect to Mollie checkout return new Response(null, { status: 303, headers: { Location: payment.getCheckoutUrl()! }, }) } catch (err) { console.error("Error creating Mollie payment:", err) return NextResponse.json({ error: "Error creating payment" }, { status: 500 }) } } export async function GET() { return NextResponse.json( { message: "This is an API endpoint. Use POST to create a checkout session." }, { status: 405 }, ) }