Skip to content

Instantly share code, notes, and snippets.

@alemagio
Created October 21, 2025 16:45
Show Gist options
  • Select an option

  • Save alemagio/92bdd99c60aab98f2e65bbf5053fefa5 to your computer and use it in GitHub Desktop.

Select an option

Save alemagio/92bdd99c60aab98f2e65bbf5053fefa5 to your computer and use it in GitHub Desktop.
ShipWithAI - Stripe customer portal
async function checkoutSessionCompletedWebhook(req) {
try {
const event = await req.json();
if (event.type === "checkout.session.completed") {
const checkoutSession = event.data.object;
// Example: add Supabase user_id from client_reference_id
const clientReferenceId = checkoutSession.client_reference_id ?? null;
// If using Supabase auth, you could fetch user info from DB here
// const { data: user } = await supabase.from("users").select("*").eq("id", clientReferenceId).single();
await stripe.subscriptions.update(checkoutSession.subscription, {
metadata: {
supabase_user_id: clientReferenceId,
source: "embedded_pricing_table"
}
});
return new Response("Metadata added", {
status: 200
});
}
return new Response("Ignored", {
status: 200
});
} catch (err) {
console.error("Error handling webhook:", err);
return new Response("Error", {
status: 400
});
}
}
<!doctype html>
<html lang=”en”>
<head>
<meta charset=”UTF-8” />
<link rel=”icon” type=”image/svg+xml” href=”/logo.svg” />
<meta name=”viewport” content=”width=device-width, initial-scale=1.0” />
<title>Your app title</title>
</head>
<body>
<script async src=”https://js.stripe.com/v3/pricing-table.js”></script>
<div id=”app”></div>
<script type=”module” src=”/src/main.ts”></script>
</body>
</html>
<template>
<stripe-pricing-table
pricing-table-id="”YOUR_PRICING_TABLE_ID”"
publishable-key="”YOUR_PUBLISHABLE_KEY”"
:client-reference-id="”authStore.user!.id”"
:customer-email="”authStore.user!.email”"
>
</stripe-pricing-table>
</template>
<script setup lang="”ts”">
import { useAuthStore } from “@/store/auth”;
const authStore = useAuthStore();
</script>
async function subscriptionsWebhook(req) {
const body = await req.text();
const sig = req.headers.get("stripe-signature");
let event;
try {
event = stripe.webhooks.constructEvent(body, sig, endpointSecret);
} catch (err) {
console.error(`Webhook signature verification failed.`, err.message);
return new Response(`Webhook Error: ${err.message}`, {
status: 400
});
}
if (event.type === "customer.subscription.created" || event.type === "customer.subscription.updated" || event.type === "customer.subscription.deleted") {
const subscription = event.data.object;
// Lookup the user_id from stripe_customer_id
const { data: user, error } = await supabase.from("users") // this is auth.users
.select("id").eq("stripe_customer_id", subscription.customer).single();
if (error || !user) {
console.error("User not found for subscription:", subscription.customer);
return new Response("User not found", {
status: 200
});
}
const status = subscription.status;
const currentPeriodEnd = new Date(subscription.current_period_end * 1000).toISOString();
// Upsert into user_subscriptions
const { error: upsertError } = await supabase.from("user_subscriptions").upsert({
user_id: user.id,
status,
current_period_end: currentPeriodEnd,
updated_at: new Date().toISOString()
});
if (upsertError) {
console.error("Upsert failed:", upsertError);
return new Response("Database error", {
status: 500
});
}
}
return new Response("ok", {
status: 200
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment