import { createClient } from "https://esm.sh/@supabase/supabase-js@2.39.3"; const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "authorization, x-client-info, apikey, content-type, x-supabase-client-platform, x-supabase-client-platform-version, x-supabase-client-runtime, x-supabase-client-runtime-version", }; Deno.serve(async (req) => { if (req.method === "OPTIONS") { return new Response(null, { headers: corsHeaders }); } try { const supabaseUrl = Deno.env.get("SUPABASE_URL")!; const supabaseServiceKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!; const supabase = createClient(supabaseUrl, supabaseServiceKey); // Verify the calling user const authHeader = req.headers.get("Authorization"); if (!authHeader) { return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } const token = authHeader.replace("Bearer ", ""); const { data: { user }, error: authError, } = await supabase.auth.getUser(token); if (authError || !user) { return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } const body = await req.json(); const { association_id, owner_id, unit_id, payment_method_type } = body; if (!association_id) { return new Response( JSON.stringify({ error: "association_id is required" }), { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" }, } ); } // Get Stripe mapping for this association const { data: mapping, error: mappingError } = await supabase .from("stripe_account_mappings") .select("*") .eq("association_id", association_id) .eq("is_active", true) .maybeSingle(); if (mappingError || !mapping?.stripe_secret_key) { return new Response( JSON.stringify({ error: "No active Stripe configuration found for this association.", }), { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" }, } ); } const stripeSecretKey = mapping.stripe_secret_key; // Get or create Stripe customer // Check if there's already an enrollment with a customer ID for this user+association const { data: existingEnrollment } = await supabase .from("autopay_enrollments") .select("stripe_customer_id") .eq("association_id", association_id) .eq("enrolled_by", user.id) .not("stripe_customer_id", "is", null) .limit(1) .maybeSingle(); let stripeCustomerId = existingEnrollment?.stripe_customer_id; if (!stripeCustomerId) { // Create a new Stripe customer const customerParams = new URLSearchParams(); customerParams.append("email", user.email || ""); customerParams.append("metadata[user_id]", user.id); customerParams.append("metadata[association_id]", association_id); const customerRes = await fetch( "https://api.stripe.com/v1/customers", { method: "POST", headers: { Authorization: `Bearer ${stripeSecretKey}`, "Content-Type": "application/x-www-form-urlencoded", }, body: customerParams.toString(), } ); const customerData = await customerRes.json(); if (!customerRes.ok) { console.error("Stripe customer error:", customerData); return new Response( JSON.stringify({ error: customerData.error?.message || "Failed to create Stripe customer", }), { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" }, } ); } stripeCustomerId = customerData.id; } // Create a SetupIntent to save a payment method const setupParams = new URLSearchParams(); setupParams.append("customer", stripeCustomerId!); setupParams.append("usage", "off_session"); const isAch = payment_method_type === "us_bank_account"; if (isAch) { setupParams.append("payment_method_types[]", "us_bank_account"); setupParams.append( "payment_method_options[us_bank_account][financial_connections][permissions][]", "payment_method" ); } else { setupParams.append("automatic_payment_methods[enabled]", "true"); } setupParams.append("metadata[association_id]", association_id); setupParams.append("metadata[user_id]", user.id); if (owner_id) setupParams.append("metadata[owner_id]", owner_id); if (unit_id) setupParams.append("metadata[unit_id]", unit_id); const setupRes = await fetch( "https://api.stripe.com/v1/setup_intents", { method: "POST", headers: { Authorization: `Bearer ${stripeSecretKey}`, "Content-Type": "application/x-www-form-urlencoded", }, body: setupParams.toString(), } ); const setupData = await setupRes.json(); if (!setupRes.ok) { console.error("Stripe SetupIntent error:", setupData); return new Response( JSON.stringify({ error: setupData.error?.message || "Failed to create SetupIntent", }), { status: 400, headers: { ...corsHeaders, "Content-Type": "application/json" }, } ); } return new Response( JSON.stringify({ clientSecret: setupData.client_secret, setupIntentId: setupData.id, customerId: stripeCustomerId, publishableKey: mapping.stripe_publishable_key, }), { status: 200, headers: { ...corsHeaders, "Content-Type": "application/json" }, } ); } catch (err: unknown) { console.error("Error in setup-autopay:", err); const message = err instanceof Error ? err.message : "Internal server error"; return new Response(JSON.stringify({ error: message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } });