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 @@ --> -
-
- - - -
- + - - Add to basket -
-
- +
+ + + {: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); };