import { serve } from "https://deno.land/std@0.190.0/http/server.ts"; import { createClient } from "https://esm.sh/@supabase/supabase-js@2.39.3"; const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "authorization, content-type", }; function stripeHeaders(secretKey: string, accountId?: string | null) { return { Authorization: `Bearer ${secretKey}`, ...(accountId?.startsWith("acct_") ? { "Stripe-Account": accountId } : {}), }; } serve(async (req) => { if (req.method === "OPTIONS") return new Response(null, { headers: corsHeaders }); const supabase = createClient( Deno.env.get("SUPABASE_URL")!, Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!, ); const supabaseUrl = Deno.env.get("SUPABASE_URL")!; const webhookUrl = `${supabaseUrl}/functions/v1/stripe-webhook`; // Today 00:00 UTC -> unix const now = new Date(); const startOfDay = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())); const createdGte = Math.floor(startOfDay.getTime() / 1000); const results: any[] = []; try { const { data: mappings, error: mErr } = await supabase .from("stripe_account_mappings") .select("association_id, stripe_account_id, stripe_secret_key") .eq("is_active", true); if (mErr) throw mErr; for (const m of mappings || []) { if (!m.stripe_secret_key) continue; const url = `https://api.stripe.com/v1/payment_intents?limit=100&created[gte]=${createdGte}`; const r = await fetch(url, { headers: stripeHeaders(m.stripe_secret_key, m.stripe_account_id) }); if (!r.ok) { results.push({ association_id: m.association_id, error: `list failed: ${r.status} ${await r.text()}` }); continue; } const list = await r.json(); let processed = 0; let posted = 0; for (const pi of list.data || []) { if (pi.status !== "succeeded") continue; processed++; const fakeEvent = { id: `evt_backfill_${pi.id}`, type: "payment_intent.succeeded", account: m.stripe_account_id, data: { object: pi }, }; const wr = await fetch(webhookUrl, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(fakeEvent), }); if (wr.ok) posted++; else console.error("webhook replay failed", pi.id, wr.status, await wr.text()); } results.push({ association_id: m.association_id, stripe_account_id: m.stripe_account_id, succeeded_today: processed, replayed: posted, }); } return new Response(JSON.stringify({ ok: true, since: startOfDay.toISOString(), results }), { status: 200, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } catch (err) { console.error("backfill error:", err); return new Response(JSON.stringify({ error: (err as Error).message, results }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } });