diff --git a/src/lib/payments.functions.ts b/src/lib/payments.functions.ts index f274b17..dbca942 100644 --- a/src/lib/payments.functions.ts +++ b/src/lib/payments.functions.ts @@ -189,3 +189,65 @@ export const getPublicPaymentRequest = createServerFn({ method: "GET" }) if (!row) throw new Error("Payment request not found"); return row; }); + +// Public: always creates a fresh Stripe Checkout session for the pay page. +// Old stripe_checkout_url values become invalid when the Stripe key is +// rotated or after 24h, so we never reuse the stored URL on click. +export const startPublicCheckout = createServerFn({ method: "POST" }) + .inputValidator((data: { id: string }) => data) + .handler(async ({ data }) => { + const { supabaseAdmin } = await import("@/integrations/supabase/client.server"); + const { data: row, error } = await supabaseAdmin + .from("payment_requests") + .select("*") + .eq("id", data.id) + .maybeSingle(); + if (error || !row) throw new Error("Payment request not found"); + if (row.status === "paid") throw new Error("This payment has already been paid"); + if (row.status === "canceled") throw new Error("This payment request was canceled"); + + const stripe = getStripe(); + const origin = getOrigin(); + const successUrl = `${origin}/pay/${row.id}?status=success`; + const cancelUrl = `${origin}/pay/${row.id}?status=cancel`; + + const session = await stripe.checkout.sessions.create({ + mode: "payment", + payment_method_types: ["card"], + customer_email: row.recipient_email || undefined, + line_items: [ + { + price_data: { + currency: "usd", + unit_amount: row.base_amount_cents, + product_data: { + name: row.description || `Payment from ${row.recipient_name}`, + }, + }, + quantity: 1, + }, + { + price_data: { + currency: "usd", + unit_amount: row.fee_amount_cents, + product_data: { name: "Processing fee (2.9% + $0.30)" }, + }, + quantity: 1, + }, + ], + success_url: successUrl, + cancel_url: cancelUrl, + metadata: { payment_request_id: row.id }, + }); + + await supabaseAdmin + .from("payment_requests") + .update({ + stripe_session_id: session.id, + stripe_checkout_url: session.url ?? null, + }) + .eq("id", row.id); + + if (!session.url) throw new Error("Stripe did not return a checkout URL"); + return { checkout_url: session.url }; + });