Skip to content
Merged
7 changes: 7 additions & 0 deletions .changeset/spicy-taxes-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@clerk/localizations': patch
'@clerk/clerk-js': patch
'@clerk/types': patch
---

Add payment source section to `UserProfile`
10 changes: 6 additions & 4 deletions packages/clerk-js/bundlewatch.config.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"files": [
{ "path": "./dist/clerk.js", "maxSize": "582.6kB" },
{ "path": "./dist/clerk.browser.js", "maxSize": "81kB" },
{ "path": "./dist/clerk.js", "maxSize": "584.6kB" },
{ "path": "./dist/clerk.browser.js", "maxSize": "81KB" },
{ "path": "./dist/clerk.headless*.js", "maxSize": "55KB" },
{ "path": "./dist/ui-common*.js", "maxSize": "96KB" },
{ "path": "./dist/vendors*.js", "maxSize": "30KB" },
Expand All @@ -19,9 +19,11 @@
{ "path": "./dist/onetap*.js", "maxSize": "1KB" },
{ "path": "./dist/waitlist*.js", "maxSize": "1.3KB" },
{ "path": "./dist/keylessPrompt*.js", "maxSize": "5.9KB" },
{ "path": "./dist/pricingTable*.js", "maxSize": "5.5KB" },
{ "path": "./dist/checkout*.js", "maxSize": "9KB" },
{ "path": "./dist/pricingTable*.js", "maxSize": "5KB" },
{ "path": "./dist/checkout*.js", "maxSize": "3KB" },
{ "path": "./dist/paymentSources*.js", "maxSize": "8KB" },
{ "path": "./dist/up-billing-page*.js", "maxSize": "1KB" },
{ "path": "./dist/op-billing-page*.js", "maxSize": "1KB" },
{ "path": "./dist/sessionTasks*.js", "maxSize": "1KB" }
]
}
6 changes: 3 additions & 3 deletions packages/clerk-js/rspack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,12 @@ const common = ({ mode, disableRHC = false }) => {
name: 'signup',
test: module => module.resource && module.resource.includes('/ui/components/SignUp'),
},
checkout: {
paymentSources: {
minChunks: 1,
name: 'checkout',
name: 'paymentSources',
test: module =>
module.resource &&
(module.resource.includes('/ui/components/Checkout') ||
(module.resource.includes('/ui/components/PaymentSources') ||
// Include `@stripe/react-stripe-js` and `@stripe/stripe-js` in the checkout chunk
module.resource.includes('/node_modules/@stripe')),
},
Expand Down
23 changes: 21 additions & 2 deletions packages/clerk-js/src/core/modules/commerce/Commerce.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
import type {
__experimental_AddPaymentSourceParams,
__experimental_CommerceBillingNamespace,
__experimental_CommerceInitializedPaymentSourceJSON,
__experimental_CommerceNamespace,
__experimental_CommercePaymentSourceJSON,
__experimental_GetPaymentSourcesParams,
__experimental_InitializePaymentSourceParams,
ClerkPaginatedResponse,
} from '@clerk/types';

import { __experimental_CommercePaymentSource, BaseResource } from '../../resources/internal';
import {
__experimental_CommerceInitializedPaymentSource,
__experimental_CommercePaymentSource,
BaseResource,
} from '../../resources/internal';
import { __experimental_CommerceBilling } from './CommerceBilling';

export class __experimental_Commerce implements __experimental_CommerceNamespace {
Expand All @@ -19,6 +26,17 @@ export class __experimental_Commerce implements __experimental_CommerceNamespace
return __experimental_Commerce._billing;
}

initializePaymentSource = async (params: __experimental_InitializePaymentSourceParams) => {
const json = (
await BaseResource._fetch({
path: `/me/commerce/payment_sources/initialize`,
method: 'POST',
body: params as any,
})
)?.response as unknown as __experimental_CommerceInitializedPaymentSourceJSON;
return new __experimental_CommerceInitializedPaymentSource(json);
};

addPaymentSource = async (params: __experimental_AddPaymentSourceParams) => {
const json = (
await BaseResource._fetch({
Expand All @@ -30,10 +48,11 @@ export class __experimental_Commerce implements __experimental_CommerceNamespace
return new __experimental_CommercePaymentSource(json);
};

getPaymentSources = async () => {
getPaymentSources = async (params: __experimental_GetPaymentSourcesParams) => {
return await BaseResource._fetch({
path: `/me/commerce/payment_sources`,
method: 'GET',
search: { orgId: params.orgId || '' },
}).then(res => {
const { data: paymentSources, total_count } =
res as unknown as ClerkPaginatedResponse<__experimental_CommercePaymentSourceJSON>;
Expand Down
44 changes: 43 additions & 1 deletion packages/clerk-js/src/core/resources/CommercePaymentSource.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import type {
__experimental_CommerceInitializedPaymentSourceJSON,
__experimental_CommerceInitializedPaymentSourceResource,
__experimental_CommercePaymentSourceJSON,
__experimental_CommercePaymentSourceResource,
__experimental_CommercePaymentSourceStatus,
DeletedObjectJSON,
} from '@clerk/types';

import { BaseResource } from './internal';
import { BaseResource, DeletedObject } from './internal';

export class __experimental_CommercePaymentSource
extends BaseResource
Expand All @@ -13,6 +17,8 @@ export class __experimental_CommercePaymentSource
last4!: string;
paymentMethod!: string;
cardType!: string;
isDefault!: boolean;
status!: __experimental_CommercePaymentSourceStatus;

constructor(data: __experimental_CommercePaymentSourceJSON) {
super();
Expand All @@ -28,6 +34,42 @@ export class __experimental_CommercePaymentSource
this.last4 = data.last4;
this.paymentMethod = data.payment_method;
this.cardType = data.card_type;
this.isDefault = false;
this.status = data.status;
return this;
}

public async remove() {
const json = (
await BaseResource._fetch({
path: `/me/commerce/payment_sources/${this.id}`,
method: 'DELETE',
})
)?.response as unknown as DeletedObjectJSON;

return new DeletedObject(json);
}
}

export class __experimental_CommerceInitializedPaymentSource
extends BaseResource
implements __experimental_CommerceInitializedPaymentSourceResource
{
externalClientSecret!: string;
externalGatewayId!: string;

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

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

this.externalClientSecret = data.external_client_secret;
this.externalGatewayId = data.external_gateway_id;

return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { Box, Button, descriptors, Heading, Icon, localizationKeys, Span, Text }
import { Drawer, LineItems } from '../../elements';
import { Check } from '../../icons';

const capitalize = (name: string) => name[0].toUpperCase() + name.slice(1);

export const CheckoutComplete = ({ checkout }: { checkout: __experimental_CommerceCheckoutResource }) => {
const { setIsOpen } = useCheckoutContext();

Expand Down Expand Up @@ -77,6 +79,7 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer
as='h2'
textVariant='h2'
>
{/* TODO(@COMMERCE): needs localization */}
Payment was successful!
</Heading>
<Text
Expand Down Expand Up @@ -113,7 +116,9 @@ export const CheckoutComplete = ({ checkout }: { checkout: __experimental_Commer
<LineItems.Title title='Payment method' />
<LineItems.Description
text={
checkout.paymentSource ? `${checkout.paymentSource.cardType} ⋯ ${checkout.paymentSource.last4}` : '–'
checkout.paymentSource
? `${capitalize(checkout.paymentSource.cardType)} ⋯ ${checkout.paymentSource.last4}`
: '–'
}
/>
</LineItems.Group>
Expand Down
Loading