Merge branch '29-refactor-stripe-and-supabase-implementation' of https://github.com/jcreek/SvelteKitSaasBoilerplate into 29-refactor-stripe-and-supabase-implementation

This commit is contained in:
OllyNicholass
2024-09-17 19:13:58 +01:00
4 changed files with 46 additions and 39 deletions
+16 -12
View File
@@ -31,8 +31,11 @@ const upsertProductRecord = async (product: stripe.Product) => {
active: product.active,
name: product.name,
description: product.description ?? null,
image: product.images?.[0] ?? null,
metadata: product.metadata
features: product.marketing_features.map(({ name }) => name || '').filter((name) => !!name),
images: product.images ?? null,
metadata: product.metadata,
created: new Date(product.created * 1000).toISOString(),
updated: new Date(product.updated * 1000).toISOString()
};
const { error: upsertError } = await supabaseAdmin.from('products').upsert([productData]);
@@ -46,6 +49,8 @@ const upsertPriceRecord = async (price: stripe.Price, retryCount = 0, maxRetries
product_id: typeof price.product === 'string' ? price.product : '',
active: price.active,
currency: price.currency,
description: price.nickname ?? null,
metadata: price.metadata,
type: price.type,
unit_amount: price.unit_amount ?? null,
interval: price.recurring?.interval ?? null,
@@ -208,9 +213,7 @@ const manageSubscriptionStatusChange = async (
metadata: subscription.metadata,
status: subscription.status,
price_id: subscription.items.data[0].price.id,
//TODO check quantity on subscription
quantity: subscription.quantity,
quantity: 1, //subscription.quantity,
cancel_at_period_end: subscription.cancel_at_period_end,
cancel_at: subscription.cancel_at ? toDateTime(subscription.cancel_at).toISOString() : null,
canceled_at: subscription.canceled_at
@@ -232,13 +235,14 @@ const manageSubscriptionStatusChange = async (
if (upsertError) throw new Error(`Subscription insert/update failed: ${upsertError.message}`);
console.log(`Inserted/updated subscription [${subscription.id}] for user [${uuid}]`);
// For a new subscription copy the billing details to the customer object.
// NOTE: This is a costly operation and should happen at the very end.
if (createAction && subscription.default_payment_method && uuid)
await copyBillingDetailsToCustomer(
uuid,
subscription.default_payment_method as stripe.PaymentMethod
);
// // For a new subscription copy the billing details to the customer object.
// // NOTE: This is a costly operation and should happen at the very end.
// if (createAction && subscription.default_payment_method && uuid) {
// await copyBillingDetailsToCustomer(
// uuid,
// subscription.default_payment_method as stripe.PaymentMethod
// );
// }
};
export {
+11 -3
View File
@@ -6,16 +6,24 @@
$: ({ supabase, session } = data);
</script>
<div class="hero bg-base-100 my-36">
{#if session !== null}
<div class="hero bg-base-100 my-36">
Welcome back {session.user.email}
</div>
{:else}
<div class="hero bg-base-100 my-36">
<div class="hero-content flex-col lg:flex-row-reverse">
<div class="text-center lg:text-left">
<h1 class="text-5xl font-bold">Sign up for this amazing SaaS right now - don't miss out!</h1>
<h1 class="text-5xl font-bold">
Sign up for this amazing SaaS right now - don't miss out!
</h1>
</div>
<div id="sign-up" class="card shrink-0 w-full max-w-sm shadow-2xl">
<SignUp {supabase} />
</div>
</div>
</div>
</div>
{/if}
<div
class="md:container md:mx-auto justify-self-center flex justify-center items-center h-full flex-col mb-10"
+13 -18
View File
@@ -1,7 +1,7 @@
import type { RequestHandler } from '@sveltejs/kit';
import stripe from 'stripe';
import { PUBLIC_STRIPE_SECRET_KEY } from '$env/static/public';
import { PUBLIC_STRIPE_ENDPOINT_SECRET } from '$env/static/public';
import { STRIPE_SECRET_KEY } from '$env/static/private';
import { STRIPE_ENDPOINT_SECRET } from '$env/static/private';
import {
deletePriceRecord,
deleteProductRecord,
@@ -10,7 +10,7 @@ import {
upsertProductRecord
} from '$lib/utils/supabase/admin';
const stripeClient = new stripe(PUBLIC_STRIPE_SECRET_KEY);
const stripeClient = new stripe(STRIPE_SECRET_KEY);
export const POST: RequestHandler = async ({ request }) => {
const body = await request.text();
@@ -23,13 +23,9 @@ export const POST: RequestHandler = async ({ request }) => {
// Only verify the event if you have an endpoint secret defined.
// Otherwise use the basic event deserialized with JSON.parse
if (PUBLIC_STRIPE_ENDPOINT_SECRET && signature) {
if (STRIPE_ENDPOINT_SECRET && signature) {
try {
event = stripeClient.webhooks.constructEvent(
body,
signature,
PUBLIC_STRIPE_ENDPOINT_SECRET
);
event = stripeClient.webhooks.constructEvent(body, signature, STRIPE_ENDPOINT_SECRET);
} catch (err) {
console.error(`⚠️ Webhook signature verification failed.`, err.message);
return {
@@ -44,6 +40,9 @@ export const POST: RequestHandler = async ({ request }) => {
case 'product.updated':
await upsertProductRecord(event.data.object as stripe.Product);
break;
case 'product.deleted':
await deleteProductRecord(event.data.object as stripe.Product);
break;
case 'price.created':
case 'price.updated':
await upsertPriceRecord(event.data.object as stripe.Price);
@@ -51,12 +50,11 @@ export const POST: RequestHandler = async ({ request }) => {
case 'price.deleted':
await deletePriceRecord(event.data.object as stripe.Price);
break;
case 'product.deleted':
await deleteProductRecord(event.data.object as stripe.Product);
break;
case 'customer.subscription.created':
case 'customer.subscription.updated':
case 'customer.subscription.deleted': {
case 'customer.subscription.deleted':
case 'customer.subscription.paused':
case 'customer.subscription.resumed': {
const subscription = event.data.object as stripe.Subscription;
await manageSubscriptionStatusChange(
subscription.id,
@@ -86,10 +84,7 @@ export const POST: RequestHandler = async ({ request }) => {
status: 200
});
} catch (err) {
console.log(`Webhook Error: ${err.message}`);
return {
status: 400,
body: `Webhook Error: ${err.message}`
};
const message = err instanceof Error ? err.message : 'An unknown error has occurred';
return new Response(`Webhook Error: ${message}`, { status: 500 });
}
};
+1 -1
View File
@@ -12,7 +12,7 @@
"moduleResolution": "bundler",
"module": "es2015"
},
"include": ["src/**/*", "node_modules/**/*.d.ts"]
"include": ["src/**/*", "node_modules/**/*.d.ts", ".svelte-kit/ambient.d.ts"]
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes