payment-kit 1.13.22 → 1.13.24

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 (36) hide show
  1. package/api/src/integrations/stripe/handlers/invoice.ts +129 -101
  2. package/api/src/jobs/event.ts +27 -13
  3. package/api/src/jobs/invoice.ts +8 -8
  4. package/api/src/jobs/payment.ts +1 -1
  5. package/api/src/jobs/subscription.ts +1 -1
  6. package/api/src/jobs/webhook.ts +26 -19
  7. package/api/src/libs/audit.ts +3 -3
  8. package/api/src/libs/event.ts +3 -0
  9. package/api/src/libs/util.ts +5 -0
  10. package/api/src/routes/connect/pay.ts +1 -1
  11. package/api/src/routes/subscriptions.ts +15 -0
  12. package/api/src/store/models/types.ts +1 -0
  13. package/blocklet.yml +2 -2
  14. package/package.json +4 -3
  15. package/src/components/actions.tsx +4 -10
  16. package/src/components/blockchain/tx.tsx +38 -9
  17. package/src/components/click-boundary.tsx +7 -0
  18. package/src/components/confirm.tsx +2 -18
  19. package/src/components/customer/actions.tsx +3 -2
  20. package/src/components/invoice/action.tsx +3 -2
  21. package/src/components/payment-intent/actions.tsx +4 -3
  22. package/src/components/payment-intent/list.tsx +2 -2
  23. package/src/components/payment-link/actions.tsx +22 -10
  24. package/src/components/payment-link/item.tsx +18 -15
  25. package/src/components/payment-method/stripe.tsx +7 -4
  26. package/src/components/price/actions.tsx +9 -6
  27. package/src/components/price/form.tsx +4 -1
  28. package/src/components/product/actions.tsx +3 -2
  29. package/src/components/subscription/actions/index.tsx +3 -2
  30. package/src/components/subscription/items/actions.tsx +17 -14
  31. package/src/libs/util.ts +21 -5
  32. package/src/locales/en.tsx +6 -0
  33. package/src/pages/admin/billing/subscriptions/detail.tsx +1 -1
  34. package/src/pages/admin/payments/intents/detail.tsx +1 -6
  35. package/src/pages/admin/products/products/create.tsx +8 -4
  36. package/src/pages/admin/settings/payment-methods/create.tsx +3 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.13.22",
3
+ "version": "1.13.24",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev",
6
6
  "eject": "vite eject",
@@ -64,6 +64,7 @@
64
64
  "axios": "^0.27.2",
65
65
  "body-parser": "^1.20.2",
66
66
  "cookie-parser": "^1.4.6",
67
+ "copy-to-clipboard": "^3.3.3",
67
68
  "cors": "^2.8.5",
68
69
  "dayjs": "^1.11.10",
69
70
  "dotenv-flow": "^3.3.0",
@@ -100,7 +101,7 @@
100
101
  "devDependencies": {
101
102
  "@arcblock/eslint-config": "^0.2.4",
102
103
  "@arcblock/eslint-config-ts": "^0.2.4",
103
- "@did-pay/types": "1.13.22",
104
+ "@did-pay/types": "1.13.24",
104
105
  "@types/cookie-parser": "^1.4.4",
105
106
  "@types/cors": "^2.8.14",
106
107
  "@types/dotenv-flow": "^3.3.1",
@@ -137,5 +138,5 @@
137
138
  "parser": "typescript"
138
139
  }
139
140
  },
140
- "gitHead": "c8b53669ddb7118a2e43596548f031547b2d3da3"
141
+ "gitHead": "abe08b8cb109399d3b53b985f628d9688b561c0c"
141
142
  }
@@ -4,6 +4,8 @@ import { Button, IconButton, ListItemText, Menu, MenuItem } from '@mui/material'
4
4
  import React, { useState } from 'react';
5
5
  import type { LiteralUnion } from 'type-fest';
6
6
 
7
+ import { stopEvent } from '../libs/util';
8
+
7
9
  type ActionItem = {
8
10
  label: string;
9
11
  handler: Function;
@@ -30,20 +32,12 @@ export default function Actions(props: ActionsProps) {
30
32
  const open = Boolean(anchorEl);
31
33
 
32
34
  const onOpen = (e: React.SyntheticEvent<any>) => {
33
- try {
34
- e.stopPropagation();
35
- e.preventDefault();
36
- // eslint-disable-next-line no-empty
37
- } catch {}
35
+ stopEvent(e);
38
36
  setAnchorEl(e.currentTarget);
39
37
  };
40
38
 
41
39
  const onClose = (e: React.SyntheticEvent<any>, handler?: Function) => {
42
- try {
43
- e.stopPropagation();
44
- e.preventDefault();
45
- // eslint-disable-next-line no-empty
46
- } catch {}
40
+ stopEvent(e);
47
41
  setAnchorEl(null);
48
42
 
49
43
  if (typeof handler === 'function') {
@@ -1,27 +1,56 @@
1
- import type { TPaymentMethod } from '@did-pay/types';
1
+ import type { PaymentDetails, TPaymentMethod } from '@did-pay/types';
2
2
  import { OpenInNewOutlined } from '@mui/icons-material';
3
3
  import { Link, Stack, Typography } from '@mui/material';
4
4
  import { joinURL } from 'ufo';
5
5
 
6
- const getTxLink = (method: TPaymentMethod, hash: string) => {
6
+ const getTxLink = (method: TPaymentMethod, details: PaymentDetails) => {
7
7
  if (method.type === 'arcblock') {
8
- return joinURL(method.settings.arcblock?.explorer_host as string, '/tx', hash);
8
+ return {
9
+ link: joinURL(method.settings.arcblock?.explorer_host as string, '/tx', details.arcblock?.tx_hash as string),
10
+ text: details.arcblock?.tx_hash as string,
11
+ };
9
12
  }
10
13
  if (method.type === 'bitcoin') {
11
- return joinURL(method.settings.bitcoin?.explorer_host as string, '/tx', hash);
14
+ return {
15
+ link: joinURL(method.settings.bitcoin?.explorer_host as string, '/tx', details.bitcoin?.tx_hash as string),
16
+ text: details.bitcoin?.tx_hash as string,
17
+ };
12
18
  }
13
19
  if (method.type === 'ethereum') {
14
- return joinURL(method.settings.ethereum?.explorer_host as string, '/tx', hash);
20
+ return {
21
+ link: joinURL(method.settings.ethereum?.explorer_host as string, '/tx', details.ethereum?.tx_hash as string),
22
+ text: details.ethereum?.tx_hash as string,
23
+ };
24
+ }
25
+ if (method.type === 'stripe') {
26
+ const dashboard = method.livemode ? 'https://dashboard.stripe.com' : 'https://dashboard.stripe.com/test';
27
+ return {
28
+ link: joinURL(
29
+ method.settings.stripe?.dashboard || dashboard,
30
+ 'payments',
31
+ details.stripe?.payment_intent_id as string
32
+ ),
33
+ text: details.stripe?.payment_intent_id as string,
34
+ };
15
35
  }
16
36
 
17
- return hash;
37
+ return { text: 'N/A', link: '' };
18
38
  };
19
39
 
20
- export default function TxLink(props: { hash: string; method: TPaymentMethod }) {
40
+ export default function TxLink(props: { details: PaymentDetails; method: TPaymentMethod }) {
41
+ if (!props.details) {
42
+ return (
43
+ <Typography component="small" color="text.secondary">
44
+ None
45
+ </Typography>
46
+ );
47
+ }
48
+
49
+ const { text, link } = getTxLink(props.method, props.details);
21
50
  return (
22
- <Link href={getTxLink(props.method, props.hash)} target="_blank" rel="noopener noreferrer">
51
+ <Link href={link} target="_blank" rel="noopener noreferrer">
23
52
  <Stack component="span" direction="row" alignItems="center" spacing={1}>
24
- <Typography component="span">{props.hash}</Typography>
53
+ <Typography component="span">{text}</Typography>
25
54
  <OpenInNewOutlined fontSize="small" />
26
55
  </Stack>
27
56
  </Link>
@@ -0,0 +1,7 @@
1
+ import { Box } from '@mui/material';
2
+
3
+ import { stopEvent } from '../libs/util';
4
+
5
+ export default function ClickBoundary({ children }: { children: React.ReactNode }) {
6
+ return <Box onClick={stopEvent}>{children}</Box>;
7
+ }
@@ -17,29 +17,13 @@ export default function ConfirmDialog({
17
17
  loading?: boolean;
18
18
  }) {
19
19
  const { t } = useLocaleContext();
20
- const handleConfirm = (e: any) => {
21
- try {
22
- e.stopPropagation();
23
- e.preventDefault();
24
- // eslint-disable-next-line no-empty
25
- } catch {}
26
- onConfirm(e);
27
- };
28
- const handleCancel = (e: any) => {
29
- try {
30
- e.stopPropagation();
31
- e.preventDefault();
32
- // eslint-disable-next-line no-empty
33
- } catch {}
34
- onCancel(e);
35
- };
36
20
 
37
21
  return (
38
22
  <Confirm
39
23
  open
40
24
  title={title}
41
- onConfirm={handleConfirm}
42
- onCancel={handleCancel}
25
+ onConfirm={onConfirm}
26
+ onCancel={onCancel}
43
27
  confirmButton={{
44
28
  text: t('common.confirm'),
45
29
  props: { color: 'error', size: 'small', variant: 'contained', disabled: !!loading },
@@ -7,6 +7,7 @@ import type { LiteralUnion } from 'type-fest';
7
7
  import api from '../../libs/api';
8
8
  import { formatError } from '../../libs/util';
9
9
  import Actions from '../actions';
10
+ import ClickBoundary from '../click-boundary';
10
11
  import ConfirmDialog from '../confirm';
11
12
 
12
13
  type Props = {
@@ -41,7 +42,7 @@ export default function CustomerActions({ data, onChange, variant }: Props) {
41
42
  };
42
43
 
43
44
  return (
44
- <>
45
+ <ClickBoundary>
45
46
  <Actions
46
47
  variant={variant}
47
48
  actions={[
@@ -68,6 +69,6 @@ export default function CustomerActions({ data, onChange, variant }: Props) {
68
69
  loading={state.loading}
69
70
  />
70
71
  )}
71
- </>
72
+ </ClickBoundary>
72
73
  );
73
74
  }
@@ -8,6 +8,7 @@ import type { LiteralUnion } from 'type-fest';
8
8
  import api from '../../libs/api';
9
9
  import { formatError } from '../../libs/util';
10
10
  import Actions from '../actions';
11
+ import ClickBoundary from '../click-boundary';
11
12
  import ConfirmDialog from '../confirm';
12
13
 
13
14
  type Props = {
@@ -78,7 +79,7 @@ export default function InvoiceActions({ data, variant, onChange }: Props) {
78
79
  }
79
80
 
80
81
  return (
81
- <>
82
+ <ClickBoundary>
82
83
  <Actions variant={variant} actions={actions} />
83
84
  {state.action === 'xxx' && (
84
85
  <ConfirmDialog
@@ -89,6 +90,6 @@ export default function InvoiceActions({ data, variant, onChange }: Props) {
89
90
  loading={state.loading}
90
91
  />
91
92
  )}
92
- </>
93
+ </ClickBoundary>
93
94
  );
94
95
  }
@@ -8,6 +8,7 @@ import type { LiteralUnion } from 'type-fest';
8
8
  import api from '../../libs/api';
9
9
  import { formatError } from '../../libs/util';
10
10
  import Actions from '../actions';
11
+ import ClickBoundary from '../click-boundary';
11
12
  import ConfirmDialog from '../confirm';
12
13
 
13
14
  type Props = {
@@ -58,14 +59,14 @@ export default function PaymentIntentActions({ data, variant }: Props) {
58
59
  if (variant === 'compact') {
59
60
  actions.push({
60
61
  label: t('admin.paymentIntent.view'),
61
- handler: () => navigate(`/admin/payments/${data.customer_id}`),
62
+ handler: () => navigate(`/admin/payments/${data.id}`),
62
63
  color: 'primary',
63
64
  disabled: false,
64
65
  });
65
66
  }
66
67
 
67
68
  return (
68
- <>
69
+ <ClickBoundary>
69
70
  <Actions variant={variant} actions={actions} />
70
71
  {state.action === 'refund' && (
71
72
  <ConfirmDialog
@@ -76,6 +77,6 @@ export default function PaymentIntentActions({ data, variant }: Props) {
76
77
  loading={state.loading}
77
78
  />
78
79
  )}
79
- </>
80
+ </ClickBoundary>
80
81
  );
81
82
  }
@@ -6,7 +6,7 @@ import { Alert, CircularProgress, ToggleButton, ToggleButtonGroup, Typography }
6
6
  import { fromUnitToToken } from '@ocap/util';
7
7
  import { useRequest } from 'ahooks';
8
8
  import { useEffect, useState } from 'react';
9
- import { Link, useNavigate } from 'react-router-dom';
9
+ import { useNavigate } from 'react-router-dom';
10
10
 
11
11
  import api from '../../libs/api';
12
12
  import { formatTime, getPaymentIntentStatusColor } from '../../libs/util';
@@ -152,7 +152,7 @@ export default function PaymentList({ customer_id, invoice_id, features }: ListP
152
152
  options: {
153
153
  customBodyRenderLite: (_: string, index: number) => {
154
154
  const item = data.list[index] as TPaymentIntentExpanded;
155
- return <Link to={`/admin/customers/${item?.customer.id}`}>{item?.customer.email}</Link>;
155
+ return item.customer.email;
156
156
  },
157
157
  },
158
158
  });
@@ -2,11 +2,14 @@ import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
2
  import Toast from '@arcblock/ux/lib/Toast';
3
3
  import type { TPaymentLinkExpanded } from '@did-pay/types';
4
4
  import { useSetState } from 'ahooks';
5
+ import Copy from 'copy-to-clipboard';
5
6
  import type { LiteralUnion } from 'type-fest';
7
+ import { joinURL } from 'ufo';
6
8
 
7
9
  import api from '../../libs/api';
8
10
  import { formatError } from '../../libs/util';
9
11
  import Actions from '../actions';
12
+ import ClickBoundary from '../click-boundary';
10
13
  import ConfirmDialog from '../confirm';
11
14
  import RenamePaymentLink from './rename';
12
15
 
@@ -53,7 +56,7 @@ export default function PaymentLinkActions({ data, variant, onChange }: Props) {
53
56
  setState({ loading: false, action: '' });
54
57
  }
55
58
  };
56
- const noRemove = async () => {
59
+ const onRemove = async () => {
57
60
  try {
58
61
  setState({ loading: true });
59
62
  await api.delete(`/api/payment-links/${data.id}`).then((res) => res.data);
@@ -66,18 +69,27 @@ export default function PaymentLinkActions({ data, variant, onChange }: Props) {
66
69
  setState({ loading: false, action: '' });
67
70
  }
68
71
  };
72
+ const onCopyLink = () => {
73
+ Copy(joinURL(window.blocklet.appUrl, window.blocklet.prefix, `/checkout/pay/${data.id}`));
74
+ Toast.success(t('common.copied'));
75
+ };
69
76
 
70
77
  return (
71
- <>
78
+ <ClickBoundary>
72
79
  <Actions
73
80
  variant={variant}
74
81
  actions={[
75
- // {
76
- // label: t('admin.paymentLink.edit'),
77
- // handler: () => setState({ action: 'edit' }),
78
- // color: 'primary',
79
- // divider: true,
80
- // },
82
+ {
83
+ label: t('admin.paymentLink.edit'),
84
+ handler: () => setState({ action: 'edit' }),
85
+ color: 'primary',
86
+ disabled: true,
87
+ },
88
+ {
89
+ label: t('admin.paymentLink.copyLink'),
90
+ handler: onCopyLink,
91
+ color: 'primary',
92
+ },
81
93
  { label: t('admin.paymentLink.rename'), handler: () => setState({ action: 'rename' }), color: 'primary' },
82
94
  { label: t('admin.paymentLink.archive'), handler: () => setState({ action: 'archive' }), color: 'primary' },
83
95
  { label: t('admin.paymentLink.remove'), handler: () => setState({ action: 'remove' }), color: 'error' },
@@ -102,13 +114,13 @@ export default function PaymentLinkActions({ data, variant, onChange }: Props) {
102
114
  )}
103
115
  {state.action === 'remove' && (
104
116
  <ConfirmDialog
105
- onConfirm={noRemove}
117
+ onConfirm={onRemove}
106
118
  onCancel={() => setState({ action: '' })}
107
119
  title={t('admin.paymentLink.remove')}
108
120
  message={t('admin.paymentLink.removeTip')}
109
121
  loading={state.loading}
110
122
  />
111
123
  )}
112
- </>
124
+ </ClickBoundary>
113
125
  );
114
126
  }
@@ -9,6 +9,7 @@ import { useSettingsContext } from '../../contexts/settings';
9
9
  import api from '../../libs/api';
10
10
  import { formatError, formatPrice } from '../../libs/util';
11
11
  import Actions from '../actions';
12
+ import ClickBoundary from '../click-boundary';
12
13
  import InfoCard from '../info-card';
13
14
  import EditProduct from '../product/edit';
14
15
 
@@ -52,21 +53,23 @@ export default function LineItem({ prefix, product, valid, onUpdate, onRemove }:
52
53
  borderRadius: 2,
53
54
  position: 'relative',
54
55
  }}>
55
- <Actions
56
- sx={{ position: 'absolute', top: 0, right: 0 }}
57
- actions={[
58
- {
59
- label: t('admin.product.edit'),
60
- handler: () => setState({ editing: true }),
61
- color: 'primary',
62
- },
63
- {
64
- label: t('admin.product.remove'),
65
- handler: onRemove,
66
- color: 'error',
67
- },
68
- ]}
69
- />
56
+ <ClickBoundary>
57
+ <Actions
58
+ sx={{ position: 'absolute', top: 0, right: 0 }}
59
+ actions={[
60
+ {
61
+ label: t('admin.product.edit'),
62
+ handler: () => setState({ editing: true }),
63
+ color: 'primary',
64
+ },
65
+ {
66
+ label: t('admin.product.remove'),
67
+ handler: onRemove,
68
+ color: 'error',
69
+ },
70
+ ]}
71
+ />
72
+ </ClickBoundary>
70
73
  <Stack direction="column" alignItems="flex-start">
71
74
  <InfoCard
72
75
  logo={product.images[0]}
@@ -9,7 +9,6 @@ export default function StripeMethodForm() {
9
9
  return (
10
10
  <>
11
11
  <FormInput
12
- key="name"
13
12
  name="name"
14
13
  type="text"
15
14
  rules={{ required: true }}
@@ -17,7 +16,6 @@ export default function StripeMethodForm() {
17
16
  placeholder={t('admin.paymentMethod.name.tip')}
18
17
  />
19
18
  <FormInput
20
- key="description"
21
19
  name="description"
22
20
  type="text"
23
21
  rules={{ required: true }}
@@ -25,7 +23,13 @@ export default function StripeMethodForm() {
25
23
  placeholder={t('admin.paymentMethod.description.tip')}
26
24
  />
27
25
  <FormInput
28
- key="publishable_key"
26
+ name="settings.stripe.dashboard"
27
+ type="text"
28
+ rules={{ required: true }}
29
+ label={t('admin.paymentMethod.stripe.dashboard.label')}
30
+ placeholder={t('admin.paymentMethod.stripe.dashboard.tip')}
31
+ />
32
+ <FormInput
29
33
  name="settings.stripe.publishable_key"
30
34
  type="text"
31
35
  rules={{ required: true }}
@@ -33,7 +37,6 @@ export default function StripeMethodForm() {
33
37
  placeholder={t('admin.paymentMethod.stripe.publishable_key.tip')}
34
38
  />
35
39
  <FormInput
36
- key="secret_key"
37
40
  name="settings.stripe.secret_key"
38
41
  type="password"
39
42
  rules={{ required: true }}
@@ -1,6 +1,7 @@
1
1
  import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
2
2
 
3
3
  import Actions from '../actions';
4
+ import ClickBoundary from '../click-boundary';
4
5
 
5
6
  type PriceActionProps = {
6
7
  onDuplicate: Function;
@@ -11,11 +12,13 @@ export default function PriceActions(props: PriceActionProps) {
11
12
  const { t } = useLocaleContext();
12
13
 
13
14
  return (
14
- <Actions
15
- actions={[
16
- { label: t('admin.price.duplicate'), handler: props.onDuplicate, color: 'primary' },
17
- { label: t('admin.price.remove'), handler: props.onRemove, color: 'error' },
18
- ]}
19
- />
15
+ <ClickBoundary>
16
+ <Actions
17
+ actions={[
18
+ { label: t('admin.price.duplicate'), handler: props.onDuplicate, color: 'primary' },
19
+ { label: t('admin.price.remove'), handler: props.onRemove, color: 'error' },
20
+ ]}
21
+ />
22
+ </ClickBoundary>
20
23
  );
21
24
  }
@@ -125,7 +125,10 @@ export default function PriceForm({ prefix, simple }: PriceFormProps) {
125
125
  <Controller
126
126
  name={getFieldName('unit_amount')}
127
127
  control={control}
128
- rules={{ required: t('admin.price.unit_amount.required'), min: t('admin.price.unit_amount.positive') }}
128
+ rules={{
129
+ required: t('admin.price.unit_amount.required'),
130
+ validate: (v) => (Number(v) > 0 ? true : t('admin.price.unit_amount.positive')),
131
+ }}
129
132
  disabled={isLocked}
130
133
  render={({ field }) => (
131
134
  <Box>
@@ -7,6 +7,7 @@ import type { LiteralUnion } from 'type-fest';
7
7
  import api from '../../libs/api';
8
8
  import { formatError } from '../../libs/util';
9
9
  import Actions from '../actions';
10
+ import ClickBoundary from '../click-boundary';
10
11
  import ConfirmDialog from '../confirm';
11
12
  import EditProduct from './edit';
12
13
 
@@ -68,7 +69,7 @@ export default function ProductActions({ data, variant, onChange }: ProductActio
68
69
  };
69
70
 
70
71
  return (
71
- <>
72
+ <ClickBoundary>
72
73
  <Actions
73
74
  variant={variant}
74
75
  actions={[
@@ -120,6 +121,6 @@ export default function ProductActions({ data, variant, onChange }: ProductActio
120
121
  loading={state.loading}
121
122
  />
122
123
  )}
123
- </>
124
+ </ClickBoundary>
124
125
  );
125
126
  }
@@ -9,6 +9,7 @@ import type { LiteralUnion } from 'type-fest';
9
9
  import api from '../../../libs/api';
10
10
  import { formatError } from '../../../libs/util';
11
11
  import Actions from '../../actions';
12
+ import ClickBoundary from '../../click-boundary';
12
13
  import ConfirmDialog from '../../confirm';
13
14
  import SubscriptionCancelForm from './cancel';
14
15
  import SubscriptionPauseForm from './pause';
@@ -111,7 +112,7 @@ function SubscriptionActionsInner({ data, variant, onChange }: Props) {
111
112
  }
112
113
 
113
114
  return (
114
- <>
115
+ <ClickBoundary>
115
116
  <Actions variant={variant} actions={actions} />
116
117
  {state.action === 'cancel' && (
117
118
  <ConfirmDialog
@@ -140,7 +141,7 @@ function SubscriptionActionsInner({ data, variant, onChange }: Props) {
140
141
  loading={state.loading}
141
142
  />
142
143
  )}
143
- </>
144
+ </ClickBoundary>
144
145
  );
145
146
  }
146
147
 
@@ -3,6 +3,7 @@ import type { TLineItemExpanded } from '@did-pay/types';
3
3
  import { useNavigate } from 'react-router-dom';
4
4
 
5
5
  import Actions from '../../actions';
6
+ import ClickBoundary from '../../click-boundary';
6
7
 
7
8
  type Props = {
8
9
  data: TLineItemExpanded;
@@ -13,19 +14,21 @@ export default function LineItemActions(props: Props) {
13
14
  const navigate = useNavigate();
14
15
 
15
16
  return (
16
- <Actions
17
- actions={[
18
- {
19
- label: t('admin.price.view'),
20
- handler: () => navigate(`/admin/products/${props.data.price_id}`),
21
- color: 'primary',
22
- },
23
- {
24
- label: t('admin.product.view'),
25
- handler: () => navigate(`/admin/products/${props.data.price.product_id}`),
26
- color: 'primary',
27
- },
28
- ]}
29
- />
17
+ <ClickBoundary>
18
+ <Actions
19
+ actions={[
20
+ {
21
+ label: t('admin.price.view'),
22
+ handler: () => navigate(`/admin/products/${props.data.price_id}`),
23
+ color: 'primary',
24
+ },
25
+ {
26
+ label: t('admin.product.view'),
27
+ handler: () => navigate(`/admin/products/${props.data.price.product_id}`),
28
+ color: 'primary',
29
+ },
30
+ ]}
31
+ />
32
+ </ClickBoundary>
30
33
  );
31
34
  }
package/src/libs/util.ts CHANGED
@@ -132,11 +132,17 @@ export const formatProductPrice = (
132
132
  return 'No price';
133
133
  };
134
134
 
135
- export const formatPrice = (price: TPrice, currency: TPaymentCurrency, unit_label?: string, quantity: number = 1) => {
136
- const amount = fromUnitToToken(
137
- new BN(getPriceUintAmountByCurrency(price, currency)).mul(new BN(quantity)),
138
- currency.decimal
139
- ).toString();
135
+ export const formatPrice = (
136
+ price: TPrice,
137
+ currency: TPaymentCurrency,
138
+ unit_label?: string,
139
+ quantity: number = 1,
140
+ bn: boolean = true
141
+ ) => {
142
+ const unit = getPriceUintAmountByCurrency(price, currency);
143
+ const amount = bn
144
+ ? fromUnitToToken(new BN(unit).mul(new BN(quantity)), currency.decimal).toString()
145
+ : +unit * quantity;
140
146
  if (price?.type === 'recurring' && price.recurring) {
141
147
  const recurring = formatRecurring(price.recurring, false, '/');
142
148
 
@@ -572,3 +578,13 @@ export function getSupportedPaymentCurrencies(items: TLineItemExpanded[]) {
572
578
  export function isValidCountry(code: string) {
573
579
  return defaultCountries.some((x) => x[1] === code);
574
580
  }
581
+
582
+ export function stopEvent(e: React.SyntheticEvent<any>) {
583
+ try {
584
+ e.stopPropagation();
585
+ e.preventDefault();
586
+ // eslint-disable-next-line no-empty
587
+ } catch {
588
+ // Do nothing
589
+ }
590
+ }
@@ -44,6 +44,7 @@ export default flat({
44
44
  loadMore: 'View more {resource}',
45
45
  loadingMore: 'Loading more {resource}...',
46
46
  noMore: 'No more {resource}',
47
+ copied: 'Copied',
47
48
  metadata: {
48
49
  label: 'Metadata',
49
50
  add: 'Add more metadata',
@@ -173,6 +174,7 @@ export default flat({
173
174
  info: 'Payment link information',
174
175
  add: 'Create payment link',
175
176
  save: 'Create link',
177
+ copyLink: 'Copy URL',
176
178
  saved: 'Payment link successfully saved',
177
179
  additional: 'Additional options',
178
180
  beforePay: 'Payment page',
@@ -232,6 +234,10 @@ export default flat({
232
234
  tip: 'Not consumer facing',
233
235
  },
234
236
  stripe: {
237
+ dashboard: {
238
+ label: 'Dashboard URL',
239
+ tip: 'Used to generate links to Stripe dashboard',
240
+ },
235
241
  publishable_key: {
236
242
  label: 'Publishable Key',
237
243
  tip: 'Publishable Key, See Dashboard > Developers > API Keys',
@@ -176,7 +176,7 @@ export default function SubscriptionDetail(props: { id: string }) {
176
176
  {data.payment_details?.arcblock?.tx_hash && (
177
177
  <InfoRow
178
178
  label={t('common.txHash')}
179
- value={<TxLink hash={data.payment_details.arcblock?.tx_hash} method={data.paymentMethod} />}
179
+ value={<TxLink details={data.payment_details} method={data.paymentMethod} />}
180
180
  />
181
181
  )}
182
182
  </Stack>
@@ -141,12 +141,7 @@ export default function PaymentIntentDetail(props: { id: string }) {
141
141
  />
142
142
  <InfoRow
143
143
  label={t('common.txHash')}
144
- value={
145
- <TxLink
146
- hash={data.payment_details?.arcblock?.tx_hash || data.metadata?.txHash}
147
- method={data.paymentMethod}
148
- />
149
- }
144
+ value={<TxLink details={data.payment_details as any} method={data.paymentMethod} />}
150
145
  />
151
146
  </Stack>
152
147
  </Box>