mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-10 12:27:44 +00:00
feat(stripe): pass context obejct to stripe plugin callbacks (#2990)
* feat(stripe): pass context obejct to stripe plugin callbacks * cleanup * cleanup * cleanup * chore: lint * fix: tests --------- Co-authored-by: ping-maxwell <maxwell.multinite@gmail.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { Fragment, useEffect, useId, useState } from "react";
|
import { useEffect, useId, useState } from "react";
|
||||||
import useMeasure from "react-use-measure";
|
import useMeasure from "react-use-measure";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import clsx from "clsx";
|
import clsx from "clsx";
|
||||||
@@ -207,131 +207,161 @@ function CodePreview() {
|
|||||||
<MotionConfig transition={{ duration: 0.5, type: "spring", bounce: 0 }}>
|
<MotionConfig transition={{ duration: 0.5, type: "spring", bounce: 0 }}>
|
||||||
<motion.div
|
<motion.div
|
||||||
animate={{ height: height > 0 ? height : undefined }}
|
animate={{ height: height > 0 ? height : undefined }}
|
||||||
className="from-stone-100 to-stone-200 dark:to-black/90 dark:via-stone-950/10 dark:from-stone-950/90 relative overflow-hidden rounded-sm bg-gradient-to-tr ring-1 ring-white/10 backdrop-blur-lg"
|
className="relative overflow-hidden rounded-xl"
|
||||||
>
|
>
|
||||||
<div ref={ref}>
|
{/* Dynamic background based on theme */}
|
||||||
<div className="absolute -top-px left-0 right-0 h-px" />
|
<div
|
||||||
<div className="absolute -bottom-px left-11 right-20 h-px" />
|
className="absolute inset-0 rounded-xl"
|
||||||
<div className="pl-4 pt-4">
|
style={{
|
||||||
<TrafficLightsIcon className="stroke-slate-500/30 h-2.5 w-auto" />
|
background:
|
||||||
|
theme.resolvedTheme === "light"
|
||||||
|
? "linear-gradient(135deg, rgba(255,255,255,0.4) 0%, rgba(255,255,255,0.6) 100%)"
|
||||||
|
: "linear-gradient(135deg, rgba(0,0,0,0.6) 0%, rgba(0,0,0,0.8) 100%)",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="mt-4 flex space-x-2 text-xs">
|
{/* Glass layers - responsive to theme */}
|
||||||
{tabs.map((tab) => (
|
<div className="absolute inset-0 rounded-xl backdrop-blur-3xl bg-gradient-to-br dark:from-black/40 dark:via-black/20 dark:to-black/60 light:from-white/30 light:via-white/10 light:to-white/40" />
|
||||||
<button
|
<div className="absolute inset-0 rounded-xl bg-gradient-to-tl dark:from-slate-800/20 dark:via-transparent dark:to-slate-700/15 light:from-blue-500/10 light:via-transparent light:to-purple-500/10" />
|
||||||
key={tab.name}
|
<div className="absolute inset-0 rounded-xl bg-gradient-to-r from-transparent dark:via-black/10 light:via-white/15 to-transparent" />
|
||||||
onClick={() => setCurrentTab(tab.name)}
|
|
||||||
className={clsx(
|
|
||||||
"relative isolate flex h-6 cursor-pointer items-center justify-center rounded-full px-2.5",
|
|
||||||
currentTab === tab.name
|
|
||||||
? "text-stone-300"
|
|
||||||
: "text-slate-500",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{tab.name}
|
|
||||||
{tab.name === currentTab && (
|
|
||||||
<motion.div
|
|
||||||
layoutId="tab-code-preview"
|
|
||||||
className="bg-stone-800 absolute inset-0 -z-10 rounded-full"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-6 flex flex-col items-start px-1 text-sm">
|
{/* Border and shadow effects - theme aware */}
|
||||||
<div className="absolute top-2 right-4">
|
<div className="absolute inset-0 rounded-xl border shadow-2xl dark:border-white/5 dark:shadow-black/50 light:border-black/10 light:shadow-black/20" />
|
||||||
<Button
|
<div className="absolute inset-0 rounded-xl border dark:border-white/[0.02] light:border-black/[0.05]" />
|
||||||
variant="outline"
|
|
||||||
size="icon"
|
{/* Inner glow - theme specific */}
|
||||||
className="absolute w-5 border-none bg-transparent h-5 top-2 right-0"
|
<div className="absolute inset-0 rounded-xl shadow-inner dark:shadow-black/20 light:shadow-white/30" />
|
||||||
onClick={() => copyToClipboard(code)}
|
|
||||||
>
|
{/* Liquid glass reflection effect - adaptive */}
|
||||||
{copyState ? (
|
<div className="absolute top-0 left-0 right-0 h-1/3 bg-gradient-to-b rounded-t-xl backdrop-blur-sm dark:from-white/[0.03] light:from-black/[0.06] to-transparent" />
|
||||||
<Check className="h-3 w-3" />
|
<div className="absolute top-0 left-1/4 right-1/4 h-1/6 bg-gradient-to-b rounded-t-xl blur-sm dark:from-white/[0.05] light:from-black/[0.08] to-transparent" />
|
||||||
) : (
|
<div className="relative z-10 bg-gradient-to-br from-black/98 via-stone-950/95 to-black/98 dark:from-black/98 dark:via-stone-950/95 dark:to-black/98 light:from-white/98 light:via-stone-50/95 light:to-white/98 backdrop-blur-xl rounded-xl border border-white/[0.02] dark:border-white/[0.02] light:border-black/[0.08]">
|
||||||
<Copy className="h-3 w-3" />
|
<div ref={ref}>
|
||||||
)}
|
<div className="absolute -top-px left-0 right-0 h-px bg-gradient-to-r from-transparent dark:via-white/[0.05] light:via-black/[0.15] to-transparent" />
|
||||||
<span className="sr-only">Copy code</span>
|
<div className="absolute -bottom-px left-11 right-20 h-px bg-gradient-to-r from-transparent dark:via-white/[0.03] light:via-black/[0.10] to-transparent" />
|
||||||
</Button>
|
<div className="pl-4 pt-4">
|
||||||
</div>
|
<TrafficLightsIcon className="stroke-stone-500/30 h-2.5 w-auto" />
|
||||||
<motion.div
|
|
||||||
initial={{ opacity: 0 }}
|
<div className="mt-4 flex space-x-2 text-xs">
|
||||||
animate={{ opacity: 1 }}
|
{tabs.map((tab) => (
|
||||||
transition={{ duration: 0.5 }}
|
<button
|
||||||
key={currentTab}
|
key={tab.name}
|
||||||
className="relative flex items-start px-1 text-sm"
|
onClick={() => setCurrentTab(tab.name)}
|
||||||
>
|
className={clsx(
|
||||||
<div
|
"relative isolate flex h-6 cursor-pointer items-center justify-center rounded-full px-2.5 transition-all duration-200",
|
||||||
aria-hidden="true"
|
currentTab === tab.name
|
||||||
className="border-slate-300/5 text-slate-600 select-none border-r pr-4 font-mono"
|
? "dark:text-gray-100 light:text-gray-900"
|
||||||
>
|
: "dark:text-slate-400 dark:hover:text-slate-300 light:text-slate-600 light:hover:text-slate-700",
|
||||||
{Array.from({
|
)}
|
||||||
length: code.split("\n").length,
|
|
||||||
}).map((_, index) => (
|
|
||||||
<Fragment key={index}>
|
|
||||||
{(index + 1).toString().padStart(2, "0")}
|
|
||||||
<br />
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<Highlight
|
|
||||||
key={theme.resolvedTheme}
|
|
||||||
code={code}
|
|
||||||
language={"javascript"}
|
|
||||||
theme={{
|
|
||||||
...codeTheme,
|
|
||||||
plain: {
|
|
||||||
backgroundColor: "transparent",
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{({
|
|
||||||
className,
|
|
||||||
style,
|
|
||||||
tokens,
|
|
||||||
getLineProps,
|
|
||||||
getTokenProps,
|
|
||||||
}) => (
|
|
||||||
<pre
|
|
||||||
className={clsx(className, "flex overflow-x-auto pb-6")}
|
|
||||||
style={style}
|
|
||||||
>
|
|
||||||
<code className="px-4">
|
|
||||||
{tokens.map((line, lineIndex) => (
|
|
||||||
<div key={lineIndex} {...getLineProps({ line })}>
|
|
||||||
{line.map((token, tokenIndex) => (
|
|
||||||
<span
|
|
||||||
key={tokenIndex}
|
|
||||||
{...getTokenProps({ token })}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</code>
|
|
||||||
</pre>
|
|
||||||
)}
|
|
||||||
</Highlight>
|
|
||||||
</motion.div>
|
|
||||||
<motion.div layout className="self-end">
|
|
||||||
<Link
|
|
||||||
href="https://demo.better-auth.com"
|
|
||||||
target="_blank"
|
|
||||||
className="shadow-md border shadow-primary-foreground mb-4 ml-auto mr-4 mt-auto flex cursor-pointer items-center gap-2 px-3 py-1 transition-all ease-in-out hover:opacity-70"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
width="1em"
|
|
||||||
height="1em"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
>
|
>
|
||||||
<path
|
{tab.name}
|
||||||
fill="currentColor"
|
{tab.name === currentTab && (
|
||||||
d="M10 20H8V4h2v2h2v3h2v2h2v2h-2v2h-2v3h-2z"
|
<motion.div
|
||||||
></path>
|
layoutId="tab-code-preview"
|
||||||
</svg>
|
className="absolute inset-0 -z-10 rounded-full backdrop-blur-sm border bg-gradient-to-r dark:from-black/60 dark:to-slate-900/80 dark:border-white/[0.05] light:from-white/60 light:to-slate-100/80 light:border-black/[0.15]"
|
||||||
<p className="text-sm">Demo</p>
|
/>
|
||||||
</Link>
|
)}
|
||||||
</motion.div>
|
</button>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-6 flex flex-col items-start px-1 text-sm">
|
||||||
|
<div className="absolute top-2 right-4">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="icon"
|
||||||
|
className="absolute w-5 border-none backdrop-blur-sm h-5 top-2 right-0 transition-all duration-200 dark:bg-black/20 dark:hover:bg-black/40 dark:border-white/[0.05] light:bg-white/20 light:hover:bg-white/40 light:border-black/[0.15]"
|
||||||
|
onClick={() => copyToClipboard(code)}
|
||||||
|
>
|
||||||
|
{copyState ? (
|
||||||
|
<Check className="h-3 w-3 dark:text-gray-300 light:text-gray-700" />
|
||||||
|
) : (
|
||||||
|
<Copy className="h-3 w-3 dark:text-gray-300 light:text-gray-700" />
|
||||||
|
)}
|
||||||
|
<span className="sr-only">Copy code</span>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
transition={{ duration: 0.5 }}
|
||||||
|
key={currentTab}
|
||||||
|
className="relative flex items-start px-1 text-sm"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
className="select-none border-r pr-4 font-mono dark:border-slate-300/5 dark:text-slate-600 light:border-slate-700/20 light:text-slate-500"
|
||||||
|
>
|
||||||
|
{Array.from({
|
||||||
|
length: code.split("\n").length,
|
||||||
|
}).map((_, index) => (
|
||||||
|
<div key={index}>
|
||||||
|
{(index + 1).toString().padStart(2, "0")}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<Highlight
|
||||||
|
key={theme.resolvedTheme}
|
||||||
|
code={code}
|
||||||
|
language={"javascript"}
|
||||||
|
theme={{
|
||||||
|
...codeTheme,
|
||||||
|
plain: {
|
||||||
|
backgroundColor: "transparent",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
tokens,
|
||||||
|
getLineProps,
|
||||||
|
getTokenProps,
|
||||||
|
}) => (
|
||||||
|
<pre
|
||||||
|
className={clsx(
|
||||||
|
className,
|
||||||
|
"flex overflow-x-auto pb-6",
|
||||||
|
)}
|
||||||
|
style={style}
|
||||||
|
>
|
||||||
|
<code className="px-4">
|
||||||
|
{tokens.map((line, lineIndex) => (
|
||||||
|
<div key={lineIndex} {...getLineProps({ line })}>
|
||||||
|
{line.map((token, tokenIndex) => (
|
||||||
|
<span
|
||||||
|
key={tokenIndex}
|
||||||
|
{...getTokenProps({ token })}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</code>
|
||||||
|
</pre>
|
||||||
|
)}
|
||||||
|
</Highlight>
|
||||||
|
</motion.div>
|
||||||
|
<motion.div layout className="self-end">
|
||||||
|
<Link
|
||||||
|
href="https://demo.better-auth.com"
|
||||||
|
target="_blank"
|
||||||
|
className="shadow-lg border backdrop-blur-sm mb-4 ml-auto mr-4 mt-auto flex cursor-pointer items-center gap-2 px-3 py-1 transition-all ease-in-out hover:opacity-90 rounded-md dark:border-white/[0.05] dark:bg-black/20 dark:hover:bg-black/40 light:border-black/[0.15] light:bg-white/20 light:hover:bg-white/40"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="1em"
|
||||||
|
height="1em"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
fill="currentColor"
|
||||||
|
d="M10 20H8V4h2v2h2v3h2v2h2v2h-2v2h-2v3h-2z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
<p className="text-sm">Demo</p>
|
||||||
|
</Link>
|
||||||
|
</motion.div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -74,12 +74,15 @@ export async function onCheckoutSessionCompleted(
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
await options.subscription?.onSubscriptionComplete?.({
|
await options.subscription?.onSubscriptionComplete?.(
|
||||||
event,
|
{
|
||||||
subscription: dbSubscription as Subscription,
|
event,
|
||||||
stripeSubscription: subscription,
|
subscription: dbSubscription as Subscription,
|
||||||
plan,
|
stripeSubscription: subscription,
|
||||||
});
|
plan,
|
||||||
|
},
|
||||||
|
ctx,
|
||||||
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -183,14 +186,14 @@ export async function onSubscriptionUpdated(
|
|||||||
subscription.status === "trialing" &&
|
subscription.status === "trialing" &&
|
||||||
plan.freeTrial?.onTrialEnd
|
plan.freeTrial?.onTrialEnd
|
||||||
) {
|
) {
|
||||||
await plan.freeTrial.onTrialEnd({ subscription }, ctx.request);
|
await plan.freeTrial.onTrialEnd({ subscription }, ctx);
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
subscriptionUpdated.status === "incomplete_expired" &&
|
subscriptionUpdated.status === "incomplete_expired" &&
|
||||||
subscription.status === "trialing" &&
|
subscription.status === "trialing" &&
|
||||||
plan.freeTrial?.onTrialExpired
|
plan.freeTrial?.onTrialExpired
|
||||||
) {
|
) {
|
||||||
await plan.freeTrial.onTrialExpired(subscription, ctx.request);
|
await plan.freeTrial.onTrialExpired(subscription, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|||||||
@@ -91,12 +91,15 @@ export const stripe = <O extends StripeOptions>(options: O) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
const isAuthorized = ctx.body?.referenceId
|
const isAuthorized = ctx.body?.referenceId
|
||||||
? await options.subscription?.authorizeReference?.({
|
? await options.subscription?.authorizeReference?.(
|
||||||
user: session.user,
|
{
|
||||||
session: session.session,
|
user: session.user,
|
||||||
referenceId,
|
session: session.session,
|
||||||
action,
|
referenceId,
|
||||||
})
|
action,
|
||||||
|
},
|
||||||
|
ctx,
|
||||||
|
)
|
||||||
: true;
|
: true;
|
||||||
if (!isAuthorized) {
|
if (!isAuthorized) {
|
||||||
throw new APIError("UNAUTHORIZED", {
|
throw new APIError("UNAUTHORIZED", {
|
||||||
@@ -387,6 +390,8 @@ export const stripe = <O extends StripeOptions>(options: O) => {
|
|||||||
subscription,
|
subscription,
|
||||||
},
|
},
|
||||||
ctx.request,
|
ctx.request,
|
||||||
|
//@ts-expect-error
|
||||||
|
ctx,
|
||||||
);
|
);
|
||||||
|
|
||||||
const freeTrail = plan.freeTrial
|
const freeTrail = plan.freeTrial
|
||||||
@@ -1035,11 +1040,14 @@ export const stripe = <O extends StripeOptions>(options: O) => {
|
|||||||
if (!customer) {
|
if (!customer) {
|
||||||
logger.error("#BETTER_AUTH: Failed to create customer");
|
logger.error("#BETTER_AUTH: Failed to create customer");
|
||||||
} else {
|
} else {
|
||||||
await options.onCustomerCreate?.({
|
await options.onCustomerCreate?.(
|
||||||
customer,
|
{
|
||||||
stripeCustomer,
|
customer,
|
||||||
user,
|
stripeCustomer,
|
||||||
});
|
user,
|
||||||
|
},
|
||||||
|
ctx,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -561,6 +561,10 @@ describe("stripe", async () => {
|
|||||||
stripeSubscription: expect.any(Object),
|
stripeSubscription: expect.any(Object),
|
||||||
plan: expect.any(Object),
|
plan: expect.any(Object),
|
||||||
}),
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
context: expect.any(Object),
|
||||||
|
_flag: expect.any(String),
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const updateEvent = {
|
const updateEvent = {
|
||||||
|
|||||||
@@ -1,4 +1,9 @@
|
|||||||
import type { InferOptionSchema, Session, User } from "better-auth";
|
import type {
|
||||||
|
GenericEndpointContext,
|
||||||
|
InferOptionSchema,
|
||||||
|
Session,
|
||||||
|
User,
|
||||||
|
} from "better-auth";
|
||||||
import type Stripe from "stripe";
|
import type Stripe from "stripe";
|
||||||
import type { subscriptions, user } from "./schema";
|
import type { subscriptions, user } from "./schema";
|
||||||
|
|
||||||
@@ -70,7 +75,7 @@ export type StripePlan = {
|
|||||||
data: {
|
data: {
|
||||||
subscription: Subscription;
|
subscription: Subscription;
|
||||||
},
|
},
|
||||||
request?: Request,
|
ctx: GenericEndpointContext,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
/**
|
/**
|
||||||
* A function that will be called when the trial
|
* A function that will be called when the trial
|
||||||
@@ -80,7 +85,7 @@ export type StripePlan = {
|
|||||||
*/
|
*/
|
||||||
onTrialExpired?: (
|
onTrialExpired?: (
|
||||||
subscription: Subscription,
|
subscription: Subscription,
|
||||||
request?: Request,
|
ctx: GenericEndpointContext,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -186,7 +191,7 @@ export interface StripeOptions {
|
|||||||
stripeCustomer: Stripe.Customer;
|
stripeCustomer: Stripe.Customer;
|
||||||
user: User;
|
user: User;
|
||||||
},
|
},
|
||||||
request?: Request,
|
ctx: GenericEndpointContext,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
/**
|
/**
|
||||||
* A custom function to get the customer create
|
* A custom function to get the customer create
|
||||||
@@ -199,7 +204,7 @@ export interface StripeOptions {
|
|||||||
user: User;
|
user: User;
|
||||||
session: Session;
|
session: Session;
|
||||||
},
|
},
|
||||||
request?: Request,
|
ctx: GenericEndpointContext,
|
||||||
) => Promise<{}>;
|
) => Promise<{}>;
|
||||||
/**
|
/**
|
||||||
* Subscriptions
|
* Subscriptions
|
||||||
@@ -233,7 +238,7 @@ export interface StripeOptions {
|
|||||||
subscription: Subscription;
|
subscription: Subscription;
|
||||||
plan: StripePlan;
|
plan: StripePlan;
|
||||||
},
|
},
|
||||||
request?: Request,
|
ctx: GenericEndpointContext,
|
||||||
) => Promise<void>;
|
) => Promise<void>;
|
||||||
/**
|
/**
|
||||||
* A callback to run after a user is about to cancel their subscription
|
* A callback to run after a user is about to cancel their subscription
|
||||||
@@ -258,7 +263,7 @@ export interface StripeOptions {
|
|||||||
* and belongs to the user
|
* and belongs to the user
|
||||||
*
|
*
|
||||||
* @param data - data containing user, session and referenceId
|
* @param data - data containing user, session and referenceId
|
||||||
* @param request - Request Object
|
* @param ctx - the context object
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
authorizeReference?: (
|
authorizeReference?: (
|
||||||
@@ -272,7 +277,7 @@ export interface StripeOptions {
|
|||||||
| "cancel-subscription"
|
| "cancel-subscription"
|
||||||
| "restore-subscription";
|
| "restore-subscription";
|
||||||
},
|
},
|
||||||
request?: Request,
|
ctx: GenericEndpointContext,
|
||||||
) => Promise<boolean>;
|
) => Promise<boolean>;
|
||||||
/**
|
/**
|
||||||
* A callback to run after a user has deleted their subscription
|
* A callback to run after a user has deleted their subscription
|
||||||
@@ -287,7 +292,7 @@ export interface StripeOptions {
|
|||||||
* parameters for session create params
|
* parameters for session create params
|
||||||
*
|
*
|
||||||
* @param data - data containing user, session and plan
|
* @param data - data containing user, session and plan
|
||||||
* @param request - Request Object
|
* @param ctx - the context object
|
||||||
*/
|
*/
|
||||||
getCheckoutSessionParams?: (
|
getCheckoutSessionParams?: (
|
||||||
data: {
|
data: {
|
||||||
@@ -296,7 +301,7 @@ export interface StripeOptions {
|
|||||||
plan: StripePlan;
|
plan: StripePlan;
|
||||||
subscription: Subscription;
|
subscription: Subscription;
|
||||||
},
|
},
|
||||||
request?: Request,
|
ctx: GenericEndpointContext,
|
||||||
) =>
|
) =>
|
||||||
| Promise<{
|
| Promise<{
|
||||||
params?: Stripe.Checkout.SessionCreateParams;
|
params?: Stripe.Checkout.SessionCreateParams;
|
||||||
|
|||||||
Reference in New Issue
Block a user