v1.3.27-c: Add adjustment/summary action for meter event

This commit is contained in:
Yudi Xiao
2025-10-14 10:42:41 +08:00
parent 87846d7668
commit 0705a5f74d
4 changed files with 1792 additions and 1677 deletions

View File

@@ -1,7 +1,7 @@
{
"name": "@bowong/better-auth-stripe",
"author": "Bowong",
"version": "1.3.27-a",
"version": "1.3.27-c",
"main": "dist/index.cjs",
"license": "MIT",
"keywords": [

File diff suppressed because it is too large Load Diff

View File

@@ -1,331 +1,336 @@
import type {
GenericEndpointContext,
InferOptionSchema,
Session,
User,
GenericEndpointContext,
InferOptionSchema,
Session,
User,
} from "better-auth";
import type Stripe from "stripe";
import type { subscriptions, user } from "./schema";
import type {subscriptions, user} from "./schema";
export type StripePlan = {
/**
* Monthly price id
*/
priceId?: string;
/**
* To use lookup key instead of price id
*
* https://docs.stripe.com/products-prices/
* manage-prices#lookup-keys
*/
lookupKey?: string;
/**
* A yearly discount price id
*
* useful when you want to offer a discount for
* yearly subscription
*/
annualDiscountPriceId?: string;
/**
* To use lookup key instead of price id
*
* https://docs.stripe.com/products-prices/
* manage-prices#lookup-keys
*/
annualDiscountLookupKey?: string;
/**
* Plan name
*/
name: string;
/**
* Limits for the plan
*/
limits?: Record<string, number>;
/**
* Plan group name
*
* useful when you want to group plans or
* when a user can subscribe to multiple plans.
*/
group?: string;
/**
* Free trial days
*/
freeTrial?: {
/**
* Number of days
*/
days: number;
/**
* A function that will be called when the trial
* starts.
*
* @param subscription
* @returns
*/
onTrialStart?: (subscription: Subscription) => Promise<void>;
/**
* A function that will be called when the trial
* ends
*
* @param subscription - Subscription
* @returns
*/
onTrialEnd?: (
data: {
subscription: Subscription;
},
ctx: GenericEndpointContext,
) => Promise<void>;
/**
* A function that will be called when the trial
* expired.
* @param subscription - Subscription
* @returns
*/
onTrialExpired?: (
subscription: Subscription,
ctx: GenericEndpointContext,
) => Promise<void>;
};
/**
* Monthly price id
*/
priceId?: string;
/**
* To use lookup key instead of price id
*
* https://docs.stripe.com/products-prices/
* manage-prices#lookup-keys
*/
lookupKey?: string;
/**
* A yearly discount price id
*
* useful when you want to offer a discount for
* yearly subscription
*/
annualDiscountPriceId?: string;
/**
* To use lookup key instead of price id
*
* https://docs.stripe.com/products-prices/
* manage-prices#lookup-keys
*/
annualDiscountLookupKey?: string;
/**
* Plan name
*/
name: string;
/**
* Limits for the plan
*/
limits?: Record<string, number>;
/**
* Plan group name
*
* useful when you want to group plans or
* when a user can subscribe to multiple plans.
*/
group?: string;
/**
* Free trial days
*/
freeTrial?: {
/**
* Number of days
*/
days: number;
/**
* A function that will be called when the trial
* starts.
*
* @param subscription
* @returns
*/
onTrialStart?: (subscription: Subscription) => Promise<void>;
/**
* A function that will be called when the trial
* ends
*
* @param subscription - Subscription
* @returns
*/
onTrialEnd?: (
data: {
subscription: Subscription;
},
ctx: GenericEndpointContext,
) => Promise<void>;
/**
* A function that will be called when the trial
* expired.
* @param subscription - Subscription
* @returns
*/
onTrialExpired?: (
subscription: Subscription,
ctx: GenericEndpointContext,
) => Promise<void>;
};
};
export type actionType =
| "upgrade-subscription"
| "list-subscription"
| "cancel-subscription"
| "restore-subscription"
| "meter-event"
| "adjust-meter-event"
| "meter-event-summary"
| "billing-portal";
export interface Subscription {
/**
* Database identifier
*/
id: string;
/**
* The plan name
*/
plan: string;
/**
* Stripe customer id
*/
stripeCustomerId?: string;
/**
* Stripe subscription id
*/
stripeSubscriptionId?: string;
/**
* Trial start date
*/
trialStart?: Date;
/**
* Trial end date
*/
trialEnd?: Date;
/**
* Price Id for the subscription
*/
priceId?: string;
/**
* To what reference id the subscription belongs to
* @example
* - userId for a user
* - workspace id for a saas platform
* - website id for a hosting platform
*
* @default - userId
*/
referenceId: string;
/**
* Subscription status
*/
status:
| "active"
| "canceled"
| "incomplete"
| "incomplete_expired"
| "past_due"
| "paused"
| "trialing"
| "unpaid";
/**
* The billing cycle start date
*/
periodStart?: Date;
/**
* The billing cycle end date
*/
periodEnd?: Date;
/**
* Cancel at period end
*/
cancelAtPeriodEnd?: boolean;
/**
* A field to group subscriptions so you can have multiple subscriptions
* for one reference id
*/
groupId?: string;
/**
* Number of seats for the subscription (useful for team plans)
*/
seats?: number;
/**
* Database identifier
*/
id: string;
/**
* The plan name
*/
plan: string;
/**
* Stripe customer id
*/
stripeCustomerId?: string;
/**
* Stripe subscription id
*/
stripeSubscriptionId?: string;
/**
* Trial start date
*/
trialStart?: Date;
/**
* Trial end date
*/
trialEnd?: Date;
/**
* Price Id for the subscription
*/
priceId?: string;
/**
* To what reference id the subscription belongs to
* @example
* - userId for a user
* - workspace id for a saas platform
* - website id for a hosting platform
*
* @default - userId
*/
referenceId: string;
/**
* Subscription status
*/
status:
| "active"
| "canceled"
| "incomplete"
| "incomplete_expired"
| "past_due"
| "paused"
| "trialing"
| "unpaid";
/**
* The billing cycle start date
*/
periodStart?: Date;
/**
* The billing cycle end date
*/
periodEnd?: Date;
/**
* Cancel at period end
*/
cancelAtPeriodEnd?: boolean;
/**
* A field to group subscriptions so you can have multiple subscriptions
* for one reference id
*/
groupId?: string;
/**
* Number of seats for the subscription (useful for team plans)
*/
seats?: number;
}
export interface StripeOptions {
/**
* Stripe Client
*/
stripeClient: Stripe;
/**
* Stripe Webhook Secret
*
* @description Stripe webhook secret key
*/
stripeWebhookSecret: string;
/**
* Enable customer creation when a user signs up
*/
createCustomerOnSignUp?: boolean;
/**
* A callback to run after a customer has been created
* @param customer - Customer Data
* @param stripeCustomer - Stripe Customer Data
* @returns
*/
onCustomerCreate?: (
data: {
stripeCustomer: Stripe.Customer;
user: User & { stripeCustomerId: string };
},
ctx: GenericEndpointContext,
) => Promise<void>;
/**
* A custom function to get the customer create
* params
* @param data - data containing user and session
* @returns
*/
getCustomerCreateParams?: (
user: User,
ctx: GenericEndpointContext,
) => Promise<Partial<Stripe.CustomerCreateParams>>;
/**
* Subscriptions
*/
subscription?: {
enabled: boolean;
/**
* Subscription Configuration
*/
/**
* List of plan
*/
plans: StripePlan[] | (() => StripePlan[] | Promise<StripePlan[]>);
/**
* Require email verification before a user is allowed to upgrade
* their subscriptions
*
* @default false
*/
requireEmailVerification?: boolean;
/**
* A callback to run after a user has subscribed to a package
* @param event - Stripe Event
* @param subscription - Subscription Data
* @returns
*/
onSubscriptionComplete?: (
data: {
event: Stripe.Event;
stripeSubscription: Stripe.Subscription;
subscription: Subscription;
plan: StripePlan;
},
ctx: GenericEndpointContext,
) => Promise<void>;
/**
* A callback to run after a user is about to cancel their subscription
* @returns
*/
onSubscriptionUpdate?: (data: {
event: Stripe.Event;
subscription: Subscription;
}) => Promise<void>;
/**
* A callback to run after a user is about to cancel their subscription
* @returns
*/
onSubscriptionCancel?: (data: {
event?: Stripe.Event;
subscription: Subscription;
stripeSubscription: Stripe.Subscription;
cancellationDetails?: Stripe.Subscription.CancellationDetails | null;
}) => Promise<void>;
/**
* A function to check if the reference id is valid
* and belongs to the user
*
* @param data - data containing user, session and referenceId
* @param ctx - the context object
* @returns
*/
authorizeReference?: (
data: {
user: User & Record<string, any>;
session: Session & Record<string, any>;
referenceId: string;
action:
| "upgrade-subscription"
| "list-subscription"
| "cancel-subscription"
| "restore-subscription"
| "meter-event"
| "billing-portal";
},
ctx: GenericEndpointContext,
) => Promise<boolean>;
/**
* A callback to run after a user has deleted their subscription
* @returns
*/
onSubscriptionDeleted?: (data: {
event: Stripe.Event;
stripeSubscription: Stripe.Subscription;
subscription: Subscription;
}) => Promise<void>;
/**
* parameters for session create params
*
* @param data - data containing user, session and plan
* @param ctx - the context object
*/
getCheckoutSessionParams?: (
data: {
user: User & Record<string, any>;
session: Session & Record<string, any>;
plan: StripePlan;
subscription: Subscription;
},
ctx: GenericEndpointContext,
) =>
| Promise<{
params?: Stripe.Checkout.SessionCreateParams;
options?: Stripe.RequestOptions;
}>
| {
params?: Stripe.Checkout.SessionCreateParams;
options?: Stripe.RequestOptions;
};
/**
* Enable organization subscription
*/
organization?: {
enabled: boolean;
};
};
/**
* A callback to run after a stripe event is received
* @param event - Stripe Event
* @returns
*/
onEvent?: (event: Stripe.Event) => Promise<void>;
/**
* Schema for the stripe plugin
*/
schema?: InferOptionSchema<typeof subscriptions & typeof user>;
/**
* Stripe Client
*/
stripeClient: Stripe;
/**
* Stripe Webhook Secret
*
* @description Stripe webhook secret key
*/
stripeWebhookSecret: string;
/**
* Enable customer creation when a user signs up
*/
createCustomerOnSignUp?: boolean;
/**
* A callback to run after a customer has been created
* @param customer - Customer Data
* @param stripeCustomer - Stripe Customer Data
* @returns
*/
onCustomerCreate?: (
data: {
stripeCustomer: Stripe.Customer;
user: User & { stripeCustomerId: string };
},
ctx: GenericEndpointContext,
) => Promise<void>;
/**
* A custom function to get the customer create
* params
* @param data - data containing user and session
* @returns
*/
getCustomerCreateParams?: (
user: User,
ctx: GenericEndpointContext,
) => Promise<Partial<Stripe.CustomerCreateParams>>;
/**
* Subscriptions
*/
subscription?: {
enabled: boolean;
/**
* Subscription Configuration
*/
/**
* List of plan
*/
plans: StripePlan[] | (() => StripePlan[] | Promise<StripePlan[]>);
/**
* Require email verification before a user is allowed to upgrade
* their subscriptions
*
* @default false
*/
requireEmailVerification?: boolean;
/**
* A callback to run after a user has subscribed to a package
* @param event - Stripe Event
* @param subscription - Subscription Data
* @returns
*/
onSubscriptionComplete?: (
data: {
event: Stripe.Event;
stripeSubscription: Stripe.Subscription;
subscription: Subscription;
plan: StripePlan;
},
ctx: GenericEndpointContext,
) => Promise<void>;
/**
* A callback to run after a user is about to cancel their subscription
* @returns
*/
onSubscriptionUpdate?: (data: {
event: Stripe.Event;
subscription: Subscription;
}) => Promise<void>;
/**
* A callback to run after a user is about to cancel their subscription
* @returns
*/
onSubscriptionCancel?: (data: {
event?: Stripe.Event;
subscription: Subscription;
stripeSubscription: Stripe.Subscription;
cancellationDetails?: Stripe.Subscription.CancellationDetails | null;
}) => Promise<void>;
/**
* A function to check if the reference id is valid
* and belongs to the user
*
* @param data - data containing user, session and referenceId
* @param ctx - the context object
* @returns
*/
authorizeReference?: (
data: {
user: User & Record<string, any>;
session: Session & Record<string, any>;
referenceId: string;
action: actionType;
},
ctx: GenericEndpointContext,
) => Promise<boolean>;
/**
* A callback to run after a user has deleted their subscription
* @returns
*/
onSubscriptionDeleted?: (data: {
event: Stripe.Event;
stripeSubscription: Stripe.Subscription;
subscription: Subscription;
}) => Promise<void>;
/**
* parameters for session create params
*
* @param data - data containing user, session and plan
* @param ctx - the context object
*/
getCheckoutSessionParams?: (
data: {
user: User & Record<string, any>;
session: Session & Record<string, any>;
plan: StripePlan;
subscription: Subscription;
},
ctx: GenericEndpointContext,
) =>
| Promise<{
params?: Stripe.Checkout.SessionCreateParams;
options?: Stripe.RequestOptions;
}>
| {
params?: Stripe.Checkout.SessionCreateParams;
options?: Stripe.RequestOptions;
};
/**
* Enable organization subscription
*/
organization?: {
enabled: boolean;
};
};
/**
* A callback to run after a stripe event is received
* @param event - Stripe Event
* @returns
*/
onEvent?: (event: Stripe.Event) => Promise<void>;
/**
* Schema for the stripe plugin
*/
schema?: InferOptionSchema<typeof subscriptions & typeof user>;
}
export interface InputSubscription extends Omit<Subscription, "id"> {}
export interface InputSubscription extends Omit<Subscription, "id"> {
}

View File

@@ -1,30 +1,30 @@
import type { StripeOptions } from "./types";
import type {StripeOptions} from "./types";
export async function getPlans(options: StripeOptions) {
return typeof options?.subscription?.plans === "function"
? await options.subscription?.plans()
: options.subscription?.plans;
return typeof options?.subscription?.plans === "function"
? await options.subscription?.plans()
: options.subscription?.plans;
}
export async function getPlanByPriceInfo(
options: StripeOptions,
priceId: string,
priceLookupKey: string | null,
options: StripeOptions,
priceId: string,
priceLookupKey: string | null,
) {
return await getPlans(options).then((res) =>
res?.find(
(plan) =>
plan.priceId === priceId ||
plan.annualDiscountPriceId === priceId ||
(priceLookupKey &&
(plan.lookupKey === priceLookupKey ||
plan.annualDiscountLookupKey === priceLookupKey)),
),
);
return await getPlans(options).then((res) =>
res?.find(
(plan) =>
plan.priceId === priceId ||
plan.annualDiscountPriceId === priceId ||
(priceLookupKey &&
(plan.lookupKey === priceLookupKey ||
plan.annualDiscountLookupKey === priceLookupKey)),
),
);
}
export async function getPlanByName(options: StripeOptions, name: string) {
return await getPlans(options).then((res) =>
res?.find((plan) => plan.name.toLowerCase() === name.toLowerCase()),
);
return await getPlans(options).then((res) =>
res?.find((plan) => plan.name.toLowerCase() === name.toLowerCase()),
);
}