Skip to content

feat(backend): Add cancelSubscriptionItem to BillingApi #6611

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/eight-sheep-matter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/backend': minor
---

[Billing Beta] Add `cancelSubscriptionItem` to BillingApi.
5 changes: 5 additions & 0 deletions .typedoc/__tests__/__snapshots__/file-structure.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,13 @@ exports[`Typedoc output > should have a deliberate file structure 1`] = `
"backend/auth-object.mdx",
"backend/authenticate-request-options.mdx",
"backend/client.mdx",
"backend/commerce-payment-attempt-webhook-event-json.mdx",
"backend/commerce-plan-json.mdx",
"backend/commerce-plan.mdx",
"backend/commerce-subscription-item-json.mdx",
"backend/commerce-subscription-item-webhook-event-json.mdx",
"backend/commerce-subscription-item.mdx",
"backend/commerce-subscription-webhook-event-json.mdx",
"backend/email-address.mdx",
"backend/external-account.mdx",
"backend/get-auth-fn.mdx",
Expand Down
22 changes: 22 additions & 0 deletions packages/backend/src/api/endpoints/BillingApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { ClerkPaginationRequest } from '@clerk/types';

import { joinPaths } from '../../util/path';
import type { CommercePlan } from '../resources/CommercePlan';
import type { CommerceSubscriptionItem } from '../resources/CommerceSubscriptionItem';
import type { PaginatedResourceResponse } from '../resources/Deserializer';
import { AbstractAPI } from './AbstractApi';

Expand All @@ -11,6 +12,14 @@ type GetOrganizationListParams = ClerkPaginationRequest<{
payerType: 'org' | 'user';
}>;

type CancelSubscriptionItemParams = {
/**
* If true, the subscription item will be canceled immediately. If false or undefined, the subscription item will be canceled at the end of the current billing period.
* @default undefined
*/
endNow?: boolean;
};

export class BillingAPI extends AbstractAPI {
/**
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
Expand All @@ -23,4 +32,17 @@ export class BillingAPI extends AbstractAPI {
queryParams: params,
});
}

/**
* @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 cancelSubscriptionItem(subscriptionItemId: string, params?: CancelSubscriptionItemParams) {
this.requireId(subscriptionItemId);
return this.request<CommerceSubscriptionItem>({
method: 'DELETE',
path: joinPaths(basePath, 'subscription_items', subscriptionItemId),
queryParams: params,
});
}
Comment on lines +36 to +47
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Explicit return type required for public API; also ensure endNow is serialized as end_now.

  • Per guidelines, public APIs must have explicit return types.
  • Unless the transport transforms query params, end_now is likely the server-expected shape. Pass a mapped object.

Apply this diff:

   /**
    * @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 cancelSubscriptionItem(subscriptionItemId: string, params?: CancelSubscriptionItemParams) {
+  public async cancelSubscriptionItem(
+    subscriptionItemId: string,
+    params?: CancelSubscriptionItemParams,
+  ): Promise<CommerceSubscriptionItem> {
     this.requireId(subscriptionItemId);
-    return this.request<CommerceSubscriptionItem>({
+    const queryParams =
+      params && typeof params.endNow !== 'undefined'
+        ? { end_now: params.endNow }
+        : undefined;
+    return this.request<CommerceSubscriptionItem>({
       method: 'DELETE',
       path: joinPaths(basePath, 'subscription_items', subscriptionItemId),
-      queryParams: params,
+      queryParams,
     });
   }

Optional: enrich JSDoc with param details and an example:

/**
 * Cancels a subscription item.
 * @param subscriptionItemId The subscription item id.
 * @param params Optional cancellation options. When `endNow` is true, cancellation is immediate; otherwise effective at period end.
 * @returns The updated CommerceSubscriptionItem.
 */

Also, please add tests that assert:

  • Path: /commerce/subscription_items/{id}
  • Query serialization: end_now=true|false
  • Response mapped to CommerceSubscriptionItem via Deserializer

I can provide a test stub using the existing request mock harness.

🤖 Prompt for AI Agents
In packages/backend/src/api/endpoints/BillingApi.ts around lines 36 to 47, the
public method cancelSubscriptionItem lacks an explicit return type and does not
serialize the params.endNow to the server-expected end_now query param; update
the method signature to declare an explicit return type
Promise<CommerceSubscriptionItem>, map params (e.g. const queryParams = params ?
{ end_now: params.endNow } : undefined) and pass that to request, and add the
suggested JSDoc block above the method; also add tests that assert the request
path is /commerce/subscription_items/{id}, that query serialization produces
end_now=true|false, and that the response is deserialized to
CommerceSubscriptionItem using the existing request mock harness.

}
2 changes: 1 addition & 1 deletion packages/backend/src/api/resources/CommercePlan.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Feature } from './Feature';
import type { CommercePlanJSON } from './JSON';

type CommerceMoneyAmount = {
export type CommerceMoneyAmount = {
amount: number;
amountFormatted: string;
currency: string;
Expand Down
99 changes: 99 additions & 0 deletions packages/backend/src/api/resources/CommerceSubscriptionItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import type { CommerceMoneyAmountJSON } from '@clerk/types';

import { type CommerceMoneyAmount, CommercePlan } from './CommercePlan';
import type { CommerceSubscriptionItemJSON } 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 CommerceSubscriptionItem {
constructor(
/**
* The unique identifier for the subscription item.
*/
readonly id: string,
/**
* The status of the subscription item.
*/
readonly status: CommerceSubscriptionItemJSON['status'],
/**
* The plan period for the subscription item.
*/
readonly planPeriod: 'month' | 'annual',
/**
* The start of the current period.
*/
readonly periodStart: number,
/**
* The next payment information.
*/
readonly nextPayment: {
amount: number;
date: number;
} | null,
/**
* The current amount for the subscription item.
*/
readonly amount: CommerceMoneyAmount | null | undefined,
/**
Comment on lines +36 to +39
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Narrow the amount type to match JSON (avoid undefined)

JSON shape defines amount: CommerceMoneyAmountJSON | null, not undefined. Allowing undefined here expands the public API unnecessarily and mismatches the deserializer contract.

Apply:

-    readonly amount: CommerceMoneyAmount | null | undefined,
+    readonly amount: CommerceMoneyAmount | null,
📝 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.

Suggested change
* The current amount for the subscription item.
*/
readonly amount: CommerceMoneyAmount | null | undefined,
/**
* The current amount for the subscription item.
*/
readonly amount: CommerceMoneyAmount | null,
/**
🤖 Prompt for AI Agents
In packages/backend/src/api/resources/CommerceSubscriptionItem.ts around lines
36–39, the `amount` field is typed as `CommerceMoneyAmount | null | undefined`,
which permits `undefined` but the JSON contract is `CommerceMoneyAmountJSON |
null`; remove `undefined` and narrow the type to match the JSON contract (i.e.,
use `CommerceMoneyAmountJSON | null`), and update any related
serializer/deserializer or callers to treat absent values as `null` per the JSON
shape.

* The plan associated with this subscription item.
*/
readonly plan: CommercePlan,
/**
* The plan ID.
*/
readonly planId: string,
/**
* The end of the current period.
*/
readonly periodEnd?: number,
/**
* When the subscription item was canceled.
*/
readonly canceledAt?: number,
/**
* When the subscription item became past due.
*/
readonly pastDueAt?: number,
/**
* The lifetime amount paid for this subscription item.
*/
readonly lifetimePaid?: CommerceMoneyAmount | null,
) {}
Comment on lines +60 to +63
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

lifetimePaid should be required and non-null to match JSON

CommerceSubscriptionItemJSON.lifetime_paid is required and non-null. Making lifetimePaid optional (and nullable) diverges and forces extra undefined/null checks downstream.

Apply:

-    readonly lifetimePaid?: CommerceMoneyAmount | null,
+    readonly lifetimePaid: CommerceMoneyAmount,
📝 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.

Suggested change
* The lifetime amount paid for this subscription item.
*/
readonly lifetimePaid?: CommerceMoneyAmount | null,
) {}
/**
* The lifetime amount paid for this subscription item.
*/
readonly lifetimePaid: CommerceMoneyAmount,
🤖 Prompt for AI Agents
In packages/backend/src/api/resources/CommerceSubscriptionItem.ts around lines
60 to 63, the lifetimePaid property is declared as optional and nullable but the
JSON model (CommerceSubscriptionItemJSON.lifetime_paid) is required and
non-null; change the class/property signature to make lifetimePaid required and
non-null (remove the "?" and "| null"), update any constructor parameters and
assignments to expect a non-null CommerceMoneyAmount, and adjust
parsing/serialization code to validate and map
CommerceSubscriptionItemJSON.lifetime_paid into this required property (throw or
surface an error if the JSON field is missing/null).


static fromJSON(data: CommerceSubscriptionItemJSON): CommerceSubscriptionItem {
function formatAmountJSON(
amount: CommerceMoneyAmountJSON | null | undefined,
): CommerceMoneyAmount | null | undefined;
function formatAmountJSON(
amount: CommerceMoneyAmountJSON | null | undefined,
): CommerceMoneyAmount | null | undefined {
if (!amount) {
return amount;
}

return {
amount: amount.amount,
amountFormatted: amount.amount_formatted,
currency: amount.currency,
currencySymbol: amount.currency_symbol,
};
}

Comment on lines +66 to +83
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Simplify and type the amount formatter overloads precisely

  • The current overload is redundant and permits undefined which the JSON never returns.
  • Tighten types and keep one public overload for nullability plus a concrete implementation.

Apply:

-    function formatAmountJSON(
-      amount: CommerceMoneyAmountJSON | null | undefined,
-    ): CommerceMoneyAmount | null | undefined;
-    function formatAmountJSON(
-      amount: CommerceMoneyAmountJSON | null | undefined,
-    ): CommerceMoneyAmount | null | undefined {
-      if (!amount) {
-        return amount;
-      }
-
-      return {
-        amount: amount.amount,
-        amountFormatted: amount.amount_formatted,
-        currency: amount.currency,
-        currencySymbol: amount.currency_symbol,
-      };
-    }
+    function formatAmountJSON(amount: CommerceMoneyAmountJSON): CommerceMoneyAmount;
+    function formatAmountJSON(amount: CommerceMoneyAmountJSON | null): CommerceMoneyAmount | null;
+    function formatAmountJSON(amount: CommerceMoneyAmountJSON | null): CommerceMoneyAmount | null {
+      if (amount === null) {
+        return null;
+      }
+      return {
+        amount: amount.amount,
+        amountFormatted: amount.amount_formatted,
+        currency: amount.currency,
+        currencySymbol: amount.currency_symbol,
+      };
+    }

And adjust the constructor call sites accordingly (type inference will keep lifetimePaid non-null while allowing amount to be null).

📝 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.

Suggested change
function formatAmountJSON(
amount: CommerceMoneyAmountJSON | null | undefined,
): CommerceMoneyAmount | null | undefined;
function formatAmountJSON(
amount: CommerceMoneyAmountJSON | null | undefined,
): CommerceMoneyAmount | null | undefined {
if (!amount) {
return amount;
}
return {
amount: amount.amount,
amountFormatted: amount.amount_formatted,
currency: amount.currency,
currencySymbol: amount.currency_symbol,
};
}
function formatAmountJSON(amount: CommerceMoneyAmountJSON): CommerceMoneyAmount;
function formatAmountJSON(amount: CommerceMoneyAmountJSON | null): CommerceMoneyAmount | null;
function formatAmountJSON(amount: CommerceMoneyAmountJSON | null): CommerceMoneyAmount | null {
if (amount === null) {
return null;
}
return {
amount: amount.amount,
amountFormatted: amount.amount_formatted,
currency: amount.currency,
currencySymbol: amount.currency_symbol,
};
}
🤖 Prompt for AI Agents
In packages/backend/src/api/resources/CommerceSubscriptionItem.ts around lines
66 to 83, the amount formatter currently has a redundant overload that allows
undefined (which the JSON never returns) and duplicates the signature; replace
both overloads with a single exported function signature that accepts
CommerceMoneyAmountJSON | null (no undefined) and returns CommerceMoneyAmount |
null, keep a single concrete implementation that handles null and maps amount,
amount_formatted, currency, currency_symbol to the camelCased fields, and then
update all constructor/call sites to pass non-undefined JSON (or null) so type
inference keeps fields like lifetimePaid non-null while still allowing amount to
be null.

return new CommerceSubscriptionItem(
data.id,
data.status,
data.plan_period,
data.period_start,
data.next_payment,
formatAmountJSON(data.amount),
CommercePlan.fromJSON(data.plan),
data.plan_id,
data.period_end,
data.canceled_at,
data.past_due_at,
formatAmountJSON(data.lifetime_paid),
);
}
}
3 changes: 3 additions & 0 deletions packages/backend/src/api/resources/Deserializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import {
} from '.';
import { AccountlessApplication } from './AccountlessApplication';
import { CommercePlan } from './CommercePlan';
import { CommerceSubscriptionItem } from './CommerceSubscriptionItem';
import { Feature } from './Feature';
import type { PaginatedResponseJSON } from './JSON';
import { ObjectType } from './JSON';
Expand Down Expand Up @@ -183,6 +184,8 @@ function jsonToObject(item: any): any {
return WaitlistEntry.fromJSON(item);
case ObjectType.CommercePlan:
return CommercePlan.fromJSON(item);
case ObjectType.CommerceSubscriptionItem:
return CommerceSubscriptionItem.fromJSON(item);
case ObjectType.Feature:
return Feature.fromJSON(item);
default:
Expand Down
51 changes: 46 additions & 5 deletions packages/backend/src/api/resources/JSON.ts
Original file line number Diff line number Diff line change
Expand Up @@ -843,9 +843,44 @@ export interface CommercePlanJSON extends ClerkResourceJSON {
features: FeatureJSON[];
}

type CommerceSubscriptionItemStatus =
| 'abandoned'
| 'active'
| 'canceled'
| 'ended'
| 'expired'
| 'incomplete'
| 'past_due'
| 'upcoming';

/**
* @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 interface CommerceSubscriptionItemJSON extends ClerkResourceJSON {
object: typeof ObjectType.CommerceSubscriptionItem;
status: 'abandoned' | 'active' | 'canceled' | 'ended' | 'expired' | 'incomplete' | 'past_due' | 'upcoming';
status: CommerceSubscriptionItemStatus;
plan_period: 'month' | 'annual';
period_start: number;
period_end?: number;
canceled_at?: number;
past_due_at?: number;
lifetime_paid: CommerceMoneyAmountJSON;
next_payment: {
amount: number;
date: number;
} | null;
amount: CommerceMoneyAmountJSON | null;
plan: CommercePlanJSON;
plan_id: string;
}

/**
* Webhooks specific interface for CommerceSubscriptionItem.
*/
export interface CommerceSubscriptionItemWebhookEventJSON extends ClerkResourceJSON {
object: typeof ObjectType.CommerceSubscriptionItem;
status: CommerceSubscriptionItemStatus;
credit: {
amount: CommerceMoneyAmountJSON;
cycle_days_remaining: number;
Expand Down Expand Up @@ -882,7 +917,10 @@ export interface CommerceSubscriptionItemJSON extends ClerkResourceJSON {
plan_id: string;
}

export interface CommercePaymentAttemptJSON extends ClerkResourceJSON {
/**
* Webhooks specific interface for CommercePaymentAttempt.
*/
export interface CommercePaymentAttemptWebhookEventJSON extends ClerkResourceJSON {
object: typeof ObjectType.CommercePaymentAttempt;
instance_id: string;
payment_id: string;
Expand Down Expand Up @@ -912,10 +950,13 @@ export interface CommercePaymentAttemptJSON extends ClerkResourceJSON {
card_type?: string;
last4?: string;
};
subscription_items: CommerceSubscriptionItemJSON[];
subscription_items: CommerceSubscriptionItemWebhookEventJSON[];
}

export interface CommerceSubscriptionJSON extends ClerkResourceJSON {
/**
* Webhooks specific interface for CommerceSubscription.
*/
export interface CommerceSubscriptionWebhookEventJSON extends ClerkResourceJSON {
object: typeof ObjectType.CommerceSubscription;
status: 'abandoned' | 'active' | 'canceled' | 'ended' | 'expired' | 'incomplete' | 'past_due' | 'upcoming';
active_at?: number;
Expand All @@ -928,7 +969,7 @@ export interface CommerceSubscriptionJSON extends ClerkResourceJSON {
payer_id: string;
payer: CommercePayerJSON;
payment_source_id: string;
items: CommerceSubscriptionItemJSON[];
items: CommerceSubscriptionItemWebhookEventJSON[];
}

export interface WebhooksSvixJSON {
Expand Down
12 changes: 6 additions & 6 deletions packages/backend/src/api/resources/Webhooks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type {
CommercePaymentAttemptJSON,
CommerceSubscriptionItemJSON,
CommerceSubscriptionJSON,
CommercePaymentAttemptWebhookEventJSON,
CommerceSubscriptionItemWebhookEventJSON,
CommerceSubscriptionWebhookEventJSON,
DeletedObjectJSON,
EmailJSON,
OrganizationDomainJSON,
Expand Down Expand Up @@ -67,12 +67,12 @@ export type WaitlistEntryWebhookEvent = Webhook<'waitlistEntry.created' | 'waitl

export type CommercePaymentAttemptWebhookEvent = Webhook<
'paymentAttempt.created' | 'paymentAttempt.updated',
CommercePaymentAttemptJSON
CommercePaymentAttemptWebhookEventJSON
>;

export type CommerceSubscriptionWebhookEvent = Webhook<
'subscription.created' | 'subscription.updated' | 'subscription.active' | 'subscription.past_due',
CommerceSubscriptionJSON
CommerceSubscriptionWebhookEventJSON
>;

export type CommerceSubscriptionItemWebhookEvent = Webhook<
Expand All @@ -85,7 +85,7 @@ export type CommerceSubscriptionItemWebhookEvent = Webhook<
| 'subscriptionItem.abandoned'
| 'subscriptionItem.incomplete'
| 'subscriptionItem.past_due',
CommerceSubscriptionItemJSON
CommerceSubscriptionItemWebhookEventJSON
>;

export type WebhookEvent =
Expand Down
1 change: 1 addition & 0 deletions packages/backend/src/api/resources/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export * from './Verification';
export * from './WaitlistEntry';
export * from './Web3Wallet';
export * from './CommercePlan';
export * from './CommerceSubscriptionItem';

export type {
EmailWebhookEvent,
Expand Down
7 changes: 4 additions & 3 deletions packages/backend/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,11 +100,8 @@ export type {
PaginatedResponseJSON,
TestingTokenJSON,
WebhooksSvixJSON,
CommercePayerJSON,
CommercePlanJSON,
CommerceSubscriptionItemJSON,
CommercePaymentAttemptJSON,
CommerceSubscriptionJSON,
} from './api/resources/JSON';

/**
Expand Down Expand Up @@ -147,6 +144,7 @@ export type {
User,
TestingToken,
CommercePlan,
CommerceSubscriptionItem,
} from './api/resources';

/**
Expand All @@ -166,6 +164,9 @@ export type {
WaitlistEntryWebhookEvent,
WebhookEvent,
WebhookEventType,
CommercePaymentAttemptWebhookEvent,
CommerceSubscriptionWebhookEvent,
CommerceSubscriptionItemWebhookEvent,
} from './api/resources/Webhooks';
Comment on lines +167 to 170
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Webhook event type re-exports: verify event set coverage and SemVer impact

  • Coverage: Do we intentionally omit a 'subscriptionItem.expired' event even though 'expired' exists in CommerceSubscriptionItemStatus? If 'expired' is a real webhook type, consider adding it to the union for completeness; if it’s not emitted, we’re good. Please confirm with BAPI/webhook docs.
  • SemVer: This PR also removes previously exported JSON types (e.g., CommerceSubscriptionJSON, CommercePaymentAttemptJSON, CommercePayerJSON) from the public surface. If those were published before, that’s a breaking change. Consider shipping deprecation aliases for one release cycle or bumping major.

Proposed non-breaking aliases (deprecate old names, point to new webhook-specific JSONs):

+/**
+ * @deprecated Use CommerceSubscriptionWebhookEventJSON instead. Will be removed in the next major.
+ */
+export type {
+  CommerceSubscriptionWebhookEventJSON as CommerceSubscriptionJSON,
+  CommercePaymentAttemptWebhookEventJSON as CommercePaymentAttemptJSON,
+} from './api/resources/JSON';
+
+/**
+ * @deprecated This type is still referenced by webhook JSON payloads, but we no longer recommend importing it from the backend package.
+ * Consider removing in the next major.
+ */
+export type { CommercePayerJSON } from './api/resources/JSON';

To check for potential breakages in consumers inside this repo:


🏁 Script executed:

#!/bin/bash
# Find usages of removed type names across the monorepo
rg -nP -C2 '\b(CommerceSubscriptionJSON|CommercePaymentAttemptJSON|CommercePayerJSON)\b'

Length of output: 5038


SemVer-breaking removal of JSON types – deprecate or bump major

Confirmed that the following JSON interfaces are still referenced across the monorepo (in packages/types, packages/backend, and packages/clerk-js), so removing their re-exports from the public surface will be a breaking change:

  • CommerceSubscriptionJSON (used to describe top-level subscriptions)
  • CommercePaymentAttemptJSON
  • CommercePayerJSON

Please address this before merging by either:

  • Shipping one release with deprecation aliases, pointing the old names to the new JSON types, then removing them in the next major; or
  • Bumping the major version immediately to acknowledge the breaking change.

Locations to update (in packages/backend/src/index.ts):

  • After the existing export { … } from './api/resources/Webhooks'; block

Suggested deprecation alias diff:

 // keep existing webhook re-exports
 export {
   CommercePaymentAttemptWebhookEvent,
   CommerceSubscriptionWebhookEvent,
   CommerceSubscriptionItemWebhookEvent,
 } from './api/resources/Webhooks';

+/**
+ * @deprecated Use CommerceSubscriptionWebhookEventJSON instead. Will be removed in the next major.
+ */
+export type {
+  CommerceSubscriptionWebhookEventJSON as CommerceSubscriptionJSON,
+  CommercePaymentAttemptWebhookEventJSON as CommercePaymentAttemptJSON,
+} from './api/resources/JSON';
+
+/**
+ * @deprecated Use CommercePayerWebhookEventJSON instead. Will be removed in the next major.
+ */
+export type { CommercePayerJSON } from './api/resources/JSON';

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In packages/backend/src/index.ts around lines 167 to 170, the public re-exports
removed the CommerceSubscriptionJSON, CommercePaymentAttemptJSON, and
CommercePayerJSON types which are still referenced elsewhere; restore
compatibility by adding deprecation aliases that re-export the old names
pointing to the new JSON types (add export statements for
CommerceSubscriptionJSON, CommercePaymentAttemptJSON, CommercePayerJSON that
forward to the current types and mark them with a deprecated JSDoc comment),
commit that change for a deprecation release, or alternatively, if you intend to
remove them now, bump the package major version to reflect the breaking change.


/**
Expand Down
Loading