From 2f7ec9860ad0959c3102b8e732c64d195fcafaa1 Mon Sep 17 00:00:00 2001
From: Josh Creek <8179928+jcreek@users.noreply.github.com>
Date: Tue, 8 Oct 2024 20:14:29 +0100
Subject: [PATCH 1/3] feat(#32): Add separate flow for subscriptions
---
src/routes/api/subscribe/+server.ts | 41 ++++
src/routes/products/[productId]/+page.svelte | 217 ++++++++++---------
src/routes/products/[productId]/+server.ts | 5 +
3 files changed, 164 insertions(+), 99 deletions(-)
create mode 100644 src/routes/api/subscribe/+server.ts
diff --git a/src/routes/api/subscribe/+server.ts b/src/routes/api/subscribe/+server.ts
new file mode 100644
index 0000000..ca7dafc
--- /dev/null
+++ b/src/routes/api/subscribe/+server.ts
@@ -0,0 +1,41 @@
+import { type RequestHandler, redirect } from '@sveltejs/kit';
+import { stripe as stripeClient } from '$lib/utils/stripe';
+
+// Create a subscription checkout session
+export const POST: RequestHandler = async ({ request, cookies, locals: { safeGetSession } }) => {
+ const { session } = await safeGetSession();
+
+ if (!session) {
+ // If no session is found, the user isn't authenticated, so block the action
+ return new Response('Unauthorized', { status: 401 });
+ }
+
+ const userId = session.user.id;
+
+ // Get the price ID from the request body
+ const { priceId } = await request.json();
+
+ if (!priceId) {
+ return new Response('Price ID is required', { status: 400 });
+ }
+
+ // Create a subscription checkout session with Stripe
+ const checkoutSession = await stripeClient.checkout.sessions.create({
+ line_items: [
+ {
+ price: priceId,
+ quantity: 1
+ }
+ ],
+ mode: 'subscription',
+ success_url: `${request.headers.get('origin')}/checkout/success`,
+ cancel_url: `${request.headers.get('origin')}/checkout/cancelled`,
+ metadata: {
+ userId: userId
+ }
+ });
+
+ // Store the checkout session.id for access from the frontend
+ cookies.set('checkout_session_id', checkoutSession.id, { path: '/' });
+ return redirect(303, checkoutSession.url as string);
+};
diff --git a/src/routes/products/[productId]/+page.svelte b/src/routes/products/[productId]/+page.svelte
index 6e81008..77f5fed 100644
--- a/src/routes/products/[productId]/+page.svelte
+++ b/src/routes/products/[productId]/+page.svelte
@@ -44,6 +44,20 @@
}, 5000);
}
+ function startSubscription() {
+ fetch('/api/subscribe', {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json'
+ },
+ body: JSON.stringify({ priceId: item.priceId })
+ })
+ .then((response) => response.json())
+ .then((data) => {
+ console.log(data);
+ });
+ }
+
let item = {};
onMount(() => {
@@ -58,7 +72,8 @@
imgSrc: product.images[0],
price: 0,
priceId: product.default_price,
- quantity: 1
+ quantity: 1,
+ isSubscription: product.isSubscription
};
// Fetch the price of the product
@@ -301,99 +316,101 @@
-->
-
-
-
-
-
+
+
+ {:else}
+
+
+
+
+ {/if}
diff --git a/src/routes/products/[productId]/+server.ts b/src/routes/products/[productId]/+server.ts
index f2bead5..43b0bb8 100644
--- a/src/routes/products/[productId]/+server.ts
+++ b/src/routes/products/[productId]/+server.ts
@@ -4,5 +4,10 @@ import { stripe as stripeClient } from '$lib/utils/stripe';
export const GET = async ({ params }) => {
const { productId } = params;
const product = await stripeClient.products.retrieve(productId);
+
+ const price = await stripeClient.prices.retrieve(product.default_price);
+
+ const isSubscription = price.type === 'recurring';
+ product.isSubscription = isSubscription;
return json(product);
};
From e51aeb7c6320b8f01ed515827540590d7cdfb158 Mon Sep 17 00:00:00 2001
From: Josh Creek <8179928+jcreek@users.noreply.github.com>
Date: Tue, 8 Oct 2024 20:32:24 +0100
Subject: [PATCH 2/3] feat(#32): Strongly type the price.type
---
src/routes/products/[productId]/+server.ts | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/src/routes/products/[productId]/+server.ts b/src/routes/products/[productId]/+server.ts
index 43b0bb8..bde4ae6 100644
--- a/src/routes/products/[productId]/+server.ts
+++ b/src/routes/products/[productId]/+server.ts
@@ -1,5 +1,6 @@
import { json } from '@sveltejs/kit';
import { stripe as stripeClient } from '$lib/utils/stripe';
+import Stripe from 'stripe';
export const GET = async ({ params }) => {
const { productId } = params;
@@ -7,7 +8,7 @@ export const GET = async ({ params }) => {
const price = await stripeClient.prices.retrieve(product.default_price);
- const isSubscription = price.type === 'recurring';
+ const isSubscription = (price.type as Stripe.Price.type) === 'recurring';
product.isSubscription = isSubscription;
return json(product);
};
From 73cb21d9de75c3945331ea4a9181de603fb843a2 Mon Sep 17 00:00:00 2001
From: Josh Creek <8179928+jcreek@users.noreply.github.com>
Date: Tue, 8 Oct 2024 20:34:25 +0100
Subject: [PATCH 3/3] fix(#32): Strongly type the recurring string enum
---
src/routes/products/[productId]/+server.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/routes/products/[productId]/+server.ts b/src/routes/products/[productId]/+server.ts
index bde4ae6..c1b83c8 100644
--- a/src/routes/products/[productId]/+server.ts
+++ b/src/routes/products/[productId]/+server.ts
@@ -8,7 +8,7 @@ export const GET = async ({ params }) => {
const price = await stripeClient.prices.retrieve(product.default_price);
- const isSubscription = (price.type as Stripe.Price.type) === 'recurring';
+ const isSubscription = price.type === ('recurring' as Stripe.Price.type);
product.isSubscription = isSubscription;
return json(product);
};