Skip to content
Merged
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/public-goats-sort.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@clerk/backend': minor
---

Extend the trial of a subscription item via the BillingAPI.
24 changes: 24 additions & 0 deletions packages/backend/src/api/endpoints/BillingApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ type CancelSubscriptionItemParams = {
endNow?: boolean;
};

type ExtendSubscriptionItemFreeTrialParams = {
/**
* RFC3339 timestamp to extend the free trial to.
* Must be in the future and not more than 365 days from the current trial end.
*/
extendTo: Date;
};
Comment on lines +26 to +32
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

Export the public params type

This type appears in a public method signature; make it exported to avoid d.ts naming issues and to enable reuse.

Apply this diff:

-type ExtendSubscriptionItemFreeTrialParams = {
+export type ExtendSubscriptionItemFreeTrialParams = {
   /**
    * RFC3339 timestamp to extend the free trial to.
    * Must be in the future and not more than 365 days from the current trial end.
    */
   extendTo: Date;
 };
📝 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
type ExtendSubscriptionItemFreeTrialParams = {
/**
* RFC3339 timestamp to extend the free trial to.
* Must be in the future and not more than 365 days from the current trial end.
*/
extendTo: Date;
};
export type ExtendSubscriptionItemFreeTrialParams = {
/**
* RFC3339 timestamp to extend the free trial to.
* Must be in the future and not more than 365 days from the current trial end.
*/
extendTo: Date;
};
🤖 Prompt for AI Agents
In packages/backend/src/api/endpoints/BillingApi.ts around lines 26 to 32, the
ExtendSubscriptionItemFreeTrialParams type is used in a public method signature
but is declared without export; change its declaration to export the type
(export type ExtendSubscriptionItemFreeTrialParams = { extendTo: Date; }) so it
is included in emitted d.ts files and can be reused externally, and update any
local references/imports if necessary.


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 @@ -49,6 +57,22 @@ 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.
* It is advised to pin the SDK version to avoid breaking changes.
*/
public async extendSubscriptionItemFreeTrial(
subscriptionItemId: string,
params: ExtendSubscriptionItemFreeTrialParams,
) {
this.requireId(subscriptionItemId);
return this.request<CommerceSubscriptionItem>({
method: 'POST',
path: joinPaths('/billing', 'subscription_items', subscriptionItemId, 'extend_free_trial'),
bodyParams: params,
});
}

Comment on lines +64 to +75
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

Add explicit return type; ensure RFC3339 serialization; avoid hardcoded '/billing'

  • Public API should declare return type.
  • Serialize Date to RFC3339 explicitly to avoid transport ambiguity.
  • Use a billingBasePath constant for consistency with basePath.

Apply this diff within the shown lines:

-  public async extendSubscriptionItemFreeTrial(
+  public async extendSubscriptionItemFreeTrial(
     subscriptionItemId: string,
     params: ExtendSubscriptionItemFreeTrialParams,
-  ) {
+  ): Promise<CommerceSubscriptionItem> {
     this.requireId(subscriptionItemId);
     return this.request<CommerceSubscriptionItem>({
       method: 'POST',
-      path: joinPaths('/billing', 'subscription_items', subscriptionItemId, 'extend_free_trial'),
-      bodyParams: params,
+      path: joinPaths(billingBasePath, 'subscription_items', subscriptionItemId, 'extend_free_trial'),
+      bodyParams: {
+        // Ensure RFC3339 payload
+        extendTo: params.extendTo.toISOString(),
+      },
     });
   }

And add this near the other base path constants:

const billingBasePath = '/billing';

Verification script to confirm serializer expectations and path correctness:


🏁 Script executed:

#!/bin/bash
# Inspect request serialization to confirm Date handling and key-casing.
fd 'AbstractApi.ts' -t f
if [[ -f packages/backend/src/api/endpoints/AbstractApi.ts ]]; then
  sed -n '1,220p' packages/backend/src/api/endpoints/AbstractApi.ts | sed -n '1,220p'
fi
rg -nP -C3 --type=ts 'bodyParams:\s*{[^}]*toISOString\('
rg -nP -C3 --type=ts "joinPaths\('/billing'|billingBasePath" packages/backend
# Check if there is a params key transform (camelCase -> snake_case)
rg -nP -C2 --type=ts 'snake|camel.*case|serialize.*params|toSnake' packages

Length of output: 12970


Declare explicit return type, billingBasePath constant, and enable deepSnakecaseBodyParamKeys

  • Change signature to ): Promise<CommerceSubscriptionItem>.
  • Add const billingBasePath = '/billing'; alongside other base‐path constants.
  • Replace path: joinPaths('/billing', …) with joinPaths(billingBasePath, …).
  • Pass options: { deepSnakecaseBodyParamKeys: true } on the request call so keys are snake_cased and JSON.stringify emits dates as RFC3339.
🤖 Prompt for AI Agents
packages/backend/src/api/endpoints/BillingApi.ts around lines 64 to 75: the
method signature and request call need updates — change the method return type
to Promise<CommerceSubscriptionItem>, add a billingBasePath constant alongside
other base-path constants and use it in joinPaths instead of the string literal
'/billing', and pass options: { deepSnakecaseBodyParamKeys: true } into the
request call so body keys are deep-snake_cased and dates are serialized as
RFC3339.

/**
* @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.
Expand Down
Loading