Switch billing provider from Stripe to Lemon Squeezy
Changed the billing provider in the `.env.development` file from Stripe to Lemon Squeezy. This requires adaptations at many levels: at the web app to load Lemon Squeezy's script in the checkout process, at the billing gateway to handle Lemon Squeezy calls, and in the database to reflect the current billing provider. The checkout process is now done using Lemon Squeezy Sessions and its billing strategy was adjusted accordingly. Billing-related components and services were also updated.
This commit is contained in:
@@ -1,3 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { useEffect } from 'react';
|
||||
|
||||
interface LemonSqueezyWindow extends Window {
|
||||
createLemonSqueezy: () => void;
|
||||
LemonSqueezy: {
|
||||
@@ -14,16 +18,24 @@ interface LemonSqueezyWindow extends Window {
|
||||
}
|
||||
|
||||
export function LemonSqueezyEmbeddedCheckout(props: { checkoutToken: string }) {
|
||||
return (
|
||||
<script
|
||||
src="https://app.lemonsqueezy.com/js/lemon.js"
|
||||
defer
|
||||
onLoad={() => {
|
||||
const win = window as unknown as LemonSqueezyWindow;
|
||||
useLoadScript(props.checkoutToken);
|
||||
|
||||
win.createLemonSqueezy();
|
||||
win.LemonSqueezy.Url.Open(props.checkoutToken);
|
||||
}}
|
||||
></script>
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
function useLoadScript(checkoutToken: string) {
|
||||
useEffect(() => {
|
||||
const script = document.createElement('script');
|
||||
|
||||
script.src = 'https://app.lemonsqueezy.com/js/lemon.js';
|
||||
|
||||
script.onload = () => {
|
||||
const win = window as unknown as LemonSqueezyWindow;
|
||||
|
||||
win.createLemonSqueezy();
|
||||
win.LemonSqueezy.Url.Open(checkoutToken);
|
||||
};
|
||||
|
||||
document.body.appendChild(script);
|
||||
}, [checkoutToken]);
|
||||
}
|
||||
|
||||
@@ -29,12 +29,9 @@ export async function createLemonSqueezyCheckout(
|
||||
}
|
||||
|
||||
const env = getLemonSqueezyEnv();
|
||||
const storeId = env.storeId;
|
||||
const variantId = lineItem.id;
|
||||
|
||||
const urls = getUrls({
|
||||
returnUrl: params.returnUrl,
|
||||
});
|
||||
const storeId = Number(env.storeId);
|
||||
const variantId = Number(lineItem.id);
|
||||
|
||||
const customer = params.customerId
|
||||
? await getCustomer(params.customerId)
|
||||
@@ -63,7 +60,7 @@ export async function createLemonSqueezyCheckout(
|
||||
},
|
||||
},
|
||||
productOptions: {
|
||||
redirectUrl: urls.return_url,
|
||||
redirectUrl: params.returnUrl,
|
||||
},
|
||||
expiresAt: null,
|
||||
preview: true,
|
||||
@@ -72,11 +69,3 @@ export async function createLemonSqueezyCheckout(
|
||||
|
||||
return createCheckout(storeId, variantId, newCheckout);
|
||||
}
|
||||
|
||||
function getUrls(params: { returnUrl: string }) {
|
||||
const returnUrl = `${params.returnUrl}?session_id={CHECKOUT_SESSION_ID}`;
|
||||
|
||||
return {
|
||||
return_url: returnUrl,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ export class LemonSqueezyBillingStrategyService
|
||||
accountId: params.accountId,
|
||||
returnUrl: params.returnUrl,
|
||||
trialDays: params.trialDays,
|
||||
planId: params.plan.id,
|
||||
},
|
||||
'Creating checkout session...',
|
||||
);
|
||||
@@ -40,6 +39,8 @@ export class LemonSqueezyBillingStrategyService
|
||||
const { data: response, error } = await createLemonSqueezyCheckout(params);
|
||||
|
||||
if (error ?? !response?.data.id) {
|
||||
console.log(error);
|
||||
|
||||
Logger.error(
|
||||
{
|
||||
name: 'billing.lemon-squeezy',
|
||||
@@ -62,7 +63,9 @@ export class LemonSqueezyBillingStrategyService
|
||||
'Checkout session created successfully',
|
||||
);
|
||||
|
||||
return { checkoutToken: response.data.id };
|
||||
return {
|
||||
checkoutToken: response.data.attributes.url,
|
||||
};
|
||||
}
|
||||
|
||||
async createBillingPortalSession(
|
||||
|
||||
@@ -296,6 +296,8 @@ export class LemonSqueezyWebhookHandlerService
|
||||
return {
|
||||
id: item.id,
|
||||
quantity,
|
||||
interval: params.interval,
|
||||
interval_count: params.intervalCount,
|
||||
subscription_id: params.id,
|
||||
product_id: item.product,
|
||||
variant_id: item.variant,
|
||||
@@ -317,8 +319,12 @@ export class LemonSqueezyWebhookHandlerService
|
||||
cancel_at_period_end: params.cancelAtPeriodEnd ?? false,
|
||||
period_starts_at: getISOString(params.periodStartsAt) as string,
|
||||
period_ends_at: getISOString(params.periodEndsAt) as string,
|
||||
trial_starts_at: getISOString(params.trialStartsAt),
|
||||
trial_ends_at: getISOString(params.trialEndsAt),
|
||||
trial_starts_at: params.trialStartsAt
|
||||
? getISOString(params.trialStartsAt)
|
||||
: undefined,
|
||||
trial_ends_at: params.trialEndsAt
|
||||
? getISOString(params.trialEndsAt)
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -360,7 +366,7 @@ export class LemonSqueezyWebhookHandlerService
|
||||
}
|
||||
|
||||
function getISOString(date: number | null) {
|
||||
return date ? new Date(date * 1000).toISOString() : undefined;
|
||||
return date ? new Date(date).toISOString() : undefined;
|
||||
}
|
||||
|
||||
function isSigningSecretValid(rawBody: Buffer, signatureHeader: string) {
|
||||
|
||||
Reference in New Issue
Block a user