From 37ef0a93226ec576ef65ee05923fa76bd2150357 Mon Sep 17 00:00:00 2001 From: Charlie Lamb Date: Mon, 23 Feb 2026 13:31:33 +0000 Subject: [PATCH] feat: add setup payment v2 --- server/src/external/autumn/autumnCli.ts | 6 +- server/src/external/stripe/stripeCusUtils.ts | 30 ++++++ .../handleStripeCheckoutSessionCompleted.ts | 7 ++ .../tasks/handleSetupPaymentMetadata.ts | 80 ++++++++++++++++ .../tasks/handleStandaloneSetupCheckout.ts | 17 +--- server/src/internal/billing/billingRouter.ts | 2 + .../src/internal/billing/v2/actions/index.ts | 2 + .../createSetupCheckoutSession.ts | 96 +++++++++++++++++++ .../v2/actions/setupPayment/setupPayment.ts | 69 +++++++++++++ .../actions/setupPayment/setupPaymentUtils.ts | 14 +++ .../v2/handlers/handleSetupPaymentV2.ts | 27 ++++++ .../executeStripeCheckoutSessionAction.ts | 32 ++----- .../createStripeSessionWithCardFallback.ts | 29 ++++++ .../billingModels/plan/autumnBillingPlan.ts | 8 ++ .../models/billingModels/plan/billingPlan.ts | 2 + shared/models/otherModels/metadataTable.ts | 1 + 16 files changed, 384 insertions(+), 38 deletions(-) create mode 100644 server/src/external/stripe/webhookHandlers/handleStripeCheckoutSessionCompleted/tasks/handleSetupPaymentMetadata.ts create mode 100644 server/src/internal/billing/v2/actions/setupPayment/createSetupCheckoutSession.ts create mode 100644 server/src/internal/billing/v2/actions/setupPayment/setupPayment.ts create mode 100644 server/src/internal/billing/v2/actions/setupPayment/setupPaymentUtils.ts create mode 100644 server/src/internal/billing/v2/handlers/handleSetupPaymentV2.ts create mode 100644 server/src/internal/billing/v2/providers/stripe/utils/checkoutSessions/createStripeSessionWithCardFallback.ts diff --git a/server/src/external/autumn/autumnCli.ts b/server/src/external/autumn/autumnCli.ts index 2cf1520c7..bc2ad68bc 100644 --- a/server/src/external/autumn/autumnCli.ts +++ b/server/src/external/autumn/autumnCli.ts @@ -30,7 +30,7 @@ import { type ProductItem, type RewardRedemption, type SetUsageParams, - type SetupPaymentParamsV0, + type SetupPaymentParamsV1, type TrackParams, type UpdateBalanceParamsV0, type UpdateSubscriptionV0Params, @@ -908,8 +908,8 @@ export class AutumnInt { return data; }, - setupPayment: async (params: SetupPaymentParamsV0) => { - const data = await this.post(`/setup_payment`, params); + setupPayment: async (params: SetupPaymentParamsV1) => { + const data = await this.post(`/billing.setup_payment`, params); return data; }, }; diff --git a/server/src/external/stripe/stripeCusUtils.ts b/server/src/external/stripe/stripeCusUtils.ts index e94fa8217..fc570fb15 100644 --- a/server/src/external/stripe/stripeCusUtils.ts +++ b/server/src/external/stripe/stripeCusUtils.ts @@ -280,3 +280,33 @@ const deleteAllStripeCustomers = async ({ ); } }; + +/** + * Retrieves the customer's payment method and sets it as their default for invoices. + * Returns the payment method if found and set, or null if none available. + */ +export const updateDefaultPaymentMethod = async ({ + stripeCli, + stripeCustomerId, +}: { + stripeCli: Stripe; + stripeCustomerId: string; +}) => { + const paymentMethod = await getCusPaymentMethod({ + stripeCli, + stripeId: stripeCustomerId, + errorIfNone: false, + }); + + if (!paymentMethod) { + return null; + } + + await stripeCli.customers.update(stripeCustomerId, { + invoice_settings: { + default_payment_method: paymentMethod.id, + }, + }); + + return paymentMethod; +}; diff --git a/server/src/external/stripe/webhookHandlers/handleStripeCheckoutSessionCompleted/handleStripeCheckoutSessionCompleted.ts b/server/src/external/stripe/webhookHandlers/handleStripeCheckoutSessionCompleted/handleStripeCheckoutSessionCompleted.ts index eca343ccb..7537dfa25 100644 --- a/server/src/external/stripe/webhookHandlers/handleStripeCheckoutSessionCompleted/handleStripeCheckoutSessionCompleted.ts +++ b/server/src/external/stripe/webhookHandlers/handleStripeCheckoutSessionCompleted/handleStripeCheckoutSessionCompleted.ts @@ -3,6 +3,7 @@ import { handleCheckoutSessionMetadataV2 } from "@/external/stripe/webhookHandle import type { StripeWebhookContext } from "../../webhookMiddlewares/stripeWebhookContext.js"; import { setupCheckoutSessionCompletedContext } from "./setupCheckoutSessionCompletedContext.js"; import { handleLegacyCheckoutSessionMetadata } from "./tasks/handleLegacyCheckoutSessionMetadata.ts/handleCheckoutSessionCompletedLegacy.js"; +import { handleSetupPaymentMetadata } from "./tasks/handleSetupPaymentMetadata.js"; import { handleStandaloneSetupCheckout } from "./tasks/handleStandaloneSetupCheckout.js"; import { updateCustomerFromCheckout } from "./tasks/updateCustomerFromCheckout.js"; @@ -24,6 +25,12 @@ export const handleStripeCheckoutSessionCompleted = async ({ checkoutContext, }); + // Setup payment with metadata (plan attachment after setup) + await handleSetupPaymentMetadata({ + ctx, + checkoutContext, + }); + // Legacy flow await handleLegacyCheckoutSessionMetadata({ ctx, diff --git a/server/src/external/stripe/webhookHandlers/handleStripeCheckoutSessionCompleted/tasks/handleSetupPaymentMetadata.ts b/server/src/external/stripe/webhookHandlers/handleStripeCheckoutSessionCompleted/tasks/handleSetupPaymentMetadata.ts new file mode 100644 index 000000000..78c7f831a --- /dev/null +++ b/server/src/external/stripe/webhookHandlers/handleStripeCheckoutSessionCompleted/tasks/handleSetupPaymentMetadata.ts @@ -0,0 +1,80 @@ +import { type DeferredSetupPaymentData, MetadataType } from "@autumn/shared"; +import { createStripeCli } from "@/external/connect/createStripeCli"; +import { updateDefaultPaymentMethod } from "@/external/stripe/stripeCusUtils"; +import { billingActions } from "@/internal/billing/v2/actions"; +import { setupPaymentToAttachParams } from "@/internal/billing/v2/actions/setupPayment/setupPaymentUtils"; +import { MetadataService } from "@/internal/metadata/MetadataService"; +import type { StripeWebhookContext } from "../../../webhookMiddlewares/stripeWebhookContext"; +import type { CheckoutSessionCompletedContext } from "../setupCheckoutSessionCompletedContext"; + +/** + * Handles setup checkout sessions with metadata (plan attachment after setup). + * Updates the customer's payment method, then attaches the plan if specified. + */ +export const handleSetupPaymentMetadata = async ({ + ctx, + checkoutContext, +}: { + ctx: StripeWebhookContext; + checkoutContext: CheckoutSessionCompletedContext; +}): Promise => { + const { org, env, logger } = ctx; + const { stripeCheckoutSession, metadata } = checkoutContext; + + if (metadata?.type !== MetadataType.SetupPaymentV2) { + return; + } + + logger.info( + `[checkout.completed] Handling setup payment metadata: ${metadata.id}`, + ); + + const deferredData = metadata.data as DeferredSetupPaymentData; + const stripeCustomerId = stripeCheckoutSession.customer as string; + + if (!stripeCustomerId) { + logger.warn("Setup payment metadata: no Stripe customer ID, skipping"); + await MetadataService.delete({ db: ctx.db, id: metadata.id }); + return; + } + + // 1. Update customer's default payment method + const stripeCli = createStripeCli({ org, env }); + const paymentMethod = await updateDefaultPaymentMethod({ + stripeCli, + stripeCustomerId, + }); + + if (paymentMethod) { + logger.info( + `Setup payment metadata: set default payment method for ${stripeCustomerId}`, + ); + } else { + logger.warn("Setup payment metadata: no payment method found after setup"); + } + + // 2. Attach plan if plan_id was specified + if (deferredData.params.plan_id) { + logger.info( + `Setup payment metadata: attaching plan ${deferredData.params.plan_id}`, + ); + + const attachParams = setupPaymentToAttachParams({ + params: deferredData.params, + }); + + await billingActions.attach({ + ctx, + params: attachParams, + preview: false, + skipAutumnCheckout: true, + }); + + logger.info( + `Setup payment metadata: plan ${deferredData.params.plan_id} attached successfully`, + ); + } + + // 3. Cleanup metadata + await MetadataService.delete({ db: ctx.db, id: metadata.id }); +}; diff --git a/server/src/external/stripe/webhookHandlers/handleStripeCheckoutSessionCompleted/tasks/handleStandaloneSetupCheckout.ts b/server/src/external/stripe/webhookHandlers/handleStripeCheckoutSessionCompleted/tasks/handleStandaloneSetupCheckout.ts index 8c4e898d1..e7c81984b 100644 --- a/server/src/external/stripe/webhookHandlers/handleStripeCheckoutSessionCompleted/tasks/handleStandaloneSetupCheckout.ts +++ b/server/src/external/stripe/webhookHandlers/handleStripeCheckoutSessionCompleted/tasks/handleStandaloneSetupCheckout.ts @@ -1,5 +1,5 @@ import { createStripeCli } from "@/external/connect/createStripeCli.js"; -import { getCusPaymentMethod } from "@/external/stripe/stripeCusUtils.js"; +import { updateDefaultPaymentMethod } from "@/external/stripe/stripeCusUtils.js"; import { CusService } from "@/internal/customers/CusService.js"; import type { StripeWebhookContext } from "../../../webhookMiddlewares/stripeWebhookContext.js"; import type { CheckoutSessionCompletedContext } from "../setupCheckoutSessionCompletedContext.js"; @@ -42,11 +42,9 @@ export const handleStandaloneSetupCheckout = async ({ } const stripeCli = createStripeCli({ org, env }); - - const paymentMethod = await getCusPaymentMethod({ + const paymentMethod = await updateDefaultPaymentMethod({ stripeCli, - stripeId: stripeCustomerId, - errorIfNone: false, + stripeCustomerId, }); if (!paymentMethod) { @@ -57,13 +55,6 @@ export const handleStandaloneSetupCheckout = async ({ } logger.info( - `Standalone setup checkout: updating default payment method for customer ${customer.id}`, + `Standalone setup checkout: updated default payment method for customer ${customer.id}`, ); - - // Set as customer's default payment method - await stripeCli.customers.update(stripeCustomerId, { - invoice_settings: { - default_payment_method: paymentMethod.id, - }, - }); }; diff --git a/server/src/internal/billing/billingRouter.ts b/server/src/internal/billing/billingRouter.ts index ccf6353cb..656303301 100644 --- a/server/src/internal/billing/billingRouter.ts +++ b/server/src/internal/billing/billingRouter.ts @@ -9,6 +9,7 @@ import { handleCheckoutV2 } from "./checkout/handleCheckoutV2.js"; import { handleSetupPayment } from "./handlers/handleSetupPayment.js"; import { handleAttachV2 } from "./v2/handlers/handleAttachV2.js"; import { handlePreviewUpdateSubscription } from "./v2/handlers/handlePreviewUpdateSubscription.js"; +import { handleSetupPaymentV2 } from "./v2/handlers/handleSetupPaymentV2.js"; import { handleUpdateSubscription } from "./v2/handlers/handleUpdateSubscription.js"; export const billingRouter = new Hono(); @@ -29,6 +30,7 @@ billingRpcRouter.post( ); billingRpcRouter.post("/billing.attach", ...handleAttachV2); billingRpcRouter.post("/billing.preview_attach", ...handlePreviewAttach); +billingRpcRouter.post("/billing.setup_payment", ...handleSetupPaymentV2); billingRpcRouter.post( "/billing.open_customer_portal", ...handleOpenCustomerPortalV2, diff --git a/server/src/internal/billing/v2/actions/index.ts b/server/src/internal/billing/v2/actions/index.ts index 27198896e..b5c53d532 100644 --- a/server/src/internal/billing/v2/actions/index.ts +++ b/server/src/internal/billing/v2/actions/index.ts @@ -4,10 +4,12 @@ import { legacyAttach } from "@/internal/billing/v2/actions/legacy/legacyAttach" import { renew } from "@/internal/billing/v2/actions/legacy/renew"; import { updateQuantity } from "@/internal/billing/v2/actions/legacy/updateQuantity"; import { migrate } from "@/internal/billing/v2/actions/migrate/migrate"; +import { setupPayment } from "@/internal/billing/v2/actions/setupPayment/setupPayment"; import { updateSubscription } from "@/internal/billing/v2/actions/updateSubscription/updateSubscription"; export const billingActions = { attach: attach, + setupPayment: setupPayment, updateSubscription: updateSubscription, migrate: migrate, diff --git a/server/src/internal/billing/v2/actions/setupPayment/createSetupCheckoutSession.ts b/server/src/internal/billing/v2/actions/setupPayment/createSetupCheckoutSession.ts new file mode 100644 index 000000000..6df56aa4d --- /dev/null +++ b/server/src/internal/billing/v2/actions/setupPayment/createSetupCheckoutSession.ts @@ -0,0 +1,96 @@ +import { + type Customer, + type DeferredSetupPaymentData, + MetadataType, + type SetupPaymentParamsV1, +} from "@autumn/shared"; +import { addDays } from "date-fns"; +import type Stripe from "stripe"; +import { createStripeCli } from "@/external/connect/createStripeCli"; +import type { AutumnContext } from "@/honoUtils/HonoEnv"; +import { createStripeSessionWithCardFallback } from "@/internal/billing/v2/providers/stripe/utils/checkoutSessions/createStripeSessionWithCardFallback"; +import { MetadataService } from "@/internal/metadata/MetadataService"; +import { toSuccessUrl } from "@/internal/orgs/orgUtils/convertOrgUtils"; +import { generateId } from "@/utils/genUtils"; + +/** + * Inserts deferred metadata so the webhook can attach the plan after setup completes. + */ +const insertSetupPaymentMetadata = async ({ + ctx, + params, +}: { + ctx: AutumnContext; + params: SetupPaymentParamsV1; +}) => { + const payload: DeferredSetupPaymentData = { + requestId: ctx.id, + orgId: ctx.org.id, + env: ctx.env, + params, + }; + + return MetadataService.insert({ + db: ctx.db, + data: { + id: generateId("meta"), + type: MetadataType.SetupPaymentV2, + data: payload, + created_at: Date.now(), + expires_at: addDays(Date.now(), 10).getTime(), + }, + }); +}; + +/** + * Creates a Stripe checkout session in setup mode. + * If plan_id is specified, stores metadata so the webhook can attach the plan after setup. + */ +export const createSetupCheckoutSession = async ({ + ctx, + customer, + params, +}: { + ctx: AutumnContext; + customer: Customer; + params: SetupPaymentParamsV1; +}) => { + const { org, env, logger } = ctx; + const stripeCli = createStripeCli({ org, env }); + + // 1. Insert metadata (if plan_id specified) + const metadata = params.plan_id + ? await insertSetupPaymentMetadata({ ctx, params }) + : null; + + // 2. Build session params + const fullParams: Stripe.Checkout.SessionCreateParams = { + customer: customer.processor?.id ?? undefined, + mode: "setup", + success_url: params.success_url || toSuccessUrl({ org, env }), + currency: org.default_currency || "usd", + ...params.checkout_session_params, + ...(metadata ? { metadata: { autumn_metadata_id: metadata.id } } : {}), + }; + + // 3. Create session with card-type fallback + const session = await createStripeSessionWithCardFallback({ + stripeCli, + params: fullParams, + }); + + logger.info( + `Created setup checkout session for ${customer.id ?? customer.internal_id}`, + ); + + // 4. Link metadata to checkout session + if (metadata) { + await MetadataService.update({ + db: ctx.db, + id: metadata.id, + updates: { stripe_checkout_session_id: session.id }, + }); + } + + return { url: session.url }; +}; diff --git a/server/src/internal/billing/v2/actions/setupPayment/setupPayment.ts b/server/src/internal/billing/v2/actions/setupPayment/setupPayment.ts new file mode 100644 index 000000000..207569d44 --- /dev/null +++ b/server/src/internal/billing/v2/actions/setupPayment/setupPayment.ts @@ -0,0 +1,69 @@ +import type { SetupPaymentParamsV1 } from "@autumn/shared"; +import { getOrCreateStripeCustomer } from "@/external/stripe/customers/operations/getOrCreateStripeCustomer"; +import type { AutumnContext } from "@/honoUtils/HonoEnv"; +import { billingActions } from "@/internal/billing/v2/actions"; +import { getOrCreateCustomer } from "@/internal/customers/cusUtils/getOrCreateCustomer"; +import { createSetupCheckoutSession } from "./createSetupCheckoutSession"; +import { setupPaymentToAttachParams } from "./setupPaymentUtils"; + +export interface SetupPaymentResult { + customer_id: string; + entity_id?: string; + url: string; +} + +/** + * Creates a Stripe checkout session in setup mode. + * If plan_id is specified, validates the plan via preview and attaches it after setup completes. + */ +export const setupPayment = async ({ + ctx, + params, +}: { + ctx: AutumnContext; + params: SetupPaymentParamsV1; +}): Promise => { + const { logger } = ctx; + + // 1. Get or create customer (+ Stripe customer) + const fullCustomer = await getOrCreateCustomer({ + ctx, + customerId: params.customer_id, + customerData: params.customer_data, + entityId: params.entity_id, + entityData: params.entity_data, + }); + + await getOrCreateStripeCustomer({ + ctx, + customer: fullCustomer, + }); + + // 2. If plan_id specified, run attach in preview mode to validate + if (params.plan_id) { + logger.info(`Setup payment: validating plan ${params.plan_id} via preview`); + + const attachParams = setupPaymentToAttachParams({ params }); + + await billingActions.attach({ + ctx, + params: attachParams, + preview: true, + }); + + logger.info(`Setup payment: plan ${params.plan_id} validated successfully`); + } + + // 3. Create Stripe setup checkout session + const { url } = await createSetupCheckoutSession({ + ctx, + customer: fullCustomer, + params, + }); + + return { + customer_id: fullCustomer.id ?? fullCustomer.internal_id, + entity_id: params.entity_id, + url: url ?? "", + }; +}; diff --git a/server/src/internal/billing/v2/actions/setupPayment/setupPaymentUtils.ts b/server/src/internal/billing/v2/actions/setupPayment/setupPaymentUtils.ts new file mode 100644 index 000000000..bd450d3b1 --- /dev/null +++ b/server/src/internal/billing/v2/actions/setupPayment/setupPaymentUtils.ts @@ -0,0 +1,14 @@ +import type { AttachParamsV1, SetupPaymentParamsV1 } from "@autumn/shared"; + +/** + * Converts setup payment params to attach params for the preview/attach call. + */ +export const setupPaymentToAttachParams = ({ + params, +}: { + params: SetupPaymentParamsV1; +}): AttachParamsV1 => ({ + ...params, + plan_id: params.plan_id as string, + redirect_mode: "if_required", +}); diff --git a/server/src/internal/billing/v2/handlers/handleSetupPaymentV2.ts b/server/src/internal/billing/v2/handlers/handleSetupPaymentV2.ts new file mode 100644 index 000000000..bb610b0b5 --- /dev/null +++ b/server/src/internal/billing/v2/handlers/handleSetupPaymentV2.ts @@ -0,0 +1,27 @@ +import { + AffectedResource, + ApiVersion, + SetupPaymentParamsV0Schema, + SetupPaymentParamsV1Schema, +} from "@autumn/shared"; +import { createRoute } from "@/honoMiddlewares/routeHandler"; +import { billingActions } from "@/internal/billing/v2/actions"; + +export const handleSetupPaymentV2 = createRoute({ + versionedBody: { + latest: SetupPaymentParamsV1Schema, + [ApiVersion.V1_Beta]: SetupPaymentParamsV0Schema, + }, + resource: AffectedResource.Customer, + handler: async (c) => { + const ctx = c.get("ctx"); + const body = c.req.valid("json"); + + const result = await billingActions.setupPayment({ + ctx, + params: body, + }); + + return c.json(result, 200); + }, +}); diff --git a/server/src/internal/billing/v2/providers/stripe/execute/executeStripeCheckoutSessionAction.ts b/server/src/internal/billing/v2/providers/stripe/execute/executeStripeCheckoutSessionAction.ts index 71d47d31c..3001e89be 100644 --- a/server/src/internal/billing/v2/providers/stripe/execute/executeStripeCheckoutSessionAction.ts +++ b/server/src/internal/billing/v2/providers/stripe/execute/executeStripeCheckoutSessionAction.ts @@ -8,6 +8,7 @@ import { addDays } from "date-fns"; import type Stripe from "stripe"; import { createStripeCli } from "@/external/connect/createStripeCli"; import type { AutumnContext } from "@/honoUtils/HonoEnv"; +import { createStripeSessionWithCardFallback } from "@/internal/billing/v2/providers/stripe/utils/checkoutSessions/createStripeSessionWithCardFallback"; import { insertMetadataFromBillingPlan, updateMetadataWithCheckoutSession, @@ -60,28 +61,15 @@ export const executeStripeCheckoutSessionAction = async ({ metadata: { autumn_metadata_id: metadata.id }, }; - // 3. Create checkout session with fallback for payment method types - let stripeCheckoutSession: Stripe.Checkout.Session; - try { - stripeCheckoutSession = - await stripeCli.checkout.sessions.create(fullParams); - logger.info( - `✅ Created checkout session for customer ${fullCustomer.id ?? fullCustomer.internal_id}`, - ); - } catch (error) { - const msg = error instanceof Error ? error.message : undefined; - if (msg?.includes("No valid payment method types")) { - stripeCheckoutSession = await stripeCli.checkout.sessions.create({ - ...fullParams, - payment_method_types: ["card"], - }); - logger.info( - "✅ Created fallback checkout session with card payment method", - ); - } else { - throw error; - } - } + // 3. Create checkout session with card-type fallback + const stripeCheckoutSession = await createStripeSessionWithCardFallback({ + stripeCli, + params: fullParams, + }); + + logger.info( + `Created checkout session for customer ${fullCustomer.id ?? fullCustomer.internal_id}`, + ); // 4. Update metadata with checkout session ID await updateMetadataWithCheckoutSession({ diff --git a/server/src/internal/billing/v2/providers/stripe/utils/checkoutSessions/createStripeSessionWithCardFallback.ts b/server/src/internal/billing/v2/providers/stripe/utils/checkoutSessions/createStripeSessionWithCardFallback.ts new file mode 100644 index 000000000..96af3ab6c --- /dev/null +++ b/server/src/internal/billing/v2/providers/stripe/utils/checkoutSessions/createStripeSessionWithCardFallback.ts @@ -0,0 +1,29 @@ +import type Stripe from "stripe"; +import type { createStripeCli } from "@/external/connect/createStripeCli"; + +/** + * Creates a Stripe checkout session, retrying with explicit `payment_method_types: ["card"]` + * if Stripe rejects automatic payment method determination. + */ +export const createStripeSessionWithCardFallback = async ({ + stripeCli, + params, +}: { + stripeCli: ReturnType; + params: Stripe.Checkout.SessionCreateParams; +}) => { + try { + return await stripeCli.checkout.sessions.create(params); + } catch (error) { + const msg = error instanceof Error ? error.message : undefined; + + if (msg?.includes("payment method") || msg?.includes("No valid payment")) { + return stripeCli.checkout.sessions.create({ + ...params, + payment_method_types: ["card"], + }); + } + + throw error; + } +}; diff --git a/shared/models/billingModels/plan/autumnBillingPlan.ts b/shared/models/billingModels/plan/autumnBillingPlan.ts index 08ee94ccc..8944c6e0a 100644 --- a/shared/models/billingModels/plan/autumnBillingPlan.ts +++ b/shared/models/billingModels/plan/autumnBillingPlan.ts @@ -1,3 +1,4 @@ +import type { SetupPaymentParamsV1 } from "@api/billing/setupPayment/setupPaymentParamsV1"; import { type AppEnv, CusProductStatus, @@ -93,3 +94,10 @@ export type DeferredAutumnBillingPlanData = { billingContext: BillingContext; resumeAfter?: StripeBillingStage; }; + +export type DeferredSetupPaymentData = { + requestId: string; + orgId: string; + env: AppEnv; + params: SetupPaymentParamsV1; +}; diff --git a/shared/models/billingModels/plan/billingPlan.ts b/shared/models/billingModels/plan/billingPlan.ts index aa5f6f3a6..b5563b49c 100644 --- a/shared/models/billingModels/plan/billingPlan.ts +++ b/shared/models/billingModels/plan/billingPlan.ts @@ -13,11 +13,13 @@ import { type AutumnBillingPlan, AutumnBillingPlanSchema, type DeferredAutumnBillingPlanData, + type DeferredSetupPaymentData, } from "./autumnBillingPlan"; export type { AutumnBillingPlan, DeferredAutumnBillingPlanData, + DeferredSetupPaymentData, StripeBillingPlan, StripeCheckoutSessionAction, StripeInvoiceAction, diff --git a/shared/models/otherModels/metadataTable.ts b/shared/models/otherModels/metadataTable.ts index 8bddd9ab3..c8a41ab2f 100644 --- a/shared/models/otherModels/metadataTable.ts +++ b/shared/models/otherModels/metadataTable.ts @@ -9,6 +9,7 @@ export enum MetadataType { DeferredInvoice = "deferred_invoice", CheckoutSessionV2 = "checkout_session_v2", + SetupPaymentV2 = "setup_payment_v2", } export const metadata = pgTable("metadata", {