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.
- package/api/src/integrations/stripe/handlers/invoice.ts +129 -101
- package/api/src/jobs/event.ts +27 -13
- package/api/src/jobs/invoice.ts +8 -8
- package/api/src/jobs/payment.ts +1 -1
- package/api/src/jobs/subscription.ts +1 -1
- package/api/src/jobs/webhook.ts +26 -19
- package/api/src/libs/audit.ts +3 -3
- package/api/src/libs/event.ts +3 -0
- package/api/src/libs/util.ts +5 -0
- package/api/src/routes/connect/pay.ts +1 -1
- package/api/src/routes/subscriptions.ts +15 -0
- package/api/src/store/models/types.ts +1 -0
- package/blocklet.yml +2 -2
- package/package.json +4 -3
- package/src/components/actions.tsx +4 -10
- package/src/components/blockchain/tx.tsx +38 -9
- package/src/components/click-boundary.tsx +7 -0
- package/src/components/confirm.tsx +2 -18
- package/src/components/customer/actions.tsx +3 -2
- package/src/components/invoice/action.tsx +3 -2
- package/src/components/payment-intent/actions.tsx +4 -3
- package/src/components/payment-intent/list.tsx +2 -2
- package/src/components/payment-link/actions.tsx +22 -10
- package/src/components/payment-link/item.tsx +18 -15
- package/src/components/payment-method/stripe.tsx +7 -4
- package/src/components/price/actions.tsx +9 -6
- package/src/components/price/form.tsx +4 -1
- package/src/components/product/actions.tsx +3 -2
- package/src/components/subscription/actions/index.tsx +3 -2
- package/src/components/subscription/items/actions.tsx +17 -14
- package/src/libs/util.ts +21 -5
- package/src/locales/en.tsx +6 -0
- package/src/pages/admin/billing/subscriptions/detail.tsx +1 -1
- package/src/pages/admin/payments/intents/detail.tsx +1 -6
- package/src/pages/admin/products/products/create.tsx +8 -4
- 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.
|
|
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.
|
|
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": "
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
6
|
+
const getTxLink = (method: TPaymentMethod, details: PaymentDetails) => {
|
|
7
7
|
if (method.type === 'arcblock') {
|
|
8
|
-
return
|
|
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
|
|
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
|
|
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
|
|
37
|
+
return { text: 'N/A', link: '' };
|
|
18
38
|
};
|
|
19
39
|
|
|
20
|
-
export default function TxLink(props: {
|
|
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={
|
|
51
|
+
<Link href={link} target="_blank" rel="noopener noreferrer">
|
|
23
52
|
<Stack component="span" direction="row" alignItems="center" spacing={1}>
|
|
24
|
-
<Typography component="span">{
|
|
53
|
+
<Typography component="span">{text}</Typography>
|
|
25
54
|
<OpenInNewOutlined fontSize="small" />
|
|
26
55
|
</Stack>
|
|
27
56
|
</Link>
|
|
@@ -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={
|
|
42
|
-
onCancel={
|
|
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.
|
|
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 {
|
|
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
|
|
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
|
|
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
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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={
|
|
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
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
{
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
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
|
-
<
|
|
15
|
-
|
|
16
|
-
{
|
|
17
|
-
|
|
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={{
|
|
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
|
-
<
|
|
17
|
-
|
|
18
|
-
{
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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 = (
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
+
}
|
package/src/locales/en.tsx
CHANGED
|
@@ -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
|
|
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>
|