From e9c2ca9fea4319d6cf8286afbfede077694f786a Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Tue, 28 Apr 2026 12:18:53 -0500 Subject: [PATCH 1/3] feat(ui): Add per-seat costs to checkout totals --- .../src/components/Checkout/CheckoutForm.tsx | 36 ++++++++++++++++++- packages/ui/src/elements/LineItems.tsx | 1 - 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/packages/ui/src/components/Checkout/CheckoutForm.tsx b/packages/ui/src/components/Checkout/CheckoutForm.tsx index 2a6dc9c4122..da366388493 100644 --- a/packages/ui/src/components/Checkout/CheckoutForm.tsx +++ b/packages/ui/src/components/Checkout/CheckoutForm.tsx @@ -48,10 +48,34 @@ export const CheckoutForm = withCardStateProvider(() => { : // eslint-disable-next-line @typescript-eslint/no-non-null-assertion plan.annualMonthlyFee!; - const descriptionElements = []; + const seatPerUnitTotal = totals.perUnitTotals?.find(({ name }) => name === 'seats'); + const seatPerUnitTotalTiers = seatPerUnitTotal?.tiers ?? []; + const firstSeatTotalTier = seatPerUnitTotalTiers[0]; + const secondSeatTotalTier = seatPerUnitTotalTiers[1]; + const paidSeatTotalTier = + firstSeatTotalTier?.feePerBlock.amount && + firstSeatTotalTier.feePerBlock.amount > 0 && + seatPerUnitTotalTiers.length === 1 + ? firstSeatTotalTier + : secondSeatTotalTier?.feePerBlock.amount && secondSeatTotalTier.feePerBlock.amount > 0 + ? secondSeatTotalTier + : undefined; + + const descriptionElements: Array> = []; if (planPeriod === 'annual') { descriptionElements.push(localizationKeys('billing.billedAnnually')); } + if ( + seatPerUnitTotalTiers.length > 1 && + firstSeatTotalTier?.feePerBlock.amount === 0 && + firstSeatTotalTier.quantity !== null + ) { + descriptionElements.push( + localizationKeys('billing.pricingTable.seatCost.includedSeats', { + includedSeats: firstSeatTotalTier.quantity, + }), + ); + } const seatUnitPrice = getSeatUnitPrice(plan); if (seatUnitPrice && seatUnitPrice.tiers.length === 1 && seatUnitPrice.tiers[0].feePerBlock.amount === 0) { descriptionElements.push( @@ -91,6 +115,16 @@ export const CheckoutForm = withCardStateProvider(() => { suffix={localizationKeys('billing.checkout.perMonth')} /> + {paidSeatTotalTier && paidSeatTotalTier.quantity !== null && ( + + + + + )} (({ title, descr ({ display: 'inline-flex', - alignItems: 'center', gap: t.space.$1, })} > From f77bda61ded1fd7efbb330822156cb78cfa5c251 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Mon, 4 May 2026 10:34:50 -0500 Subject: [PATCH 2/3] feat(clerk-js,localizations,shared,ui): Add support for total_due_per_period --- .changeset/khaki-hairs-punch.md | 8 ++++++++ packages/clerk-js/src/utils/billing.ts | 3 +++ packages/localizations/src/en-US.ts | 1 + packages/shared/src/types/billing.ts | 4 ++++ packages/shared/src/types/json.ts | 1 + packages/shared/src/types/localization.ts | 1 + packages/ui/src/components/Checkout/CheckoutForm.tsx | 9 ++++++++- 7 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 .changeset/khaki-hairs-punch.md diff --git a/.changeset/khaki-hairs-punch.md b/.changeset/khaki-hairs-punch.md new file mode 100644 index 00000000000..7b4e9fe5ebc --- /dev/null +++ b/.changeset/khaki-hairs-punch.md @@ -0,0 +1,8 @@ +--- +'@clerk/localizations': minor +'@clerk/clerk-js': minor +'@clerk/shared': minor +'@clerk/ui': minor +--- + +Add support for total due per period to checkout diff --git a/packages/clerk-js/src/utils/billing.ts b/packages/clerk-js/src/utils/billing.ts index 77b28782197..e994843361f 100644 --- a/packages/clerk-js/src/utils/billing.ts +++ b/packages/clerk-js/src/utils/billing.ts @@ -77,6 +77,9 @@ export const billingTotalsFromJSON = ; + totalDuePerPeriod: LocalizationValue; perMonth: LocalizationValue; }; }; diff --git a/packages/ui/src/components/Checkout/CheckoutForm.tsx b/packages/ui/src/components/Checkout/CheckoutForm.tsx index da366388493..f5933e69465 100644 --- a/packages/ui/src/components/Checkout/CheckoutForm.tsx +++ b/packages/ui/src/components/Checkout/CheckoutForm.tsx @@ -163,7 +163,7 @@ export const CheckoutForm = withCardStateProvider(() => { )} - {!!freeTrialEndsAt && !!plan.freeTrialDays && totals.totalDueAfterFreeTrial && ( + {!!freeTrialEndsAt && !!plan.freeTrialDays && totals.totalDueAfterFreeTrial ? ( { text={`${totals.totalDueAfterFreeTrial.currencySymbol}${totals.totalDueAfterFreeTrial.amountFormatted}`} /> + ) : ( + + + + )} From eadf76119de32887aa92096754dd7ed77d9dbd15 Mon Sep 17 00:00:00 2001 From: Dylan Staley <88163+dstaley@users.noreply.github.com> Date: Mon, 4 May 2026 10:39:06 -0500 Subject: [PATCH 3/3] chore(repo): Add changeset --- .changeset/good-ads-greet.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/good-ads-greet.md diff --git a/.changeset/good-ads-greet.md b/.changeset/good-ads-greet.md new file mode 100644 index 00000000000..06dfb32119a --- /dev/null +++ b/.changeset/good-ads-greet.md @@ -0,0 +1,5 @@ +--- +'@clerk/ui': minor +--- + +Add support for rendering per-seat costs in checkout