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
19 changes: 19 additions & 0 deletions .changeset/proud-walls-travel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'@clerk/clerk-js': minor
'@clerk/shared': minor
'@clerk/types': minor
---

[Billing Beta] `checkout.confirm()` now infers the resource id resulting in less repetition and improved DX.

After
```tsx
const checkout = Clerk.billing.startCheckout({orgId})
checkout.confirm() // orgId is always implied
```

Before
```tsx
const checkout = clerk.billing.startCheckout({orgId})
checkout.confirm({orgId})
```
2 changes: 2 additions & 0 deletions .typedoc/__tests__/__snapshots__/file-structure.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ exports[`Typedoc output > should have a deliberate file structure 1`] = `
"types/commerce-initialized-payment-source-resource.mdx",
"types/commerce-money-amount-json.mdx",
"types/commerce-money-amount.mdx",
"types/commerce-payer-json.mdx",
"types/commerce-payer-resource-type.mdx",
"types/commerce-payer-resource.mdx",
"types/commerce-payment-charge-type.mdx",
"types/commerce-payment-json.mdx",
"types/commerce-payment-resource.mdx",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,6 @@ export class CommerceBilling implements CommerceBillingNamespace {
})
)?.response as unknown as CommerceCheckoutJSON;

return new CommerceCheckout(json, orgId);
return new CommerceCheckout(json);
};
}
15 changes: 8 additions & 7 deletions packages/clerk-js/src/core/resources/CommerceCheckout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ import type {
CommerceCheckoutJSON,
CommerceCheckoutResource,
CommerceCheckoutTotals,
CommercePayerResource,
CommerceSubscriptionPlanPeriod,
ConfirmCheckoutParams,
} from '@clerk/types';

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

import { commerceTotalsFromJSON } from '../../utils';
import { CommercePayer } from './CommercePayer';
import { BaseResource, CommercePaymentSource, CommercePlan, isClerkAPIResponseError } from './internal';

export class CommerceCheckout extends BaseResource implements CommerceCheckoutResource {
Expand All @@ -24,11 +26,11 @@ export class CommerceCheckout extends BaseResource implements CommerceCheckoutRe
totals!: CommerceCheckoutTotals;
isImmediatePlanChange!: boolean;
freeTrialEndsAt!: Date | null;
payer!: CommercePayerResource;

constructor(data: CommerceCheckoutJSON, orgId?: string) {
constructor(data: CommerceCheckoutJSON) {
super();
this.fromJSON(data);
this.pathRoot = orgId ? `/organizations/${orgId}/commerce/checkouts` : `/me/commerce/checkouts`;
}

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

confirm = (params: ConfirmCheckoutParams): Promise<this> => {
const { orgId, ...rest } = params;

// Retry confirmation in case of a 500 error
// This will retry up to 3 times with an increasing delay
// It retries at 2s, 4s, 6s and 8s
return retry(
() =>
this._basePatch({
path: orgId
? `/organizations/${orgId}/commerce/checkouts/${this.id}/confirm`
path: this.payer.organizationId
? `/organizations/${this.payer.organizationId}/commerce/checkouts/${this.id}/confirm`
: `/me/commerce/checkouts/${this.id}/confirm`,
body: rest as any,
body: params as any,
}),
{
factor: 1.1,
Expand Down
41 changes: 41 additions & 0 deletions packages/clerk-js/src/core/resources/CommercePayer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import type { CommercePayerJSON, CommercePayerResource } from '@clerk/types';

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

import { BaseResource } from './internal';

export class CommercePayer extends BaseResource implements CommercePayerResource {
id!: string;
createdAt!: Date;
updatedAt!: Date;
imageUrl!: string | null;
userId?: string;
email?: string;
firstName?: string;
lastName?: string;
organizationId?: string;
organizationName?: string;

constructor(data: CommercePayerJSON) {
super();
this.fromJSON(data);
}

protected fromJSON(data: CommercePayerJSON | null): this {
if (!data) {
return this;
}

this.id = data.id;
this.createdAt = unixEpochToDate(data.created_at);
this.updatedAt = unixEpochToDate(data.updated_at);
this.imageUrl = data.image_url;
this.userId = data.user_id;
this.email = data.email;
this.firstName = data.first_name;
this.lastName = data.last_name;
this.organizationId = data.organization_id;
this.organizationName = data.organization_name;
return this;
}
}
9 changes: 2 additions & 7 deletions packages/clerk-js/src/ui/components/Checkout/CheckoutForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { __experimental_useCheckout as useCheckout, useOrganization } from '@clerk/shared/react';
import { __experimental_useCheckout as useCheckout } from '@clerk/shared/react';
import type { CommerceMoneyAmount, CommercePaymentSourceResource, ConfirmCheckoutParams } from '@clerk/types';
import { useMemo, useState } from 'react';

Expand Down Expand Up @@ -136,7 +136,6 @@ export const CheckoutForm = withCardStateProvider(() => {
});

const useCheckoutMutations = () => {
const { organization } = useOrganization();
const { for: _for, onSubscriptionComplete } = useCheckoutContext();
const { checkout } = useCheckout();
const { id, confirm } = checkout;
Expand All @@ -150,11 +149,7 @@ const useCheckoutMutations = () => {
card.setLoading();
card.setError(undefined);

const { data, error } = await confirm({
...params,
// TODO(@COMMERCE): Come back to this, this should not be needed
...(_for === 'organization' ? { orgId: organization?.id } : {}),
});
const { data, error } = await confirm(params);

if (error) {
handleError(error, [], card.setError);
Expand Down
1 change: 1 addition & 0 deletions packages/shared/src/react/hooks/useCheckout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ export const useCheckout = (options?: Params): __experimental_UseCheckoutReturn
plan: null,
paymentSource: null,
freeTrialEndsAt: null,
payer: null,
};
}
const {
Expand Down
115 changes: 112 additions & 3 deletions packages/types/src/commerce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1356,7 +1356,7 @@ export type CreateCheckoutParams = WithOptionalOrgType<{
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
export type ConfirmCheckoutParams = WithOptionalOrgType<
export type ConfirmCheckoutParams =
| {
/**
* @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 Down Expand Up @@ -1407,8 +1407,7 @@ export type ConfirmCheckoutParams = WithOptionalOrgType<
* ```
*/
useTestCard?: boolean;
}
>;
};

/**
* @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 Down Expand Up @@ -1519,4 +1518,114 @@ export interface CommerceCheckoutResource extends ClerkResource {
* ```
*/
freeTrialEndsAt: Date | null;
/**
* @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 and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
payer: CommercePayerResource;
}

/**
* @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 and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
export interface CommercePayerResource extends ClerkResource {
/**
* @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 and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
id: string;
/**
* @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 and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
createdAt: Date;
/**
* @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 and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
updatedAt: Date;
/**
* @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 and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
imageUrl: string | null;
/**
* @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 and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
userId?: string;
/**
* @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 and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
email?: string;
/**
* @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 and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
firstName?: string;
/**
* @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 and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
lastName?: string;
/**
* @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 and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
organizationId?: string;
/**
* @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 and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
organizationName?: string;
}
27 changes: 27 additions & 0 deletions packages/types/src/json.ts
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,33 @@ export interface CommerceCheckoutJSON extends ClerkResourceJSON {
is_immediate_plan_change: boolean;
// TODO(@COMMERCE): Remove optional after GA.
free_trial_ends_at?: number | null;
payer: CommercePayerJSON;
}

/**
* @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 and the clerk-js version to a specific version to avoid breaking changes.
* @example
* ```tsx
* <ClerkProvider clerkJsVersion="x.x.x" />
* ```
*/
export interface CommercePayerJSON extends ClerkResourceJSON {
object: 'commerce_payer';
id: string;
created_at: number;
updated_at: number;
image_url: string | null;

// User attributes
user_id?: string;
email?: string;
first_name?: string;
last_name?: string;

// Organization attributes
organization_id?: string;
organization_name?: string;
}

export interface ApiKeyJSON extends ClerkResourceJSON {
Expand Down