B2B-88: add starter kit structure and elements
This commit is contained in:
136
packages/billing/stripe/src/services/create-stripe-checkout.ts
Normal file
136
packages/billing/stripe/src/services/create-stripe-checkout.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
import type { Stripe } from 'stripe';
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { CreateBillingCheckoutSchema } from '@kit/billing/schema';
|
||||
|
||||
/**
|
||||
* @description If set to true, users can start a trial without entering their credit card details
|
||||
*/
|
||||
const enableTrialWithoutCreditCard =
|
||||
process.env.STRIPE_ENABLE_TRIAL_WITHOUT_CC === 'true';
|
||||
|
||||
/**
|
||||
* @name createStripeCheckout
|
||||
* @description Creates a Stripe Checkout session, and returns an Object
|
||||
* containing the session, which you can use to redirect the user to the
|
||||
* checkout page
|
||||
*/
|
||||
export async function createStripeCheckout(
|
||||
stripe: Stripe,
|
||||
params: z.infer<typeof CreateBillingCheckoutSchema>,
|
||||
) {
|
||||
// in MakerKit, a subscription belongs to an organization,
|
||||
// rather than to a user
|
||||
// if you wish to change it, use the current user ID instead
|
||||
const clientReferenceId = params.accountId;
|
||||
|
||||
// we pass an optional customer ID, so we do not duplicate the Stripe
|
||||
// customers if an organization subscribes multiple times
|
||||
const customer = params.customerId ?? undefined;
|
||||
|
||||
// docs: https://stripe.com/docs/billing/subscriptions/build-subscription
|
||||
const mode: Stripe.Checkout.SessionCreateParams.Mode =
|
||||
params.plan.paymentType === 'recurring' ? 'subscription' : 'payment';
|
||||
|
||||
const isSubscription = mode === 'subscription';
|
||||
|
||||
let trialDays: number | null | undefined = params.plan.trialDays;
|
||||
|
||||
// if the customer already exists, we do not set a trial period
|
||||
if (customer) {
|
||||
trialDays = undefined;
|
||||
}
|
||||
|
||||
const trialSettings =
|
||||
trialDays && enableTrialWithoutCreditCard
|
||||
? {
|
||||
trial_settings: {
|
||||
end_behavior: {
|
||||
missing_payment_method: 'cancel' as const,
|
||||
},
|
||||
},
|
||||
}
|
||||
: {};
|
||||
|
||||
// this should only be set if the mode is 'subscription'
|
||||
const subscriptionData:
|
||||
| Stripe.Checkout.SessionCreateParams.SubscriptionData
|
||||
| undefined = isSubscription
|
||||
? {
|
||||
trial_period_days: trialDays,
|
||||
metadata: {
|
||||
accountId: params.accountId,
|
||||
...(params.metadata ?? {}),
|
||||
},
|
||||
...trialSettings,
|
||||
}
|
||||
: {};
|
||||
|
||||
const urls = getUrls({
|
||||
returnUrl: params.returnUrl,
|
||||
});
|
||||
|
||||
// we use the embedded mode, so the user does not leave the page
|
||||
const uiMode = 'embedded';
|
||||
|
||||
const customerData = customer
|
||||
? {
|
||||
customer,
|
||||
}
|
||||
: {
|
||||
customer_email: params.customerEmail,
|
||||
};
|
||||
|
||||
const customerCreation =
|
||||
isSubscription || customer
|
||||
? ({} as Record<string, string>)
|
||||
: { customer_creation: 'always' };
|
||||
|
||||
const lineItems = params.plan.lineItems.map((item) => {
|
||||
if (item.type === 'metered') {
|
||||
return {
|
||||
price: item.id,
|
||||
};
|
||||
}
|
||||
|
||||
// if we pass a custom quantity for the item ID
|
||||
// we use that - otherwise we set it to 1 by default
|
||||
const quantity =
|
||||
params.variantQuantities.find((variant) => {
|
||||
return variant.variantId === item.id;
|
||||
})?.quantity ?? 1;
|
||||
|
||||
return {
|
||||
price: item.id,
|
||||
quantity,
|
||||
};
|
||||
});
|
||||
|
||||
const paymentCollectionMethod =
|
||||
enableTrialWithoutCreditCard && params.plan.trialDays
|
||||
? {
|
||||
payment_method_collection: 'if_required' as const,
|
||||
}
|
||||
: {};
|
||||
|
||||
return stripe.checkout.sessions.create({
|
||||
mode,
|
||||
allow_promotion_codes: params.enableDiscountField,
|
||||
ui_mode: uiMode,
|
||||
line_items: lineItems,
|
||||
client_reference_id: clientReferenceId,
|
||||
subscription_data: subscriptionData,
|
||||
...customerCreation,
|
||||
...customerData,
|
||||
...urls,
|
||||
...paymentCollectionMethod,
|
||||
});
|
||||
}
|
||||
|
||||
function getUrls(params: { returnUrl: string }) {
|
||||
const returnUrl = `${params.returnUrl}?session_id={CHECKOUT_SESSION_ID}`;
|
||||
|
||||
return {
|
||||
return_url: returnUrl,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user