feat(#32): Add separate flow for subscriptions

This commit is contained in:
Josh Creek
2024-10-08 20:14:29 +01:00
parent fcbd69373c
commit 2f7ec9860a
3 changed files with 164 additions and 99 deletions
+41
View File
@@ -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);
};
+118 -99
View File
@@ -44,6 +44,20 @@
}, 5000); }, 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 = {}; let item = {};
onMount(() => { onMount(() => {
@@ -58,7 +72,8 @@
imgSrc: product.images[0], imgSrc: product.images[0],
price: 0, price: 0,
priceId: product.default_price, priceId: product.default_price,
quantity: 1 quantity: 1,
isSubscription: product.isSubscription
}; };
// Fetch the price of the product // Fetch the price of the product
@@ -301,99 +316,101 @@
</div> </div>
</div> --> </div> -->
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 py-8"> {#if !item.isSubscription}
<div class="flex sm:items-center sm:justify-center w-full"> <div class="grid grid-cols-1 sm:grid-cols-2 gap-3 py-8">
<button <div class="flex sm:items-center sm:justify-center w-full">
class="group py-4 px-6 border border-gray-400 rounded-l-full bg-white transition-all duration-300 hover:bg-gray-50 hover:shadow-sm hover:shadow-gray-300" <button
> class="group py-4 px-6 border border-gray-400 rounded-l-full bg-white transition-all duration-300 hover:bg-gray-50 hover:shadow-sm hover:shadow-gray-300"
<svg
class="stroke-gray-900 group-hover:stroke-black"
width="22"
height="22"
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
> >
<path d="M16.5 11H5.5" stroke="" stroke-width="1.6" stroke-linecap="round" /> <svg
<path class="stroke-gray-900 group-hover:stroke-black"
d="M16.5 11H5.5" width="22"
stroke="" height="22"
stroke-opacity="0.2" viewBox="0 0 22 22"
stroke-width="1.6" fill="none"
stroke-linecap="round" xmlns="http://www.w3.org/2000/svg"
/> >
<path <path d="M16.5 11H5.5" stroke="" stroke-width="1.6" stroke-linecap="round" />
d="M16.5 11H5.5" <path
stroke="" d="M16.5 11H5.5"
stroke-opacity="0.2" stroke=""
stroke-width="1.6" stroke-opacity="0.2"
stroke-linecap="round" stroke-width="1.6"
/> stroke-linecap="round"
</svg> />
</button> <path
<input d="M16.5 11H5.5"
type="text" stroke=""
class="font-semibold text-gray-900 cursor-pointer text-lg py-[13px] px-6 w-full sm:max-w-[118px] outline-0 border-y border-gray-400 bg-transparent placeholder:text-gray-900 text-center hover:bg-gray-50" stroke-opacity="0.2"
placeholder="1" stroke-width="1.6"
/> stroke-linecap="round"
<button />
class="group py-4 px-6 border border-gray-400 rounded-r-full bg-white transition-all duration-300 hover:bg-gray-50 hover:shadow-sm hover:shadow-gray-300" </svg>
> </button>
<svg <input
class="stroke-gray-900 group-hover:stroke-black" type="text"
width="22" class="font-semibold text-gray-900 cursor-pointer text-lg py-[13px] px-6 w-full sm:max-w-[118px] outline-0 border-y border-gray-400 bg-transparent placeholder:text-gray-900 text-center hover:bg-gray-50"
height="22" placeholder="1"
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11 5.5V16.5M16.5 11H5.5"
stroke="#9CA3AF"
stroke-width="1.6"
stroke-linecap="round"
/>
<path
d="M11 5.5V16.5M16.5 11H5.5"
stroke="black"
stroke-opacity="0.2"
stroke-width="1.6"
stroke-linecap="round"
/>
<path
d="M11 5.5V16.5M16.5 11H5.5"
stroke="black"
stroke-opacity="0.2"
stroke-width="1.6"
stroke-linecap="round"
/>
</svg>
</button>
</div>
<button
class="group py-4 px-5 rounded-full bg-indigo-50 text-indigo-600 font-semibold text-lg w-full flex items-center justify-center gap-2 transition-all duration-500 hover:bg-indigo-100"
on:click={addToBasket}
>
<svg
class="stroke-indigo-600"
width="22"
height="22"
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.7394 17.875C10.7394 18.6344 10.1062 19.25 9.32511 19.25C8.54402 19.25 7.91083 18.6344 7.91083 17.875M16.3965 17.875C16.3965 18.6344 15.7633 19.25 14.9823 19.25C14.2012 19.25 13.568 18.6344 13.568 17.875M4.1394 5.5L5.46568 12.5908C5.73339 14.0221 5.86724 14.7377 6.37649 15.1605C6.88573 15.5833 7.61377 15.5833 9.06984 15.5833H15.2379C16.6941 15.5833 17.4222 15.5833 17.9314 15.1605C18.4407 14.7376 18.5745 14.0219 18.8421 12.5906L19.3564 9.84059C19.7324 7.82973 19.9203 6.8243 19.3705 6.16215C18.8207 5.5 17.7979 5.5 15.7522 5.5H4.1394ZM4.1394 5.5L3.66797 2.75"
stroke=""
stroke-width="1.6"
stroke-linecap="round"
/> />
</svg> <button
Add to basket</button class="group py-4 px-6 border border-gray-400 rounded-r-full bg-white transition-all duration-300 hover:bg-gray-50 hover:shadow-sm hover:shadow-gray-300"
> >
</div> <svg
<div class="flex items-center gap-3"> class="stroke-gray-900 group-hover:stroke-black"
<button width="22"
height="22"
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11 5.5V16.5M16.5 11H5.5"
stroke="#9CA3AF"
stroke-width="1.6"
stroke-linecap="round"
/>
<path
d="M11 5.5V16.5M16.5 11H5.5"
stroke="black"
stroke-opacity="0.2"
stroke-width="1.6"
stroke-linecap="round"
/>
<path
d="M11 5.5V16.5M16.5 11H5.5"
stroke="black"
stroke-opacity="0.2"
stroke-width="1.6"
stroke-linecap="round"
/>
</svg>
</button>
</div>
<button
class="group py-4 px-5 rounded-full bg-indigo-50 text-indigo-600 font-semibold text-lg w-full flex items-center justify-center gap-2 transition-all duration-500 hover:bg-indigo-100"
on:click={addToBasket}
>
<svg
class="stroke-indigo-600"
width="22"
height="22"
viewBox="0 0 22 22"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M10.7394 17.875C10.7394 18.6344 10.1062 19.25 9.32511 19.25C8.54402 19.25 7.91083 18.6344 7.91083 17.875M16.3965 17.875C16.3965 18.6344 15.7633 19.25 14.9823 19.25C14.2012 19.25 13.568 18.6344 13.568 17.875M4.1394 5.5L5.46568 12.5908C5.73339 14.0221 5.86724 14.7377 6.37649 15.1605C6.88573 15.5833 7.61377 15.5833 9.06984 15.5833H15.2379C16.6941 15.5833 17.4222 15.5833 17.9314 15.1605C18.4407 14.7376 18.5745 14.0219 18.8421 12.5906L19.3564 9.84059C19.7324 7.82973 19.9203 6.8243 19.3705 6.16215C18.8207 5.5 17.7979 5.5 15.7522 5.5H4.1394ZM4.1394 5.5L3.66797 2.75"
stroke=""
stroke-width="1.6"
stroke-linecap="round"
/>
</svg>
Add to basket</button
>
</div>
{:else}
<div class="flex items-center gap-3">
<!-- <button
class="group transition-all duration-500 p-4 rounded-full bg-indigo-50 hover:bg-indigo-100 hover:shadow-sm hover:shadow-indigo-300" class="group transition-all duration-500 p-4 rounded-full bg-indigo-50 hover:bg-indigo-100 hover:shadow-sm hover:shadow-indigo-300"
> >
<svg <svg
@@ -412,13 +429,15 @@
stroke-linejoin="round" stroke-linejoin="round"
/> />
</svg> </svg>
</button> </button> -->
<button <button
class="text-center w-full px-5 py-4 rounded-[100px] bg-indigo-600 flex items-center justify-center font-semibold text-lg text-white shadow-sm transition-all duration-500 hover:bg-indigo-700 hover:shadow-indigo-400" class="text-center w-full px-5 py-4 rounded-[100px] bg-indigo-600 flex items-center justify-center font-semibold text-lg text-white shadow-sm transition-all duration-500 hover:bg-indigo-700 hover:shadow-indigo-400"
> on:click={startSubscription}
Buy Now >
</button> Subscribe
</div> </button>
</div>
{/if}
</div> </div>
</div> </div>
</div> </div>
@@ -4,5 +4,10 @@ import { stripe as stripeClient } from '$lib/utils/stripe';
export const GET = async ({ params }) => { export const GET = async ({ params }) => {
const { productId } = params; const { productId } = params;
const product = await stripeClient.products.retrieve(productId); 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); return json(product);
}; };