diff --git a/src/lib/payments.functions.ts b/src/lib/payments.functions.ts index 6782b5d..f274b17 100644 --- a/src/lib/payments.functions.ts +++ b/src/lib/payments.functions.ts @@ -176,15 +176,10 @@ export const cancelPaymentRequest = createServerFn({ method: "POST" }) export const getPublicPaymentRequest = createServerFn({ method: "GET" }) .inputValidator((data: { id: string }) => data) .handler(async ({ data }) => { - // Use anon-key client; RLS policy "pr_select_public_by_id" allows public reads - const { createClient } = await import("@supabase/supabase-js"); - const url = process.env.SUPABASE_URL; - const anonKey = process.env.SUPABASE_PUBLISHABLE_KEY; - if (!url || !anonKey) throw new Error("Supabase env not configured"); - const sb = createClient(url, anonKey, { - auth: { persistSession: false, autoRefreshToken: false }, - }); - const { data: row } = await sb + // Use service-role client to bypass RLS; return only a strict whitelist + // of non-sensitive fields for the public pay page. + const { supabaseAdmin } = await import("@/integrations/supabase/client.server"); + const { data: row } = await supabaseAdmin .from("payment_requests") .select( "id, recipient_name, description, base_amount_cents, fee_amount_cents, total_amount_cents, currency, status, stripe_checkout_url, paid_at", diff --git a/supabase/migrations/20260428005239_2d5dd166-67e0-4072-b14a-6bad8450ceb2.sql b/supabase/migrations/20260428005239_2d5dd166-67e0-4072-b14a-6bad8450ceb2.sql new file mode 100644 index 0000000..35960dc --- /dev/null +++ b/supabase/migrations/20260428005239_2d5dd166-67e0-4072-b14a-6bad8450ceb2.sql @@ -0,0 +1,9 @@ +-- Remove overly-permissive public SELECT policy on payment_requests +DROP POLICY IF EXISTS pr_select_public_by_id ON public.payment_requests; + +-- Authenticated users can see payment requests they created, or admins see all +CREATE POLICY pr_select_owner_or_admin +ON public.payment_requests +FOR SELECT +TO authenticated +USING (is_admin(auth.uid()) OR created_by = auth.uid()); \ No newline at end of file