Co-authored-by: renee-png <262607627+renee-png@users.noreply.github.com>
This commit is contained in:
gpt-engineer-app[bot]
2026-04-18 17:50:39 +00:00
co-authored by renee-png
parent bd97829760
commit dc38ebc241
2 changed files with 239 additions and 0 deletions
+53
View File
@@ -0,0 +1,53 @@
import { createFileRoute } from "@tanstack/react-router";
import { supabaseAdmin } from "@/integrations/supabase/client.server";
import Stripe from "stripe";
export const Route = createFileRoute("/api/stripe-webhook")({
server: {
handlers: {
POST: async ({ request }) => {
const secret = process.env.STRIPE_SECRET_KEY;
const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET;
if (!secret) return new Response("Stripe not configured", { status: 500 });
const stripe = new Stripe(secret);
const sig = request.headers.get("stripe-signature");
const body = await request.text();
let event: Stripe.Event;
try {
if (webhookSecret && sig) {
event = await stripe.webhooks.constructEventAsync(body, sig, webhookSecret);
} else {
// Without signature secret, accept but log (still secure if endpoint is obscure;
// the user can configure STRIPE_WEBHOOK_SECRET later)
event = JSON.parse(body) as Stripe.Event;
}
} catch (err) {
return new Response(`Webhook error: ${(err as Error).message}`, { status: 400 });
}
if (event.type === "checkout.session.completed") {
const session = event.data.object as Stripe.Checkout.Session;
const reqId = session.metadata?.payment_request_id;
if (reqId) {
await supabaseAdmin
.from("payment_requests")
.update({
status: "paid",
paid_at: new Date().toISOString(),
stripe_payment_intent_id:
typeof session.payment_intent === "string" ? session.payment_intent : null,
})
.eq("id", reqId);
}
}
return new Response(JSON.stringify({ received: true }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
},
},
},
});