-
Notifications
You must be signed in to change notification settings - Fork 422
chore(backend): Add getOrganizationBillingSubscription to BillingApi
#6632
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
🦋 Changeset detectedLatest commit: f6443b8 The changes in this PR will be included in the next version bump. This PR includes changesets to release 11 packages
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 |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
WalkthroughAdds an experimental Billing API method to fetch an organization’s billing subscription, introduces a new CommerceSubscription resource and JSON types, extends CommerceSubscriptionItem fields/timestamps, updates deserialization to support CommerceSubscription, and re-exports the new types. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
actor Client
participant BillingAPI
participant HTTP as "HTTP Client"
participant Deserializer
Client->>BillingAPI: getOrganizationBillingSubscription(orgId)
BillingAPI->>HTTP: GET /organizations/{orgId}/billing/subscription
HTTP-->>BillingAPI: 200 CommerceSubscriptionJSON
BillingAPI->>Deserializer: CommerceSubscription.fromJSON(JSON)
Deserializer-->>BillingAPI: CommerceSubscription
BillingAPI-->>Client: CommerceSubscription
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
Status, Documentation and Community
|
@clerk/agent-toolkit
@clerk/astro
@clerk/backend
@clerk/chrome-extension
@clerk/clerk-js
@clerk/dev-cli
@clerk/elements
@clerk/clerk-expo
@clerk/expo-passkeys
@clerk/express
@clerk/fastify
@clerk/localizations
@clerk/nextjs
@clerk/nuxt
@clerk/clerk-react
@clerk/react-router
@clerk/remix
@clerk/shared
@clerk/tanstack-react-start
@clerk/testing
@clerk/themes
@clerk/types
@clerk/upgrade
@clerk/vue
commit: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
🧹 Nitpick comments (9)
packages/backend/src/api/endpoints/BillingApi.ts (1)
11-11: Localize organizations base pathDefining
organizationBasePathhere is a clear improvement for readability and maintainability. At the moment, there isn’t a shared constants module for API paths elsewhere in the codebase. As an optional enhancement, you could:
- Create a new file (e.g.
packages/backend/src/api/constants.ts)
• Export shared base paths such as:export const COMMERCE_BASE_PATH = '/commerce'; export const ORGANIZATIONS_BASE_PATH = '/organizations';- Update
BillingApi.tsto import and use these constants instead of declaring them inline.This change isn’t required now, but if you find the same paths used in multiple places (or expect them to grow), centralizing them will help avoid drift.
packages/backend/src/api/resources/CommerceSubscription.ts (2)
42-46: Prefer immutable collection typing for subscriptionItemsExpose as
ReadonlyArrayto prevent accidental mutation by consumers.Apply this diff:
- readonly subscriptionItems: CommerceSubscriptionItem[], + readonly subscriptionItems: ReadonlyArray<CommerceSubscriptionItem>,
53-66: Deduplicate money amount mapping (optional)The
nextPayment.amountmapping mirrors logic inCommerceSubscriptionItem. Consider a tiny local helper to keep things DRY and uniform.static fromJSON(data: CommerceSubscriptionJSON): CommerceSubscription { + const toMoney = (a: { amount: number; amount_formatted: string; currency: string; currency_symbol: string }) => ({ + amount: a.amount, + amountFormatted: a.amount_formatted, + currency: a.currency, + currencySymbol: a.currency_symbol, + }); const nextPayment = data.next_payment ? { date: data.next_payment.date, - amount: { - amount: data.next_payment.amount.amount, - amountFormatted: data.next_payment.amount.amount_formatted, - currency: data.next_payment.amount.currency, - currencySymbol: data.next_payment.amount.currency_symbol, - }, + amount: toMoney(data.next_payment.amount), } : null;packages/backend/src/api/resources/CommerceSubscriptionItem.ts (4)
47-55: Clarify timestamp units across newly added fieldsPlease state whether these timestamps are seconds or milliseconds since epoch to avoid consumer confusion and downstream bugs.
Apply this diff to each timestamp JSDoc (example shown for createdAt/updatedAt; mirror for periodEnd, canceledAt, pastDueAt, endedAt):
/** - * The date and time the subscription item was created. + * The date and time the subscription item was created (Unix timestamp in milliseconds). */ readonly createdAt: number, /** - * The date and time the subscription item was last updated. + * The date and time the subscription item was last updated (Unix timestamp in milliseconds). */ readonly updatedAt: number,Also applies to: 58-58, 62-66, 70-74
80-83: Align lifetimePaid optionality with JSON contractJSON declares
lifetime_paidas always present (object), whereas the instance property is optional. Consider making it non-optional to reflect the wire contract and simplify consumer null checks.Apply this diff:
- readonly lifetimePaid?: CommerceMoneyAmount | null, + readonly lifetimePaid: CommerceMoneyAmount | null,
1-1: Avoid cross-package type drift for money JSONThis file mixes
CommerceMoneyAmountJSONfrom@clerk/typeswith local JSON contracts. To prevent drift, exportCommerceMoneyAmountJSONfrom./JSONand import it here.Apply these diffs in both files:
In
packages/backend/src/api/resources/JSON.tsexport the interface (see separate comment on that file for exact diff).Update the import here:
-import type { CommerceMoneyAmountJSON } from '@clerk/types'; +import type { CommerceMoneyAmountJSON } from './JSON';
85-120: Add serialization tests for new fieldsPlease add unit tests covering: createdAt/updatedAt mapping, nullability for periodEnd/canceledAt/pastDueAt/endedAt, payerId propagation, and isFreeTrial passthrough.
I can scaffold tests in
packages/backend/src/api/resources/__tests__/CommerceSubscriptionItem.test.tsthat construct JSON payloads and assert the instance fields. Want me to generate them?packages/backend/src/api/resources/JSON.ts (2)
803-809: Export CommerceMoneyAmountJSON to keep money types consistent across backend resources
CommerceSubscriptionItem.tsneeds a local, single source of truth for money JSON. Exporting this interface allows importing from./JSONinstead of@clerk/types.Apply this diff:
-interface CommerceMoneyAmountJSON { +export interface CommerceMoneyAmountJSON { amount: number; amount_formatted: string; currency: string; currency_symbol: string; }
860-873: Item JSON: move to explicit nulls is good; add concise field docsSwitching to explicit
nullis clear. Please add short comments for new fields (payer_id, ended_at, created_at/updated_at, canceled_at/past_due_at, is_free_trial) to guide integrators.Example (apply similarly to others):
export interface CommerceSubscriptionItemJSON extends ClerkResourceJSON { object: typeof ObjectType.CommerceSubscriptionItem; status: CommerceSubscriptionItemStatus; plan_period: 'month' | 'annual'; - payer_id: string; + /** The associated payer ID for this item. */ + payer_id: string; period_start: number; - period_end: number | null; + /** Null for items on the free plan. */ + period_end: number | null; - is_free_trial?: boolean; + /** Temporary flag during beta; may be removed after GA. */ + is_free_trial?: boolean; - ended_at: number | null; + /** Timestamp when the item ended, or null if active. */ + ended_at: number | null; - created_at: number; - updated_at: number; + /** Creation timestamp. */ + created_at: number; + /** Last update timestamp. */ + updated_at: number; - canceled_at: number | null; - past_due_at: number | null; + /** When cancellation took effect, if applicable. */ + canceled_at: number | null; + /** When the item became past due, if applicable. */ + past_due_at: number | null;
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (7)
packages/backend/src/api/endpoints/BillingApi.ts(2 hunks)packages/backend/src/api/resources/CommerceSubscription.ts(1 hunks)packages/backend/src/api/resources/CommerceSubscriptionItem.ts(2 hunks)packages/backend/src/api/resources/Deserializer.ts(2 hunks)packages/backend/src/api/resources/JSON.ts(2 hunks)packages/backend/src/api/resources/index.ts(1 hunks)packages/backend/src/index.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (8)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/backend/src/index.tspackages/backend/src/api/resources/index.tspackages/backend/src/api/resources/Deserializer.tspackages/backend/src/api/resources/CommerceSubscription.tspackages/backend/src/api/endpoints/BillingApi.tspackages/backend/src/api/resources/JSON.tspackages/backend/src/api/resources/CommerceSubscriptionItem.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/backend/src/index.tspackages/backend/src/api/resources/index.tspackages/backend/src/api/resources/Deserializer.tspackages/backend/src/api/resources/CommerceSubscription.tspackages/backend/src/api/endpoints/BillingApi.tspackages/backend/src/api/resources/JSON.tspackages/backend/src/api/resources/CommerceSubscriptionItem.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/backend/src/index.tspackages/backend/src/api/resources/index.tspackages/backend/src/api/resources/Deserializer.tspackages/backend/src/api/resources/CommerceSubscription.tspackages/backend/src/api/endpoints/BillingApi.tspackages/backend/src/api/resources/JSON.tspackages/backend/src/api/resources/CommerceSubscriptionItem.ts
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/backend/src/index.tspackages/backend/src/api/resources/index.tspackages/backend/src/api/resources/Deserializer.tspackages/backend/src/api/resources/CommerceSubscription.tspackages/backend/src/api/endpoints/BillingApi.tspackages/backend/src/api/resources/JSON.tspackages/backend/src/api/resources/CommerceSubscriptionItem.ts
packages/**/index.{js,ts}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use tree-shaking friendly exports
Files:
packages/backend/src/index.tspackages/backend/src/api/resources/index.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Preferreadonlyfor properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertionsfor literal types:as const
Usesatisfiesoperator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noanytypes without justification
Proper error handling with typed errors
Consistent use ofreadonlyfor immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/backend/src/index.tspackages/backend/src/api/resources/index.tspackages/backend/src/api/resources/Deserializer.tspackages/backend/src/api/resources/CommerceSubscription.tspackages/backend/src/api/endpoints/BillingApi.tspackages/backend/src/api/resources/JSON.tspackages/backend/src/api/resources/CommerceSubscriptionItem.ts
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/backend/src/index.tspackages/backend/src/api/resources/index.tspackages/backend/src/api/resources/Deserializer.tspackages/backend/src/api/resources/CommerceSubscription.tspackages/backend/src/api/endpoints/BillingApi.tspackages/backend/src/api/resources/JSON.tspackages/backend/src/api/resources/CommerceSubscriptionItem.ts
**/index.ts
📄 CodeRabbit inference engine (.cursor/rules/react.mdc)
Use index.ts files for clean imports but avoid deep barrel exports
Avoid barrel files (index.ts re-exports) as they can cause circular dependencies
Files:
packages/backend/src/index.tspackages/backend/src/api/resources/index.ts
🧬 Code graph analysis (4)
packages/backend/src/api/resources/Deserializer.ts (3)
packages/backend/src/api/resources/JSON.ts (2)
ObjectType(19-74)ObjectType(76-76)packages/backend/src/api/resources/CommerceSubscription.ts (1)
CommerceSubscription(9-79)packages/backend/src/index.ts (1)
CommerceSubscription(148-148)
packages/backend/src/api/resources/CommerceSubscription.ts (2)
packages/backend/src/api/resources/JSON.ts (1)
CommerceSubscriptionJSON(980-994)packages/backend/src/api/resources/CommerceSubscriptionItem.ts (1)
CommerceSubscriptionItem(10-121)
packages/backend/src/api/endpoints/BillingApi.ts (2)
packages/backend/src/api/resources/CommerceSubscription.ts (1)
CommerceSubscription(9-79)packages/backend/src/index.ts (1)
CommerceSubscription(148-148)
packages/backend/src/api/resources/JSON.ts (1)
packages/types/src/json.ts (4)
CommerceSubscriptionJSON(804-824)ClerkResourceJSON(36-40)CommerceSubscriptionItemJSON(773-794)CommerceMoneyAmountJSON(834-839)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: Build Packages
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (8)
packages/backend/src/api/resources/Deserializer.ts (2)
41-41: Importing CommerceSubscription at runtime is correctNeeded as a runtime value to call
fromJSON; type-only import would break deserialization.
188-191: Coverage Confirmed: CommerceSubscription DeserializationThe switch in
Deserializer.tsnow handlesObjectType.CommerceSubscription(line 188) andObjectType.CommerceSubscriptionItem(line 190) exactly once. The corresponding JSON mappings for'commerce_subscription'and'commerce_subscription_item'are present inJSON.ts(lines 70–71). All object types are fully supported—ready to merge.packages/backend/src/api/resources/index.ts (1)
61-61: Expose CommerceSubscription via resources barrelConsistent with existing pattern; keeps public surface coherent for the new resource.
packages/backend/src/index.ts (2)
104-106: Export CommerceSubscriptionJSON typeGood addition; keeps JSON surface in sync with new resource.
148-150: Export CommerceSubscription as a type-only exportMatches existing convention (resources exposed as types from the root), avoiding unintended runtime exports. LGTM.
packages/backend/src/api/endpoints/BillingApi.ts (1)
5-5: Type-only import for CommerceSubscription is appropriatePrevents pulling the class value into the bundle while preserving type safety on the method signature.
packages/backend/src/api/resources/CommerceSubscription.ts (1)
1-79: Solid resource model and JSON mappingConstructor shape, field nullability, and
fromJSONmapping (including money amount normalization and nested items) look correct and consistent withCommerceSubscriptionItem.packages/backend/src/api/resources/CommerceSubscriptionItem.ts (1)
85-99: Item-levelnextPaymentis correctly defined and used
- The
CommerceSubscriptionItemconstructor expectsnextPaymentas{ amount: number; date: number } | null, which exactly matches the item-level JSON schema inJSON.ts(lines 874–876).- A search across the codebase found no places where
nextPayment.amountis treated as an object (e.g. accessingamountFormatted), so there are no erroneous call sites.- The asymmetry—item level using a raw
numberforamount, and subscription level using a fullCommerceMoneyAmountJSON—is intentional and handled correctly in eachfromJSONmethod.
| /** | ||
| * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. | ||
| * It is advised to pin the SDK version to avoid breaking changes. | ||
| */ | ||
| public async getOrganizationBillingSubscription(organizationId: string) { | ||
| this.requireId(organizationId); | ||
| return this.request<CommerceSubscription>({ | ||
| method: 'GET', | ||
| path: joinPaths(organizationBasePath, organizationId, 'billing', 'subscription'), | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Add explicit return type and complete JSDoc for the public API
Per guidelines, public APIs should declare explicit return types and comprehensive JSDoc.
Apply this diff within the method block:
/**
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
* It is advised to pin the SDK version to avoid breaking changes.
+ * @param organizationId - The target organization ID (e.g. "org_...").
+ * @returns The organization's billing subscription resource.
+ * @throws ClerkAPIResponseError If the organization does not exist or billing is not enabled.
*/
- public async getOrganizationBillingSubscription(organizationId: string) {
+ public async getOrganizationBillingSubscription(organizationId: string): Promise<CommerceSubscription> {
this.requireId(organizationId);
return this.request<CommerceSubscription>({
method: 'GET',
path: joinPaths(organizationBasePath, organizationId, 'billing', 'subscription'),
});
}I can add an integration test stub that mocks AbstractAPI.request to verify the shape and deserialization path—want me to push that?
🏁 Script executed:
#!/bin/bash
# Verify that the new method compiles to a Promise<CommerceSubscription> (textual check)
rg -nP "public\s+async\s+getOrganizationBillingSubscription\([^)]*\)\s*:\s*Promise<CommerceSubscription>" packages/backend/src/api/endpoints/BillingApi.ts -n
# Ensure the endpoint path matches server route conventions elsewhere in the repo
rg -n "billing.*subscription" -C2 packages | sed -n '1,200p'Length of output: 11395
Add explicit return type and complete JSDoc for getOrganizationBillingSubscription
Public SDK methods must declare explicit return types and comprehensive JSDoc for parameters, return values, and thrown errors per our API guidelines.
– In packages/backend/src/api/endpoints/BillingApi.ts, update the method signature:
/**
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
* It is advised to pin the SDK version to avoid breaking changes.
+ * @param organizationId – The target organization’s ID (e.g. "org_…").
+ * @returns The organization’s billing subscription resource.
+ * @throws ClerkAPIResponseError If the organization does not exist or billing is not enabled.
*/
- public async getOrganizationBillingSubscription(organizationId: string) {
+ public async getOrganizationBillingSubscription(
+ organizationId: string
+ ): Promise<CommerceSubscription> {
this.requireId(organizationId);
return this.request<CommerceSubscription>({
method: 'GET',
path: joinPaths(organizationBasePath, organizationId, 'billing', 'subscription'),
});
}• Please also verify that the billing/subscription endpoint path aligns exactly with our server’s API specification—I wasn’t able to locate a matching route definition in the repo.
• Let me know if you’d like me to add an integration‐test stub that mocks AbstractAPI.request to validate the response shape and deserialization path.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| /** | |
| * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. | |
| * It is advised to pin the SDK version to avoid breaking changes. | |
| */ | |
| public async getOrganizationBillingSubscription(organizationId: string) { | |
| this.requireId(organizationId); | |
| return this.request<CommerceSubscription>({ | |
| method: 'GET', | |
| path: joinPaths(organizationBasePath, organizationId, 'billing', 'subscription'), | |
| }); | |
| } | |
| /** | |
| * @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change. | |
| * It is advised to pin the SDK version to avoid breaking changes. | |
| * @param organizationId – The target organization’s ID (e.g. "org_…"). | |
| * @returns The organization’s billing subscription resource. | |
| * @throws ClerkAPIResponseError If the organization does not exist or billing is not enabled. | |
| */ | |
| public async getOrganizationBillingSubscription( | |
| organizationId: string | |
| ): Promise<CommerceSubscription> { | |
| this.requireId(organizationId); | |
| return this.request<CommerceSubscription>({ | |
| method: 'GET', | |
| path: joinPaths(organizationBasePath, organizationId, 'billing', 'subscription'), | |
| }); | |
| } |
🤖 Prompt for AI Agents
In packages/backend/src/api/endpoints/BillingApi.ts around lines 51-61, the
public method getOrganizationBillingSubscription lacks an explicit return type
and complete JSDoc; update the method signature to explicitly return
Promise<CommerceSubscription> and add a JSDoc block that documents the
organizationId parameter, the Promise<CommerceSubscription> return value, and
any errors that can be thrown (e.g., validation errors from requireId and
network/request errors from AbstractAPI.request). While changing the
signature/JSDoc, verify the endpoint path "billing/subscription" against the
server route definitions (search the repo for matching routes) and correct the
joinPaths call if the server uses a different path (e.g.,
"billing/subscriptions" or a nested route); if you change the path, mirror that
in the JSDoc. Finally, run unit/integration tests or add a small test stub that
mocks AbstractAPI.request to assert the response deserializes to
CommerceSubscription.
| items: CommerceSubscriptionItemWebhookEventJSON[]; | ||
| } | ||
|
|
||
| export interface CommerceSubscriptionJSON extends ClerkResourceJSON { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Add comprehensive JSDoc for the new public API
Per repo guidelines, all public APIs need JSDoc. Add the experimental notice and field-level docs for payer_id, subscription_items, and next_payment.
Apply this diff:
- export interface CommerceSubscriptionJSON extends ClerkResourceJSON {
+ /**
+ * @experimental This is an experimental API for the Billing feature that is available under a public beta,
+ * and the API is subject to change. Pin the SDK version to avoid breaking changes.
+ *
+ * Data model returned by `BillingApi.getOrganizationBillingSubscription`.
+ * - `subscription_items`: list of items in this subscription.
+ * - `next_payment`: next scheduled charge for the subscription, or null if none.
+ */
+ export interface CommerceSubscriptionJSON extends ClerkResourceJSON {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export interface CommerceSubscriptionJSON extends ClerkResourceJSON { | |
| /** | |
| * @experimental This is an experimental API for the Billing feature that is available under a public beta, | |
| * and the API is subject to change. Pin the SDK version to avoid breaking changes. | |
| * | |
| * Data model returned by `BillingApi.getOrganizationBillingSubscription`. | |
| * - `subscription_items`: list of items in this subscription. | |
| * - `next_payment`: next scheduled charge for the subscription, or null if none. | |
| */ | |
| export interface CommerceSubscriptionJSON extends ClerkResourceJSON { |
🤖 Prompt for AI Agents
In packages/backend/src/api/resources/JSON.ts around line 980, add a
comprehensive JSDoc comment immediately above the exported interface
CommerceSubscriptionJSON: mark the API as experimental, document the interface
purpose, and add field-level @property tags describing payer_id (the unique
identifier for the payer, format and nullable/optional status),
subscription_items (array shape, item type and meaning), and next_payment (next
payment date/time or object shape and nullable/optional status). Ensure the
JSDoc follows repo conventions, uses @experimental or an explicit experimental
notice, and documents types/optionality for each field so the public API is
fully documented.
| export interface CommerceSubscriptionJSON extends ClerkResourceJSON { | ||
| object: typeof ObjectType.CommerceSubscription; | ||
| status: 'active' | 'past_due' | 'canceled' | 'ended' | 'abandoned' | 'incomplete'; | ||
| payer_id: string; | ||
| created_at: number; | ||
| updated_at: number; | ||
| active_at: number | null; | ||
| past_due_at: number | null; | ||
| subscription_items: CommerceSubscriptionItemJSON[]; | ||
| next_payment: { | ||
| date: number; | ||
| amount: CommerceMoneyAmountJSON; | ||
| } | null; | ||
| eligible_for_free_trial?: boolean | null; | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
New CommerceSubscriptionJSON: align with public typing and document the API
There are notable divergences from @clerk/types (status union breadth, subscription_items nullability, updated_at nullability, next_payment optionality/shape). Confirm the server contract and either (a) match the published types or (b) intentionally version/scope this as a backend-only contract to avoid consumer confusion. Also, add JSDoc as this is exported publicly by the backend package.
- If this should match
@clerk/types, apply:
export interface CommerceSubscriptionJSON extends ClerkResourceJSON {
object: typeof ObjectType.CommerceSubscription;
- status: 'active' | 'past_due' | 'canceled' | 'ended' | 'abandoned' | 'incomplete';
+ status: 'active' | 'past_due';
payer_id: string;
created_at: number;
- updated_at: number;
- active_at: number | null;
+ updated_at: number | null;
+ active_at: number;
past_due_at: number | null;
- subscription_items: CommerceSubscriptionItemJSON[];
- next_payment: {
+ subscription_items: CommerceSubscriptionItemJSON[] | null;
+ next_payment?: {
date: number;
amount: CommerceMoneyAmountJSON;
} | null;
eligible_for_free_trial?: boolean | null;
}- If divergence is intentional for backend, keep the shape but add explicit documentation:
+/**
+ * @experimental This is an experimental Billing API shape returned by
+ * `BillingApi.getOrganizationBillingSubscription`.
+ * Notes:
+ * - `status` includes terminal states for internal workflows.
+ * - `subscription_items` is never `null`; an empty array indicates no items.
+ * - `next_payment` is `null` when there is no upcoming charge.
+ */
export interface CommerceSubscriptionJSON extends ClerkResourceJSON {Run this to find all consumers and confirm assumptions about the above fields:
🏁 Script executed:
#!/bin/bash
# Find all references and check assumptions about nullability and status narrowing
rg -nP -C2 --type=ts '\bCommerceSubscriptionJSON\b' packages
# Inspect uses of `subscription_items` expecting null
rg -nP -C2 --type=ts '\bsubscription_items\s*[!=]==?\s*null\b' packages
# Inspect status narrowing to only 'active' | 'past_due'
rg -nP -C2 --type=ts "\.status\b" packages | sed -n '1,200p'Length of output: 19263
🏁 Script executed:
#!/bin/bash
# Print published types CommerceSubscriptionJSON shape from @clerk/types around interface definition
sed -n '780,840p' packages/types/src/json.tsLength of output: 2039
Align or Scope CommerceSubscriptionJSON with Public SDK Types
The CommerceSubscriptionJSON interface in packages/backend/src/api/resources/JSON.ts diverges significantly from the published SDK shape in @clerk/types. To avoid client confusion, please choose one of the following:
• Match the published SDK types
Update the backend JSON shape so it exactly mirrors packages/types/src/json.ts’s CommerceSubscriptionJSON. For example:
export interface CommerceSubscriptionJSON extends ClerkResourceJSON {
object: typeof ObjectType.CommerceSubscription;
- status: 'active' | 'past_due' | 'canceled' | 'ended' | 'abandoned' | 'incomplete';
+ status: Extract<CommerceSubscriptionStatus, 'active' | 'past_due'>;
- payer_id: string;
+ payment_source_id: string;
created_at: number;
- updated_at: number;
- active_at: number | null;
+ active_at: number;
+ updated_at: number | null;
past_due_at: number | null;
- subscription_items: CommerceSubscriptionItemJSON[];
- next_payment: {
- date: number;
- amount: CommerceMoneyAmountJSON;
- } | null;
- eligible_for_free_trial?: boolean | null;
+ subscription_items: CommerceSubscriptionItemJSON[] | null;
+ next_payment?: { date: number; amount: CommerceMoneyAmountJSON };
+ eligible_for_free_trial?: boolean;
}• Ensures the backend-generated JSON matches the client’s expectations.
• Removes internal-only statuses and nullability mismatches.
• Keep the extended backend contract but scope/document it
If the extra statuses (canceled, ended, etc.) and the current nullability choices are intentional for internal workflows:
- Move this interface out of the public resources JSON file or rename it to indicate “internal.”
- Add JSDoc calling out that it is a backend-only or experimental shape:
/** * @internal Experimental backend shape for CommerceSubscription. * - Includes terminal statuses for internal workflows. * - `active_at` may be null until activation. * - `subscription_items` is always an array. * - `next_payment` is null if no upcoming charge. */ export interface InternalCommerceSubscriptionJSON { … }
- Expose only the aligned SDK shape in the public API layer.
Mandatory refactors are required here to maintain a consistent public contract. Please pick one approach and update the code accordingly.
🤖 Prompt for AI Agents
In packages/backend/src/api/resources/JSON.ts around lines 980-995, the
CommerceSubscriptionJSON interface diverges from the published @clerk/types SDK
shape (extra statuses and different nullability); either 1) change this
interface to exactly match packages/types/src/json.ts CommerceSubscriptionJSON
(same fields, types, nullability) so backend JSON aligns with client
expectations, or 2) if the extended shape is required for internal workflows,
move/rename it (e.g., InternalCommerceSubscriptionJSON) out of the public
resources file, add a clear JSDoc @internal explaining the extra statuses and
nullability, and ensure the public API layer continues to export only the
SDK-aligned CommerceSubscriptionJSON type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 0
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
.changeset/fresh-clubs-float.md (1)
1-6: Please include a bump for theclerk-jspackage in this changesetThis PR touches
packages/clerk-js—you’ve modifiedsrc/core/clerk.ts, the debug transports undersrc/core/modules/debug/, and theutils/debug.tshelper—so the@clerk/clerk-jspackage needs its own version bump in the same changeset. The existing bump for@clerk/backendis correct, but without a bump forclerk-js, those changes won’t be published.• Update
.changeset/fresh-clubs-float.mdfront-matter to something like:--- '@clerk/backend': minor '@clerk/clerk-js': patch ---• No other public packages (astro, fastify, vue) have API changes—they only had devDependency updates—so they don’t require bumps here.
🧹 Nitpick comments (2)
.changeset/fresh-clubs-float.md (2)
5-6: Make the changeset summary more informative (mention experimental status, signature, and return type)Clarify what was added and how consumers can use it. Suggested replacement keeps it concise while capturing key details.
-Add `getOrganizationBillingSubscription` to BillingApi. +feat(backend): add experimental BillingApi.getOrganizationBillingSubscription(organizationId) + +- Calls GET /organizations/{organizationId}/billing/subscription +- Returns a CommerceSubscription (and re-exports its types)
6-6: Ensure file ends with a trailing newlineA missing trailing newline can trip some linters/formatters. Add one if it's not already present.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
.changeset/fresh-clubs-float.md(1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
.changeset/**
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Automated releases must use Changesets.
Files:
.changeset/fresh-clubs-float.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (25)
- GitHub Check: Integration Tests (billing, chrome)
- GitHub Check: Integration Tests (ap-flows, chrome)
- GitHub Check: Integration Tests (machine, chrome)
- GitHub Check: Integration Tests (react-router, chrome)
- GitHub Check: Integration Tests (vue, chrome)
- GitHub Check: Integration Tests (nuxt, chrome)
- GitHub Check: Integration Tests (expo-web, chrome)
- GitHub Check: Integration Tests (nextjs, chrome, 14)
- GitHub Check: Integration Tests (astro, chrome)
- GitHub Check: Integration Tests (nextjs, chrome, 15)
- GitHub Check: Integration Tests (localhost, chrome)
- GitHub Check: Integration Tests (elements, chrome)
- GitHub Check: Integration Tests (sessions, chrome)
- GitHub Check: Integration Tests (express, chrome)
- GitHub Check: Integration Tests (quickstart, chrome)
- GitHub Check: Integration Tests (tanstack-react-router, chrome)
- GitHub Check: Integration Tests (generic, chrome)
- GitHub Check: Integration Tests (tanstack-react-start, chrome)
- GitHub Check: Unit Tests (18, --filter=@clerk/astro --filter=@clerk/backend --filter=@clerk/express --filter=@c...
- GitHub Check: Publish with pkg-pr-new
- GitHub Check: Unit Tests (22, **)
- GitHub Check: Static analysis
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (1)
.changeset/fresh-clubs-float.md (1)
1-3: Valid changeset front‑matter and semver choice (minor) look correct
- Uses Changesets properly per repo guidelines for automated releases.
- Adding a new public API method is a backward‑compatible feature; "minor" is appropriate.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
♻️ Duplicate comments (1)
packages/backend/src/api/resources/JSON.ts (1)
980-980: Add comprehensive JSDoc for the new public API (duplicate of existing review)There’s already a prior comment asking for experimental notice and field-level docs for CommerceSubscriptionJSON. Agree and reiterate: this is required before merge if this interface remains public.
🧹 Nitpick comments (2)
packages/backend/src/api/resources/CommerceSubscription.ts (2)
10-51: Nit: Make access modifiers explicit for public API fieldsRepo guidelines prefer explicit visibility. These constructor parameter properties are public but not marked. Consider adding the public modifier for clarity in public APIs.
- constructor( + constructor( - readonly id: string, + public readonly id: string, - readonly status: CommerceSubscriptionJSON['status'], + public readonly status: CommerceSubscriptionJSON['status'], - readonly payerId: string, + public readonly payerId: string, - readonly createdAt: number, + public readonly createdAt: number, - readonly updatedAt: number, + public readonly updatedAt: number, - readonly activeAt: number | null, + public readonly activeAt: number | null, - readonly pastDueAt: number | null, + public readonly pastDueAt: number | null, - readonly subscriptionItems: CommerceSubscriptionItem[], + public readonly subscriptionItems: CommerceSubscriptionItem[], - readonly nextPayment: { date: number; amount: CommerceMoneyAmount } | null, + public readonly nextPayment: { date: number; amount: CommerceMoneyAmount } | null, - readonly eligibleForFreeTrial: boolean, + public readonly eligibleForFreeTrial: boolean, ) {}
54-64: Deduplicate money-amount shaping via a shared helperYou manually shape CommerceMoneyAmount here, while CommerceSubscriptionItem.fromJSON already defines a format function. To avoid divergence and ease maintenance, extract a shared formatter and reuse it.
- const nextPayment = data.next_payment - ? { - date: data.next_payment.date, - amount: { - amount: data.next_payment.amount.amount, - amountFormatted: data.next_payment.amount.amount_formatted, - currency: data.next_payment.amount.currency, - currencySymbol: data.next_payment.amount.currency_symbol, - }, - } - : null; + const nextPayment = data.next_payment + ? { + date: data.next_payment.date, + amount: formatMoneyAmountJSON(data.next_payment.amount), + } + : null;Outside selected lines (new tiny utility):
// packages/backend/src/api/resources/money.ts import type { CommerceMoneyAmount } from './CommercePlan'; export function formatMoneyAmountJSON(amount?: { amount: number; amount_formatted: string; currency: string; currency_symbol: string; } | null): CommerceMoneyAmount | null | undefined { if (amount == null) return amount as null | undefined; return { amount: amount.amount, amountFormatted: amount.amount_formatted, currency: amount.currency, currencySymbol: amount.currency_symbol, }; }And import it here:
+import { formatMoneyAmountJSON } from './money';
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
💡 Knowledge Base configuration:
- MCP integration is disabled by default for public repositories
- Jira integration is disabled by default for public repositories
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
packages/backend/src/api/resources/CommerceSubscription.ts(1 hunks)packages/backend/src/api/resources/JSON.ts(2 hunks)
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{js,jsx,ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
**/*.{js,jsx,ts,tsx}: All code must pass ESLint checks with the project's configuration
Follow established naming conventions (PascalCase for components, camelCase for variables)
Maintain comprehensive JSDoc comments for public APIs
Use dynamic imports for optional features
All public APIs must be documented with JSDoc
Provide meaningful error messages to developers
Include error recovery suggestions where applicable
Log errors appropriately for debugging
Lazy load components and features when possible
Implement proper caching strategies
Use efficient data structures and algorithms
Profile and optimize critical paths
Validate all inputs and sanitize outputs
Implement proper logging with different levels
Files:
packages/backend/src/api/resources/CommerceSubscription.tspackages/backend/src/api/resources/JSON.ts
**/*.{js,jsx,ts,tsx,json,css,scss,md,yaml,yml}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use Prettier for consistent code formatting
Files:
packages/backend/src/api/resources/CommerceSubscription.tspackages/backend/src/api/resources/JSON.ts
packages/**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
TypeScript is required for all packages
Files:
packages/backend/src/api/resources/CommerceSubscription.tspackages/backend/src/api/resources/JSON.ts
packages/**/*.{ts,tsx,d.ts}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Packages should export TypeScript types alongside runtime code
Files:
packages/backend/src/api/resources/CommerceSubscription.tspackages/backend/src/api/resources/JSON.ts
**/*.{ts,tsx}
📄 CodeRabbit inference engine (.cursor/rules/development.mdc)
Use proper TypeScript error types
**/*.{ts,tsx}: Always define explicit return types for functions, especially public APIs
Use proper type annotations for variables and parameters where inference isn't clear
Avoidanytype - preferunknownwhen type is uncertain, then narrow with type guards
Useinterfacefor object shapes that might be extended
Usetypefor unions, primitives, and computed types
Preferreadonlyproperties for immutable data structures
Useprivatefor internal implementation details
Useprotectedfor inheritance hierarchies
Usepublicexplicitly for clarity in public APIs
Preferreadonlyfor properties that shouldn't change after construction
Prefer composition and interfaces over deep inheritance chains
Use mixins for shared behavior across unrelated classes
Implement dependency injection for loose coupling
Let TypeScript infer when types are obvious
Useconst assertionsfor literal types:as const
Usesatisfiesoperator for type checking without widening
Use mapped types for transforming object types
Use conditional types for type-level logic
Leverage template literal types for string manipulation
Use ES6 imports/exports consistently
Use default exports sparingly, prefer named exports
Use type-only imports:import type { ... } from ...
Noanytypes without justification
Proper error handling with typed errors
Consistent use ofreadonlyfor immutable data
Proper generic constraints
No unused type parameters
Proper use of utility types instead of manual type construction
Type-only imports where possible
Proper tree-shaking friendly exports
No circular dependencies
Efficient type computations (avoid deep recursion)
Files:
packages/backend/src/api/resources/CommerceSubscription.tspackages/backend/src/api/resources/JSON.ts
**/*.{js,ts,tsx,jsx}
📄 CodeRabbit inference engine (.cursor/rules/monorepo.mdc)
Support multiple Clerk environment variables (CLERK_, NEXT_PUBLIC_CLERK_, etc.) for configuration.
Files:
packages/backend/src/api/resources/CommerceSubscription.tspackages/backend/src/api/resources/JSON.ts
🧬 Code graph analysis (2)
packages/backend/src/api/resources/CommerceSubscription.ts (2)
packages/backend/src/api/resources/JSON.ts (1)
CommerceSubscriptionJSON(980-994)packages/backend/src/api/resources/CommerceSubscriptionItem.ts (1)
CommerceSubscriptionItem(10-121)
packages/backend/src/api/resources/JSON.ts (1)
packages/types/src/json.ts (4)
CommerceSubscriptionJSON(804-824)ClerkResourceJSON(36-40)CommerceSubscriptionItemJSON(773-794)CommerceMoneyAmountJSON(834-839)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
- GitHub Check: Build Packages
- GitHub Check: Formatting | Dedupe | Changeset
- GitHub Check: semgrep/ci
- GitHub Check: Analyze (javascript-typescript)
- GitHub Check: semgrep-cloud-platform/scan
- GitHub Check: semgrep-cloud-platform/scan
🔇 Additional comments (3)
packages/backend/src/api/resources/CommerceSubscription.ts (2)
5-8: Good: Public API clearly marked experimentalThe class-level JSDoc follows the guideline to mark public beta APIs as experimental and advises pinning versions. Keep this pattern consistent across the new BillingApi surface.
66-77: No action needed—internal JSON contract is stricter than public typesI’ve confirmed that the backend’s own
CommerceSubscriptionJSON(inpackages/backend/src/api/resources/JSON.ts) declares:
updated_at: number(always present)subscription_items: CommerceSubscriptionItemJSON[](nevernull)So in
CommerceSubscription.fromJSON, bothdata.updated_atand
data.subscription_items.map(...)are safe under the internal API contract. The nullable definitions in
packages/types/src/json.tsapply to the public client-side SDK types, not this backend mapping. You can disregard the original nullability concern here.Likely an incorrect or invalid review comment.
packages/backend/src/api/resources/JSON.ts (1)
864-873: Please review backend JSON type alignment with the published SDK typesThe
CommerceSubscriptionItemJSONdefinition inpackages/backend/src/api/resources/JSON.tsdiverges from the shape inpackages/types/src/json.ts, which risks type drift and downstream confusion. Key mismatches:
- Field rename/omission
- Backend:
payer_id: string- SDK:
payment_source_id: string- Additional timestamp fields
- Backend adds
updated_at,ended_at,past_due_at,canceled_at(allnumber | null) beyond the SDK’s minimal set.- Optionality differences
- Backend:
period_end: number | null(nullable but required)- SDK:
period_end?: number \| null(optional & nullable)- New boolean flag
- Backend:
is_free_trial?: boolean(absent in SDK)- Monetary shape mismatch
- Backend:
lifetime_paid: CommerceMoneyAmountJSON- SDK:
next_payment: { amount: number; currency: string }at the item levelDecide one of the following:
• Keep SDK-aligned public contract:
– Renamepayer_id→payment_source_id
– Remove or optionalize fields not present inpackages/types/src/json.ts
– Matchlifetime_paid/next_paymentshapes exactlyMinimal patch example:
- payer_id: string; + payment_source_id: string;• Expose an extended internal shape:
– AnnotateCommerceSubscriptionItemJSONwith@internalJSDoc
– Document each extra field’s purpose and data origin
– Avoid re-exporting this type in the public APIPlease confirm which approach reflects intended design and update the code (or documentation) accordingly.
| export interface CommerceSubscriptionJSON extends ClerkResourceJSON { | ||
| object: typeof ObjectType.CommerceSubscription; | ||
| status: 'active' | 'past_due' | 'canceled' | 'ended' | 'abandoned' | 'incomplete'; | ||
| payer_id: string; | ||
| created_at: number; | ||
| updated_at: number; | ||
| active_at: number | null; | ||
| past_due_at: number | null; | ||
| subscription_items: CommerceSubscriptionItemJSON[]; | ||
| next_payment?: { | ||
| date: number; | ||
| amount: CommerceMoneyAmountJSON; | ||
| }; | ||
| eligible_for_free_trial?: boolean; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Public contract mismatch with packages/types: high risk of consumer breakage
This CommerceSubscriptionJSON differs from packages/types/src/json.ts in multiple ways:
- status union includes terminal/internal states; SDK restricts to 'active' | 'past_due'
- payer_id exists here; SDK has no top-level payer_id
- active_at is nullable here; SDK requires it
- updated_at is non-null here; SDK allows null
- subscription_items is non-null array here; SDK allows null
- next_payment optional shape matches, fine
Two safe paths:
Option A — Align with SDK shape (preferred for a public surface):
export interface CommerceSubscriptionJSON extends ClerkResourceJSON {
object: typeof ObjectType.CommerceSubscription;
- status: 'active' | 'past_due' | 'canceled' | 'ended' | 'abandoned' | 'incomplete';
- payer_id: string;
+ status: 'active' | 'past_due';
created_at: number;
- updated_at: number;
- active_at: number | null;
+ active_at: number;
+ updated_at: number | null;
past_due_at: number | null;
- subscription_items: CommerceSubscriptionItemJSON[];
+ subscription_items: CommerceSubscriptionItemJSON[] | null;
next_payment?: {
date: number;
amount: CommerceMoneyAmountJSON;
};
eligible_for_free_trial?: boolean;
}Additionally, update CommerceSubscription.fromJSON to handle possibly-null subscription_items and nullable updated_at; and drop payerId unless it exists at another level.
Option B — Keep extended backend shape but scope it:
- Rename to InternalCommerceSubscriptionJSON (or add @internal) and avoid exporting it via public index files.
- Add a mapper that produces the SDK-aligned shape for anything exposed publicly.
- Ensure BillingApi.getOrganizationBillingSubscription returns the class CommerceSubscription (stable surface), not this extended JSON.
Given the scope of impact, please choose one path before merging. I can provide a follow-up patch for either approach, including the mapper changes in CommerceSubscription.ts.
🏁 Script executed:
#!/bin/bash
set -e
# Locate json.ts in packages/types/src
json_file=$(fd -t f json.ts packages/types/src | head -n1)
echo "Using file: $json_file"
# Show the CommerceSubscriptionJSON interface and its key fields
rg -n 'interface CommerceSubscriptionJSON' -C10 "$json_file"
rg -n 'status' -C2 "$json_file"
rg -n 'payer_id' -C2 "$json_file"
rg -n 'active_at' -C2 "$json_file"
rg -n 'updated_at' -C2 "$json_file"
rg -n 'subscription_items' -C2 "$json_file"Length of output: 4023
🏁 Script executed:
#!/bin/bash
set -e
types_file="packages/types/src/json.ts"
echo "SDK JSON File: $types_file"
# Extract the CommerceSubscriptionJSON block
start=$(rg -n "export interface CommerceSubscriptionJSON" -n "$types_file" | cut -d: -f1)
# Capture through the matching closing brace (assuming no nested braces inside)
relative_end=$(tail -n +$start "$types_file" | sed -n '1,/^}/p' | tail -n1 | grep -n '^}' | cut -d: -f1)
end=$((start + relative_end - 1))
echo "--- CommerceSubscriptionJSON Definition ---"
sed -n "${start},${end}p" "$types_file"
echo
echo "--- Key Property Definitions ---"
echo "status:"
rg -n "status:" -C2 "$types_file" | sed -n '1,5p'
echo
echo "updated_at:"
rg -n "updated_at:" -C2 "$types_file" | sed -n '1,5p'
echo
echo "subscription_items:"
rg -n "subscription_items:" -C2 "$types_file" | sed -n '1,5p'Length of output: 1458
🏁 Script executed:
#!/bin/bash
set -e
# Print lines 970-1040 of the backend JSON file to inspect CommerceSubscriptionJSON
sed -n '970,1040p' packages/backend/src/api/resources/JSON.tsLength of output: 857
Public JSON contract mismatch in CommerceSubscriptionJSON (packages/backend/src/api/resources/JSON.ts)
The CommerceSubscriptionJSON defined in the backend package diverges from the SDK’s CommerceSubscriptionJSON in packages/types/src/json.ts. Left unaddressed, these differences will break downstream consumers:
• status
– Backend:
'active' | 'past_due' | 'canceled' | 'ended' | 'abandoned' | 'incomplete'
– SDK:
Extract<CommerceSubscriptionStatus, 'active' | 'past_due'> (only 'active' or 'past_due')
• payer_id
– Backend: payer_id: string present at top level
– SDK: no payer_id field
• active_at
– Backend: active_at: number | null
– SDK: active_at: number (non-nullable)
• updated_at
– Backend: updated_at: number (non-nullable)
– SDK: updated_at: number | null
• subscription_items
– Backend: subscription_items: CommerceSubscriptionItemJSON[] (non-nullable)
– SDK: subscription_items: CommerceSubscriptionItemJSON[] | null
Two paths forward:
-
Align backend JSON with SDK public shape (preferred for stability)
– Restrictstatusto'active' | 'past_due'
– Remove top-levelpayer_id
– Makeactive_at: number(non-nullable) andupdated_at: number | null
– Allowsubscription_items: CommerceSubscriptionItemJSON[] | null
– UpdateCommerceSubscription.fromJSONto handle nullable fields and droppayerId -
Scope extended backend shape as internal
– Rename toInternalCommerceSubscriptionJSON(or mark@internal) and don’t re-export it publicly
– Add a mapper inCommerceSubscription.tsthat converts the internal JSON into the SDK’s public shape
– Ensure public methods (e.g.BillingApi.getOrganizationBillingSubscription) return the publicCommerceSubscription
Given the high risk of breaking changes, please choose one approach before merging. I can follow up with a patch for either path.
Description
Adds serialization for
CommerceSubscriptionand updatesCommerceSubscriptionItemin order to addgetOrganizationBillingSubscriptionto BillingApi.Checklist
pnpm testruns as expected.pnpm buildruns as expected.Type of change
Summary by CodeRabbit