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.
Files changed (32) hide show
  1. package/api/src/libs/notification/template/one-time-payment-succeeded.ts +5 -3
  2. package/api/src/libs/notification/template/subscription-canceled.ts +3 -3
  3. package/api/src/libs/notification/template/subscription-refund-succeeded.ts +4 -3
  4. package/api/src/libs/notification/template/subscription-renew-failed.ts +5 -4
  5. package/api/src/libs/notification/template/subscription-renewed.ts +2 -1
  6. package/api/src/libs/notification/template/subscription-stake-slash-succeeded.ts +3 -4
  7. package/api/src/libs/notification/template/subscription-succeeded.ts +2 -1
  8. package/api/src/libs/notification/template/subscription-upgraded.ts +6 -4
  9. package/api/src/libs/notification/template/subscription-will-canceled.ts +6 -3
  10. package/api/src/libs/notification/template/subscription-will-renew.ts +1 -1
  11. package/api/src/routes/customers.ts +79 -5
  12. package/api/src/routes/subscriptions.ts +13 -1
  13. package/api/src/store/models/invoice.ts +4 -2
  14. package/blocklet.yml +2 -2
  15. package/package.json +4 -4
  16. package/src/app.tsx +17 -17
  17. package/src/components/actions.tsx +32 -9
  18. package/src/components/copyable.tsx +2 -2
  19. package/src/components/layout/user.tsx +37 -0
  20. package/src/components/subscription/portal/actions.tsx +26 -5
  21. package/src/components/subscription/portal/list.tsx +24 -6
  22. package/src/components/subscription/status.tsx +2 -2
  23. package/src/libs/util.ts +15 -0
  24. package/src/pages/admin/payments/payouts/detail.tsx +6 -1
  25. package/src/pages/customer/index.tsx +247 -154
  26. package/src/pages/customer/invoice/detail.tsx +1 -1
  27. package/src/pages/customer/payout/detail.tsx +9 -2
  28. package/src/pages/customer/recharge.tsx +6 -2
  29. package/src/pages/customer/subscription/change-payment.tsx +1 -1
  30. package/src/pages/customer/subscription/change-plan.tsx +1 -1
  31. package/src/pages/customer/subscription/detail.tsx +8 -3
  32. 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: true,
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
- navigate(`/customer/invoice/past-due?subscription=${subscription.id}&currency=${extraActions?.batchPay}`);
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 { Paginated, TSubscriptionExpanded } from '@blocklet/payment-types';
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
- const fetchData = (params: Record<string, any> = {}): Promise<Paginated<TSubscriptionExpanded>> => {
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<Paginated<TSubscriptionExpanded>>(
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="default"
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="default"
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={<DID did={paymentIntent?.customer?.did} />}
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
  />