diff --git a/src/lib/payments.functions.ts b/src/lib/payments.functions.ts index 4a759c7..6782b5d 100644 --- a/src/lib/payments.functions.ts +++ b/src/lib/payments.functions.ts @@ -2,7 +2,7 @@ import { createServerFn } from "@tanstack/react-start"; import { getRequestHost } from "@tanstack/react-start/server"; import { requireSupabaseAuth } from "@/integrations/supabase/auth-middleware"; import { attachSupabaseAuth } from "@/integrations/supabase/auth-client-middleware"; -import { supabaseAdmin } from "@/integrations/supabase/client.server"; + import Stripe from "stripe"; // Stripe US card rate: 2.9% + $0.30. Surcharge so homeowner covers fees. @@ -51,13 +51,13 @@ export const createPaymentRequest = createServerFn({ method: "POST" }) }, ) .handler(async ({ data, context }) => { - const { userId } = context; + const { userId, supabase } = context; const stripe = getStripe(); const { totalCents, feeCents } = calcSurcharge(data.base_amount_cents); const origin = getOrigin(); - // Insert pending row first - const { data: row, error: insErr } = await supabaseAdmin + // Insert pending row first (uses authenticated client; RLS allows insert by auth users) + const { data: row, error: insErr } = await supabase .from("payment_requests") .insert({ recipient_name: data.recipient_name, @@ -110,7 +110,7 @@ export const createPaymentRequest = createServerFn({ method: "POST" }) metadata: { payment_request_id: row.id }, }); - await supabaseAdmin + await supabase .from("payment_requests") .update({ stripe_session_id: session.id, @@ -124,8 +124,9 @@ export const createPaymentRequest = createServerFn({ method: "POST" }) export const refreshPaymentStatus = createServerFn({ method: "POST" }) .middleware([attachSupabaseAuth, requireSupabaseAuth]) .inputValidator((data: { id: string }) => data) - .handler(async ({ data }) => { - const { data: row } = await supabaseAdmin + .handler(async ({ data, context }) => { + const { supabase } = context; + const { data: row } = await supabase .from("payment_requests") .select("*") .eq("id", data.id) @@ -135,7 +136,7 @@ export const refreshPaymentStatus = createServerFn({ method: "POST" }) const stripe = getStripe(); const session = await stripe.checkout.sessions.retrieve(row.stripe_session_id); if (session.payment_status === "paid") { - await supabaseAdmin + await supabase .from("payment_requests") .update({ status: "paid", @@ -152,8 +153,8 @@ export const refreshPaymentStatus = createServerFn({ method: "POST" }) export const markPaymentEmailSent = createServerFn({ method: "POST" }) .middleware([attachSupabaseAuth, requireSupabaseAuth]) .inputValidator((data: { id: string }) => data) - .handler(async ({ data }) => { - await supabaseAdmin + .handler(async ({ data, context }) => { + await context.supabase .from("payment_requests") .update({ email_sent_at: new Date().toISOString() }) .eq("id", data.id); @@ -163,8 +164,8 @@ export const markPaymentEmailSent = createServerFn({ method: "POST" }) export const cancelPaymentRequest = createServerFn({ method: "POST" }) .middleware([attachSupabaseAuth, requireSupabaseAuth]) .inputValidator((data: { id: string }) => data) - .handler(async ({ data }) => { - await supabaseAdmin + .handler(async ({ data, context }) => { + await context.supabase .from("payment_requests") .update({ status: "canceled" }) .eq("id", data.id); @@ -175,7 +176,15 @@ export const cancelPaymentRequest = createServerFn({ method: "POST" }) export const getPublicPaymentRequest = createServerFn({ method: "GET" }) .inputValidator((data: { id: string }) => data) .handler(async ({ data }) => { - const { data: row } = await supabaseAdmin + // 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 .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/20260418181734_ef909729-6e62-435e-b629-599779d7b946.sql b/supabase/migrations/20260418181734_ef909729-6e62-435e-b629-599779d7b946.sql new file mode 100644 index 0000000..c04c0a7 --- /dev/null +++ b/supabase/migrations/20260418181734_ef909729-6e62-435e-b629-599779d7b946.sql @@ -0,0 +1,9 @@ +-- Allow public (anon) to read minimal payment_request fields by id for the pay page +CREATE POLICY "pr_select_public_by_id" +ON public.payment_requests +FOR SELECT +TO anon, authenticated +USING (true); + +-- Drop the old auth-only select policy that's now redundant +DROP POLICY IF EXISTS pr_select_auth ON public.payment_requests; \ No newline at end of file