payment-kit 1.15.33 → 1.15.35
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/setup-intent.ts +3 -1
- package/api/src/integrations/stripe/handlers/subscription.ts +2 -8
- package/api/src/integrations/stripe/resource.ts +0 -11
- package/api/src/libs/invoice.ts +202 -1
- package/api/src/libs/notification/template/subscription-canceled.ts +15 -2
- package/api/src/libs/notification/template/subscription-renew-failed.ts +1 -1
- package/api/src/libs/notification/template/subscription-renewed.ts +1 -1
- package/api/src/libs/notification/template/subscription-trial-will-end.ts +9 -5
- package/api/src/libs/notification/template/subscription-will-canceled.ts +9 -5
- package/api/src/libs/notification/template/subscription-will-renew.ts +10 -12
- package/api/src/libs/payment.ts +3 -2
- package/api/src/libs/refund.ts +4 -0
- package/api/src/libs/subscription.ts +58 -14
- package/api/src/queues/invoice.ts +1 -0
- package/api/src/queues/payment.ts +3 -1
- package/api/src/queues/refund.ts +9 -8
- package/api/src/queues/subscription.ts +111 -40
- package/api/src/routes/checkout-sessions.ts +22 -6
- package/api/src/routes/connect/change-payment.ts +51 -34
- package/api/src/routes/connect/change-plan.ts +25 -3
- package/api/src/routes/connect/recharge.ts +28 -3
- package/api/src/routes/connect/setup.ts +27 -6
- package/api/src/routes/connect/shared.ts +223 -1
- package/api/src/routes/connect/subscribe.ts +25 -3
- package/api/src/routes/customers.ts +2 -2
- package/api/src/routes/invoices.ts +27 -105
- package/api/src/routes/payment-links.ts +3 -0
- package/api/src/routes/refunds.ts +22 -1
- package/api/src/routes/subscriptions.ts +112 -21
- package/api/src/routes/webhook-attempts.ts +14 -1
- package/api/src/store/models/invoice.ts +3 -1
- package/blocklet.yml +1 -1
- package/package.json +4 -4
- package/src/app.tsx +3 -1
- package/src/components/invoice/list.tsx +83 -31
- package/src/components/invoice/recharge.tsx +244 -0
- package/src/components/payment-intent/actions.tsx +2 -1
- package/src/components/payment-link/actions.tsx +6 -6
- package/src/components/payment-link/item.tsx +53 -18
- package/src/components/pricing-table/actions.tsx +14 -3
- package/src/components/pricing-table/payment-settings.tsx +1 -1
- package/src/components/refund/actions.tsx +43 -1
- package/src/components/refund/list.tsx +1 -1
- package/src/components/subscription/actions/cancel.tsx +10 -7
- package/src/components/subscription/metrics.tsx +1 -1
- package/src/components/subscription/portal/actions.tsx +22 -1
- package/src/components/subscription/portal/list.tsx +1 -0
- package/src/components/webhook/attempts.tsx +19 -121
- package/src/components/webhook/request-info.tsx +139 -0
- package/src/locales/en.tsx +4 -0
- package/src/locales/zh.tsx +8 -0
- package/src/pages/admin/billing/invoices/detail.tsx +15 -0
- package/src/pages/admin/billing/invoices/index.tsx +1 -1
- package/src/pages/admin/billing/subscriptions/detail.tsx +12 -4
- package/src/pages/admin/customers/customers/detail.tsx +1 -0
- package/src/pages/admin/payments/refunds/detail.tsx +2 -2
- package/src/pages/admin/products/links/create.tsx +4 -1
- package/src/pages/customer/index.tsx +1 -1
- package/src/pages/customer/invoice/detail.tsx +34 -14
- package/src/pages/customer/recharge.tsx +45 -35
- package/src/pages/customer/subscription/change-plan.tsx +8 -1
- package/src/pages/customer/subscription/detail.tsx +12 -22
- package/src/pages/customer/subscription/embed.tsx +3 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import CodeBlock from '@arcblock/ux/lib/CodeBlock';
|
|
3
3
|
import { api, formatTime } from '@blocklet/payment-react';
|
|
4
4
|
import type { Paginated, TEvent, TWebhookAttemptExpanded } from '@blocklet/payment-types';
|
|
5
|
-
import { CheckCircleOutlined, ErrorOutlined
|
|
5
|
+
import { CheckCircleOutlined, ErrorOutlined } from '@mui/icons-material';
|
|
6
6
|
import {
|
|
7
7
|
Box,
|
|
8
8
|
Button,
|
|
@@ -15,15 +15,13 @@ import {
|
|
|
15
15
|
ListSubheader,
|
|
16
16
|
Stack,
|
|
17
17
|
Typography,
|
|
18
|
-
Popper,
|
|
19
|
-
Paper,
|
|
20
18
|
} from '@mui/material';
|
|
21
19
|
import { useInfiniteScroll } from 'ahooks';
|
|
22
20
|
import React, { useEffect, useState } from 'react';
|
|
23
21
|
|
|
24
22
|
import { isEmpty } from 'lodash';
|
|
25
23
|
import { isSuccessAttempt } from '../../libs/util';
|
|
26
|
-
import
|
|
24
|
+
import RequestInfoPopper, { RequestType } from './request-info';
|
|
27
25
|
|
|
28
26
|
const fetchData = (params: Record<string, any> = {}): Promise<Paginated<TWebhookAttemptExpanded>> => {
|
|
29
27
|
const search = new URLSearchParams();
|
|
@@ -48,7 +46,7 @@ const groupAttemptsByDate = (attempts: TWebhookAttemptExpanded[]) => {
|
|
|
48
46
|
type Props = {
|
|
49
47
|
event_id?: string;
|
|
50
48
|
webhook_endpoint_id?: string;
|
|
51
|
-
event?: TEvent & { requestInfo?:
|
|
49
|
+
event?: TEvent & { requestInfo?: RequestInfo };
|
|
52
50
|
};
|
|
53
51
|
|
|
54
52
|
WebhookAttempts.defaultProps = {
|
|
@@ -72,7 +70,9 @@ export default function WebhookAttempts({ event_id, webhook_endpoint_id, event }
|
|
|
72
70
|
const attempts = data?.list || [];
|
|
73
71
|
|
|
74
72
|
// @ts-ignore
|
|
75
|
-
const [selected, setSelected] = useState<
|
|
73
|
+
const [selected, setSelected] = useState<
|
|
74
|
+
(TWebhookAttemptExpanded & { event: TEvent & { requestInfo?: RequestInfo } }) | null
|
|
75
|
+
>(null);
|
|
76
76
|
const groupedAttempts = groupAttemptsByDate(attempts);
|
|
77
77
|
|
|
78
78
|
useEffect(() => {
|
|
@@ -85,35 +85,6 @@ export default function WebhookAttempts({ event_id, webhook_endpoint_id, event }
|
|
|
85
85
|
setSelected(attempt);
|
|
86
86
|
};
|
|
87
87
|
|
|
88
|
-
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
|
89
|
-
|
|
90
|
-
const handleClick = (e: React.MouseEvent<HTMLElement>) => {
|
|
91
|
-
setAnchorEl(anchorEl ? null : e.currentTarget);
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
useEffect(() => {
|
|
95
|
-
const handleClickOutside = (e: MouseEvent) => {
|
|
96
|
-
if (anchorEl && !anchorEl.contains(e.target as Node) && !(e.target as Element).closest('.popper-content')) {
|
|
97
|
-
setAnchorEl(null);
|
|
98
|
-
}
|
|
99
|
-
};
|
|
100
|
-
|
|
101
|
-
const handleScroll = (e: Event) => {
|
|
102
|
-
// @ts-ignore
|
|
103
|
-
if (anchorEl && !e.target?.closest('.popper-content')) {
|
|
104
|
-
setAnchorEl(null);
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
document.addEventListener('click', handleClickOutside);
|
|
109
|
-
window.addEventListener('scroll', handleScroll, true);
|
|
110
|
-
|
|
111
|
-
return () => {
|
|
112
|
-
document.removeEventListener('click', handleClickOutside);
|
|
113
|
-
window.removeEventListener('scroll', handleScroll, true);
|
|
114
|
-
};
|
|
115
|
-
}, [anchorEl]);
|
|
116
|
-
|
|
117
88
|
if (loading) {
|
|
118
89
|
return <CircularProgress />;
|
|
119
90
|
}
|
|
@@ -172,7 +143,14 @@ export default function WebhookAttempts({ event_id, webhook_endpoint_id, event }
|
|
|
172
143
|
<CodeBlock language="json">{JSON.stringify(selected.response_body, null, 2)}</CodeBlock>
|
|
173
144
|
</Box>
|
|
174
145
|
<Box>
|
|
175
|
-
<
|
|
146
|
+
<Stack direction="row" alignItems="center" spacing={1}>
|
|
147
|
+
<Typography variant="h6">Request</Typography>
|
|
148
|
+
<RequestInfoPopper
|
|
149
|
+
// @ts-ignore
|
|
150
|
+
requestInfo={selected?.event?.requestInfo}
|
|
151
|
+
request={selected?.event.request as RequestType}
|
|
152
|
+
/>
|
|
153
|
+
</Stack>
|
|
176
154
|
{/* @ts-ignore */}
|
|
177
155
|
<CodeBlock language="json">{JSON.stringify(selected.event, null, 2)}</CodeBlock>
|
|
178
156
|
</Box>
|
|
@@ -182,91 +160,11 @@ export default function WebhookAttempts({ event_id, webhook_endpoint_id, event }
|
|
|
182
160
|
<Box>
|
|
183
161
|
<Stack direction="row" alignItems="center" spacing={1}>
|
|
184
162
|
<Typography variant="h6">Event Data</Typography>
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
sx={{
|
|
191
|
-
color: 'text.secondary',
|
|
192
|
-
opacity: 0.6,
|
|
193
|
-
cursor: 'pointer',
|
|
194
|
-
}}
|
|
195
|
-
/>
|
|
196
|
-
<Popper
|
|
197
|
-
open={Boolean(anchorEl)}
|
|
198
|
-
anchorEl={anchorEl}
|
|
199
|
-
placement="right"
|
|
200
|
-
sx={{
|
|
201
|
-
zIndex: 1000,
|
|
202
|
-
'@media (max-width: 600px)': {
|
|
203
|
-
'& .MuiPaper-root': {
|
|
204
|
-
width: 'calc(100vw - 32px)',
|
|
205
|
-
maxWidth: 'none',
|
|
206
|
-
},
|
|
207
|
-
},
|
|
208
|
-
}}
|
|
209
|
-
modifiers={[
|
|
210
|
-
{
|
|
211
|
-
name: 'preventOverflow',
|
|
212
|
-
options: {
|
|
213
|
-
boundary: window,
|
|
214
|
-
altAxis: true,
|
|
215
|
-
padding: 16,
|
|
216
|
-
},
|
|
217
|
-
},
|
|
218
|
-
{
|
|
219
|
-
name: 'flip',
|
|
220
|
-
options: {
|
|
221
|
-
fallbackPlacements: ['bottom'],
|
|
222
|
-
},
|
|
223
|
-
},
|
|
224
|
-
{
|
|
225
|
-
name: 'matchWidth',
|
|
226
|
-
enabled: true,
|
|
227
|
-
fn: ({ state }) => {
|
|
228
|
-
if (window.innerWidth <= 600) {
|
|
229
|
-
state.styles.popper = {
|
|
230
|
-
...state.styles.popper,
|
|
231
|
-
width: 'calc(100vw - 32px)',
|
|
232
|
-
maxWidth: 'none',
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
return state;
|
|
236
|
-
},
|
|
237
|
-
},
|
|
238
|
-
]}>
|
|
239
|
-
<Paper
|
|
240
|
-
className="popper-content"
|
|
241
|
-
elevation={3}
|
|
242
|
-
sx={{
|
|
243
|
-
p: 2,
|
|
244
|
-
border: '1px solid',
|
|
245
|
-
borderColor: 'divider',
|
|
246
|
-
maxWidth: 300,
|
|
247
|
-
'@media (max-width: 600px)': {
|
|
248
|
-
maxWidth: 'none',
|
|
249
|
-
margin: '0 auto',
|
|
250
|
-
},
|
|
251
|
-
}}>
|
|
252
|
-
{event.requestInfo ? (
|
|
253
|
-
<>
|
|
254
|
-
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
|
|
255
|
-
Requested by:
|
|
256
|
-
</Typography>
|
|
257
|
-
<InfoCard
|
|
258
|
-
logo={event.requestInfo.avatar}
|
|
259
|
-
name={event.requestInfo.email}
|
|
260
|
-
description={event.requestInfo.did || event.request.requested_by}
|
|
261
|
-
size={40}
|
|
262
|
-
/>
|
|
263
|
-
</>
|
|
264
|
-
) : (
|
|
265
|
-
<Typography>Requested by: {event.request?.requested_by || 'system'}</Typography>
|
|
266
|
-
)}
|
|
267
|
-
</Paper>
|
|
268
|
-
</Popper>
|
|
269
|
-
</>
|
|
163
|
+
<RequestInfoPopper
|
|
164
|
+
// @ts-ignore
|
|
165
|
+
requestInfo={event.requestInfo}
|
|
166
|
+
request={event.request as RequestType}
|
|
167
|
+
/>
|
|
270
168
|
</Stack>
|
|
271
169
|
{/* @ts-ignore */}
|
|
272
170
|
<CodeBlock language="json">{JSON.stringify(event.data, null, 2)}</CodeBlock>
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { InfoOutlined } from '@mui/icons-material';
|
|
2
|
+
import { Popper, Paper, Typography } from '@mui/material';
|
|
3
|
+
import { useState, useEffect } from 'react';
|
|
4
|
+
import InfoCard from '../info-card';
|
|
5
|
+
|
|
6
|
+
export type RequestInfo = {
|
|
7
|
+
avatar: string;
|
|
8
|
+
email: string;
|
|
9
|
+
did: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export type RequestType = {
|
|
13
|
+
requested_by: string;
|
|
14
|
+
};
|
|
15
|
+
type Props = {
|
|
16
|
+
requestInfo?: RequestInfo;
|
|
17
|
+
request?: RequestType;
|
|
18
|
+
};
|
|
19
|
+
RequestInfoPopper.defaultProps = {
|
|
20
|
+
requestInfo: null,
|
|
21
|
+
request: null,
|
|
22
|
+
};
|
|
23
|
+
export default function RequestInfoPopper({ requestInfo, request }: Props) {
|
|
24
|
+
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
|
25
|
+
|
|
26
|
+
const handleClick = (e: React.MouseEvent<HTMLElement>) => {
|
|
27
|
+
setAnchorEl(anchorEl ? null : e.currentTarget);
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
const handleClickOutside = (e: MouseEvent) => {
|
|
32
|
+
if (anchorEl && !anchorEl.contains(e.target as Node) && !(e.target as Element).closest('.popper-content')) {
|
|
33
|
+
setAnchorEl(null);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const handleScroll = (e: Event) => {
|
|
38
|
+
if (anchorEl && !(e.target as Element)?.closest('.popper-content')) {
|
|
39
|
+
setAnchorEl(null);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
document.addEventListener('click', handleClickOutside);
|
|
44
|
+
window.addEventListener('scroll', handleScroll, true);
|
|
45
|
+
|
|
46
|
+
return () => {
|
|
47
|
+
document.removeEventListener('click', handleClickOutside);
|
|
48
|
+
window.removeEventListener('scroll', handleScroll, true);
|
|
49
|
+
};
|
|
50
|
+
}, [anchorEl]);
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<>
|
|
54
|
+
<InfoOutlined
|
|
55
|
+
fontSize="small"
|
|
56
|
+
// @ts-ignore
|
|
57
|
+
onClick={(e) => handleClick(e)}
|
|
58
|
+
sx={{
|
|
59
|
+
color: 'text.secondary',
|
|
60
|
+
opacity: 0.6,
|
|
61
|
+
cursor: 'pointer',
|
|
62
|
+
}}
|
|
63
|
+
/>
|
|
64
|
+
<Popper
|
|
65
|
+
open={Boolean(anchorEl)}
|
|
66
|
+
anchorEl={anchorEl}
|
|
67
|
+
placement="right"
|
|
68
|
+
sx={{
|
|
69
|
+
zIndex: 1000,
|
|
70
|
+
'@media (max-width: 600px)': {
|
|
71
|
+
'& .MuiPaper-root': {
|
|
72
|
+
width: 'calc(100vw - 32px)',
|
|
73
|
+
maxWidth: 'none',
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
}}
|
|
77
|
+
modifiers={[
|
|
78
|
+
{
|
|
79
|
+
name: 'preventOverflow',
|
|
80
|
+
options: {
|
|
81
|
+
boundary: window,
|
|
82
|
+
altAxis: true,
|
|
83
|
+
padding: 16,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
name: 'flip',
|
|
88
|
+
options: {
|
|
89
|
+
fallbackPlacements: ['bottom'],
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'matchWidth',
|
|
94
|
+
enabled: true,
|
|
95
|
+
fn: ({ state }) => {
|
|
96
|
+
if (window.innerWidth <= 600) {
|
|
97
|
+
state.styles.popper = {
|
|
98
|
+
...state.styles.popper,
|
|
99
|
+
width: 'calc(100vw - 32px)',
|
|
100
|
+
maxWidth: 'none',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return state;
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
]}>
|
|
107
|
+
<Paper
|
|
108
|
+
className="popper-content"
|
|
109
|
+
elevation={3}
|
|
110
|
+
sx={{
|
|
111
|
+
p: 2,
|
|
112
|
+
border: '1px solid',
|
|
113
|
+
borderColor: 'divider',
|
|
114
|
+
maxWidth: 360,
|
|
115
|
+
'@media (max-width: 600px)': {
|
|
116
|
+
maxWidth: 'none',
|
|
117
|
+
margin: '0 auto',
|
|
118
|
+
},
|
|
119
|
+
}}>
|
|
120
|
+
{requestInfo ? (
|
|
121
|
+
<>
|
|
122
|
+
<Typography variant="caption" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
|
|
123
|
+
Requested by:
|
|
124
|
+
</Typography>
|
|
125
|
+
<InfoCard
|
|
126
|
+
logo={requestInfo.avatar}
|
|
127
|
+
name={requestInfo.email}
|
|
128
|
+
description={requestInfo.did || request?.requested_by}
|
|
129
|
+
size={40}
|
|
130
|
+
/>
|
|
131
|
+
</>
|
|
132
|
+
) : (
|
|
133
|
+
<Typography>Requested by: {request?.requested_by || 'system'}</Typography>
|
|
134
|
+
)}
|
|
135
|
+
</Paper>
|
|
136
|
+
</Popper>
|
|
137
|
+
</>
|
|
138
|
+
);
|
|
139
|
+
}
|
package/src/locales/en.tsx
CHANGED
|
@@ -22,6 +22,7 @@ export default flat({
|
|
|
22
22
|
latinOnly:
|
|
23
23
|
'At least one letter and cannot include Chinese characters and special characters such as <, >、"、’ or \\',
|
|
24
24
|
loading: 'Loading...',
|
|
25
|
+
rechargeTime: 'Recharge Time',
|
|
25
26
|
},
|
|
26
27
|
admin: {
|
|
27
28
|
balances: 'Balances',
|
|
@@ -252,11 +253,13 @@ export default flat({
|
|
|
252
253
|
label: 'Name',
|
|
253
254
|
placeholder: 'Not consumer facing',
|
|
254
255
|
},
|
|
256
|
+
adjustableQuantityError: 'Minimum must be less than maximum',
|
|
255
257
|
},
|
|
256
258
|
pricingTable: {
|
|
257
259
|
view: 'View pricing table',
|
|
258
260
|
add: 'Create pricing table',
|
|
259
261
|
save: 'Create',
|
|
262
|
+
openLink: 'Open URL',
|
|
260
263
|
copyLink: 'Copy URL',
|
|
261
264
|
saved: 'Pricing table successfully saved',
|
|
262
265
|
edit: 'Edit pricing table',
|
|
@@ -628,6 +631,7 @@ export default flat({
|
|
|
628
631
|
custom: 'Custom',
|
|
629
632
|
estimatedDuration: '{duration} {unit} est.',
|
|
630
633
|
intervals: 'intervals',
|
|
634
|
+
history: 'Recharge History',
|
|
631
635
|
},
|
|
632
636
|
},
|
|
633
637
|
});
|
package/src/locales/zh.tsx
CHANGED
|
@@ -21,6 +21,7 @@ export default flat({
|
|
|
21
21
|
invalidCharacters: '无效字符',
|
|
22
22
|
latinOnly: '至少包含一个字母,并且不能包含中文字符和特殊字符如 <, >、"、’ 或 \\',
|
|
23
23
|
loading: '加载中...',
|
|
24
|
+
rechargeTime: '充值时间',
|
|
24
25
|
},
|
|
25
26
|
admin: {
|
|
26
27
|
balances: '余额',
|
|
@@ -245,6 +246,7 @@ export default flat({
|
|
|
245
246
|
label: '名称',
|
|
246
247
|
placeholder: '不向消费者展示',
|
|
247
248
|
},
|
|
249
|
+
adjustableQuantityError: '最小数量必须小于最大数量',
|
|
248
250
|
},
|
|
249
251
|
payout: {
|
|
250
252
|
list: '对外支付',
|
|
@@ -257,6 +259,7 @@ export default flat({
|
|
|
257
259
|
view: '查看定价表',
|
|
258
260
|
add: '创建定价表',
|
|
259
261
|
save: '创建',
|
|
262
|
+
openLink: '打开URL',
|
|
260
263
|
copyLink: '复制URL',
|
|
261
264
|
saved: '定价表已成功保存',
|
|
262
265
|
edit: '编辑定价表',
|
|
@@ -283,6 +286,10 @@ export default flat({
|
|
|
283
286
|
attention: '失败的付款',
|
|
284
287
|
refundError: '退款申请失败',
|
|
285
288
|
refundSuccess: '退款申请已成功创建',
|
|
289
|
+
cancelRefund: '取消退款',
|
|
290
|
+
refundCanceled: '取消退款成功',
|
|
291
|
+
refundCanceledError: '取消退款失败',
|
|
292
|
+
refundCanceledTip: '您确定要取消退款申请吗?取消后,退款将不再进行。',
|
|
286
293
|
refundForm: {
|
|
287
294
|
reason: '退款原因',
|
|
288
295
|
amount: '退款金额',
|
|
@@ -616,6 +623,7 @@ export default flat({
|
|
|
616
623
|
estimatedDuration: '预计可用 {duration} {unit}',
|
|
617
624
|
custom: '自定义',
|
|
618
625
|
intervals: '个周期',
|
|
626
|
+
history: '充值记录',
|
|
619
627
|
},
|
|
620
628
|
},
|
|
621
629
|
});
|
|
@@ -322,6 +322,21 @@ export default function InvoiceDetail(props: { id: string }) {
|
|
|
322
322
|
alignItems={InfoAlignItems}
|
|
323
323
|
/>
|
|
324
324
|
)}
|
|
325
|
+
{data.billing_reason === 'stake' && data?.metadata?.payment_details?.arcblock?.tx_hash && (
|
|
326
|
+
<InfoRow
|
|
327
|
+
label={t('common.stakeTxHash')}
|
|
328
|
+
value={
|
|
329
|
+
<TxLink
|
|
330
|
+
details={{
|
|
331
|
+
arcblock: { tx_hash: data.metadata?.payment_details?.arcblock?.tx_hash, payer: '' },
|
|
332
|
+
}}
|
|
333
|
+
method={data.paymentMethod}
|
|
334
|
+
/>
|
|
335
|
+
}
|
|
336
|
+
direction={InfoDirection}
|
|
337
|
+
alignItems={InfoAlignItems}
|
|
338
|
+
/>
|
|
339
|
+
)}
|
|
325
340
|
{data.subscription && (
|
|
326
341
|
<InfoRow
|
|
327
342
|
label={t('admin.subscription.name')}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import InvoiceList from '../../../../components/invoice/list';
|
|
2
2
|
|
|
3
3
|
export default function InvoicesList() {
|
|
4
|
-
return <InvoiceList features={{ customer: true, toolbar: true, filter: true }} />;
|
|
4
|
+
return <InvoiceList features={{ customer: true, toolbar: true, filter: true }} include_staking />;
|
|
5
5
|
}
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
/* eslint-disable react/no-unstable-nested-components */
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
3
|
import Toast from '@arcblock/ux/lib/Toast';
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
TxLink,
|
|
6
|
+
api,
|
|
7
|
+
formatError,
|
|
8
|
+
formatSubscriptionProduct,
|
|
9
|
+
formatTime,
|
|
10
|
+
useMobile,
|
|
11
|
+
hasDelegateTxHash,
|
|
12
|
+
} from '@blocklet/payment-react';
|
|
5
13
|
import type { TProduct, TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
6
14
|
import { ArrowBackOutlined } from '@mui/icons-material';
|
|
7
15
|
import { Alert, Box, Button, CircularProgress, Divider, Stack, Typography } from '@mui/material';
|
|
@@ -268,7 +276,7 @@ export default function SubscriptionDetail(props: { id: string }) {
|
|
|
268
276
|
direction={InfoDirection}
|
|
269
277
|
alignItems={InfoAlignItems}
|
|
270
278
|
/>
|
|
271
|
-
{
|
|
279
|
+
{data.payment_details && hasDelegateTxHash(data.payment_details, data.paymentMethod) && (
|
|
272
280
|
<InfoRow
|
|
273
281
|
label={t('common.delegateTxHash')}
|
|
274
282
|
value={<TxLink details={data.payment_details} method={data.paymentMethod} />}
|
|
@@ -276,7 +284,7 @@ export default function SubscriptionDetail(props: { id: string }) {
|
|
|
276
284
|
alignItems={InfoAlignItems}
|
|
277
285
|
/>
|
|
278
286
|
)}
|
|
279
|
-
{data.payment_details?.arcblock?.staking?.tx_hash && (
|
|
287
|
+
{data.paymentMethod?.type === 'arcblock' && data.payment_details?.arcblock?.staking?.tx_hash && (
|
|
280
288
|
<InfoRow
|
|
281
289
|
label={t('common.stakeTxHash')}
|
|
282
290
|
value={
|
|
@@ -322,7 +330,7 @@ export default function SubscriptionDetail(props: { id: string }) {
|
|
|
322
330
|
<Box className="section">
|
|
323
331
|
<SectionHeader title={t('admin.invoices')} />
|
|
324
332
|
<Box className="section-body">
|
|
325
|
-
<InvoiceList features={{ customer: true, toolbar: false }} subscription_id={data.id} />
|
|
333
|
+
<InvoiceList features={{ customer: true, toolbar: false }} subscription_id={data.id} include_staking />
|
|
326
334
|
</Box>
|
|
327
335
|
</Box>
|
|
328
336
|
<Divider />
|
|
@@ -367,6 +367,7 @@ export default function CustomerDetail(props: { id: string }) {
|
|
|
367
367
|
features={{ customer: false, toolbar: false }}
|
|
368
368
|
customer_id={data.customer.id}
|
|
369
369
|
status={['open', 'paid', 'uncollectible', 'draft', 'void'].join(',')}
|
|
370
|
+
include_staking
|
|
370
371
|
/>
|
|
371
372
|
</Box>
|
|
372
373
|
</Box>
|
|
@@ -104,7 +104,7 @@ export default function RefundDetail(props: { id: string }) {
|
|
|
104
104
|
{t('admin.refunds')}
|
|
105
105
|
</Typography>
|
|
106
106
|
</Stack>
|
|
107
|
-
<RefundActions data={data} variant="normal" />
|
|
107
|
+
<RefundActions data={data} variant="normal" onChange={() => runAsync()} />
|
|
108
108
|
</Stack>
|
|
109
109
|
<Box
|
|
110
110
|
mt={4}
|
|
@@ -234,7 +234,7 @@ export default function RefundDetail(props: { id: string }) {
|
|
|
234
234
|
value={
|
|
235
235
|
<Stack direction="row" alignItems="center" spacing={1}>
|
|
236
236
|
<Status label={data.status} color={getRefundStatusColor(data.status)} />
|
|
237
|
-
{data.last_attempt_error && (
|
|
237
|
+
{data.last_attempt_error && data.status !== 'canceled' && (
|
|
238
238
|
<Tooltip
|
|
239
239
|
title={
|
|
240
240
|
<pre style={{ whiteSpace: 'break-spaces' }}>
|
|
@@ -35,6 +35,7 @@ export default function CreatePaymentLink() {
|
|
|
35
35
|
|
|
36
36
|
const methods = useForm<PaymentLink>({
|
|
37
37
|
shouldUnregister: false,
|
|
38
|
+
mode: 'onChange',
|
|
38
39
|
defaultValues: {
|
|
39
40
|
name: '',
|
|
40
41
|
line_items: [],
|
|
@@ -160,7 +161,9 @@ export default function CreatePaymentLink() {
|
|
|
160
161
|
// @ts-ignore
|
|
161
162
|
current={current}
|
|
162
163
|
// @ts-ignore
|
|
163
|
-
onChange={(v: string) =>
|
|
164
|
+
onChange={(v: string) => {
|
|
165
|
+
methods.handleSubmit(() => setCurrent(v))();
|
|
166
|
+
}}
|
|
164
167
|
style={{ width: '100%' }}
|
|
165
168
|
scrollButtons="auto"
|
|
166
169
|
/>
|
|
@@ -318,7 +318,7 @@ export default function CustomerHome() {
|
|
|
318
318
|
)}
|
|
319
319
|
</Box>
|
|
320
320
|
<Box className="section-body">
|
|
321
|
-
<CustomerInvoiceList customer_id={data.id} type="table" />
|
|
321
|
+
<CustomerInvoiceList customer_id={data.id} type="table" include_staking />
|
|
322
322
|
</Box>
|
|
323
323
|
</Box>
|
|
324
324
|
);
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
TxGas,
|
|
7
7
|
TxLink,
|
|
8
8
|
api,
|
|
9
|
+
formatAmount,
|
|
9
10
|
formatError,
|
|
10
11
|
formatTime,
|
|
11
12
|
getInvoiceStatusColor,
|
|
@@ -19,6 +20,7 @@ import { useRequest, useSetState } from 'ahooks';
|
|
|
19
20
|
import { useEffect } from 'react';
|
|
20
21
|
import { Link, useParams, useSearchParams } from 'react-router-dom';
|
|
21
22
|
|
|
23
|
+
import { useSessionContext } from '../../../contexts/session';
|
|
22
24
|
import Currency from '../../../components/currency';
|
|
23
25
|
import CustomerLink from '../../../components/customer/link';
|
|
24
26
|
import InfoRow from '../../../components/info-row';
|
|
@@ -46,6 +48,7 @@ export default function CustomerInvoiceDetail() {
|
|
|
46
48
|
|
|
47
49
|
const { loading, error, data, runAsync } = useRequest(() => fetchData(params.id as string));
|
|
48
50
|
const action = searchParams.get('action');
|
|
51
|
+
const { session } = useSessionContext();
|
|
49
52
|
|
|
50
53
|
const onPay = () => {
|
|
51
54
|
setState({ paying: true });
|
|
@@ -93,6 +96,10 @@ export default function CustomerInvoiceDetail() {
|
|
|
93
96
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
94
97
|
}, [error]);
|
|
95
98
|
|
|
99
|
+
if (data?.customer?.did && session?.user?.did && data.customer.did !== session.user.did) {
|
|
100
|
+
return <Alert severity="error">You do not have permission to access other customer data</Alert>;
|
|
101
|
+
}
|
|
102
|
+
|
|
96
103
|
if (error) {
|
|
97
104
|
return <Alert severity="error">{formatError(error)}</Alert>;
|
|
98
105
|
}
|
|
@@ -101,9 +108,10 @@ export default function CustomerInvoiceDetail() {
|
|
|
101
108
|
return <CircularProgress />;
|
|
102
109
|
}
|
|
103
110
|
|
|
104
|
-
const
|
|
105
|
-
|
|
111
|
+
const isStake = data.paymentMethod?.type === 'arcblock' && data.billing_reason === 'stake';
|
|
112
|
+
const isSlashStake = data.paymentMethod?.type === 'arcblock' && data.billing_reason.includes('slash_stake');
|
|
106
113
|
|
|
114
|
+
const paymentDetails = data.paymentIntent?.payment_details || data.metadata?.payment_details;
|
|
107
115
|
return (
|
|
108
116
|
<InvoiceDetailRoot direction="column" spacing={3} sx={{ my: 2 }}>
|
|
109
117
|
<Stack direction="row" justifyContent="space-between">
|
|
@@ -263,12 +271,20 @@ export default function CustomerInvoiceDetail() {
|
|
|
263
271
|
alignItems={InfoAlignItems}
|
|
264
272
|
/>
|
|
265
273
|
)}
|
|
266
|
-
{
|
|
274
|
+
{paymentDetails && (
|
|
267
275
|
<InfoRow
|
|
268
|
-
label={
|
|
269
|
-
|
|
270
|
-
<TxLink details={data.paymentIntent.payment_details} method={data.paymentMethod} mode="customer" />
|
|
276
|
+
label={
|
|
277
|
+
isStake ? t('common.stakeTxHash') : t(`common.${paymentDetails?.arcblock?.type || 'transfer'}TxHash`)
|
|
271
278
|
}
|
|
279
|
+
value={<TxLink details={paymentDetails} method={data.paymentMethod} mode="customer" />}
|
|
280
|
+
direction={InfoDirection}
|
|
281
|
+
alignItems={InfoAlignItems}
|
|
282
|
+
/>
|
|
283
|
+
)}
|
|
284
|
+
{(isStake || isSlashStake) && (
|
|
285
|
+
<InfoRow
|
|
286
|
+
label={isSlashStake ? t('common.slashStakeAmount') : t('common.stakeAmount')}
|
|
287
|
+
value={`${formatAmount(data.total, data.paymentCurrency.decimal)} ${data.paymentCurrency.symbol}`}
|
|
272
288
|
direction={InfoDirection}
|
|
273
289
|
alignItems={InfoAlignItems}
|
|
274
290
|
/>
|
|
@@ -289,15 +305,19 @@ export default function CustomerInvoiceDetail() {
|
|
|
289
305
|
)}
|
|
290
306
|
</Stack>
|
|
291
307
|
</Box>
|
|
292
|
-
{!
|
|
308
|
+
{!isSlashStake && (
|
|
293
309
|
<>
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
310
|
+
{!isStake && (
|
|
311
|
+
<>
|
|
312
|
+
<Divider />
|
|
313
|
+
<Box className="section">
|
|
314
|
+
<Typography variant="h3" mb={1.5} className="section-header">
|
|
315
|
+
{t('payment.customer.products')}
|
|
316
|
+
</Typography>
|
|
317
|
+
<InvoiceTable invoice={data} simple />
|
|
318
|
+
</Box>
|
|
319
|
+
</>
|
|
320
|
+
)}
|
|
301
321
|
<Divider />
|
|
302
322
|
<Box className="section">
|
|
303
323
|
<Typography variant="h3" className="section-header">
|