Skip to content

Commit 09a0fda

Browse files
committed
feat(clerk-js,shared): Add parsing for SBB fields
1 parent b66eb8d commit 09a0fda

6 files changed

Lines changed: 222 additions & 0 deletions

File tree

.changeset/cute-ideas-appear.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@clerk/clerk-js': minor
3+
'@clerk/shared': minor
4+
---
5+
6+
Add support for parsing seat-based billing fields from FAPI.

packages/clerk-js/src/core/resources/BillingPlan.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {
33
BillingPayerResourceType,
44
BillingPlanJSON,
55
BillingPlanResource,
6+
BillingPlanUnitPrice,
67
} from '@clerk/shared/types';
78

89
import { billingMoneyAmountFromJSON } from '@/utils/billing';
@@ -24,6 +25,7 @@ export class BillingPlan extends BaseResource implements BillingPlanResource {
2425
slug!: string;
2526
avatarUrl: string | null = null;
2627
features!: Feature[];
28+
unitPrices?: BillingPlanUnitPrice[];
2729
freeTrialDays!: number | null;
2830
freeTrialEnabled!: boolean;
2931

@@ -53,6 +55,16 @@ export class BillingPlan extends BaseResource implements BillingPlanResource {
5355
this.freeTrialDays = this.withDefault(data.free_trial_days, null);
5456
this.freeTrialEnabled = this.withDefault(data.free_trial_enabled, false);
5557
this.features = (data.features || []).map(feature => new Feature(feature));
58+
this.unitPrices = data.unit_prices?.map(unitPrice => ({
59+
name: unitPrice.name,
60+
blockSize: unitPrice.block_size,
61+
tiers: unitPrice.tiers.map(tier => ({
62+
id: tier.id,
63+
startsAtBlock: tier.starts_at_block,
64+
endsAfterBlock: tier.ends_after_block,
65+
feePerBlock: billingMoneyAmountFromJSON(tier.fee_per_block),
66+
})),
67+
}));
5668

5769
return this;
5870
}

packages/clerk-js/src/core/resources/BillingSubscription.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type {
22
BillingMoneyAmount,
33
BillingSubscriptionItemJSON,
44
BillingSubscriptionItemResource,
5+
BillingSubscriptionItemSeats,
56
BillingSubscriptionJSON,
67
BillingSubscriptionPlanPeriod,
78
BillingSubscriptionResource,
@@ -75,6 +76,7 @@ export class BillingSubscriptionItem extends BaseResource implements BillingSubs
7576
credit?: {
7677
amount: BillingMoneyAmount;
7778
};
79+
seats?: BillingSubscriptionItemSeats;
7880
isFreeTrial!: boolean;
7981

8082
constructor(data: BillingSubscriptionItemJSON) {
@@ -102,6 +104,7 @@ export class BillingSubscriptionItem extends BaseResource implements BillingSubs
102104
this.amount = data.amount ? billingMoneyAmountFromJSON(data.amount) : undefined;
103105
this.credit =
104106
data.credit && data.credit.amount ? { amount: billingMoneyAmountFromJSON(data.credit.amount) } : undefined;
107+
this.seats = data.seats ? { quantity: data.seats.quantity } : undefined;
105108

106109
this.isFreeTrial = this.withDefault(data.is_free_trial, false);
107110
return this;

packages/clerk-js/src/utils/billing.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import type {
33
BillingCheckoutTotalsJSON,
44
BillingMoneyAmount,
55
BillingMoneyAmountJSON,
6+
BillingPerUnitTotal,
7+
BillingPerUnitTotalJSON,
68
BillingStatementTotals,
79
BillingStatementTotalsJSON,
810
} from '@clerk/shared/types';
@@ -16,6 +18,18 @@ export const billingMoneyAmountFromJSON = (data: BillingMoneyAmountJSON): Billin
1618
};
1719
};
1820

21+
const billingPerUnitTotalsFromJSON = (data: BillingPerUnitTotalJSON[]): BillingPerUnitTotal[] => {
22+
return data.map(unitTotal => ({
23+
name: unitTotal.name,
24+
blockSize: unitTotal.block_size,
25+
tiers: unitTotal.tiers.map(tier => ({
26+
quantity: tier.quantity,
27+
feePerBlock: billingMoneyAmountFromJSON(tier.fee_per_block),
28+
total: billingMoneyAmountFromJSON(tier.total),
29+
})),
30+
}));
31+
};
32+
1933
export const billingTotalsFromJSON = <T extends BillingStatementTotalsJSON | BillingCheckoutTotalsJSON>(
2034
data: T,
2135
): T extends { total_due_now: BillingMoneyAmountJSON } ? BillingCheckoutTotals : BillingStatementTotals => {
@@ -31,6 +45,9 @@ export const billingTotalsFromJSON = <T extends BillingStatementTotalsJSON | Bil
3145
if ('credit' in data) {
3246
totals.credit = data.credit ? billingMoneyAmountFromJSON(data.credit) : null;
3347
}
48+
if ('per_unit_totals' in data) {
49+
totals.perUnitTotals = data.per_unit_totals ? billingPerUnitTotalsFromJSON(data.per_unit_totals) : undefined;
50+
}
3451

3552
if ('total_due_now' in data) {
3653
totals.totalDueNow = billingMoneyAmountFromJSON(data.total_due_now);

packages/shared/src/types/billing.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,10 @@ export interface BillingPlanResource extends ClerkResource {
206206
* The Features the Plan offers.
207207
*/
208208
features: FeatureResource[];
209+
/**
210+
* Per-unit pricing tiers for this Plan (for example, seats).
211+
*/
212+
unitPrices?: BillingPlanUnitPrice[];
209213
/**
210214
* The number of days of the free trial for the Plan. `null` if the Plan does not have a free trial.
211215
*/
@@ -216,6 +220,102 @@ export interface BillingPlanResource extends ClerkResource {
216220
freeTrialEnabled: boolean;
217221
}
218222

223+
/**
224+
* The `BillingSubscriptionItemSeats` type represents seat entitlements attached to a subscription item.
225+
*
226+
* @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](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
227+
*/
228+
export interface BillingSubscriptionItemSeats {
229+
/**
230+
* The seat limit active while the parent subscription item was active. `null` means unlimited.
231+
*/
232+
quantity: number | null;
233+
}
234+
235+
/**
236+
* The `BillingPlanUnitPriceTier` type represents a single pricing tier for a unit type on a plan.
237+
*
238+
* @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](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
239+
*/
240+
export interface BillingPlanUnitPriceTier {
241+
/**
242+
* The unique identifier of the unit price tier.
243+
*/
244+
id: string;
245+
/**
246+
* The first block number this tier applies to.
247+
*/
248+
startsAtBlock: number;
249+
/**
250+
* The final block this tier applies to. `null` means unlimited.
251+
*/
252+
endsAfterBlock: number | null;
253+
/**
254+
* The fee charged for each block in this tier.
255+
*/
256+
feePerBlock: BillingMoneyAmount;
257+
}
258+
259+
/**
260+
* The `BillingPlanUnitPrice` type represents unit pricing for a specific unit type (for example, seats) on a plan.
261+
*
262+
* @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](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
263+
*/
264+
export interface BillingPlanUnitPrice {
265+
/**
266+
* The unit name, for example `seats`.
267+
*/
268+
name: string;
269+
/**
270+
* Number of units represented by one billable block.
271+
*/
272+
blockSize: number;
273+
/**
274+
* Tiers that define how each block range is priced.
275+
*/
276+
tiers: BillingPlanUnitPriceTier[];
277+
}
278+
279+
/**
280+
* The `BillingPerUnitTotalTier` type represents the cost breakdown for a single tier in checkout totals.
281+
*
282+
* @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](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
283+
*/
284+
export interface BillingPerUnitTotalTier {
285+
/**
286+
* The quantity billed within this tier. `null` means unlimited.
287+
*/
288+
quantity: number | null;
289+
/**
290+
* The fee charged per block for this tier.
291+
*/
292+
feePerBlock: BillingMoneyAmount;
293+
/**
294+
* The total billed amount for this tier.
295+
*/
296+
total: BillingMoneyAmount;
297+
}
298+
299+
/**
300+
* The `BillingPerUnitTotal` type represents the per-unit cost breakdown in checkout totals.
301+
*
302+
* @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](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
303+
*/
304+
export interface BillingPerUnitTotal {
305+
/**
306+
* The unit name, for example `seats`.
307+
*/
308+
name: string;
309+
/**
310+
* Number of units represented by one billable block.
311+
*/
312+
blockSize: number;
313+
/**
314+
* Detailed tier breakdown for this unit total.
315+
*/
316+
tiers: BillingPerUnitTotalTier[];
317+
}
318+
219319
/**
220320
* The `FeatureResource` type represents a Feature of a Plan.
221321
*
@@ -593,6 +693,11 @@ export interface BillingSubscriptionItemResource extends ClerkResource {
593693
*/
594694
amount: BillingMoneyAmount;
595695
};
696+
/**
697+
* Seat entitlement details for this subscription item. Only set for organization subscription items with
698+
* seat-based billing.
699+
*/
700+
seats?: BillingSubscriptionItemSeats;
596701
/**
597702
* A function to cancel the subscription item. Accepts the following parameters:
598703
* <ul>
@@ -708,6 +813,10 @@ export interface BillingCheckoutTotals {
708813
* The amount of tax included in the checkout.
709814
*/
710815
taxTotal: BillingMoneyAmount;
816+
/**
817+
* Per-unit cost breakdown for this checkout (for example, seats).
818+
*/
819+
perUnitTotals?: BillingPerUnitTotal[];
711820
/**
712821
* The amount that needs to be immediately paid to complete the checkout.
713822
*/

packages/shared/src/types/json.ts

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,68 @@ export interface FeatureJSON extends ClerkResourceJSON {
596596
avatar_url: string | null;
597597
}
598598

599+
/**
600+
* @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](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
601+
*/
602+
export interface BillingSubscriptionItemSeatsJSON {
603+
/**
604+
* The number of seats available. `null` means unlimited.
605+
*/
606+
quantity: number | null;
607+
}
608+
609+
/**
610+
* @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](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
611+
*
612+
* Represents a single pricing tier for a unit type on a plan.
613+
*/
614+
export interface BillingPlanUnitPriceTierJSON {
615+
id: string;
616+
object: 'commerce_unit_price';
617+
starts_at_block: number;
618+
/**
619+
* `null` means unlimited.
620+
*/
621+
ends_after_block: number | null;
622+
fee_per_block: BillingMoneyAmountJSON;
623+
}
624+
625+
/**
626+
* @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](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
627+
*
628+
* Represents unit pricing for a specific unit type (for example, seats) on a plan.
629+
*/
630+
export interface BillingPlanUnitPriceJSON {
631+
name: string;
632+
block_size: number;
633+
tiers: BillingPlanUnitPriceTierJSON[];
634+
}
635+
636+
/**
637+
* @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](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
638+
*
639+
* Represents the cost breakdown for a single tier in checkout totals.
640+
*/
641+
export interface BillingPerUnitTotalTierJSON {
642+
/**
643+
* `null` means unlimited.
644+
*/
645+
quantity: number | null;
646+
fee_per_block: BillingMoneyAmountJSON;
647+
total: BillingMoneyAmountJSON;
648+
}
649+
650+
/**
651+
* @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](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
652+
*
653+
* Represents the per-unit cost breakdown in checkout totals.
654+
*/
655+
export interface BillingPerUnitTotalJSON {
656+
name: string;
657+
block_size: number;
658+
tiers: BillingPerUnitTotalTierJSON[];
659+
}
660+
599661
/**
600662
* @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](https://clerk.com/docs/pinning) the SDK version and the clerk-js version to avoid breaking changes.
601663
*/
@@ -617,6 +679,10 @@ export interface BillingPlanJSON extends ClerkResourceJSON {
617679
features?: FeatureJSON[];
618680
free_trial_days?: number | null;
619681
free_trial_enabled?: boolean;
682+
/**
683+
* Per-unit pricing tiers for this plan (for example, seats).
684+
*/
685+
unit_prices?: BillingPlanUnitPriceJSON[];
620686
}
621687

622688
/**
@@ -695,6 +761,11 @@ export interface BillingSubscriptionItemJSON extends ClerkResourceJSON {
695761
credit?: {
696762
amount: BillingMoneyAmountJSON;
697763
};
764+
/**
765+
* Seat entitlement details for this subscription item. Only set for organization subscription items with
766+
* seat-based billing.
767+
*/
768+
seats?: BillingSubscriptionItemSeatsJSON;
698769
plan: BillingPlanJSON;
699770
plan_period: BillingSubscriptionPlanPeriod;
700771
status: BillingSubscriptionStatus;
@@ -751,6 +822,10 @@ export interface BillingCheckoutTotalsJSON {
751822
grand_total: BillingMoneyAmountJSON;
752823
subtotal: BillingMoneyAmountJSON;
753824
tax_total: BillingMoneyAmountJSON;
825+
/**
826+
* Per-unit cost breakdown for this checkout (for example, seats).
827+
*/
828+
per_unit_totals?: BillingPerUnitTotalJSON[];
754829
total_due_now: BillingMoneyAmountJSON;
755830
credit: BillingMoneyAmountJSON | null;
756831
past_due: BillingMoneyAmountJSON | null;

0 commit comments

Comments
 (0)