import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'; import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'; import Stripe from 'https://esm.sh/stripe@14?target=deno'; const stripe = new Stripe(Deno.env.get('STRIPE_SECRET_KEY')!, { apiVersion: '2023-10-16' }); const STRIPE_CURRENCY = 'usd'; const STRIPE_CURRENCY_LABEL = 'USD'; const corsHeaders = { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', }; serve(async (req) => { if (req.method === 'OPTIONS') return new Response('ok', { headers: corsHeaders }); try { const { invoice_id, invoice_ref } = await req.json(); const invoiceRef = invoice_ref || invoice_id; if (!invoiceRef) return new Response(JSON.stringify({ error: 'invoice_ref required' }), { status: 400, headers: corsHeaders }); const supabase = createClient( Deno.env.get('SUPABASE_URL')!, Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!, ); let invoice = null; let error = null; ({ data: invoice, error } = await supabase .from('invoices') .select('*') .eq('id', invoiceRef) .maybeSingle()); if (!invoice) { ({ data: invoice, error } = await supabase .from('invoices') .select('*') .eq('invoice_number', invoiceRef) .maybeSingle()); } if (error || !invoice) return new Response(JSON.stringify({ error: 'Invoice not found' }), { status: 404, headers: corsHeaders }); if (invoice.status === 'paid') return new Response(JSON.stringify({ error: 'Invoice already paid' }), { status: 400, headers: corsHeaders }); const { data: company } = invoice.company_id ? await supabase .from('companies') .select('name, email') .eq('id', invoice.company_id) .maybeSingle() : { data: null }; const origin = 'https://portal.fourgebranding.com'; const billToLabel = invoice.bill_to || company?.name; const session = await stripe.checkout.sessions.create({ // No payment_method_types — Stripe uses whatever is enabled in the dashboard // (cards, ACH, etc.) without needing to hardcode them here. mode: 'payment', customer_email: company?.email || undefined, line_items: [{ price_data: { currency: STRIPE_CURRENCY, product_data: { name: `Invoice ${invoice.invoice_number} (${STRIPE_CURRENCY_LABEL})`, description: billToLabel ? `Fourge Branding — ${billToLabel} (${STRIPE_CURRENCY_LABEL})` : `Fourge Branding invoice charged in ${STRIPE_CURRENCY_LABEL}`, }, unit_amount: Math.round(Number(invoice.total) * 100), }, quantity: 1, }], // Stamp invoice_id on both the session and the payment intent so the // webhook can match it whether the payment settles instantly (card) or // asynchronously (ACH — payment_intent.succeeded fires days later). metadata: { invoice_id: invoice.id, currency: STRIPE_CURRENCY }, payment_intent_data: { metadata: { invoice_id: invoice.id }, }, success_url: `${origin}/pay/${encodeURIComponent(invoice.invoice_number)}?success=1`, cancel_url: `${origin}/pay/${encodeURIComponent(invoice.invoice_number)}?cancelled=1`, }); return new Response(JSON.stringify({ url: session.url }), { headers: { ...corsHeaders, 'Content-Type': 'application/json' } }); } catch (err) { console.error(err); return new Response(JSON.stringify({ error: err.message }), { status: 500, headers: corsHeaders }); } });