fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements - #9401

Merged
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in
Aug 11, 2026
Merged

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements#9401
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in

Conversation

@mauricioabreu

Copy link
Copy Markdown
Contributor

Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"

Description

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@mauricioabreumauricioabreu self-assigned this Aug 11, 2026
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5f52f54

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 23 packages
NameType
@clerk/sharedPatch
@clerk/clerk-jsPatch
@clerk/backendPatch
@clerk/uiPatch
@clerk/astroPatch
@clerk/chrome-extensionPatch
@clerk/electronPatch
@clerk/expo-passkeysPatch
@clerk/expoPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/headlessPatch
@clerk/honoPatch
@clerk/localizationsPatch
@clerk/mswPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/reactPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch
@clerk/vuePatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 8:32pm
swingsetReadyReadyPreviewAug 11, 2026 8:32pm

Request Review

…nd statements
Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"
@mauricioabreu
mauricioabreuforce-pushed the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch from 5e56b47 to 5f52f54CompareAugust 11, 2026 20:29
@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9401

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9401

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9401

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9401

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9401

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9401

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9401

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9401

@clerk/expo-google-signin

npm i https://pkg.pr.new/@clerk/expo-google-signin@9401

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9401

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9401

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9401

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9401

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9401

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9401

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9401

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9401

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9401

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9401

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9401

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9401

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9401

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9401

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9401

commit: 5f52f54

@mauricioabreumauricioabreu changed the title fix(billing): Use duration in cycles in payments and statementsfix(shared,clerk-js,backend,ui): use duration in cycles in payments and statementsAug 11, 2026
@coderabbitai

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds optional nullable discount duration fields to shared billing types and JSON models. Backend and Clerk JS billing converters map duration_in_cycles to durationInCycles. Payment attempt and statement pages pass this value to discount descriptions. The description utility handles both missing and null durations. Tests update billing and checkout fixtures and assertions. A changeset records patch releases for four packages.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers:clerk-cookie

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change to use discount duration in cycles for payment and statement messaging.
Description check✅ PassedThe description directly explains the billing messaging bug and the intended change from months to cycles.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/utils/billing.ts (1)

27-32: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use cycle labels for discount duration.

getBillingPeriodLabel(planPeriod, cycles, t) returns month(s) or year(s). A two-cycle discount can therefore render as “first 2 months” or “first 2 years”, not “first 2 cycles”. Use a cycle-specific label or localization key for billing.discountDuration.

The PR objective requires discount duration to be displayed in cycles.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 27 - 32, Update the
discount-duration path in the billing utility to use a cycle-specific label or
localization key instead of the period returned by getBillingPeriodLabel. Ensure
billing.discountDuration renders durations as cycles (for example, “first 2
cycles”), while preserving the existing no-cycles discountAmount behavior.
🧹 Nitpick comments (1)
packages/ui/src/utils/billing.ts (1)

14-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an explicit return type to the exported utility.

Declare getDiscountDescription with a : string return type.

As per coding guidelines, public TypeScript functions must define explicit return types.

Based on learnings, exported functions and public APIs require explicit return annotations.

Proposed change
 export function getDiscountDescription(
discount: Discount,
cycles: number | null | undefined,
planPeriod: BillingSubscriptionPlanPeriod,
{ $, t }: Localizations,
-) {+): string {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 14 - 19, Update the exported
getDiscountDescription function signature to explicitly declare a string return
type with : string, preserving its existing parameters and implementation.

Sources: Coding guidelines, Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 27-32: Update the discount-duration path in the billing utility to
use a cycle-specific label or localization key instead of the period returned by
getBillingPeriodLabel. Ensure billing.discountDuration renders durations as
cycles (for example, “first 2 cycles”), while preserving the existing no-cycles
discountAmount behavior.
---
Nitpick comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 14-19: Update the exported getDiscountDescription function
signature to explicitly declare a string return type with : string, preserving
its existing parameters and implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 10854210-e1c4-4131-8f08-e5ad80fe5ca0

📥 Commits

Reviewing files that changed from the base of the PR and between 605225e and 5e56b47.

📒 Files selected for processing (10)
  • .changeset/applied-discount-duration-cycles.md
  • packages/backend/src/util/billing.ts
  • packages/clerk-js/src/utils/__tests__/billing.test.ts
  • packages/clerk-js/src/utils/billing.ts
  • packages/shared/src/types/billing.ts
  • packages/shared/src/types/json.ts
  • packages/ui/src/components/Checkout/__tests__/Checkout.test.tsx
  • packages/ui/src/components/PaymentAttempts/PaymentAttemptPage.tsx
  • packages/ui/src/components/Statements/StatementPage.tsx
  • packages/ui/src/utils/billing.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T20:33:51.508Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions2

@clerk/shared

Current version: 4.28.1
Recommended bump: MINOR → 4.29.0

Subpath ./types

🟢 Additions (2)

Added: BillingAppliedDiscount.durationInCycles
+ durationInCycles?: number | null;

Added property BillingAppliedDiscount.durationInCycles

Added: BillingAppliedDiscountJSON.duration_in_cycles
+ duration_in_cycles?: number | null;

Added property BillingAppliedDiscountJSON.duration_in_cycles


Report generated by Break Check

Last ran on 5f52f54.

@mauricioabreu
mauricioabreu merged commit 81840b3 into mainAug 11, 2026
53 of 54 checks passed
@mauricioabreu
mauricioabreu deleted the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch August 11, 2026 20:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@mauricioabreu@dstaley
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements - #9401

Merged
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in
Aug 11, 2026
Merged

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements#9401
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in

Conversation

@mauricioabreu

Copy link
Copy Markdown
Contributor

Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"

Description

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@mauricioabreumauricioabreu self-assigned this Aug 11, 2026
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5f52f54

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 23 packages
NameType
@clerk/sharedPatch
@clerk/clerk-jsPatch
@clerk/backendPatch
@clerk/uiPatch
@clerk/astroPatch
@clerk/chrome-extensionPatch
@clerk/electronPatch
@clerk/expo-passkeysPatch
@clerk/expoPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/headlessPatch
@clerk/honoPatch
@clerk/localizationsPatch
@clerk/mswPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/reactPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch
@clerk/vuePatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 8:32pm
swingsetReadyReadyPreviewAug 11, 2026 8:32pm

Request Review

…nd statements
Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"
@mauricioabreu
mauricioabreuforce-pushed the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch from 5e56b47 to 5f52f54CompareAugust 11, 2026 20:29
@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9401

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9401

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9401

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9401

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9401

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9401

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9401

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9401

@clerk/expo-google-signin

npm i https://pkg.pr.new/@clerk/expo-google-signin@9401

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9401

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9401

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9401

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9401

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9401

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9401

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9401

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9401

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9401

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9401

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9401

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9401

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9401

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9401

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9401

commit: 5f52f54

@mauricioabreumauricioabreu changed the title fix(billing): Use duration in cycles in payments and statementsfix(shared,clerk-js,backend,ui): use duration in cycles in payments and statementsAug 11, 2026
@coderabbitai

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds optional nullable discount duration fields to shared billing types and JSON models. Backend and Clerk JS billing converters map duration_in_cycles to durationInCycles. Payment attempt and statement pages pass this value to discount descriptions. The description utility handles both missing and null durations. Tests update billing and checkout fixtures and assertions. A changeset records patch releases for four packages.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers:clerk-cookie

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change to use discount duration in cycles for payment and statement messaging.
Description check✅ PassedThe description directly explains the billing messaging bug and the intended change from months to cycles.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/utils/billing.ts (1)

27-32: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use cycle labels for discount duration.

getBillingPeriodLabel(planPeriod, cycles, t) returns month(s) or year(s). A two-cycle discount can therefore render as “first 2 months” or “first 2 years”, not “first 2 cycles”. Use a cycle-specific label or localization key for billing.discountDuration.

The PR objective requires discount duration to be displayed in cycles.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 27 - 32, Update the
discount-duration path in the billing utility to use a cycle-specific label or
localization key instead of the period returned by getBillingPeriodLabel. Ensure
billing.discountDuration renders durations as cycles (for example, “first 2
cycles”), while preserving the existing no-cycles discountAmount behavior.
🧹 Nitpick comments (1)
packages/ui/src/utils/billing.ts (1)

14-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an explicit return type to the exported utility.

Declare getDiscountDescription with a : string return type.

As per coding guidelines, public TypeScript functions must define explicit return types.

Based on learnings, exported functions and public APIs require explicit return annotations.

Proposed change
 export function getDiscountDescription(
discount: Discount,
cycles: number | null | undefined,
planPeriod: BillingSubscriptionPlanPeriod,
{ $, t }: Localizations,
-) {+): string {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 14 - 19, Update the exported
getDiscountDescription function signature to explicitly declare a string return
type with : string, preserving its existing parameters and implementation.

Sources: Coding guidelines, Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 27-32: Update the discount-duration path in the billing utility to
use a cycle-specific label or localization key instead of the period returned by
getBillingPeriodLabel. Ensure billing.discountDuration renders durations as
cycles (for example, “first 2 cycles”), while preserving the existing no-cycles
discountAmount behavior.
---
Nitpick comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 14-19: Update the exported getDiscountDescription function
signature to explicitly declare a string return type with : string, preserving
its existing parameters and implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 10854210-e1c4-4131-8f08-e5ad80fe5ca0

📥 Commits

Reviewing files that changed from the base of the PR and between 605225e and 5e56b47.

📒 Files selected for processing (10)
  • .changeset/applied-discount-duration-cycles.md
  • packages/backend/src/util/billing.ts
  • packages/clerk-js/src/utils/__tests__/billing.test.ts
  • packages/clerk-js/src/utils/billing.ts
  • packages/shared/src/types/billing.ts
  • packages/shared/src/types/json.ts
  • packages/ui/src/components/Checkout/__tests__/Checkout.test.tsx
  • packages/ui/src/components/PaymentAttempts/PaymentAttemptPage.tsx
  • packages/ui/src/components/Statements/StatementPage.tsx
  • packages/ui/src/utils/billing.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T20:33:51.508Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions2

@clerk/shared

Current version: 4.28.1
Recommended bump: MINOR → 4.29.0

Subpath ./types

🟢 Additions (2)

Added: BillingAppliedDiscount.durationInCycles
+ durationInCycles?: number | null;

Added property BillingAppliedDiscount.durationInCycles

Added: BillingAppliedDiscountJSON.duration_in_cycles
+ duration_in_cycles?: number | null;

Added property BillingAppliedDiscountJSON.duration_in_cycles


Report generated by Break Check

Last ran on 5f52f54.

@mauricioabreu
mauricioabreu merged commit 81840b3 into mainAug 11, 2026
53 of 54 checks passed
@mauricioabreu
mauricioabreu deleted the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch August 11, 2026 20:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@mauricioabreu@dstaley
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements - #9401

Merged
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in
Aug 11, 2026
Merged

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements#9401
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in

Conversation

@mauricioabreu

Copy link
Copy Markdown
Contributor

Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"

Description

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@mauricioabreumauricioabreu self-assigned this Aug 11, 2026
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5f52f54

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 23 packages
NameType
@clerk/sharedPatch
@clerk/clerk-jsPatch
@clerk/backendPatch
@clerk/uiPatch
@clerk/astroPatch
@clerk/chrome-extensionPatch
@clerk/electronPatch
@clerk/expo-passkeysPatch
@clerk/expoPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/headlessPatch
@clerk/honoPatch
@clerk/localizationsPatch
@clerk/mswPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/reactPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch
@clerk/vuePatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 8:32pm
swingsetReadyReadyPreviewAug 11, 2026 8:32pm

Request Review

…nd statements
Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"
@mauricioabreu
mauricioabreuforce-pushed the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch from 5e56b47 to 5f52f54CompareAugust 11, 2026 20:29
@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9401

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9401

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9401

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9401

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9401

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9401

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9401

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9401

@clerk/expo-google-signin

npm i https://pkg.pr.new/@clerk/expo-google-signin@9401

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9401

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9401

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9401

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9401

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9401

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9401

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9401

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9401

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9401

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9401

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9401

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9401

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9401

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9401

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9401

commit: 5f52f54

@mauricioabreumauricioabreu changed the title fix(billing): Use duration in cycles in payments and statementsfix(shared,clerk-js,backend,ui): use duration in cycles in payments and statementsAug 11, 2026
@coderabbitai

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds optional nullable discount duration fields to shared billing types and JSON models. Backend and Clerk JS billing converters map duration_in_cycles to durationInCycles. Payment attempt and statement pages pass this value to discount descriptions. The description utility handles both missing and null durations. Tests update billing and checkout fixtures and assertions. A changeset records patch releases for four packages.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers:clerk-cookie

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change to use discount duration in cycles for payment and statement messaging.
Description check✅ PassedThe description directly explains the billing messaging bug and the intended change from months to cycles.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/utils/billing.ts (1)

27-32: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use cycle labels for discount duration.

getBillingPeriodLabel(planPeriod, cycles, t) returns month(s) or year(s). A two-cycle discount can therefore render as “first 2 months” or “first 2 years”, not “first 2 cycles”. Use a cycle-specific label or localization key for billing.discountDuration.

The PR objective requires discount duration to be displayed in cycles.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 27 - 32, Update the
discount-duration path in the billing utility to use a cycle-specific label or
localization key instead of the period returned by getBillingPeriodLabel. Ensure
billing.discountDuration renders durations as cycles (for example, “first 2
cycles”), while preserving the existing no-cycles discountAmount behavior.
🧹 Nitpick comments (1)
packages/ui/src/utils/billing.ts (1)

14-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an explicit return type to the exported utility.

Declare getDiscountDescription with a : string return type.

As per coding guidelines, public TypeScript functions must define explicit return types.

Based on learnings, exported functions and public APIs require explicit return annotations.

Proposed change
 export function getDiscountDescription(
discount: Discount,
cycles: number | null | undefined,
planPeriod: BillingSubscriptionPlanPeriod,
{ $, t }: Localizations,
-) {+): string {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 14 - 19, Update the exported
getDiscountDescription function signature to explicitly declare a string return
type with : string, preserving its existing parameters and implementation.

Sources: Coding guidelines, Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 27-32: Update the discount-duration path in the billing utility to
use a cycle-specific label or localization key instead of the period returned by
getBillingPeriodLabel. Ensure billing.discountDuration renders durations as
cycles (for example, “first 2 cycles”), while preserving the existing no-cycles
discountAmount behavior.
---
Nitpick comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 14-19: Update the exported getDiscountDescription function
signature to explicitly declare a string return type with : string, preserving
its existing parameters and implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 10854210-e1c4-4131-8f08-e5ad80fe5ca0

📥 Commits

Reviewing files that changed from the base of the PR and between 605225e and 5e56b47.

📒 Files selected for processing (10)
  • .changeset/applied-discount-duration-cycles.md
  • packages/backend/src/util/billing.ts
  • packages/clerk-js/src/utils/__tests__/billing.test.ts
  • packages/clerk-js/src/utils/billing.ts
  • packages/shared/src/types/billing.ts
  • packages/shared/src/types/json.ts
  • packages/ui/src/components/Checkout/__tests__/Checkout.test.tsx
  • packages/ui/src/components/PaymentAttempts/PaymentAttemptPage.tsx
  • packages/ui/src/components/Statements/StatementPage.tsx
  • packages/ui/src/utils/billing.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T20:33:51.508Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions2

@clerk/shared

Current version: 4.28.1
Recommended bump: MINOR → 4.29.0

Subpath ./types

🟢 Additions (2)

Added: BillingAppliedDiscount.durationInCycles
+ durationInCycles?: number | null;

Added property BillingAppliedDiscount.durationInCycles

Added: BillingAppliedDiscountJSON.duration_in_cycles
+ duration_in_cycles?: number | null;

Added property BillingAppliedDiscountJSON.duration_in_cycles


Report generated by Break Check

Last ran on 5f52f54.

@mauricioabreu
mauricioabreu merged commit 81840b3 into mainAug 11, 2026
53 of 54 checks passed
@mauricioabreu
mauricioabreu deleted the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch August 11, 2026 20:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@mauricioabreu@dstaley
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements - #9401

Merged
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in
Aug 11, 2026
Merged

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements#9401
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in

Conversation

@mauricioabreu

Copy link
Copy Markdown
Contributor

Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"

Description

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@mauricioabreumauricioabreu self-assigned this Aug 11, 2026
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5f52f54

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 23 packages
NameType
@clerk/sharedPatch
@clerk/clerk-jsPatch
@clerk/backendPatch
@clerk/uiPatch
@clerk/astroPatch
@clerk/chrome-extensionPatch
@clerk/electronPatch
@clerk/expo-passkeysPatch
@clerk/expoPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/headlessPatch
@clerk/honoPatch
@clerk/localizationsPatch
@clerk/mswPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/reactPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch
@clerk/vuePatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 8:32pm
swingsetReadyReadyPreviewAug 11, 2026 8:32pm

Request Review

…nd statements
Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"
@mauricioabreu
mauricioabreuforce-pushed the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch from 5e56b47 to 5f52f54CompareAugust 11, 2026 20:29
@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9401

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9401

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9401

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9401

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9401

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9401

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9401

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9401

@clerk/expo-google-signin

npm i https://pkg.pr.new/@clerk/expo-google-signin@9401

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9401

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9401

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9401

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9401

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9401

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9401

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9401

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9401

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9401

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9401

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9401

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9401

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9401

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9401

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9401

commit: 5f52f54

@mauricioabreumauricioabreu changed the title fix(billing): Use duration in cycles in payments and statementsfix(shared,clerk-js,backend,ui): use duration in cycles in payments and statementsAug 11, 2026
@coderabbitai

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds optional nullable discount duration fields to shared billing types and JSON models. Backend and Clerk JS billing converters map duration_in_cycles to durationInCycles. Payment attempt and statement pages pass this value to discount descriptions. The description utility handles both missing and null durations. Tests update billing and checkout fixtures and assertions. A changeset records patch releases for four packages.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers:clerk-cookie

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change to use discount duration in cycles for payment and statement messaging.
Description check✅ PassedThe description directly explains the billing messaging bug and the intended change from months to cycles.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/utils/billing.ts (1)

27-32: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use cycle labels for discount duration.

getBillingPeriodLabel(planPeriod, cycles, t) returns month(s) or year(s). A two-cycle discount can therefore render as “first 2 months” or “first 2 years”, not “first 2 cycles”. Use a cycle-specific label or localization key for billing.discountDuration.

The PR objective requires discount duration to be displayed in cycles.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 27 - 32, Update the
discount-duration path in the billing utility to use a cycle-specific label or
localization key instead of the period returned by getBillingPeriodLabel. Ensure
billing.discountDuration renders durations as cycles (for example, “first 2
cycles”), while preserving the existing no-cycles discountAmount behavior.
🧹 Nitpick comments (1)
packages/ui/src/utils/billing.ts (1)

14-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an explicit return type to the exported utility.

Declare getDiscountDescription with a : string return type.

As per coding guidelines, public TypeScript functions must define explicit return types.

Based on learnings, exported functions and public APIs require explicit return annotations.

Proposed change
 export function getDiscountDescription(
discount: Discount,
cycles: number | null | undefined,
planPeriod: BillingSubscriptionPlanPeriod,
{ $, t }: Localizations,
-) {+): string {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 14 - 19, Update the exported
getDiscountDescription function signature to explicitly declare a string return
type with : string, preserving its existing parameters and implementation.

Sources: Coding guidelines, Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 27-32: Update the discount-duration path in the billing utility to
use a cycle-specific label or localization key instead of the period returned by
getBillingPeriodLabel. Ensure billing.discountDuration renders durations as
cycles (for example, “first 2 cycles”), while preserving the existing no-cycles
discountAmount behavior.
---
Nitpick comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 14-19: Update the exported getDiscountDescription function
signature to explicitly declare a string return type with : string, preserving
its existing parameters and implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 10854210-e1c4-4131-8f08-e5ad80fe5ca0

📥 Commits

Reviewing files that changed from the base of the PR and between 605225e and 5e56b47.

📒 Files selected for processing (10)
  • .changeset/applied-discount-duration-cycles.md
  • packages/backend/src/util/billing.ts
  • packages/clerk-js/src/utils/__tests__/billing.test.ts
  • packages/clerk-js/src/utils/billing.ts
  • packages/shared/src/types/billing.ts
  • packages/shared/src/types/json.ts
  • packages/ui/src/components/Checkout/__tests__/Checkout.test.tsx
  • packages/ui/src/components/PaymentAttempts/PaymentAttemptPage.tsx
  • packages/ui/src/components/Statements/StatementPage.tsx
  • packages/ui/src/utils/billing.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T20:33:51.508Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions2

@clerk/shared

Current version: 4.28.1
Recommended bump: MINOR → 4.29.0

Subpath ./types

🟢 Additions (2)

Added: BillingAppliedDiscount.durationInCycles
+ durationInCycles?: number | null;

Added property BillingAppliedDiscount.durationInCycles

Added: BillingAppliedDiscountJSON.duration_in_cycles
+ duration_in_cycles?: number | null;

Added property BillingAppliedDiscountJSON.duration_in_cycles


Report generated by Break Check

Last ran on 5f52f54.

@mauricioabreu
mauricioabreu merged commit 81840b3 into mainAug 11, 2026
53 of 54 checks passed
@mauricioabreu
mauricioabreu deleted the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch August 11, 2026 20:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@mauricioabreu@dstaley
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements - #9401

Merged
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in
Aug 11, 2026
Merged

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements#9401
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in

Conversation

@mauricioabreu

Copy link
Copy Markdown
Contributor

Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"

Description

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@mauricioabreumauricioabreu self-assigned this Aug 11, 2026
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5f52f54

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 23 packages
NameType
@clerk/sharedPatch
@clerk/clerk-jsPatch
@clerk/backendPatch
@clerk/uiPatch
@clerk/astroPatch
@clerk/chrome-extensionPatch
@clerk/electronPatch
@clerk/expo-passkeysPatch
@clerk/expoPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/headlessPatch
@clerk/honoPatch
@clerk/localizationsPatch
@clerk/mswPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/reactPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch
@clerk/vuePatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 8:32pm
swingsetReadyReadyPreviewAug 11, 2026 8:32pm

Request Review

…nd statements
Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"
@mauricioabreu
mauricioabreuforce-pushed the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch from 5e56b47 to 5f52f54CompareAugust 11, 2026 20:29
@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9401

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9401

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9401

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9401

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9401

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9401

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9401

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9401

@clerk/expo-google-signin

npm i https://pkg.pr.new/@clerk/expo-google-signin@9401

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9401

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9401

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9401

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9401

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9401

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9401

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9401

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9401

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9401

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9401

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9401

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9401

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9401

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9401

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9401

commit: 5f52f54

@mauricioabreumauricioabreu changed the title fix(billing): Use duration in cycles in payments and statementsfix(shared,clerk-js,backend,ui): use duration in cycles in payments and statementsAug 11, 2026
@coderabbitai

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds optional nullable discount duration fields to shared billing types and JSON models. Backend and Clerk JS billing converters map duration_in_cycles to durationInCycles. Payment attempt and statement pages pass this value to discount descriptions. The description utility handles both missing and null durations. Tests update billing and checkout fixtures and assertions. A changeset records patch releases for four packages.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers:clerk-cookie

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change to use discount duration in cycles for payment and statement messaging.
Description check✅ PassedThe description directly explains the billing messaging bug and the intended change from months to cycles.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/utils/billing.ts (1)

27-32: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use cycle labels for discount duration.

getBillingPeriodLabel(planPeriod, cycles, t) returns month(s) or year(s). A two-cycle discount can therefore render as “first 2 months” or “first 2 years”, not “first 2 cycles”. Use a cycle-specific label or localization key for billing.discountDuration.

The PR objective requires discount duration to be displayed in cycles.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 27 - 32, Update the
discount-duration path in the billing utility to use a cycle-specific label or
localization key instead of the period returned by getBillingPeriodLabel. Ensure
billing.discountDuration renders durations as cycles (for example, “first 2
cycles”), while preserving the existing no-cycles discountAmount behavior.
🧹 Nitpick comments (1)
packages/ui/src/utils/billing.ts (1)

14-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an explicit return type to the exported utility.

Declare getDiscountDescription with a : string return type.

As per coding guidelines, public TypeScript functions must define explicit return types.

Based on learnings, exported functions and public APIs require explicit return annotations.

Proposed change
 export function getDiscountDescription(
discount: Discount,
cycles: number | null | undefined,
planPeriod: BillingSubscriptionPlanPeriod,
{ $, t }: Localizations,
-) {+): string {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 14 - 19, Update the exported
getDiscountDescription function signature to explicitly declare a string return
type with : string, preserving its existing parameters and implementation.

Sources: Coding guidelines, Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 27-32: Update the discount-duration path in the billing utility to
use a cycle-specific label or localization key instead of the period returned by
getBillingPeriodLabel. Ensure billing.discountDuration renders durations as
cycles (for example, “first 2 cycles”), while preserving the existing no-cycles
discountAmount behavior.
---
Nitpick comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 14-19: Update the exported getDiscountDescription function
signature to explicitly declare a string return type with : string, preserving
its existing parameters and implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 10854210-e1c4-4131-8f08-e5ad80fe5ca0

📥 Commits

Reviewing files that changed from the base of the PR and between 605225e and 5e56b47.

📒 Files selected for processing (10)
  • .changeset/applied-discount-duration-cycles.md
  • packages/backend/src/util/billing.ts
  • packages/clerk-js/src/utils/__tests__/billing.test.ts
  • packages/clerk-js/src/utils/billing.ts
  • packages/shared/src/types/billing.ts
  • packages/shared/src/types/json.ts
  • packages/ui/src/components/Checkout/__tests__/Checkout.test.tsx
  • packages/ui/src/components/PaymentAttempts/PaymentAttemptPage.tsx
  • packages/ui/src/components/Statements/StatementPage.tsx
  • packages/ui/src/utils/billing.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T20:33:51.508Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions2

@clerk/shared

Current version: 4.28.1
Recommended bump: MINOR → 4.29.0

Subpath ./types

🟢 Additions (2)

Added: BillingAppliedDiscount.durationInCycles
+ durationInCycles?: number | null;

Added property BillingAppliedDiscount.durationInCycles

Added: BillingAppliedDiscountJSON.duration_in_cycles
+ duration_in_cycles?: number | null;

Added property BillingAppliedDiscountJSON.duration_in_cycles


Report generated by Break Check

Last ran on 5f52f54.

@mauricioabreu
mauricioabreu merged commit 81840b3 into mainAug 11, 2026
53 of 54 checks passed
@mauricioabreu
mauricioabreu deleted the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch August 11, 2026 20:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@mauricioabreu@dstaley
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements - #9401

Merged
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in
Aug 11, 2026
Merged

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements#9401
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in

Conversation

@mauricioabreu

Copy link
Copy Markdown
Contributor

Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"

Description

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@mauricioabreumauricioabreu self-assigned this Aug 11, 2026
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5f52f54

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 23 packages
NameType
@clerk/sharedPatch
@clerk/clerk-jsPatch
@clerk/backendPatch
@clerk/uiPatch
@clerk/astroPatch
@clerk/chrome-extensionPatch
@clerk/electronPatch
@clerk/expo-passkeysPatch
@clerk/expoPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/headlessPatch
@clerk/honoPatch
@clerk/localizationsPatch
@clerk/mswPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/reactPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch
@clerk/vuePatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 8:32pm
swingsetReadyReadyPreviewAug 11, 2026 8:32pm

Request Review

…nd statements
Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"
@mauricioabreu
mauricioabreuforce-pushed the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch from 5e56b47 to 5f52f54CompareAugust 11, 2026 20:29
@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9401

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9401

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9401

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9401

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9401

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9401

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9401

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9401

@clerk/expo-google-signin

npm i https://pkg.pr.new/@clerk/expo-google-signin@9401

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9401

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9401

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9401

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9401

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9401

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9401

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9401

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9401

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9401

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9401

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9401

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9401

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9401

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9401

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9401

commit: 5f52f54

@mauricioabreumauricioabreu changed the title fix(billing): Use duration in cycles in payments and statementsfix(shared,clerk-js,backend,ui): use duration in cycles in payments and statementsAug 11, 2026
@coderabbitai

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds optional nullable discount duration fields to shared billing types and JSON models. Backend and Clerk JS billing converters map duration_in_cycles to durationInCycles. Payment attempt and statement pages pass this value to discount descriptions. The description utility handles both missing and null durations. Tests update billing and checkout fixtures and assertions. A changeset records patch releases for four packages.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers:clerk-cookie

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change to use discount duration in cycles for payment and statement messaging.
Description check✅ PassedThe description directly explains the billing messaging bug and the intended change from months to cycles.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/utils/billing.ts (1)

27-32: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use cycle labels for discount duration.

getBillingPeriodLabel(planPeriod, cycles, t) returns month(s) or year(s). A two-cycle discount can therefore render as “first 2 months” or “first 2 years”, not “first 2 cycles”. Use a cycle-specific label or localization key for billing.discountDuration.

The PR objective requires discount duration to be displayed in cycles.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 27 - 32, Update the
discount-duration path in the billing utility to use a cycle-specific label or
localization key instead of the period returned by getBillingPeriodLabel. Ensure
billing.discountDuration renders durations as cycles (for example, “first 2
cycles”), while preserving the existing no-cycles discountAmount behavior.
🧹 Nitpick comments (1)
packages/ui/src/utils/billing.ts (1)

14-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an explicit return type to the exported utility.

Declare getDiscountDescription with a : string return type.

As per coding guidelines, public TypeScript functions must define explicit return types.

Based on learnings, exported functions and public APIs require explicit return annotations.

Proposed change
 export function getDiscountDescription(
discount: Discount,
cycles: number | null | undefined,
planPeriod: BillingSubscriptionPlanPeriod,
{ $, t }: Localizations,
-) {+): string {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 14 - 19, Update the exported
getDiscountDescription function signature to explicitly declare a string return
type with : string, preserving its existing parameters and implementation.

Sources: Coding guidelines, Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 27-32: Update the discount-duration path in the billing utility to
use a cycle-specific label or localization key instead of the period returned by
getBillingPeriodLabel. Ensure billing.discountDuration renders durations as
cycles (for example, “first 2 cycles”), while preserving the existing no-cycles
discountAmount behavior.
---
Nitpick comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 14-19: Update the exported getDiscountDescription function
signature to explicitly declare a string return type with : string, preserving
its existing parameters and implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 10854210-e1c4-4131-8f08-e5ad80fe5ca0

📥 Commits

Reviewing files that changed from the base of the PR and between 605225e and 5e56b47.

📒 Files selected for processing (10)
  • .changeset/applied-discount-duration-cycles.md
  • packages/backend/src/util/billing.ts
  • packages/clerk-js/src/utils/__tests__/billing.test.ts
  • packages/clerk-js/src/utils/billing.ts
  • packages/shared/src/types/billing.ts
  • packages/shared/src/types/json.ts
  • packages/ui/src/components/Checkout/__tests__/Checkout.test.tsx
  • packages/ui/src/components/PaymentAttempts/PaymentAttemptPage.tsx
  • packages/ui/src/components/Statements/StatementPage.tsx
  • packages/ui/src/utils/billing.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T20:33:51.508Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions2

@clerk/shared

Current version: 4.28.1
Recommended bump: MINOR → 4.29.0

Subpath ./types

🟢 Additions (2)

Added: BillingAppliedDiscount.durationInCycles
+ durationInCycles?: number | null;

Added property BillingAppliedDiscount.durationInCycles

Added: BillingAppliedDiscountJSON.duration_in_cycles
+ duration_in_cycles?: number | null;

Added property BillingAppliedDiscountJSON.duration_in_cycles


Report generated by Break Check

Last ran on 5f52f54.

@mauricioabreu
mauricioabreu merged commit 81840b3 into mainAug 11, 2026
53 of 54 checks passed
@mauricioabreu
mauricioabreu deleted the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch August 11, 2026 20:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@mauricioabreu@dstaley
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements - #9401

Merged
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in
Aug 11, 2026
Merged

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements#9401
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in

Conversation

@mauricioabreu

Copy link
Copy Markdown
Contributor

Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"

Description

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@mauricioabreumauricioabreu self-assigned this Aug 11, 2026
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5f52f54

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 23 packages
NameType
@clerk/sharedPatch
@clerk/clerk-jsPatch
@clerk/backendPatch
@clerk/uiPatch
@clerk/astroPatch
@clerk/chrome-extensionPatch
@clerk/electronPatch
@clerk/expo-passkeysPatch
@clerk/expoPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/headlessPatch
@clerk/honoPatch
@clerk/localizationsPatch
@clerk/mswPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/reactPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch
@clerk/vuePatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 8:32pm
swingsetReadyReadyPreviewAug 11, 2026 8:32pm

Request Review

…nd statements
Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"
@mauricioabreu
mauricioabreuforce-pushed the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch from 5e56b47 to 5f52f54CompareAugust 11, 2026 20:29
@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9401

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9401

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9401

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9401

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9401

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9401

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9401

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9401

@clerk/expo-google-signin

npm i https://pkg.pr.new/@clerk/expo-google-signin@9401

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9401

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9401

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9401

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9401

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9401

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9401

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9401

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9401

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9401

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9401

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9401

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9401

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9401

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9401

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9401

commit: 5f52f54

@mauricioabreumauricioabreu changed the title fix(billing): Use duration in cycles in payments and statementsfix(shared,clerk-js,backend,ui): use duration in cycles in payments and statementsAug 11, 2026
@coderabbitai

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds optional nullable discount duration fields to shared billing types and JSON models. Backend and Clerk JS billing converters map duration_in_cycles to durationInCycles. Payment attempt and statement pages pass this value to discount descriptions. The description utility handles both missing and null durations. Tests update billing and checkout fixtures and assertions. A changeset records patch releases for four packages.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers:clerk-cookie

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change to use discount duration in cycles for payment and statement messaging.
Description check✅ PassedThe description directly explains the billing messaging bug and the intended change from months to cycles.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/utils/billing.ts (1)

27-32: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use cycle labels for discount duration.

getBillingPeriodLabel(planPeriod, cycles, t) returns month(s) or year(s). A two-cycle discount can therefore render as “first 2 months” or “first 2 years”, not “first 2 cycles”. Use a cycle-specific label or localization key for billing.discountDuration.

The PR objective requires discount duration to be displayed in cycles.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 27 - 32, Update the
discount-duration path in the billing utility to use a cycle-specific label or
localization key instead of the period returned by getBillingPeriodLabel. Ensure
billing.discountDuration renders durations as cycles (for example, “first 2
cycles”), while preserving the existing no-cycles discountAmount behavior.
🧹 Nitpick comments (1)
packages/ui/src/utils/billing.ts (1)

14-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an explicit return type to the exported utility.

Declare getDiscountDescription with a : string return type.

As per coding guidelines, public TypeScript functions must define explicit return types.

Based on learnings, exported functions and public APIs require explicit return annotations.

Proposed change
 export function getDiscountDescription(
discount: Discount,
cycles: number | null | undefined,
planPeriod: BillingSubscriptionPlanPeriod,
{ $, t }: Localizations,
-) {+): string {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 14 - 19, Update the exported
getDiscountDescription function signature to explicitly declare a string return
type with : string, preserving its existing parameters and implementation.

Sources: Coding guidelines, Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 27-32: Update the discount-duration path in the billing utility to
use a cycle-specific label or localization key instead of the period returned by
getBillingPeriodLabel. Ensure billing.discountDuration renders durations as
cycles (for example, “first 2 cycles”), while preserving the existing no-cycles
discountAmount behavior.
---
Nitpick comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 14-19: Update the exported getDiscountDescription function
signature to explicitly declare a string return type with : string, preserving
its existing parameters and implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 10854210-e1c4-4131-8f08-e5ad80fe5ca0

📥 Commits

Reviewing files that changed from the base of the PR and between 605225e and 5e56b47.

📒 Files selected for processing (10)
  • .changeset/applied-discount-duration-cycles.md
  • packages/backend/src/util/billing.ts
  • packages/clerk-js/src/utils/__tests__/billing.test.ts
  • packages/clerk-js/src/utils/billing.ts
  • packages/shared/src/types/billing.ts
  • packages/shared/src/types/json.ts
  • packages/ui/src/components/Checkout/__tests__/Checkout.test.tsx
  • packages/ui/src/components/PaymentAttempts/PaymentAttemptPage.tsx
  • packages/ui/src/components/Statements/StatementPage.tsx
  • packages/ui/src/utils/billing.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T20:33:51.508Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions2

@clerk/shared

Current version: 4.28.1
Recommended bump: MINOR → 4.29.0

Subpath ./types

🟢 Additions (2)

Added: BillingAppliedDiscount.durationInCycles
+ durationInCycles?: number | null;

Added property BillingAppliedDiscount.durationInCycles

Added: BillingAppliedDiscountJSON.duration_in_cycles
+ duration_in_cycles?: number | null;

Added property BillingAppliedDiscountJSON.duration_in_cycles


Report generated by Break Check

Last ran on 5f52f54.

@mauricioabreu
mauricioabreu merged commit 81840b3 into mainAug 11, 2026
53 of 54 checks passed
@mauricioabreu
mauricioabreu deleted the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch August 11, 2026 20:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@mauricioabreu@dstaley
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements - #9401

Merged
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in
Aug 11, 2026
Merged

fix(shared,clerk-js,backend,ui): use duration in cycles in payments and statements#9401
mauricioabreu merged 1 commit into
mainfrom
mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in

Conversation

@mauricioabreu

Copy link
Copy Markdown
Contributor

Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"

Description

Checklist

  • pnpm test runs as expected.
  • pnpm build runs as expected.
  • (If applicable) JSDoc comments have been added or updated for any package exports
  • (If applicable) Documentation has been updated

Type of change

  • 🐛 Bug fix
  • 🌟 New feature
  • 🔨 Breaking change
  • 📖 Refactoring / dependency upgrade / documentation
  • other:

@mauricioabreumauricioabreu self-assigned this Aug 11, 2026
@changeset-bot

changeset-botBot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 5f52f54

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 23 packages
NameType
@clerk/sharedPatch
@clerk/clerk-jsPatch
@clerk/backendPatch
@clerk/uiPatch
@clerk/astroPatch
@clerk/chrome-extensionPatch
@clerk/electronPatch
@clerk/expo-passkeysPatch
@clerk/expoPatch
@clerk/expressPatch
@clerk/fastifyPatch
@clerk/headlessPatch
@clerk/honoPatch
@clerk/localizationsPatch
@clerk/mswPatch
@clerk/nextjsPatch
@clerk/nuxtPatch
@clerk/react-routerPatch
@clerk/reactPatch
@clerk/tanstack-react-startPatch
@clerk/testingPatch
@clerk/vuePatch
@clerk/swingsetPatch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercelBot commented Aug 11, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

ProjectDeploymentActionsUpdated (UTC)
clerk-js-sandboxReadyReadyPreviewAug 11, 2026 8:32pm
swingsetReadyReadyPreviewAug 11, 2026 8:32pm

Request Review

…nd statements
Right now, we are displaying "10% off first 2 months" for a case where the discount lasts 2 cycles, when it should be "10% off first 2 cycles"
@mauricioabreu
mauricioabreuforce-pushed the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch from 5e56b47 to 5f52f54CompareAugust 11, 2026 20:29
@pkg-pr-new

pkg-pr-newBot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@clerk/astro

npm i https://pkg.pr.new/@clerk/astro@9401

@clerk/backend

npm i https://pkg.pr.new/@clerk/backend@9401

@clerk/chrome-extension

npm i https://pkg.pr.new/@clerk/chrome-extension@9401

@clerk/clerk-js

npm i https://pkg.pr.new/@clerk/clerk-js@9401

@clerk/electron

npm i https://pkg.pr.new/@clerk/electron@9401

@clerk/electron-passkeys

npm i https://pkg.pr.new/@clerk/electron-passkeys@9401

@clerk/eslint-plugin

npm i https://pkg.pr.new/@clerk/eslint-plugin@9401

@clerk/expo

npm i https://pkg.pr.new/@clerk/expo@9401

@clerk/expo-google-signin

npm i https://pkg.pr.new/@clerk/expo-google-signin@9401

@clerk/expo-passkeys

npm i https://pkg.pr.new/@clerk/expo-passkeys@9401

@clerk/express

npm i https://pkg.pr.new/@clerk/express@9401

@clerk/fastify

npm i https://pkg.pr.new/@clerk/fastify@9401

@clerk/hono

npm i https://pkg.pr.new/@clerk/hono@9401

@clerk/localizations

npm i https://pkg.pr.new/@clerk/localizations@9401

@clerk/nextjs

npm i https://pkg.pr.new/@clerk/nextjs@9401

@clerk/nuxt

npm i https://pkg.pr.new/@clerk/nuxt@9401

@clerk/react

npm i https://pkg.pr.new/@clerk/react@9401

@clerk/react-router

npm i https://pkg.pr.new/@clerk/react-router@9401

@clerk/shared

npm i https://pkg.pr.new/@clerk/shared@9401

@clerk/tanstack-react-start

npm i https://pkg.pr.new/@clerk/tanstack-react-start@9401

@clerk/testing

npm i https://pkg.pr.new/@clerk/testing@9401

@clerk/ui

npm i https://pkg.pr.new/@clerk/ui@9401

@clerk/upgrade

npm i https://pkg.pr.new/@clerk/upgrade@9401

@clerk/vue

npm i https://pkg.pr.new/@clerk/vue@9401

commit: 5f52f54

@mauricioabreumauricioabreu changed the title fix(billing): Use duration in cycles in payments and statementsfix(shared,clerk-js,backend,ui): use duration in cycles in payments and statementsAug 11, 2026
@coderabbitai

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The change adds optional nullable discount duration fields to shared billing types and JSON models. Backend and Clerk JS billing converters map duration_in_cycles to durationInCycles. Payment attempt and statement pages pass this value to discount descriptions. The description utility handles both missing and null durations. Tests update billing and checkout fixtures and assertions. A changeset records patch releases for four packages.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

Suggested reviewers:clerk-cookie

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly summarizes the main change to use discount duration in cycles for payment and statement messaging.
Description check✅ PassedThe description directly explains the billing messaging bug and the intended change from months to cycles.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitaicoderabbitaiBot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/ui/src/utils/billing.ts (1)

27-32: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Use cycle labels for discount duration.

getBillingPeriodLabel(planPeriod, cycles, t) returns month(s) or year(s). A two-cycle discount can therefore render as “first 2 months” or “first 2 years”, not “first 2 cycles”. Use a cycle-specific label or localization key for billing.discountDuration.

The PR objective requires discount duration to be displayed in cycles.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 27 - 32, Update the
discount-duration path in the billing utility to use a cycle-specific label or
localization key instead of the period returned by getBillingPeriodLabel. Ensure
billing.discountDuration renders durations as cycles (for example, “first 2
cycles”), while preserving the existing no-cycles discountAmount behavior.
🧹 Nitpick comments (1)
packages/ui/src/utils/billing.ts (1)

14-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add an explicit return type to the exported utility.

Declare getDiscountDescription with a : string return type.

As per coding guidelines, public TypeScript functions must define explicit return types.

Based on learnings, exported functions and public APIs require explicit return annotations.

Proposed change
 export function getDiscountDescription(
discount: Discount,
cycles: number | null | undefined,
planPeriod: BillingSubscriptionPlanPeriod,
{ $, t }: Localizations,
-) {+): string {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/ui/src/utils/billing.ts` around lines 14 - 19, Update the exported
getDiscountDescription function signature to explicitly declare a string return
type with : string, preserving its existing parameters and implementation.

Sources: Coding guidelines, Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 27-32: Update the discount-duration path in the billing utility to
use a cycle-specific label or localization key instead of the period returned by
getBillingPeriodLabel. Ensure billing.discountDuration renders durations as
cycles (for example, “first 2 cycles”), while preserving the existing no-cycles
discountAmount behavior.
---
Nitpick comments:
In `@packages/ui/src/utils/billing.ts`:
- Around line 14-19: Update the exported getDiscountDescription function
signature to explicitly declare a string return type with : string, preserving
its existing parameters and implementation.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository YAML (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 10854210-e1c4-4131-8f08-e5ad80fe5ca0

📥 Commits

Reviewing files that changed from the base of the PR and between 605225e and 5e56b47.

📒 Files selected for processing (10)
  • .changeset/applied-discount-duration-cycles.md
  • packages/backend/src/util/billing.ts
  • packages/clerk-js/src/utils/__tests__/billing.test.ts
  • packages/clerk-js/src/utils/billing.ts
  • packages/shared/src/types/billing.ts
  • packages/shared/src/types/json.ts
  • packages/ui/src/components/Checkout/__tests__/Checkout.test.tsx
  • packages/ui/src/components/PaymentAttempts/PaymentAttemptPage.tsx
  • packages/ui/src/components/Statements/StatementPage.tsx
  • packages/ui/src/utils/billing.ts
🔗 Linked repositories identified

CodeRabbit considers these linked repositories for cross-repo context during reviews:

  • clerk/clerk_go(manual)
  • clerk/dashboard(manual)
  • clerk/accounts(manual)
  • clerk/backoffice(manual)
  • clerk/clerk(manual)
  • clerk/clerk-docs(manual)
  • clerk/cloudflare-workers(manual)
  • clerk/cli(auto-detected)
  • clerk/clerk-ios(auto-detected)
  • clerk/clerk-android(auto-detected)

@github-actions

Copy link
Copy Markdown
Contributor

API Changes Report

Generated by Break Check on 2026-08-11T20:33:51.508Z

Summary

MetricCount
Packages analyzed19
Packages with changes1
🔴 Breaking changes0
🟡 Non-breaking changes0
🟢 Additions2

@clerk/shared

Current version: 4.28.1
Recommended bump: MINOR → 4.29.0

Subpath ./types

🟢 Additions (2)

Added: BillingAppliedDiscount.durationInCycles
+ durationInCycles?: number | null;

Added property BillingAppliedDiscount.durationInCycles

Added: BillingAppliedDiscountJSON.duration_in_cycles
+ duration_in_cycles?: number | null;

Added property BillingAppliedDiscountJSON.duration_in_cycles


Report generated by Break Check

Last ran on 5f52f54.

@mauricioabreu
mauricioabreu merged commit 81840b3 into mainAug 11, 2026
53 of 54 checks passed
@mauricioabreu
mauricioabreu deleted the mauricio-antunes/bill-1937-include-duration_cycles-and-cycles_applied-in branch August 11, 2026 20:40
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@mauricioabreu@dstaley