"use strict"; const __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.createCheckout = void 0; const https_1 = require("firebase-functions/v2/https"); const firebase_functions_1 = require("firebase-functions"); const firestore_1 = require("firebase-admin/firestore"); const stripe_1 = __importDefault(require("stripe")); const stripe = new stripe_1.default(process.env.STRIPE_SECRET_KEY, { apiVersion: "2024-11-20.acacia", }); const db = (0, firestore_1.getFirestore)(); const PLATFORM_FEE_BPS = parseInt(process.env.PLATFORM_FEE_BPS || "300"); /** * Creates a Stripe Checkout Session for a connected account * POST /api/checkout/create */ exports.createCheckout = (0, https_1.onRequest)({ cors: true, enforceAppCheck: false, region: "us-central1", }, async (req, res) => { if (req.method !== "POST") { res.status(405).json({ error: "Method not allowed" }); return; } try { const { orgId, eventId, ticketTypeId, qty, purchaserEmail, successUrl, cancelUrl, } = req.body; // Validate input if (!orgId || !eventId || !ticketTypeId || !qty || qty <= 0) { res.status(400).json({ error: "Missing required fields: orgId, eventId, ticketTypeId, qty", }); return; } if (!successUrl || !cancelUrl) { res.status(400).json({ error: "Missing required URLs: successUrl, cancelUrl", }); return; } firebase_functions_1.logger.info("Creating checkout session", { orgId, eventId, ticketTypeId, qty, purchaserEmail: purchaserEmail ? "provided" : "not provided", }); // Get organization payment info const orgDoc = await db.collection("orgs").doc(orgId).get(); if (!orgDoc.exists) { res.status(404).json({ error: "Organization not found" }); return; } const orgData = orgDoc.data(); const stripeAccountId = orgData.payment?.stripe?.accountId; if (!stripeAccountId) { res.status(400).json({ error: "Organization has no connected Stripe account", }); return; } // Validate account is properly onboarded if (!orgData.payment?.stripe?.chargesEnabled) { res.status(400).json({ error: "Stripe account is not ready to accept payments", }); return; } // Get event const eventDoc = await db.collection("events").doc(eventId).get(); if (!eventDoc.exists) { res.status(404).json({ error: "Event not found" }); return; } const eventData = eventDoc.data(); if (eventData.orgId !== orgId) { res.status(403).json({ error: "Event does not belong to organization" }); return; } // Get ticket type const ticketTypeDoc = await db.collection("ticket_types").doc(ticketTypeId).get(); if (!ticketTypeDoc.exists) { res.status(404).json({ error: "Ticket type not found" }); return; } const ticketTypeData = ticketTypeDoc.data(); if (ticketTypeData.orgId !== orgId || ticketTypeData.eventId !== eventId) { res.status(403).json({ error: "Ticket type does not belong to organization/event", }); return; } // Check inventory const available = ticketTypeData.inventory - (ticketTypeData.sold || 0); if (available < qty) { res.status(400).json({ error: `Not enough tickets available. Requested: ${qty}, Available: ${available}`, }); return; } // Calculate application fee const subtotal = ticketTypeData.priceCents * qty; const applicationFeeAmount = Math.round((subtotal * PLATFORM_FEE_BPS) / 10000); firebase_functions_1.logger.info("Checkout calculation", { priceCents: ticketTypeData.priceCents, qty, subtotal, platformFeeBps: PLATFORM_FEE_BPS, applicationFeeAmount, }); // Create Stripe Checkout Session const session = await stripe.checkout.sessions.create({ mode: "payment", payment_method_types: ["card"], customer_email: purchaserEmail || undefined, line_items: [ { price_data: { currency: "usd", product_data: { name: `${eventData.name} – ${ticketTypeData.name}`, description: `Tickets for ${eventData.name}`, }, unit_amount: ticketTypeData.priceCents, }, quantity: qty, }, ], success_url: `${successUrl}?session_id={CHECKOUT_SESSION_ID}`, cancel_url: cancelUrl, metadata: { orgId, eventId, ticketTypeId, qty: String(qty), purchaserEmail: purchaserEmail || "", }, payment_intent_data: { application_fee_amount: applicationFeeAmount, metadata: { orgId, eventId, ticketTypeId, qty: String(qty), }, }, }, { stripeAccount: stripeAccountId }); // Create placeholder order for UI polling const orderData = { orgId, eventId, ticketTypeId, qty, sessionId: session.id, status: "pending", totalCents: subtotal, createdAt: new Date(), purchaserEmail: purchaserEmail || null, paymentIntentId: null, stripeAccountId, }; await db.collection("orders").doc(session.id).set(orderData); firebase_functions_1.logger.info("Checkout session created", { sessionId: session.id, url: session.url, orgId, eventId, stripeAccountId, }); const response = { url: session.url, sessionId: session.id, }; res.status(200).json(response); } catch (error) { firebase_functions_1.logger.error("Error creating checkout session", { error: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, }); if (error instanceof stripe_1.default.errors.StripeError) { res.status(400).json({ error: `Stripe error: ${error.message}`, code: error.code, }); return; } res.status(500).json({ error: "Internal server error creating checkout session", }); } }); // # sourceMappingURL=checkout.js.map