Created
October 21, 2025 16:45
-
-
Save alemagio/92bdd99c60aab98f2e65bbf5053fefa5 to your computer and use it in GitHub Desktop.
ShipWithAI - Stripe customer portal
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 | |
| }); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <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> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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