Skip to content

Commit 377f67b

Browse files
authored
feat(clerk-js,types,shared): Infer resource in checkout.confirm() (#6642)
1 parent ee3c4a5 commit 377f67b

File tree

9 files changed

+213
-18
lines changed

9 files changed

+213
-18
lines changed

.changeset/proud-walls-travel.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
'@clerk/clerk-js': minor
3+
'@clerk/shared': minor
4+
'@clerk/types': minor
5+
---
6+
7+
[Billing Beta] `checkout.confirm()` now infers the resource id resulting in less repetition and improved DX.
8+
9+
After
10+
```tsx
11+
const checkout = Clerk.billing.startCheckout({orgId})
12+
checkout.confirm() // orgId is always implied
13+
```
14+
15+
Before
16+
```tsx
17+
const checkout = clerk.billing.startCheckout({orgId})
18+
checkout.confirm({orgId})
19+
```

.typedoc/__tests__/__snapshots__/file-structure.test.ts.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@ exports[`Typedoc output > should have a deliberate file structure 1`] = `
3232
"types/commerce-initialized-payment-source-resource.mdx",
3333
"types/commerce-money-amount-json.mdx",
3434
"types/commerce-money-amount.mdx",
35+
"types/commerce-payer-json.mdx",
3536
"types/commerce-payer-resource-type.mdx",
37+
"types/commerce-payer-resource.mdx",
3638
"types/commerce-payment-charge-type.mdx",
3739
"types/commerce-payment-json.mdx",
3840
"types/commerce-payment-resource.mdx",

packages/clerk-js/src/core/modules/commerce/CommerceBilling.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,6 @@ export class CommerceBilling implements CommerceBillingNamespace {
154154
})
155155
)?.response as unknown as CommerceCheckoutJSON;
156156

157-
return new CommerceCheckout(json, orgId);
157+
return new CommerceCheckout(json);
158158
};
159159
}

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

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import type {
33
CommerceCheckoutJSON,
44
CommerceCheckoutResource,
55
CommerceCheckoutTotals,
6+
CommercePayerResource,
67
CommerceSubscriptionPlanPeriod,
78
ConfirmCheckoutParams,
89
} from '@clerk/types';
910

1011
import { unixEpochToDate } from '@/utils/date';
1112

1213
import { commerceTotalsFromJSON } from '../../utils';
14+
import { CommercePayer } from './CommercePayer';
1315
import { BaseResource, CommercePaymentSource, CommercePlan, isClerkAPIResponseError } from './internal';
1416

1517
export class CommerceCheckout extends BaseResource implements CommerceCheckoutResource {
@@ -24,11 +26,11 @@ export class CommerceCheckout extends BaseResource implements CommerceCheckoutRe
2426
totals!: CommerceCheckoutTotals;
2527
isImmediatePlanChange!: boolean;
2628
freeTrialEndsAt!: Date | null;
29+
payer!: CommercePayerResource;
2730

28-
constructor(data: CommerceCheckoutJSON, orgId?: string) {
31+
constructor(data: CommerceCheckoutJSON) {
2932
super();
3033
this.fromJSON(data);
31-
this.pathRoot = orgId ? `/organizations/${orgId}/commerce/checkouts` : `/me/commerce/checkouts`;
3234
}
3335

3436
protected fromJSON(data: CommerceCheckoutJSON | null): this {
@@ -47,22 +49,21 @@ export class CommerceCheckout extends BaseResource implements CommerceCheckoutRe
4749
this.totals = commerceTotalsFromJSON(data.totals);
4850
this.isImmediatePlanChange = data.is_immediate_plan_change;
4951
this.freeTrialEndsAt = data.free_trial_ends_at ? unixEpochToDate(data.free_trial_ends_at) : null;
52+
this.payer = new CommercePayer(data.payer);
5053
return this;
5154
}
5255

5356
confirm = (params: ConfirmCheckoutParams): Promise<this> => {
54-
const { orgId, ...rest } = params;
55-
5657
// Retry confirmation in case of a 500 error
5758
// This will retry up to 3 times with an increasing delay
5859
// It retries at 2s, 4s, 6s and 8s
5960
return retry(
6061
() =>
6162
this._basePatch({
62-
path: orgId
63-
? `/organizations/${orgId}/commerce/checkouts/${this.id}/confirm`
63+
path: this.payer.organizationId
64+
? `/organizations/${this.payer.organizationId}/commerce/checkouts/${this.id}/confirm`
6465
: `/me/commerce/checkouts/${this.id}/confirm`,
65-
body: rest as any,
66+
body: params as any,
6667
}),
6768
{
6869
factor: 1.1,
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import type { CommercePayerJSON, CommercePayerResource } from '@clerk/types';
2+
3+
import { unixEpochToDate } from '@/utils/date';
4+
5+
import { BaseResource } from './internal';
6+
7+
export class CommercePayer extends BaseResource implements CommercePayerResource {
8+
id!: string;
9+
createdAt!: Date;
10+
updatedAt!: Date;
11+
imageUrl!: string | null;
12+
userId?: string;
13+
email?: string;
14+
firstName?: string;
15+
lastName?: string;
16+
organizationId?: string;
17+
organizationName?: string;
18+
19+
constructor(data: CommercePayerJSON) {
20+
super();
21+
this.fromJSON(data);
22+
}
23+
24+
protected fromJSON(data: CommercePayerJSON | null): this {
25+
if (!data) {
26+
return this;
27+
}
28+
29+
this.id = data.id;
30+
this.createdAt = unixEpochToDate(data.created_at);
31+
this.updatedAt = unixEpochToDate(data.updated_at);
32+
this.imageUrl = data.image_url;
33+
this.userId = data.user_id;
34+
this.email = data.email;
35+
this.firstName = data.first_name;
36+
this.lastName = data.last_name;
37+
this.organizationId = data.organization_id;
38+
this.organizationName = data.organization_name;
39+
return this;
40+
}
41+
}

packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { __experimental_useCheckout as useCheckout, useOrganization } from '@clerk/shared/react';
1+
import { __experimental_useCheckout as useCheckout } from '@clerk/shared/react';
22
import type { CommerceMoneyAmount, CommercePaymentSourceResource, ConfirmCheckoutParams } from '@clerk/types';
33
import { useMemo, useState } from 'react';
44

@@ -136,7 +136,6 @@ export const CheckoutForm = withCardStateProvider(() => {
136136
});
137137

138138
const useCheckoutMutations = () => {
139-
const { organization } = useOrganization();
140139
const { for: _for, onSubscriptionComplete } = useCheckoutContext();
141140
const { checkout } = useCheckout();
142141
const { id, confirm } = checkout;
@@ -150,11 +149,7 @@ const useCheckoutMutations = () => {
150149
card.setLoading();
151150
card.setError(undefined);
152151

153-
const { data, error } = await confirm({
154-
...params,
155-
// TODO(@COMMERCE): Come back to this, this should not be needed
156-
...(_for === 'organization' ? { orgId: organization?.id } : {}),
157-
});
152+
const { data, error } = await confirm(params);
158153

159154
if (error) {
160155
handleError(error, [], card.setError);

packages/shared/src/react/hooks/useCheckout.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export const useCheckout = (options?: Params): __experimental_UseCheckoutReturn
113113
plan: null,
114114
paymentSource: null,
115115
freeTrialEndsAt: null,
116+
payer: null,
116117
};
117118
}
118119
const {

packages/types/src/commerce.ts

Lines changed: 112 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1356,7 +1356,7 @@ export type CreateCheckoutParams = WithOptionalOrgType<{
13561356
* <ClerkProvider clerkJsVersion="x.x.x" />
13571357
* ```
13581358
*/
1359-
export type ConfirmCheckoutParams = WithOptionalOrgType<
1359+
export type ConfirmCheckoutParams =
13601360
| {
13611361
/**
13621362
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
@@ -1407,8 +1407,7 @@ export type ConfirmCheckoutParams = WithOptionalOrgType<
14071407
* ```
14081408
*/
14091409
useTestCard?: boolean;
1410-
}
1411-
>;
1410+
};
14121411

14131412
/**
14141413
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
@@ -1519,4 +1518,114 @@ export interface CommerceCheckoutResource extends ClerkResource {
15191518
* ```
15201519
*/
15211520
freeTrialEndsAt: Date | null;
1521+
/**
1522+
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
1523+
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
1524+
* @example
1525+
* ```tsx
1526+
* <ClerkProvider clerkJsVersion="x.x.x" />
1527+
* ```
1528+
*/
1529+
payer: CommercePayerResource;
1530+
}
1531+
1532+
/**
1533+
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
1534+
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
1535+
* @example
1536+
* ```tsx
1537+
* <ClerkProvider clerkJsVersion="x.x.x" />
1538+
* ```
1539+
*/
1540+
export interface CommercePayerResource extends ClerkResource {
1541+
/**
1542+
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
1543+
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
1544+
* @example
1545+
* ```tsx
1546+
* <ClerkProvider clerkJsVersion="x.x.x" />
1547+
* ```
1548+
*/
1549+
id: string;
1550+
/**
1551+
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
1552+
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
1553+
* @example
1554+
* ```tsx
1555+
* <ClerkProvider clerkJsVersion="x.x.x" />
1556+
* ```
1557+
*/
1558+
createdAt: Date;
1559+
/**
1560+
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
1561+
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
1562+
* @example
1563+
* ```tsx
1564+
* <ClerkProvider clerkJsVersion="x.x.x" />
1565+
* ```
1566+
*/
1567+
updatedAt: Date;
1568+
/**
1569+
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
1570+
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
1571+
* @example
1572+
* ```tsx
1573+
* <ClerkProvider clerkJsVersion="x.x.x" />
1574+
* ```
1575+
*/
1576+
imageUrl: string | null;
1577+
/**
1578+
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
1579+
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
1580+
* @example
1581+
* ```tsx
1582+
* <ClerkProvider clerkJsVersion="x.x.x" />
1583+
* ```
1584+
*/
1585+
userId?: string;
1586+
/**
1587+
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
1588+
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
1589+
* @example
1590+
* ```tsx
1591+
* <ClerkProvider clerkJsVersion="x.x.x" />
1592+
* ```
1593+
*/
1594+
email?: string;
1595+
/**
1596+
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
1597+
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
1598+
* @example
1599+
* ```tsx
1600+
* <ClerkProvider clerkJsVersion="x.x.x" />
1601+
* ```
1602+
*/
1603+
firstName?: string;
1604+
/**
1605+
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
1606+
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
1607+
* @example
1608+
* ```tsx
1609+
* <ClerkProvider clerkJsVersion="x.x.x" />
1610+
* ```
1611+
*/
1612+
lastName?: string;
1613+
/**
1614+
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
1615+
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
1616+
* @example
1617+
* ```tsx
1618+
* <ClerkProvider clerkJsVersion="x.x.x" />
1619+
* ```
1620+
*/
1621+
organizationId?: string;
1622+
/**
1623+
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
1624+
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
1625+
* @example
1626+
* ```tsx
1627+
* <ClerkProvider clerkJsVersion="x.x.x" />
1628+
* ```
1629+
*/
1630+
organizationName?: string;
15221631
}

packages/types/src/json.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -887,6 +887,33 @@ export interface CommerceCheckoutJSON extends ClerkResourceJSON {
887887
is_immediate_plan_change: boolean;
888888
// TODO(@COMMERCE): Remove optional after GA.
889889
free_trial_ends_at?: number | null;
890+
payer: CommercePayerJSON;
891+
}
892+
893+
/**
894+
* @experimental This is an experimental API for the Billing feature that is available under a public beta, and the API is subject to change.
895+
* It is advised to pin the SDK version and the clerk-js version to a specific version to avoid breaking changes.
896+
* @example
897+
* ```tsx
898+
* <ClerkProvider clerkJsVersion="x.x.x" />
899+
* ```
900+
*/
901+
export interface CommercePayerJSON extends ClerkResourceJSON {
902+
object: 'commerce_payer';
903+
id: string;
904+
created_at: number;
905+
updated_at: number;
906+
image_url: string | null;
907+
908+
// User attributes
909+
user_id?: string;
910+
email?: string;
911+
first_name?: string;
912+
last_name?: string;
913+
914+
// Organization attributes
915+
organization_id?: string;
916+
organization_name?: string;
890917
}
891918

892919
export interface ApiKeyJSON extends ClerkResourceJSON {

0 commit comments

Comments
 (0)