Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 470
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
08ef1220e94cc6da68f924082bf33321c50f6443b8File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| '@clerk/backend': minor | ||
| --- | ||
| Add `getOrganizationBillingSubscription` to BillingApi. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,79 @@ | ||
| import { type CommerceMoneyAmount } from './CommercePlan'; | ||
| import { CommerceSubscriptionItem } from './CommerceSubscriptionItem'; | ||
| import type { CommerceSubscriptionJSON } from './JSON'; | ||
| /** | ||
| * @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. | ||
| */ | ||
| export class CommerceSubscription { | ||
| constructor( | ||
| /** | ||
| * The unique identifier for the commerce subscription. | ||
| */ | ||
| readonly id: string, | ||
| /** | ||
| * The current status of the subscription. | ||
| */ | ||
| readonly status: CommerceSubscriptionJSON['status'], | ||
| /** | ||
| * The ID of the payer for this subscription. | ||
| */ | ||
| readonly payerId: string, | ||
| /** | ||
| * Unix timestamp (milliseconds) of creation. | ||
| */ | ||
| readonly createdAt: number, | ||
| /** | ||
| * Unix timestamp (milliseconds) of last update. | ||
| */ | ||
| readonly updatedAt: number, | ||
| /** | ||
| * Unix timestamp (milliseconds) when the subscription became active. | ||
| */ | ||
| readonly activeAt: number | null, | ||
| /** | ||
| * Unix timestamp (milliseconds) when the subscription became past due. | ||
| */ | ||
| readonly pastDueAt: number | null, | ||
| /** | ||
| * Array of subscription items in this subscription. | ||
| */ | ||
| readonly subscriptionItems: CommerceSubscriptionItem[], | ||
| /** | ||
| * Information about the next scheduled payment. | ||
| */ | ||
| readonly nextPayment: { date: number; amount: CommerceMoneyAmount } | null, | ||
| /** | ||
| * Whether the payer is eligible for a free trial. | ||
| */ | ||
| readonly eligibleForFreeTrial: boolean, | ||
| ) {} | ||
| static fromJSON(data: CommerceSubscriptionJSON): CommerceSubscription { | ||
| 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; | ||
| return new CommerceSubscription( | ||
| data.id, | ||
| data.status, | ||
| data.payer_id, | ||
| data.created_at, | ||
| data.updated_at, | ||
| data.active_at ?? null, | ||
| data.past_due_at ?? null, | ||
| data.subscription_items.map(item => CommerceSubscriptionItem.fromJSON(item)), | ||
| nextPayment, | ||
| data.eligible_for_free_trial ?? false, | ||
| ); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -861,10 +861,15 @@ export interface CommerceSubscriptionItemJSON extends ClerkResourceJSON { | ||||||||||||||||||||||
| object: typeof ObjectType.CommerceSubscriptionItem; | ||||||||||||||||||||||
| status: CommerceSubscriptionItemStatus; | ||||||||||||||||||||||
| plan_period: 'month' | 'annual'; | ||||||||||||||||||||||
| payer_id: string; | ||||||||||||||||||||||
| period_start: number; | ||||||||||||||||||||||
| period_end?: number; | ||||||||||||||||||||||
| canceled_at?: number; | ||||||||||||||||||||||
| past_due_at?: number; | ||||||||||||||||||||||
| period_end: number | null; | ||||||||||||||||||||||
| is_free_trial?: boolean; | ||||||||||||||||||||||
| ended_at: number | null; | ||||||||||||||||||||||
| created_at: number; | ||||||||||||||||||||||
| updated_at: number; | ||||||||||||||||||||||
| canceled_at: number | null; | ||||||||||||||||||||||
| past_due_at: number | null; | ||||||||||||||||||||||
| lifetime_paid: CommerceMoneyAmountJSON; | ||||||||||||||||||||||
| next_payment: { | ||||||||||||||||||||||
| amount: number; | ||||||||||||||||||||||
| @@ -972,6 +977,22 @@ export interface CommerceSubscriptionWebhookEventJSON extends ClerkResourceJSON | ||||||||||||||||||||||
| items: CommerceSubscriptionItemWebhookEventJSON[]; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
| export interface CommerceSubscriptionJSON extends ClerkResourceJSON { | ||||||||||||||||||||||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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
Suggested change
🤖 Prompt for AI Agents | ||||||||||||||||||||||
| 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; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
Comment on lines
+980
to
+994
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainPublic contract mismatch with packages/types: high risk of consumer breakage This CommerceSubscriptionJSON differs from packages/types/src/json.ts in multiple ways:
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:
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/bashset -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/bashset -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"echoecho"--- Key Property Definitions ---"echo"status:"
rg -n "status:" -C2 "$types_file"| sed -n '1,5p'echoecho"updated_at:"
rg -n "updated_at:" -C2 "$types_file"| sed -n '1,5p'echoecho"subscription_items:"
rg -n "subscription_items:" -C2 "$types_file"| sed -n '1,5p'Length of output: 1458 🏁 Script executed: #!/bin/bashset -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 • status Two paths forward:
Given the high risk of breaking changes, please choose one approach before merging. I can follow up with a patch for either path. | ||||||||||||||||||||||
| export interface WebhooksSvixJSON { | ||||||||||||||||||||||
| svix_url: string; | ||||||||||||||||||||||
| } | ||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
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:
I can add an integration test stub that mocks
AbstractAPI.requestto verify the shape and deserialization path—want me to push that?🏁 Script executed:
Length of output: 11395
Add explicit return type and complete JSDoc for
getOrganizationBillingSubscriptionPublic 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:• Please also verify that the
billing/subscriptionendpoint 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.requestto validate the response shape and deserialization path.📝 Committable suggestion
🤖 Prompt for AI Agents