feat: billing

This commit is contained in:
David Nguyen
2025-05-19 12:38:50 +10:00
parent 7abfc9e271
commit 2805478e0d
221 changed files with 8436 additions and 5847 deletions

View File

@ -1,36 +1,39 @@
import type { Subscription } from '@prisma/client';
import { SubscriptionStatus } from '@prisma/client';
import type { Subscription } from '@documenso/prisma/generated/zod/modelSchema/SubscriptionSchema';
import { IS_BILLING_ENABLED } from '../constants/app';
import { AppErrorCode } from '../errors/app-error';
import { AppError } from '../errors/app-error';
import type { StripeOrganisationCreateMetadata } from '../types/subscription';
export const generateStripeOrganisationCreateMetadata = (
organisationName: string,
userId: number,
) => {
const metadata: StripeOrganisationCreateMetadata = {
organisationName,
userId,
};
return {
organisationCreateData: JSON.stringify(metadata),
};
};
/**
* Returns true if there is a subscription that is active and is one of the provided price IDs.
* Throws an error if billing is enabled and no subscription is found.
*/
export const subscriptionsContainsActivePlan = (
subscriptions: Subscription[],
priceIds: string[],
allowPastDue?: boolean,
) => {
const allowedSubscriptionStatuses: SubscriptionStatus[] = [SubscriptionStatus.ACTIVE];
export const validateIfSubscriptionIsRequired = (subscription?: Subscription | null) => {
const isBillingEnabled = IS_BILLING_ENABLED();
if (allowPastDue) {
allowedSubscriptionStatuses.push(SubscriptionStatus.PAST_DUE);
if (!isBillingEnabled) {
return;
}
return subscriptions.some(
(subscription) =>
allowedSubscriptionStatuses.includes(subscription.status) &&
priceIds.includes(subscription.priceId),
);
};
if (isBillingEnabled && !subscription) {
throw new AppError(AppErrorCode.NOT_FOUND, {
message: 'Subscription not found',
});
}
/**
* Returns true if there is a subscription that is active and is one of the provided product IDs.
*/
export const subscriptionsContainsActiveProductId = (
subscriptions: Subscription[],
productId: string[],
) => {
return subscriptions.some(
(subscription) =>
subscription.status === SubscriptionStatus.ACTIVE && productId.includes(subscription.planId),
);
return subscription;
};