merge: upstream/main — latest MakerKit fixes and dependency updates
This commit is contained in:
@@ -33,7 +33,8 @@ export abstract class BillingStrategyProviderService {
|
||||
abstract createCheckoutSession(
|
||||
params: z.output<typeof CreateBillingCheckoutSchema>,
|
||||
): Promise<{
|
||||
checkoutToken: string;
|
||||
checkoutToken: string | null;
|
||||
url?: string | null;
|
||||
}>;
|
||||
|
||||
abstract cancelSubscription(
|
||||
|
||||
@@ -16,7 +16,7 @@ const { publishableKey } = StripeClientEnvSchema.parse({
|
||||
publishableKey: process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY,
|
||||
});
|
||||
|
||||
const stripePromise = loadStripe(publishableKey);
|
||||
const stripePromise = loadStripe(publishableKey as string);
|
||||
|
||||
export function StripeCheckout({
|
||||
checkoutToken,
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
import * as z from 'zod';
|
||||
|
||||
const isHostedMode = process.env.STRIPE_UI_MODE === 'hosted_page';
|
||||
|
||||
export const StripeClientEnvSchema = z
|
||||
.object({
|
||||
publishableKey: z.string().min(1),
|
||||
publishableKey: isHostedMode ? z.string().optional() : z.string().min(1),
|
||||
})
|
||||
.refine(
|
||||
(schema) => {
|
||||
if (isHostedMode || !schema.publishableKey) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return schema.publishableKey.startsWith('pk_');
|
||||
},
|
||||
{
|
||||
|
||||
@@ -9,6 +9,13 @@ import type { CreateBillingCheckoutSchema } from '@kit/billing/schema';
|
||||
const enableTrialWithoutCreditCard =
|
||||
process.env.STRIPE_ENABLE_TRIAL_WITHOUT_CC === 'true';
|
||||
|
||||
const UI_MODE_VALUES = ['embedded_page', 'hosted_page'] as const;
|
||||
|
||||
const uiMode = z
|
||||
.enum(UI_MODE_VALUES)
|
||||
.default('embedded_page')
|
||||
.parse(process.env.STRIPE_UI_MODE);
|
||||
|
||||
/**
|
||||
* @name createStripeCheckout
|
||||
* @description Creates a Stripe Checkout session, and returns an Object
|
||||
@@ -68,11 +75,9 @@ export async function createStripeCheckout(
|
||||
|
||||
const urls = getUrls({
|
||||
returnUrl: params.returnUrl,
|
||||
uiMode,
|
||||
});
|
||||
|
||||
// we use the embedded mode, so the user does not leave the page
|
||||
const uiMode = 'embedded';
|
||||
|
||||
const customerData = customer
|
||||
? {
|
||||
customer,
|
||||
@@ -127,10 +132,20 @@ export async function createStripeCheckout(
|
||||
});
|
||||
}
|
||||
|
||||
function getUrls(params: { returnUrl: string }) {
|
||||
const returnUrl = `${params.returnUrl}?session_id={CHECKOUT_SESSION_ID}`;
|
||||
function getUrls(params: {
|
||||
returnUrl: string;
|
||||
uiMode: (typeof UI_MODE_VALUES)[number];
|
||||
}) {
|
||||
const url = `${params.returnUrl}?session_id={CHECKOUT_SESSION_ID}`;
|
||||
|
||||
if (params.uiMode === 'hosted_page') {
|
||||
return {
|
||||
success_url: url,
|
||||
cancel_url: params.returnUrl,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
return_url: returnUrl,
|
||||
return_url: url,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -47,9 +47,9 @@ export class StripeBillingStrategyService implements BillingStrategyProviderServ
|
||||
|
||||
logger.info(ctx, 'Creating checkout session...');
|
||||
|
||||
const { client_secret } = await createStripeCheckout(stripe, params);
|
||||
const { client_secret, url } = await createStripeCheckout(stripe, params);
|
||||
|
||||
if (!client_secret) {
|
||||
if (!client_secret && !url) {
|
||||
logger.error(ctx, 'Failed to create checkout session');
|
||||
|
||||
throw new Error('Failed to create checkout session');
|
||||
@@ -57,7 +57,10 @@ export class StripeBillingStrategyService implements BillingStrategyProviderServ
|
||||
|
||||
logger.info(ctx, 'Checkout session created successfully');
|
||||
|
||||
return { checkoutToken: client_secret };
|
||||
return {
|
||||
checkoutToken: client_secret ?? null,
|
||||
url,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'server-only';
|
||||
import { StripeServerEnvSchema } from '../schema/stripe-server-env.schema';
|
||||
|
||||
const STRIPE_API_VERSION = '2026-02-25.clover';
|
||||
const STRIPE_API_VERSION = '2026-03-25.dahlia';
|
||||
|
||||
/**
|
||||
* @description returns a Stripe instance
|
||||
|
||||
26
packages/mcp-server/src/tools/env/model.ts
vendored
26
packages/mcp-server/src/tools/env/model.ts
vendored
@@ -625,7 +625,8 @@ export const envVariables: EnvVariableModel[] = [
|
||||
{
|
||||
name: 'NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY',
|
||||
displayName: 'Stripe Publishable Key',
|
||||
description: 'Your Stripe publishable key.',
|
||||
description:
|
||||
'Your Stripe publishable key. Required when using embedded checkout (default), optional when STRIPE_UI_MODE is set to hosted_page.',
|
||||
hint: `Ex. pk_test_123456789012345678901234`,
|
||||
category: 'Billing',
|
||||
type: 'string',
|
||||
@@ -635,7 +636,13 @@ export const envVariables: EnvVariableModel[] = [
|
||||
variable: 'NEXT_PUBLIC_BILLING_PROVIDER',
|
||||
condition: (value) => value === 'stripe',
|
||||
message:
|
||||
'NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY is required when NEXT_PUBLIC_BILLING_PROVIDER is set to "stripe"',
|
||||
'NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY is required when NEXT_PUBLIC_BILLING_PROVIDER is set to "stripe" and STRIPE_UI_MODE is not "hosted_page"',
|
||||
},
|
||||
{
|
||||
variable: 'STRIPE_UI_MODE',
|
||||
condition: (value) => value !== 'hosted_page',
|
||||
message:
|
||||
'NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY is required when STRIPE_UI_MODE is not set to "hosted_page"',
|
||||
},
|
||||
],
|
||||
validate: ({ value }) => {
|
||||
@@ -1391,6 +1398,21 @@ export const envVariables: EnvVariableModel[] = [
|
||||
return z.coerce.boolean().optional().safeParse(value);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'STRIPE_UI_MODE',
|
||||
displayName: 'Stripe Checkout UI Mode',
|
||||
description:
|
||||
'Controls whether Stripe Checkout uses an embedded page or a hosted page. Defaults to embedded_page.',
|
||||
category: 'Billing',
|
||||
type: 'enum',
|
||||
values: ['embedded_page', 'hosted_page'],
|
||||
validate: ({ value }) => {
|
||||
return z
|
||||
.enum(['embedded_page', 'hosted_page'])
|
||||
.optional()
|
||||
.safeParse(value);
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'NEXT_PUBLIC_THEME_COLOR',
|
||||
displayName: 'Theme Color',
|
||||
|
||||
@@ -106,40 +106,40 @@
|
||||
"test:unit": "vitest run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@base-ui/react": "^1.3.0",
|
||||
"@hookform/resolvers": "^5.2.2",
|
||||
"@base-ui/react": "catalog:",
|
||||
"@hookform/resolvers": "catalog:",
|
||||
"@kit/shared": "workspace:*",
|
||||
"clsx": "^2.1.1",
|
||||
"cmdk": "^1.1.1",
|
||||
"embla-carousel-react": "^8.6.0",
|
||||
"input-otp": "^1.4.2",
|
||||
"clsx": "catalog:",
|
||||
"cmdk": "catalog:",
|
||||
"embla-carousel-react": "catalog:",
|
||||
"input-otp": "catalog:",
|
||||
"lucide-react": "catalog:",
|
||||
"react-dropzone": "^15.0.0",
|
||||
"react-dropzone": "catalog:",
|
||||
"react-resizable-panels": "catalog:",
|
||||
"react-top-loading-bar": "^3.0.2",
|
||||
"recharts": "3.7.0",
|
||||
"tailwind-merge": "^3.5.0"
|
||||
"react-top-loading-bar": "catalog:",
|
||||
"recharts": "catalog:",
|
||||
"tailwind-merge": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@kit/i18n": "workspace:*",
|
||||
"@kit/tsconfig": "workspace:*",
|
||||
"@supabase/supabase-js": "catalog:",
|
||||
"@tanstack/react-query": "catalog:",
|
||||
"@tanstack/react-table": "^8.21.3",
|
||||
"@tanstack/react-table": "catalog:",
|
||||
"@types/react": "catalog:",
|
||||
"@types/react-dom": "catalog:",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"date-fns": "^4.1.0",
|
||||
"class-variance-authority": "catalog:",
|
||||
"date-fns": "catalog:",
|
||||
"next": "catalog:",
|
||||
"next-intl": "^4.8.3",
|
||||
"next-safe-action": "^8.1.8",
|
||||
"next-themes": "0.4.6",
|
||||
"react-day-picker": "^9.14.0",
|
||||
"next-intl": "catalog:",
|
||||
"next-safe-action": "catalog:",
|
||||
"next-themes": "catalog:",
|
||||
"react-day-picker": "catalog:",
|
||||
"react-hook-form": "catalog:",
|
||||
"shadcn": "catalog:",
|
||||
"sonner": "^2.0.7",
|
||||
"sonner": "catalog:",
|
||||
"tailwindcss": "catalog:",
|
||||
"vaul": "^1.1.2",
|
||||
"vaul": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"zod": "catalog:"
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ export function PageMobileNavigation(
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'flex w-full items-center border-b px-4 py-2 lg:hidden lg:px-0',
|
||||
'container flex w-full items-center justify-between px-0 py-2 group-data-[slot="sidebar-wrapper"]/sidebar-wrapper:border-b lg:hidden',
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
@@ -73,30 +73,39 @@ function PageWithHeader(props: PageProps) {
|
||||
const { Navigation, Children, MobileNavigation } = getSlotsFromPage(props);
|
||||
|
||||
return (
|
||||
<div className={cn('flex h-screen flex-1 flex-col', props.className)}>
|
||||
<div
|
||||
className={cn(
|
||||
'bg-background flex min-h-screen flex-1 flex-col',
|
||||
props.className,
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={
|
||||
props.contentContainerClassName ?? 'flex flex-1 flex-col space-y-4'
|
||||
}
|
||||
className={props.contentContainerClassName ?? 'flex flex-1 flex-col'}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'bg-muted/40 dark:border-border dark:shadow-primary/10 flex h-14 items-center justify-between px-4 lg:justify-start lg:shadow-xs',
|
||||
'bg-background/95 supports-[backdrop-filter]:bg-background/80 border-b',
|
||||
{
|
||||
'sticky top-0 z-10 backdrop-blur-md': props.sticky ?? true,
|
||||
},
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={'hidden w-full flex-1 items-center space-x-8 lg:flex'}
|
||||
>
|
||||
{Navigation}
|
||||
</div>
|
||||
<div className="container mx-auto flex h-14 w-full items-center">
|
||||
<div
|
||||
className={
|
||||
'hidden w-full min-w-0 flex-1 items-center space-x-4 lg:flex lg:px-4'
|
||||
}
|
||||
>
|
||||
{Navigation}
|
||||
</div>
|
||||
|
||||
{MobileNavigation}
|
||||
{MobileNavigation}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={'container flex flex-1 flex-col'}>{Children}</div>
|
||||
<div className="container mx-auto flex w-full flex-1 flex-col">
|
||||
{Children}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -113,7 +122,15 @@ export function PageBody(
|
||||
}
|
||||
|
||||
export function PageNavigation(props: React.PropsWithChildren) {
|
||||
return <div className={'bg-inherit'}>{props.children}</div>;
|
||||
return (
|
||||
<div
|
||||
className={
|
||||
'flex flex-1 flex-col bg-inherit group-data-[slot="sidebar-wrapper"]/sidebar-wrapper:flex-initial'
|
||||
}
|
||||
>
|
||||
{props.children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function PageDescription(props: React.PropsWithChildren) {
|
||||
@@ -147,16 +164,25 @@ export function PageHeader({
|
||||
title,
|
||||
description,
|
||||
className,
|
||||
displaySidebarTrigger = true,
|
||||
}: React.PropsWithChildren<{
|
||||
className?: string;
|
||||
title?: string | React.ReactNode;
|
||||
description?: string | React.ReactNode;
|
||||
displaySidebarTrigger?: boolean;
|
||||
}>) {
|
||||
return (
|
||||
<div className={cn('flex items-center justify-between py-4', className)}>
|
||||
<div className={'flex flex-col gap-y-2'}>
|
||||
<div className="flex items-center gap-x-2.5">
|
||||
<SidebarTrigger className="text-muted-foreground hover:text-secondary-foreground h-4.5 w-4.5 cursor-pointer" />
|
||||
<div
|
||||
className={cn(
|
||||
'flex flex-col gap-4 py-4 sm:py-5 lg:flex-row lg:items-center lg:justify-between',
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className={'flex min-w-0 flex-col gap-y-2'}>
|
||||
<div className="flex flex-wrap items-center gap-x-2.5 gap-y-1.5">
|
||||
<If condition={displaySidebarTrigger}>
|
||||
<SidebarTrigger className="text-muted-foreground hover:text-secondary-foreground h-4.5 w-4.5 cursor-pointer" />
|
||||
</If>
|
||||
|
||||
<If condition={description}>
|
||||
<Separator
|
||||
@@ -173,7 +199,9 @@ export function PageHeader({
|
||||
</If>
|
||||
</div>
|
||||
|
||||
{children}
|
||||
<div className="flex w-full flex-wrap items-center gap-2 lg:w-auto lg:justify-end">
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user