Skip to content

Commit 19e9e11

Browse files
authored
feat(clerk-js,types,localizations): Payment history tab (#6075)
1 parent 6e0f879 commit 19e9e11

24 files changed

+657
-51
lines changed

.changeset/brown-masks-admire.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@clerk/localizations': patch
3+
'@clerk/clerk-js': patch
4+
'@clerk/types': patch
5+
---
6+
7+
Add payment history tab to UserProfile and OrgProfile

packages/clerk-js/bundlewatch.config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
{ "path": "./dist/clerk.browser.js", "maxSize": "69KB" },
55
{ "path": "./dist/clerk.legacy.browser.js", "maxSize": "113KB" },
66
{ "path": "./dist/clerk.headless*.js", "maxSize": "52KB" },
7-
{ "path": "./dist/ui-common*.js", "maxSize": "106.1KB" },
7+
{ "path": "./dist/ui-common*.js", "maxSize": "106.3KB" },
88
{ "path": "./dist/vendors*.js", "maxSize": "39.8KB" },
99
{ "path": "./dist/coinbase*.js", "maxSize": "38KB" },
1010
{ "path": "./dist/createorganization*.js", "maxSize": "5KB" },

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ import type {
22
ClerkPaginatedResponse,
33
CommerceBillingNamespace,
44
CommerceCheckoutJSON,
5+
CommercePaymentJSON,
6+
CommercePaymentResource,
57
CommercePlanResource,
68
CommerceProductJSON,
79
CommerceStatementJSON,
810
CommerceStatementResource,
911
CommerceSubscriptionJSON,
1012
CommerceSubscriptionResource,
1113
CreateCheckoutParams,
14+
GetPaymentAttemptsParams,
1215
GetPlansParams,
1316
GetStatementsParams,
1417
GetSubscriptionsParams,
@@ -18,6 +21,7 @@ import { convertPageToOffsetSearchParams } from '../../../utils/convertPageToOff
1821
import {
1922
BaseResource,
2023
CommerceCheckout,
24+
CommercePayment,
2125
CommercePlan,
2226
CommerceStatement,
2327
CommerceSubscription,
@@ -73,6 +77,25 @@ export class CommerceBilling implements CommerceBillingNamespace {
7377
});
7478
};
7579

80+
getPaymentAttempts = async (
81+
params: GetPaymentAttemptsParams,
82+
): Promise<ClerkPaginatedResponse<CommercePaymentResource>> => {
83+
const { orgId, ...rest } = params;
84+
85+
return await BaseResource._fetch({
86+
path: orgId ? `/organizations/${orgId}/commerce/payment_attempts` : `/me/commerce/payment_attempts`,
87+
method: 'GET',
88+
search: convertPageToOffsetSearchParams(rest),
89+
}).then(res => {
90+
const { data: payments, total_count } = res as unknown as ClerkPaginatedResponse<CommercePaymentJSON>;
91+
92+
return {
93+
total_count,
94+
data: payments.map(payment => new CommercePayment(payment)),
95+
};
96+
});
97+
};
98+
7699
startCheckout = async (params: CreateCheckoutParams) => {
77100
const { orgId, ...rest } = params;
78101
const json = (
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type {
2+
CommerceMoney,
3+
CommercePaymentChargeType,
4+
CommercePaymentJSON,
5+
CommercePaymentResource,
6+
CommercePaymentStatus,
7+
} from '@clerk/types';
8+
9+
import { commerceMoneyFromJSON } from '../../utils';
10+
import { BaseResource, CommercePaymentSource, CommerceSubscription } from './internal';
11+
12+
export class CommercePayment extends BaseResource implements CommercePaymentResource {
13+
id!: string;
14+
amount!: CommerceMoney;
15+
failedAt?: number;
16+
paidAt?: number;
17+
updatedAt!: number;
18+
paymentSource!: CommercePaymentSource;
19+
subscription!: CommerceSubscription;
20+
subscriptionItem!: CommerceSubscription;
21+
chargeType!: CommercePaymentChargeType;
22+
status!: CommercePaymentStatus;
23+
24+
constructor(data: CommercePaymentJSON) {
25+
super();
26+
this.fromJSON(data);
27+
}
28+
29+
protected fromJSON(data: CommercePaymentJSON | null): this {
30+
if (!data) {
31+
return this;
32+
}
33+
34+
this.id = data.id;
35+
this.amount = commerceMoneyFromJSON(data.amount);
36+
this.paidAt = data.paid_at;
37+
this.failedAt = data.failed_at;
38+
this.updatedAt = data.updated_at;
39+
this.paymentSource = new CommercePaymentSource(data.payment_source);
40+
this.subscription = new CommerceSubscription(data.subscription);
41+
this.subscriptionItem = new CommerceSubscription(data.subscription_item);
42+
this.chargeType = data.charge_type;
43+
this.status = data.status;
44+
return this;
45+
}
46+
}

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

Lines changed: 2 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,13 @@
11
import type {
2-
CommerceMoney,
3-
CommercePaymentChargeType,
4-
CommercePaymentJSON,
5-
CommercePaymentStatus,
62
CommerceStatementGroupJSON,
73
CommerceStatementJSON,
84
CommerceStatementResource,
95
CommerceStatementStatus,
106
CommerceStatementTotals,
117
} from '@clerk/types';
128

13-
import { commerceMoneyFromJSON, commerceTotalsFromJSON } from '../../utils';
14-
import { BaseResource, CommercePaymentSource, CommerceSubscription } from './internal';
9+
import { commerceTotalsFromJSON } from '../../utils';
10+
import { BaseResource, CommercePayment } from './internal';
1511

1612
export class CommerceStatement extends BaseResource implements CommerceStatementResource {
1713
id!: string;
@@ -59,30 +55,3 @@ export class CommerceStatementGroup {
5955
return this;
6056
}
6157
}
62-
63-
export class CommercePayment {
64-
id!: string;
65-
amount!: CommerceMoney;
66-
paymentSource!: CommercePaymentSource;
67-
subscription!: CommerceSubscription;
68-
chargeType!: CommercePaymentChargeType;
69-
status!: CommercePaymentStatus;
70-
71-
constructor(data: CommercePaymentJSON) {
72-
this.fromJSON(data);
73-
}
74-
75-
protected fromJSON(data: CommercePaymentJSON | null): this {
76-
if (!data) {
77-
return this;
78-
}
79-
80-
this.id = data.id;
81-
this.amount = commerceMoneyFromJSON(data.amount);
82-
this.paymentSource = new CommercePaymentSource(data.payment_source);
83-
this.subscription = new CommerceSubscription(data.subscription);
84-
this.chargeType = data.charge_type;
85-
this.status = data.status;
86-
return this;
87-
}
88-
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export * from './Client';
77
export * from './CommerceCheckout';
88
export * from './CommerceFeature';
99
export * from './CommerceStatement';
10+
export * from './CommercePayment';
1011
export * from './CommercePaymentSource';
1112
export * from './CommercePlan';
1213
export * from './CommerceProduct';

packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationBillingPage.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@ import { Protect } from '../../common';
77
import { SubscriberTypeContext } from '../../contexts';
88
import { Col, descriptors, localizationKeys } from '../../customizables';
99
import { useTabState } from '../../hooks/useTabState';
10+
import { PaymentAttemptsList } from '../PaymentAttempts';
1011
import { PaymentSources } from '../PaymentSources';
1112
import { StatementsList } from '../Statements';
1213
import { SubscriptionsList } from '../Subscriptions';
1314

1415
const orgTabMap = {
15-
0: 'plans',
16+
0: 'subscriptions',
1617
1: 'statements',
17-
2: 'payment-methods',
18+
2: 'payments',
1819
} as const;
1920

2021
const OrganizationBillingPageInternal = withCardStateProvider(() => {
@@ -50,6 +51,7 @@ const OrganizationBillingPageInternal = withCardStateProvider(() => {
5051
localizationKey={localizationKeys('organizationProfile.billingPage.start.headerTitle__subscriptions')}
5152
/>
5253
<Tab localizationKey={localizationKeys('organizationProfile.billingPage.start.headerTitle__statements')} />
54+
<Tab localizationKey={localizationKeys('organizationProfile.billingPage.start.headerTitle__payments')} />
5355
</TabsList>
5456
<TabPanels>
5557
<TabPanel sx={{ width: '100%', flexDirection: 'column' }}>
@@ -69,6 +71,9 @@ const OrganizationBillingPageInternal = withCardStateProvider(() => {
6971
<TabPanel sx={{ width: '100%' }}>
7072
<StatementsList />
7173
</TabPanel>
74+
<TabPanel sx={{ width: '100%' }}>
75+
<PaymentAttemptsList />
76+
</TabPanel>
7277
</TabPanels>
7378
</Tabs>
7479
</Col>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { SubscriberTypeContext } from '../../contexts';
2+
import { PaymentAttemptPage } from '../PaymentAttempts';
3+
4+
export const OrganizationPaymentAttemptPage = () => {
5+
return (
6+
<SubscriberTypeContext.Provider value='org'>
7+
<PaymentAttemptPage />
8+
</SubscriberTypeContext.Provider>
9+
);
10+
};

packages/clerk-js/src/ui/components/OrganizationProfile/OrganizationProfileRoutes.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useEnvironment, useOrganizationProfileContext } from '../../contexts';
66
import { Route, Switch } from '../../router';
77
import { OrganizationGeneralPage } from './OrganizationGeneralPage';
88
import { OrganizationMembers } from './OrganizationMembers';
9+
import { OrganizationPaymentAttemptPage } from './OrganizationPaymentAttemptPage';
910
import { OrganizationPlansPage } from './OrganizationPlansPage';
1011
import { OrganizationStatementPage } from './OrganizationStatementPage';
1112

@@ -85,6 +86,12 @@ export const OrganizationProfileRoutes = () => {
8586
<OrganizationStatementPage />
8687
</Suspense>
8788
</Route>
89+
<Route path='payment-attempt/:paymentAttemptId'>
90+
{/* TODO(@commerce): Should this be lazy loaded ? */}
91+
<Suspense fallback={''}>
92+
<OrganizationPaymentAttemptPage />
93+
</Suspense>
94+
</Route>
8895
</Switch>
8996
</Route>
9097
</Protect>

0 commit comments

Comments
 (0)