payment-kit 1.18.12 → 1.18.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/api/src/libs/notification/template/one-time-payment-succeeded.ts +5 -3
- package/api/src/libs/notification/template/subscription-canceled.ts +3 -3
- package/api/src/libs/notification/template/subscription-refund-succeeded.ts +4 -3
- package/api/src/libs/notification/template/subscription-renew-failed.ts +5 -4
- package/api/src/libs/notification/template/subscription-renewed.ts +2 -1
- package/api/src/libs/notification/template/subscription-stake-slash-succeeded.ts +3 -4
- package/api/src/libs/notification/template/subscription-succeeded.ts +2 -1
- package/api/src/libs/notification/template/subscription-upgraded.ts +6 -4
- package/api/src/libs/notification/template/subscription-will-canceled.ts +6 -3
- package/api/src/libs/notification/template/subscription-will-renew.ts +1 -1
- package/api/src/routes/customers.ts +79 -5
- package/api/src/routes/subscriptions.ts +13 -1
- package/api/src/store/models/invoice.ts +4 -2
- package/blocklet.yml +2 -2
- package/package.json +4 -4
- package/src/app.tsx +17 -17
- package/src/components/actions.tsx +32 -9
- package/src/components/copyable.tsx +2 -2
- package/src/components/layout/user.tsx +37 -0
- package/src/components/subscription/portal/actions.tsx +26 -5
- package/src/components/subscription/portal/list.tsx +24 -6
- package/src/components/subscription/status.tsx +2 -2
- package/src/libs/util.ts +15 -0
- package/src/pages/admin/payments/payouts/detail.tsx +6 -1
- package/src/pages/customer/index.tsx +247 -154
- package/src/pages/customer/invoice/detail.tsx +1 -1
- package/src/pages/customer/payout/detail.tsx +9 -2
- package/src/pages/customer/recharge.tsx +6 -2
- package/src/pages/customer/subscription/change-payment.tsx +1 -1
- package/src/pages/customer/subscription/change-plan.tsx +1 -1
- package/src/pages/customer/subscription/detail.tsx +8 -3
- package/src/pages/customer/subscription/embed.tsx +142 -84
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
getPrefix,
|
|
10
10
|
getSubscriptionAction,
|
|
11
11
|
usePaymentContext,
|
|
12
|
+
OverdueInvoicePayment,
|
|
12
13
|
} from '@blocklet/payment-react';
|
|
13
14
|
import type { TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
14
15
|
import { Button, Link, Stack, Tooltip } from '@mui/material';
|
|
@@ -22,6 +23,7 @@ import CustomerCancelForm from './cancel';
|
|
|
22
23
|
import OverdraftProtectionDialog from '../../customer/overdraft-protection';
|
|
23
24
|
import Actions from '../../actions';
|
|
24
25
|
import { useUnpaidInvoicesCheckForSubscription } from '../../../hooks/subscription';
|
|
26
|
+
import { isWillCanceled } from '../../../libs/util';
|
|
25
27
|
|
|
26
28
|
interface ActionConfig {
|
|
27
29
|
key: string;
|
|
@@ -154,6 +156,7 @@ export function SubscriptionActionsInner({
|
|
|
154
156
|
openProtection: false,
|
|
155
157
|
protectionLoading: false,
|
|
156
158
|
protectionInitValues: null,
|
|
159
|
+
batchPay: false,
|
|
157
160
|
});
|
|
158
161
|
|
|
159
162
|
const shouldFetchDelegation = showDelegation && ['active', 'trialing', 'past_due'].includes(subscription?.status);
|
|
@@ -320,7 +323,7 @@ export function SubscriptionActionsInner({
|
|
|
320
323
|
const renderActions = () => {
|
|
321
324
|
const supportUnsubscribe = action?.action === 'cancel' && showUnsubscribe;
|
|
322
325
|
const supportAction = action && (action?.action !== 'cancel' || supportUnsubscribe);
|
|
323
|
-
|
|
326
|
+
const supportResume = isWillCanceled(subscription) && action?.action === 'recover';
|
|
324
327
|
const serviceActions = subscription.service_actions?.filter((x: any) => x?.type !== 'notification') || [];
|
|
325
328
|
const actionConfigs: ActionConfig[] = [
|
|
326
329
|
{
|
|
@@ -351,7 +354,7 @@ export function SubscriptionActionsInner({
|
|
|
351
354
|
},
|
|
352
355
|
variant: 'outlined',
|
|
353
356
|
color: 'primary',
|
|
354
|
-
primary:
|
|
357
|
+
primary: !isWillCanceled(subscription),
|
|
355
358
|
},
|
|
356
359
|
{
|
|
357
360
|
key: 'changePlan',
|
|
@@ -371,7 +374,9 @@ export function SubscriptionActionsInner({
|
|
|
371
374
|
label: action?.text || t('admin.subscription.batchPay.button'),
|
|
372
375
|
onClick: (e) => {
|
|
373
376
|
e?.stopPropagation();
|
|
374
|
-
|
|
377
|
+
setState({
|
|
378
|
+
batchPay: true,
|
|
379
|
+
});
|
|
375
380
|
},
|
|
376
381
|
variant: 'outlined',
|
|
377
382
|
color: 'error',
|
|
@@ -397,6 +402,7 @@ export function SubscriptionActionsInner({
|
|
|
397
402
|
color: action?.color || 'primary',
|
|
398
403
|
sx: action?.sx,
|
|
399
404
|
divider: serviceActions.length > 0,
|
|
405
|
+
primary: supportResume,
|
|
400
406
|
},
|
|
401
407
|
// @ts-ignore
|
|
402
408
|
...serviceActions.map((x) => ({
|
|
@@ -445,7 +451,7 @@ export function SubscriptionActionsInner({
|
|
|
445
451
|
</Button>
|
|
446
452
|
);
|
|
447
453
|
if (mode === 'menu-only') {
|
|
448
|
-
return <Actions actions={visibleActions.map(toMenuItem)} />;
|
|
454
|
+
return <Actions actions={visibleActions.map(toMenuItem)} variant="outlined" />;
|
|
449
455
|
}
|
|
450
456
|
|
|
451
457
|
if (mode === 'primary-buttons') {
|
|
@@ -454,7 +460,7 @@ export function SubscriptionActionsInner({
|
|
|
454
460
|
return (
|
|
455
461
|
<>
|
|
456
462
|
{primaryButtons.map(toButton)}
|
|
457
|
-
{menuItems.length > 0 && <Actions actions={menuItems.map(toMenuItem)} />}
|
|
463
|
+
{menuItems.length > 0 && <Actions actions={menuItems.map(toMenuItem)} variant="outlined" />}
|
|
458
464
|
</>
|
|
459
465
|
);
|
|
460
466
|
}
|
|
@@ -506,6 +512,21 @@ export function SubscriptionActionsInner({
|
|
|
506
512
|
initValues={state.protectionInitValues}
|
|
507
513
|
/>
|
|
508
514
|
)}
|
|
515
|
+
|
|
516
|
+
{state.batchPay && (
|
|
517
|
+
<OverdueInvoicePayment
|
|
518
|
+
subscriptionId={subscription.id}
|
|
519
|
+
onPaid={() => {
|
|
520
|
+
setState({ batchPay: false });
|
|
521
|
+
onChange?.('batch-pay');
|
|
522
|
+
}}
|
|
523
|
+
inSubscriptionDetail
|
|
524
|
+
dialogProps={{
|
|
525
|
+
open: state.batchPay,
|
|
526
|
+
onClose: () => setState({ batchPay: false }),
|
|
527
|
+
}}
|
|
528
|
+
/>
|
|
529
|
+
)}
|
|
509
530
|
</Stack>
|
|
510
531
|
);
|
|
511
532
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
3
|
import Empty from '@arcblock/ux/lib/Empty';
|
|
4
4
|
import { api, formatPrice, getSubscriptionTimeSummary, useMobile } from '@blocklet/payment-react';
|
|
5
|
-
import type {
|
|
5
|
+
import type { TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
6
6
|
import { Avatar, AvatarGroup, Box, Button, CircularProgress, Stack, StackProps, Typography } from '@mui/material';
|
|
7
7
|
import { useInfiniteScroll } from 'ahooks';
|
|
8
8
|
|
|
@@ -11,8 +11,16 @@ import SubscriptionDescription from '../description';
|
|
|
11
11
|
import SubscriptionActions from './actions';
|
|
12
12
|
import SubscriptionStatus from '../status';
|
|
13
13
|
import useDelayedLoading from '../../../hooks/loading';
|
|
14
|
+
import { isWillCanceled } from '../../../libs/util';
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
type SubscriptionListResponse = {
|
|
17
|
+
count: number;
|
|
18
|
+
list: TSubscriptionExpanded[];
|
|
19
|
+
paging: { page: number; pageSize: number };
|
|
20
|
+
totalCount: number;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const fetchData = (params: Record<string, any> = {}): Promise<SubscriptionListResponse> => {
|
|
16
24
|
const search = new URLSearchParams();
|
|
17
25
|
Object.keys(params).forEach((key) => {
|
|
18
26
|
search.set(key, String(params[key]));
|
|
@@ -27,6 +35,7 @@ type Props = {
|
|
|
27
35
|
onClickSubscription: (subscription: TSubscriptionExpanded) => void | Promise<void>;
|
|
28
36
|
onlyActive?: boolean;
|
|
29
37
|
changeActive?: (active: boolean) => void;
|
|
38
|
+
setStatusState?: (state: boolean) => void;
|
|
30
39
|
} & Omit<StackProps, 'onChange'>;
|
|
31
40
|
|
|
32
41
|
const pageSize = 5;
|
|
@@ -38,19 +47,25 @@ export default function CurrentSubscriptions({
|
|
|
38
47
|
onClickSubscription,
|
|
39
48
|
onlyActive,
|
|
40
49
|
changeActive = () => {},
|
|
50
|
+
setStatusState = () => {},
|
|
41
51
|
...rest
|
|
42
52
|
}: Props) {
|
|
43
53
|
const { t } = useLocaleContext();
|
|
44
54
|
const { isMobile } = useMobile();
|
|
45
55
|
const listRef = useRef<HTMLDivElement | null>(null);
|
|
46
56
|
|
|
47
|
-
const { data, loadMore, loadingMore, loading, reload } = useInfiniteScroll<
|
|
57
|
+
const { data, loadMore, loadingMore, loading, reload } = useInfiniteScroll<SubscriptionListResponse>(
|
|
48
58
|
(d) => {
|
|
49
59
|
const page = d ? Math.ceil(d.list.length / pageSize) + 1 : 1;
|
|
50
|
-
return fetchData({ page, pageSize, status, customer_id: id, activeFirst: true });
|
|
60
|
+
return fetchData({ page, pageSize, status, customer_id: id, activeFirst: true, showTotalCount: true });
|
|
51
61
|
},
|
|
52
62
|
{
|
|
53
63
|
reloadDeps: [id, status],
|
|
64
|
+
onSuccess(res) {
|
|
65
|
+
if (res.totalCount > 0 || res.count > 0) {
|
|
66
|
+
setStatusState(true);
|
|
67
|
+
}
|
|
68
|
+
},
|
|
54
69
|
...(isMobile
|
|
55
70
|
? {}
|
|
56
71
|
: {
|
|
@@ -68,6 +83,8 @@ export default function CurrentSubscriptions({
|
|
|
68
83
|
return <CircularProgress />;
|
|
69
84
|
}
|
|
70
85
|
|
|
86
|
+
const hasAnySubscriptions = data.totalCount > 0;
|
|
87
|
+
|
|
71
88
|
const hasMore = data && data.list?.length < data.count;
|
|
72
89
|
const size = { width: 48, height: 48 };
|
|
73
90
|
|
|
@@ -193,7 +210,7 @@ export default function CurrentSubscriptions({
|
|
|
193
210
|
}
|
|
194
211
|
}}
|
|
195
212
|
showUnsubscribe={false}
|
|
196
|
-
showRecharge
|
|
213
|
+
showRecharge={!isWillCanceled(subscription)}
|
|
197
214
|
actionProps={{
|
|
198
215
|
cancel: {
|
|
199
216
|
variant: 'outlined',
|
|
@@ -239,7 +256,7 @@ export default function CurrentSubscriptions({
|
|
|
239
256
|
</>
|
|
240
257
|
) : (
|
|
241
258
|
<Empty>
|
|
242
|
-
{onlyActive ? (
|
|
259
|
+
{onlyActive && hasAnySubscriptions ? (
|
|
243
260
|
<Box sx={{ textAlign: 'center' }}>
|
|
244
261
|
<Typography>{t('admin.subscription.noActiveEmpty')}</Typography>
|
|
245
262
|
{changeActive && (
|
|
@@ -261,4 +278,5 @@ CurrentSubscriptions.defaultProps = {
|
|
|
261
278
|
onChange: null,
|
|
262
279
|
onlyActive: false,
|
|
263
280
|
changeActive: null,
|
|
281
|
+
setStatusState: null,
|
|
264
282
|
};
|
|
@@ -17,7 +17,7 @@ export default function SubscriptionStatus({
|
|
|
17
17
|
<Status
|
|
18
18
|
icon={<AccessTimeOutlined />}
|
|
19
19
|
label={t('admin.subscription.cancel.will', { date: formatToDate(subscription.current_period_end * 1000) })}
|
|
20
|
-
color="
|
|
20
|
+
color="warning"
|
|
21
21
|
{...rest}
|
|
22
22
|
/>
|
|
23
23
|
);
|
|
@@ -28,7 +28,7 @@ export default function SubscriptionStatus({
|
|
|
28
28
|
<Status
|
|
29
29
|
icon={<AccessTimeOutlined />}
|
|
30
30
|
label={t('admin.subscription.cancel.will', { date: formatToDate(subscription.cancel_at * 1000) })}
|
|
31
|
-
color="
|
|
31
|
+
color="warning"
|
|
32
32
|
{...rest}
|
|
33
33
|
/>
|
|
34
34
|
);
|
package/src/libs/util.ts
CHANGED
|
@@ -377,3 +377,18 @@ export function getTokenBalanceLink(method: TPaymentMethod, address: string) {
|
|
|
377
377
|
}
|
|
378
378
|
return '';
|
|
379
379
|
}
|
|
380
|
+
|
|
381
|
+
export function isWillCanceled(subscription: TSubscriptionExpanded) {
|
|
382
|
+
const now = Date.now() / 1000;
|
|
383
|
+
if (
|
|
384
|
+
['active', 'trialing'].includes(subscription.status) &&
|
|
385
|
+
subscription.cancel_at_period_end &&
|
|
386
|
+
subscription.current_period_end > now
|
|
387
|
+
) {
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
390
|
+
if (subscription.cancel_at && subscription.cancel_at > now) {
|
|
391
|
+
return true;
|
|
392
|
+
}
|
|
393
|
+
return false;
|
|
394
|
+
}
|
|
@@ -224,7 +224,12 @@ export default function PayoutDetail(props: { id: string }) {
|
|
|
224
224
|
{paymentIntent?.customer?.name} ({paymentIntent?.customer?.email})
|
|
225
225
|
</Typography>
|
|
226
226
|
}
|
|
227
|
-
description={
|
|
227
|
+
description={
|
|
228
|
+
<DID
|
|
229
|
+
did={paymentIntent?.customer?.did}
|
|
230
|
+
{...(isMobile ? { responsive: false, compact: true } : {})}
|
|
231
|
+
/>
|
|
232
|
+
}
|
|
228
233
|
size={40}
|
|
229
234
|
variant="rounded"
|
|
230
235
|
/>
|