Files
myeasycms-v2/packages/billing/lemon-squeezy/src/services/lemon-squeezy-subscription-payload-builder.service.ts
Giancarlo Buomprisco 903ef6dc08 Update Stripe SDK to v18 and dependencies (#236)
* Update Stripe SDK and dependencies

1. Upgrade `stripe` package from version 17.7.0 to 18.0.0 in `package.json`.
2. Update `STRIPE_API_VERSION` in `stripe-sdk.ts` to '2025-03-31.basil'.
3. Refactor `StripeWebhookHandlerService` to retrieve subscription details using Supabase client, ensuring compatibility with the new Stripe version.
4. Introduce helper methods `getPeriodStartsAt` and `getPeriodEndsAt` for better handling of subscription periods based on the Stripe API changes.

These changes enhance the integration with the latest Stripe API and improve the overall reliability of the billing service.

* Refactor billing payload builders to remove config dependency

Removed direct dependency on `BillingConfig` in subscription payload builders.

Introduced `PlanTypeMap` to streamline plan type resolutions. Updated webhook handlers and event processing functions to handle plan types more efficiently and improve extensibility.

* Refactor Stripe subscription handling for improved accuracy
2025-04-22 10:42:12 +08:00

130 lines
3.5 KiB
TypeScript

import { BillingConfig } from '@kit/billing';
import { UpsertSubscriptionParams } from '@kit/billing/types';
type SubscriptionStatus =
| 'on_trial'
| 'active'
| 'cancelled'
| 'paused'
| 'expired'
| 'unpaid'
| 'past_due';
/**
* @name createLemonSqueezySubscriptionPayloadBuilderService
* @description Create a new instance of the `LemonSqueezySubscriptionPayloadBuilderService` class
*/
export function createLemonSqueezySubscriptionPayloadBuilderService() {
return new LemonSqueezySubscriptionPayloadBuilderService();
}
/**
* @name LemonSqueezySubscriptionPayloadBuilderService
* @description This class is used to build the subscription payload for Lemon Squeezy
*/
class LemonSqueezySubscriptionPayloadBuilderService {
private config?: BillingConfig;
/**
* @name build
* @description Build the subscription payload for Lemon Squeezy
* @param params
*/
build<
LineItem extends {
id: string;
quantity: number;
product: string;
variant: string;
priceAmount: number;
type: 'flat' | 'per_seat' | 'metered';
},
>(params: {
id: string;
accountId: string;
customerId: string;
lineItems: LineItem[];
interval: string;
intervalCount: number;
status: string;
currency: string;
cancelAtPeriodEnd: boolean;
periodStartsAt: number;
periodEndsAt: number;
trialStartsAt: number | null;
trialEndsAt: number | null;
}): UpsertSubscriptionParams {
const canceledAtPeriodEnd =
params.status === 'cancelled' && params.cancelAtPeriodEnd;
const active =
params.status === 'active' ||
params.status === 'trialing' ||
canceledAtPeriodEnd;
const lineItems = params.lineItems.map((item) => {
const quantity = item.quantity ?? 1;
return {
id: item.id,
subscription_item_id: item.id,
quantity,
interval: params.interval,
interval_count: params.intervalCount,
subscription_id: params.id,
product_id: item.product,
variant_id: item.variant,
price_amount: item.priceAmount,
type: item.type,
};
});
// otherwise we are updating a subscription
// and we only need to return the update payload
return {
target_subscription_id: params.id,
target_account_id: params.accountId,
target_customer_id: params.customerId,
billing_provider: 'lemon-squeezy',
status: this.getSubscriptionStatus(params.status as SubscriptionStatus),
line_items: lineItems,
active,
currency: params.currency,
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: params.trialStartsAt
? getISOString(params.trialStartsAt)
: undefined,
trial_ends_at: params.trialEndsAt
? getISOString(params.trialEndsAt)
: undefined,
};
}
private getSubscriptionStatus(status: SubscriptionStatus) {
switch (status) {
case 'active':
return 'active';
case 'cancelled':
return 'canceled';
case 'paused':
return 'paused';
case 'on_trial':
return 'trialing';
case 'past_due':
return 'past_due';
case 'unpaid':
return 'unpaid';
case 'expired':
return 'past_due';
default:
return 'active';
}
}
}
function getISOString(date: number | null) {
return date ? new Date(date).toISOString() : undefined;
}