import { type NextRequest, NextResponse } from "next/server" import Stripe from "stripe" // Lazy initialization to avoid build-time errors let stripe: Stripe | null = null function getStripe() { if (!stripe) { stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, { apiVersion: "2024-12-18.acacia", }) } return stripe } // Dynamic pricing configuration (in EUR cents) const TICKET_PRICE_CENTS = 8000 // €80 early bird // Public base URL (needed because request.nextUrl.origin returns internal Docker address) 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 paymentMethod = formData.get("paymentMethod") as string const registrationDataStr = formData.get("registrationData") as string const includeAccommodation = formData.get("includeAccommodation") === "true" const accommodationType = formData.get("accommodationType") as string || "dorm" const includeFood = formData.get("includeFood") === "true" const registrationData = registrationDataStr ? JSON.parse(registrationDataStr) : null // Accommodation pricing (in EUR cents) const DORM_PRICE_CENTS = 23520 // €235.20 (€39.20/night x 6) const DOUBLE_PRICE_CENTS = 30120 // €301.20 (€50.20/night x 6) const FOOD_PRICE_CENTS = 13500 // €135 (6 days) // Build line items dynamically const lineItems: Stripe.Checkout.SessionCreateParams.LineItem[] = [ { price_data: { currency: "eur", product_data: { name: "CCG 2026 Ticket", description: "Crypto Commons Gathering 2026 - August 16-22, Austria", }, unit_amount: TICKET_PRICE_CENTS, }, quantity: 1, }, ] if (includeAccommodation) { const isDorm = accommodationType === "dorm" lineItems.push({ price_data: { currency: "eur", product_data: { name: isDorm ? "Accommodation - Dorm (6 nights)" : "Accommodation - Double Room (6 nights)", description: `Commons Hub, ${isDorm ? "dorm bed" : "double room"} — ${isDorm ? "€39.20" : "€50.20"}/night`, }, unit_amount: isDorm ? DORM_PRICE_CENTS : DOUBLE_PRICE_CENTS, }, quantity: 1, }) } if (includeFood) { lineItems.push({ price_data: { currency: "eur", product_data: { name: "Food Package (6 days)", description: "Breakfast, catered lunches & dinners, coffee & tea", }, unit_amount: FOOD_PRICE_CENTS, }, quantity: 1, }) } let paymentMethodTypes: Stripe.Checkout.SessionCreateParams.PaymentMethodType[] = ["card"] if (paymentMethod === "sepa_debit") { paymentMethodTypes = ["sepa_debit"] } else if (paymentMethod === "crypto") { paymentMethodTypes = ["customer_balance"] } const session = await getStripe().checkout.sessions.create({ payment_method_types: paymentMethodTypes, line_items: lineItems, mode: "payment", success_url: `${BASE_URL}/success?session_id={CHECKOUT_SESSION_ID}`, cancel_url: `${BASE_URL}/register`, metadata: registrationData ? { name: registrationData.name, contact: registrationData.contact, contributions: registrationData.contributions.substring(0, 500), expectations: registrationData.expectations.substring(0, 500), howHeard: registrationData.howHeard || "", dietary: registrationData.dietary.join(", ") + (registrationData.dietaryOther ? `, ${registrationData.dietaryOther}` : ""), crewConsent: registrationData.crewConsent, accommodation: includeAccommodation ? accommodationType : "none", food: includeFood ? "yes" : "no", } : {}, ...(paymentMethod === "crypto" && { payment_method_options: { customer_balance: { funding_type: "bank_transfer", bank_transfer: { type: "us_bank_account", }, }, }, }), allow_promotion_codes: true, billing_address_collection: "required", phone_number_collection: { enabled: true, }, }) // Use 303 redirect for POST requests (tells browser to follow with GET) return new Response(null, { status: 303, headers: { Location: session.url! }, }) } catch (err) { console.error("Error creating checkout session:", err) return NextResponse.json({ error: "Error creating checkout session" }, { status: 500 }) } } export async function GET() { return NextResponse.json( { message: "This is an API endpoint. Use POST to create a checkout session." }, { status: 405 }, ) }