import { createClient } from "https://esm.sh/@supabase/supabase-js@2"; 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 authHeader = req.headers.get("Authorization"); if (!authHeader) { return new Response(JSON.stringify({ error: "Missing authorization" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } const supabaseUrl = Deno.env.get("SUPABASE_URL")!; const supabaseAnonKey = Deno.env.get("SUPABASE_ANON_KEY")!; const serviceRoleKey = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY")!; // Validate user via JWT claims (works with signing-keys auth) const token = authHeader.replace("Bearer ", ""); const authClient = createClient(supabaseUrl, supabaseAnonKey); const { data: claimsData, error: userError } = await authClient.auth.getClaims(token); if (userError || !claimsData?.claims?.sub) { console.error("Auth failed:", userError); return new Response(JSON.stringify({ error: "Unauthorized" }), { status: 401, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } const userId = claimsData.claims.sub; // Check admin role const serviceClient = createClient(supabaseUrl, serviceRoleKey); const { data: roles } = await serviceClient .from("user_roles") .select("role") .eq("user_id", userId); const isAdmin = roles?.some((r: any) => ["admin", "manager"].includes(r.role)); if (!isAdmin) { return new Response(JSON.stringify({ error: "Forbidden" }), { status: 403, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } const url = new URL(req.url); const mappingId = url.searchParams.get("mapping_id"); const limit = Math.min(parseInt(url.searchParams.get("limit") || "10"), 25); // Fetch the stripe account mapping(s) let query = serviceClient .from("stripe_account_mappings") .select("id, stripe_secret_key, label, association_id, associations(name)") .eq("is_active", true); if (mappingId) { query = query.eq("id", mappingId); } const { data: mappings, error: mapError } = await query; if (mapError) throw mapError; if (!mappings || mappings.length === 0) { return new Response(JSON.stringify({ transactions: [], accounts: [] }), { headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } // If no specific mapping requested, return accounts list + transactions from first account const accounts = mappings.map((m: any) => ({ id: m.id, name: m.label || (m.associations as any)?.name || "Unknown Account", hasSecretKey: !!m.stripe_secret_key, })); // Pick which mapping to fetch transactions from const targetMapping = mappingId ? mappings[0] : mappings[0]; // default to first if (!targetMapping?.stripe_secret_key) { return new Response( JSON.stringify({ transactions: [], accounts, error: "No secret key configured for this account.", }), { headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } // Fetch recent charges from Stripe const stripeRes = await fetch( `https://api.stripe.com/v1/charges?limit=${limit}&expand[]=data.customer`, { headers: { Authorization: `Bearer ${targetMapping.stripe_secret_key}`, }, } ); if (!stripeRes.ok) { const errBody = await stripeRes.text(); console.error("Stripe API error:", stripeRes.status, errBody); return new Response( JSON.stringify({ transactions: [], accounts, error: `Stripe API error: ${stripeRes.status}`, }), { headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } const stripeData = await stripeRes.json(); const transactions = (stripeData.data || []).map((charge: any) => ({ id: charge.id, amount: charge.amount, currency: charge.currency, status: charge.status, created: charge.created, description: charge.description || null, customer_name: charge.customer?.name || null, customer_email: charge.customer?.email || charge.billing_details?.email || null, payment_method: charge.payment_method_details?.type || null, })); return new Response( JSON.stringify({ transactions, accounts }), { headers: { ...corsHeaders, "Content-Type": "application/json" } } ); } catch (error: unknown) { console.error("Error in stripe-transactions:", error); const message = error instanceof Error ? error.message : "Unknown error"; return new Response(JSON.stringify({ error: message }), { status: 500, headers: { ...corsHeaders, "Content-Type": "application/json" }, }); } });