From be49af6426c950143ba3fe1c6816118aa62c0a9b Mon Sep 17 00:00:00 2001 From: Charlie Lamb Date: Tue, 5 May 2026 12:29:16 +0100 Subject: [PATCH] chore: cubic nits --- .../handleStripeSubscriptionCreated.ts | 11 +++++++++-- .../actions/attach/setup/setupAttachCheckoutMode.ts | 4 ++-- .../forms/attach-v2/components/AttachFooterV3.tsx | 9 ++++----- .../attach-v2/utils/attach-footer-label.test.ts | 13 +++++++++++++ 4 files changed, 28 insertions(+), 9 deletions(-) diff --git a/server/src/external/stripe/webhookHandlers/handleStripeSubscriptionCreated/handleStripeSubscriptionCreated.ts b/server/src/external/stripe/webhookHandlers/handleStripeSubscriptionCreated/handleStripeSubscriptionCreated.ts index cc5fdf3be..b676dce7b 100644 --- a/server/src/external/stripe/webhookHandlers/handleStripeSubscriptionCreated/handleStripeSubscriptionCreated.ts +++ b/server/src/external/stripe/webhookHandlers/handleStripeSubscriptionCreated/handleStripeSubscriptionCreated.ts @@ -1,6 +1,6 @@ import type Stripe from "stripe"; -import type { StripeWebhookContext } from "../../webhookMiddlewares/stripeWebhookContext.js"; import { getFullStripeSub } from "../../stripeSubUtils.js"; +import type { StripeWebhookContext } from "../../webhookMiddlewares/stripeWebhookContext.js"; import { setupStripeSubscriptionCreatedContext } from "./setupStripeSubscriptionCreatedContext.js"; import { autoSyncFromSubscription } from "./tasks/autoSyncFromSubscription.js"; import { linkScheduledCustomerProductsToSubscription } from "./tasks/linkScheduledCustomerProductsToSubscription.js"; @@ -17,7 +17,14 @@ export const handleStripeSubscriptionCreated = async ({ stripeId: stripeObject.id, }); - await linkScheduledCustomerProductsToSubscription({ ctx, subscription }); + try { + await linkScheduledCustomerProductsToSubscription({ ctx, subscription }); + } catch (err) { + ctx.logger.error( + `[sub.created] failed to link scheduled customer products for subscription ${subscription.id}`, + err, + ); + } const subscriptionCreatedContext = await setupStripeSubscriptionCreatedContext({ diff --git a/server/src/internal/billing/v2/actions/attach/setup/setupAttachCheckoutMode.ts b/server/src/internal/billing/v2/actions/attach/setup/setupAttachCheckoutMode.ts index e79d854b8..c070e9189 100644 --- a/server/src/internal/billing/v2/actions/attach/setup/setupAttachCheckoutMode.ts +++ b/server/src/internal/billing/v2/actions/attach/setup/setupAttachCheckoutMode.ts @@ -65,8 +65,8 @@ export const setupAttachCheckoutMode = ({ }; const checkoutMode = getStripeCheckoutOrDirectBilling(); - if (hasFutureStartDate && checkoutMode === null) { - return null; + if (hasFutureStartDate && !hasPaymentMethod) { + return "stripe_checkout"; } if (checkoutMode === null && redirectMode === "always") { diff --git a/vite/src/components/forms/attach-v2/components/AttachFooterV3.tsx b/vite/src/components/forms/attach-v2/components/AttachFooterV3.tsx index 336c37022..18e7ae92b 100644 --- a/vite/src/components/forms/attach-v2/components/AttachFooterV3.tsx +++ b/vite/src/components/forms/attach-v2/components/AttachFooterV3.tsx @@ -29,6 +29,7 @@ export function getConfirmLabel({ now?: number; }): string { if (!previewData) return "Attach Plan"; + if (previewData.redirect_to_checkout) return "Generate Checkout URL"; if (isFutureStartDate(startDate, now)) return "Schedule Plan"; const sixHoursFromNow = addHours(now ?? Date.now(), 6); @@ -39,8 +40,6 @@ export function getConfirmLabel({ ); if (isScheduled) return "Schedule Change"; - if (previewData.redirect_to_checkout) return "Generate Checkout URL"; - if (previewData.total <= 0) return "Attach Plan"; return "Charge Customer"; @@ -67,9 +66,9 @@ export function AttachFooterV3() { ? "Invoices are not available for end of cycle changes as there is no immediate charge to invoice" : hasFutureStartDate ? "Invoices are not available for future start dates. Schedule the plan instead." - : isZeroAmount - ? "Cannot send an invoice for $0 amounts. Please confirm the change instead." - : null; + : isZeroAmount + ? "Cannot send an invoice for $0 amounts. Please confirm the change instead." + : null; return ( diff --git a/vite/tests/components/forms/attach-v2/utils/attach-footer-label.test.ts b/vite/tests/components/forms/attach-v2/utils/attach-footer-label.test.ts index d824d85c3..c20aee52f 100644 --- a/vite/tests/components/forms/attach-v2/utils/attach-footer-label.test.ts +++ b/vite/tests/components/forms/attach-v2/utils/attach-footer-label.test.ts @@ -21,6 +21,19 @@ describe("getConfirmLabel", () => { ).toBe("Schedule Plan"); }); + test("checkout redirect takes precedence over future startDate", () => { + expect( + getConfirmLabel({ + previewData: { + ...previewData, + redirect_to_checkout: true, + }, + startDate: addDays(NOW, 1).getTime(), + now: NOW, + }), + ).toBe("Generate Checkout URL"); + }); + test("immediate paid attach charges customer", () => { expect( getConfirmLabel({