payment-kit 1.18.17 → 1.18.19
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/subscription.ts +116 -0
- package/api/src/routes/checkout-sessions.ts +28 -1
- package/api/src/routes/customers.ts +5 -1
- package/api/src/store/migrations/20250318-donate-invoice.ts +45 -0
- package/api/tests/libs/subscription.spec.ts +311 -0
- package/blocklet.yml +1 -1
- package/package.json +9 -9
- package/src/components/currency.tsx +11 -4
- package/src/components/customer/link.tsx +54 -14
- package/src/components/customer/overdraft-protection.tsx +36 -2
- package/src/components/info-card.tsx +55 -7
- package/src/components/info-row-group.tsx +122 -0
- package/src/components/info-row.tsx +14 -1
- package/src/components/payouts/portal/list.tsx +7 -2
- package/src/components/subscription/items/index.tsx +1 -1
- package/src/components/subscription/metrics.tsx +14 -6
- package/src/contexts/info-row.tsx +4 -0
- package/src/locales/en.tsx +1 -0
- package/src/locales/zh.tsx +1 -0
- package/src/pages/admin/billing/invoices/detail.tsx +54 -76
- package/src/pages/admin/billing/subscriptions/detail.tsx +34 -71
- package/src/pages/admin/customers/customers/detail.tsx +41 -64
- package/src/pages/admin/payments/intents/detail.tsx +28 -42
- package/src/pages/admin/payments/payouts/detail.tsx +27 -36
- package/src/pages/admin/payments/refunds/detail.tsx +27 -41
- package/src/pages/admin/products/links/detail.tsx +30 -55
- package/src/pages/admin/products/prices/detail.tsx +43 -50
- package/src/pages/admin/products/pricing-tables/detail.tsx +23 -25
- package/src/pages/admin/products/products/detail.tsx +52 -81
- package/src/pages/customer/index.tsx +183 -108
- package/src/pages/customer/invoice/detail.tsx +49 -50
- package/src/pages/customer/payout/detail.tsx +16 -22
- package/src/pages/customer/recharge/account.tsx +92 -34
- package/src/pages/customer/recharge/subscription.tsx +6 -0
- package/src/pages/customer/subscription/detail.tsx +176 -94
|
@@ -10,8 +10,26 @@ import {
|
|
|
10
10
|
useMobile,
|
|
11
11
|
} from '@blocklet/payment-react';
|
|
12
12
|
import type { TPaymentCurrency, TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
13
|
-
import {
|
|
14
|
-
|
|
13
|
+
import {
|
|
14
|
+
AccountBalanceWalletOutlined,
|
|
15
|
+
ArrowBackOutlined,
|
|
16
|
+
CheckCircle,
|
|
17
|
+
HelpOutline,
|
|
18
|
+
SettingsOutlined,
|
|
19
|
+
} from '@mui/icons-material';
|
|
20
|
+
import {
|
|
21
|
+
Alert,
|
|
22
|
+
Avatar,
|
|
23
|
+
Box,
|
|
24
|
+
Button,
|
|
25
|
+
CircularProgress,
|
|
26
|
+
Divider,
|
|
27
|
+
Stack,
|
|
28
|
+
Typography,
|
|
29
|
+
Link as MuiLink,
|
|
30
|
+
IconButton,
|
|
31
|
+
Tooltip,
|
|
32
|
+
} from '@mui/material';
|
|
15
33
|
import { useRequest } from 'ahooks';
|
|
16
34
|
import { Link, useNavigate, useParams } from 'react-router-dom';
|
|
17
35
|
import { styled } from '@mui/system';
|
|
@@ -29,6 +47,7 @@ import { useSessionContext } from '../../../contexts/session';
|
|
|
29
47
|
import InfoMetric from '../../../components/info-metric';
|
|
30
48
|
import { useUnpaidInvoicesCheckForSubscription } from '../../../hooks/subscription';
|
|
31
49
|
import { formatSmartDuration, TimeUnit } from '../../../libs/dayjs';
|
|
50
|
+
import InfoRowGroup from '../../../components/info-row-group';
|
|
32
51
|
|
|
33
52
|
const fetchData = (id: string | undefined): Promise<TSubscriptionExpanded> => {
|
|
34
53
|
return api.get(`/api/subscriptions/${id}`).then((res) => res.data);
|
|
@@ -47,9 +66,6 @@ const fetchCycleAmount = (
|
|
|
47
66
|
return api.get(`/api/subscriptions/${subscriptionId}/cycle-amount`, { params }).then((res) => res.data);
|
|
48
67
|
};
|
|
49
68
|
|
|
50
|
-
const InfoDirection = 'column';
|
|
51
|
-
const InfoAlignItems = 'flex-start';
|
|
52
|
-
|
|
53
69
|
export default function CustomerSubscriptionDetail() {
|
|
54
70
|
const { id } = useParams() as { id: string };
|
|
55
71
|
const navigate = useNavigate();
|
|
@@ -176,52 +192,79 @@ export default function CustomerSubscriptionDetail() {
|
|
|
176
192
|
}
|
|
177
193
|
|
|
178
194
|
return (
|
|
179
|
-
<Stack direction="row"
|
|
180
|
-
<Stack direction="row" spacing={
|
|
181
|
-
<
|
|
195
|
+
<Stack direction="row" alignItems="center">
|
|
196
|
+
<Stack direction="row" spacing={1} alignItems="center">
|
|
197
|
+
<Stack direction="row" spacing={0.5} alignItems="center">
|
|
198
|
+
<CheckCircle
|
|
199
|
+
sx={{
|
|
200
|
+
fontSize: '16px',
|
|
201
|
+
color: 'success.main',
|
|
202
|
+
verticalAlign: 'middle',
|
|
203
|
+
}}
|
|
204
|
+
/>
|
|
205
|
+
<Typography
|
|
206
|
+
sx={{
|
|
207
|
+
color: 'success.main',
|
|
208
|
+
fontWeight: 500,
|
|
209
|
+
}}>
|
|
210
|
+
{t('customer.overdraftProtection.enabled')}
|
|
211
|
+
</Typography>
|
|
212
|
+
</Stack>
|
|
213
|
+
<Divider
|
|
214
|
+
orientation="vertical"
|
|
215
|
+
flexItem
|
|
182
216
|
sx={{
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
verticalAlign: 'middle',
|
|
217
|
+
mx: 1,
|
|
218
|
+
borderColor: 'divider',
|
|
186
219
|
}}
|
|
187
220
|
/>
|
|
188
221
|
<Typography
|
|
189
222
|
sx={{
|
|
190
|
-
color: '
|
|
223
|
+
color: 'text.primary',
|
|
224
|
+
display: 'flex',
|
|
225
|
+
alignItems: 'center',
|
|
226
|
+
gap: 0.5,
|
|
191
227
|
fontWeight: 500,
|
|
192
228
|
}}>
|
|
193
|
-
|
|
229
|
+
<Avatar
|
|
230
|
+
src={data.paymentCurrency?.logo}
|
|
231
|
+
sx={{ width: 16, height: 16 }}
|
|
232
|
+
alt={data.paymentCurrency?.symbol}
|
|
233
|
+
/>
|
|
234
|
+
<Box display="flex" alignItems="baseline">
|
|
235
|
+
{formatBNStr(overdraftProtection?.unused, data.paymentCurrency.decimal)}
|
|
236
|
+
<Typography
|
|
237
|
+
sx={{
|
|
238
|
+
color: 'text.secondary',
|
|
239
|
+
fontSize: '14px',
|
|
240
|
+
ml: 0.5,
|
|
241
|
+
}}>
|
|
242
|
+
{data.paymentCurrency.symbol}({formatEstimatedDuration(Math.ceil(remainingStake / estimateAmount))})
|
|
243
|
+
</Typography>
|
|
244
|
+
</Box>
|
|
194
245
|
</Typography>
|
|
195
246
|
</Stack>
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
mx: 1,
|
|
201
|
-
borderColor: 'divider',
|
|
202
|
-
}}
|
|
203
|
-
/>
|
|
204
|
-
<Typography
|
|
205
|
-
sx={{
|
|
206
|
-
color: 'text.primary',
|
|
207
|
-
display: 'flex',
|
|
208
|
-
alignItems: 'center',
|
|
209
|
-
gap: 0.5,
|
|
210
|
-
fontWeight: 500,
|
|
211
|
-
}}>
|
|
212
|
-
<Avatar src={data.paymentCurrency?.logo} sx={{ width: 16, height: 16 }} alt={data.paymentCurrency?.symbol} />
|
|
213
|
-
<Box display="flex" alignItems="baseline">
|
|
214
|
-
{formatBNStr(overdraftProtection?.unused, data.paymentCurrency.decimal)}
|
|
215
|
-
<Typography
|
|
247
|
+
{data?.overdraft_protection?.enabled && (
|
|
248
|
+
<Tooltip title={t('customer.overdraftProtection.setting')} placement="top">
|
|
249
|
+
<IconButton
|
|
250
|
+
size="small"
|
|
216
251
|
sx={{
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
252
|
+
ml: -1,
|
|
253
|
+
color: 'text.disabled',
|
|
254
|
+
'&:hover': {
|
|
255
|
+
color: 'primary.main',
|
|
256
|
+
backgroundColor: 'transparent',
|
|
257
|
+
},
|
|
258
|
+
}}
|
|
259
|
+
onClick={() =>
|
|
260
|
+
actionRef.current?.openOverdraftProtection({
|
|
261
|
+
enabled: true,
|
|
262
|
+
})
|
|
263
|
+
}>
|
|
264
|
+
<SettingsOutlined fontSize="small" sx={{ fontSize: 16 }} />
|
|
265
|
+
</IconButton>
|
|
266
|
+
</Tooltip>
|
|
267
|
+
)}
|
|
225
268
|
</Stack>
|
|
226
269
|
);
|
|
227
270
|
};
|
|
@@ -337,57 +380,112 @@ export default function CustomerSubscriptionDetail() {
|
|
|
337
380
|
}}>
|
|
338
381
|
<SubscriptionMetrics subscription={data} />
|
|
339
382
|
{showOverdraftProtection && (
|
|
340
|
-
<InfoMetric
|
|
383
|
+
<InfoMetric
|
|
384
|
+
label={
|
|
385
|
+
<Stack direction="row" alignItems="center" spacing={0.5}>
|
|
386
|
+
<Typography variant="body2" component="span">
|
|
387
|
+
{t('customer.overdraftProtection.title')}
|
|
388
|
+
</Typography>
|
|
389
|
+
<MuiLink
|
|
390
|
+
href="https://www.arcblock.io/content/blog/en/payment-kit-v117-sub-guard#listen-to-the-audio-overview"
|
|
391
|
+
target="_blank"
|
|
392
|
+
rel="noopener noreferrer"
|
|
393
|
+
sx={{
|
|
394
|
+
display: 'flex',
|
|
395
|
+
alignItems: 'center',
|
|
396
|
+
}}>
|
|
397
|
+
<Tooltip title={t('customer.overdraftProtection.learnMore')} placement="top">
|
|
398
|
+
<HelpOutline
|
|
399
|
+
fontSize="small"
|
|
400
|
+
sx={{
|
|
401
|
+
fontSize: '14px',
|
|
402
|
+
ml: -0.2,
|
|
403
|
+
color: 'text.secondary',
|
|
404
|
+
cursor: 'pointer',
|
|
405
|
+
opacity: 0.8,
|
|
406
|
+
'&:hover': { color: 'primary.main' },
|
|
407
|
+
}}
|
|
408
|
+
/>
|
|
409
|
+
</Tooltip>
|
|
410
|
+
</MuiLink>
|
|
411
|
+
{data.overdraft_protection?.payment_details?.arcblock?.staking?.address && (
|
|
412
|
+
<Tooltip
|
|
413
|
+
title={
|
|
414
|
+
<Typography sx={{ fontFamily: 'monospace', fontSize: '13px' }}>
|
|
415
|
+
{t('customer.overdraftProtection.stakingAddress')}:
|
|
416
|
+
{data.overdraft_protection?.payment_details?.arcblock?.staking?.address}
|
|
417
|
+
</Typography>
|
|
418
|
+
}
|
|
419
|
+
arrow
|
|
420
|
+
placement="top">
|
|
421
|
+
<AccountBalanceWalletOutlined
|
|
422
|
+
sx={{
|
|
423
|
+
fontSize: '16px',
|
|
424
|
+
color: 'text.secondary',
|
|
425
|
+
cursor: 'pointer',
|
|
426
|
+
ml: 1,
|
|
427
|
+
'&:hover': { color: 'primary.main' },
|
|
428
|
+
display: {
|
|
429
|
+
xs: 'none',
|
|
430
|
+
md: 'block',
|
|
431
|
+
},
|
|
432
|
+
}}
|
|
433
|
+
/>
|
|
434
|
+
</Tooltip>
|
|
435
|
+
)}
|
|
436
|
+
</Stack>
|
|
437
|
+
}
|
|
438
|
+
value={renderOverdraftProtectionLabel()}
|
|
439
|
+
/>
|
|
341
440
|
)}
|
|
342
441
|
</Stack>
|
|
343
442
|
</Box>
|
|
344
443
|
</Box>
|
|
345
444
|
<Divider />
|
|
346
|
-
<Box className="section">
|
|
445
|
+
<Box className="section" sx={{ containerType: 'inline-size' }}>
|
|
347
446
|
<Typography variant="h3" mb={3} className="section-header">
|
|
348
447
|
{t('admin.details')}
|
|
349
448
|
</Typography>
|
|
350
|
-
<
|
|
449
|
+
<InfoRowGroup
|
|
351
450
|
sx={{
|
|
352
451
|
display: 'grid',
|
|
353
452
|
gridTemplateColumns: {
|
|
354
453
|
xs: 'repeat(1, 1fr)',
|
|
355
|
-
|
|
356
|
-
md: 'repeat(2, 1fr)',
|
|
357
|
-
lg: 'repeat(3, 1fr)',
|
|
454
|
+
xl: 'repeat(2, 1fr)',
|
|
358
455
|
},
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
456
|
+
'@container (min-width: 980px)': {
|
|
457
|
+
gridTemplateColumns: 'repeat(2, 1fr)',
|
|
458
|
+
},
|
|
459
|
+
'.info-row-wrapper': {
|
|
460
|
+
gap: 1,
|
|
461
|
+
flexDirection: {
|
|
462
|
+
xs: 'column',
|
|
463
|
+
xl: 'row',
|
|
464
|
+
},
|
|
465
|
+
alignItems: {
|
|
466
|
+
xs: 'flex-start',
|
|
467
|
+
xl: 'center',
|
|
468
|
+
},
|
|
469
|
+
'@container (min-width: 980px)': {
|
|
470
|
+
flexDirection: 'row',
|
|
471
|
+
alignItems: 'center',
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
'.currency-name': {
|
|
475
|
+
color: 'text.secondary',
|
|
362
476
|
},
|
|
363
477
|
}}>
|
|
364
478
|
<InfoRow
|
|
365
479
|
label={t('common.customer')}
|
|
366
|
-
value={<CustomerLink customer={data.customer} linked={false} />}
|
|
367
|
-
direction={InfoDirection}
|
|
368
|
-
alignItems={InfoAlignItems}
|
|
480
|
+
value={<CustomerLink customer={data.customer} linked={false} size={isMobile ? 'default' : 'small'} />}
|
|
369
481
|
/>
|
|
370
|
-
<InfoRow
|
|
371
|
-
|
|
372
|
-
value={formatTime(data.created_at)}
|
|
373
|
-
direction={InfoDirection}
|
|
374
|
-
alignItems={InfoAlignItems}
|
|
375
|
-
/>
|
|
376
|
-
{data.status === 'paused' && !!data.pause_collection?.resumes_at && (
|
|
377
|
-
<InfoRow
|
|
378
|
-
label={t('common.resumesAt')}
|
|
379
|
-
value={formatTime(data.pause_collection.resumes_at * 1000)}
|
|
380
|
-
direction={InfoDirection}
|
|
381
|
-
alignItems={InfoAlignItems}
|
|
382
|
-
/>
|
|
383
|
-
)}
|
|
482
|
+
<InfoRow label={t('common.createdAt')} value={formatTime(data.created_at)} />
|
|
483
|
+
|
|
384
484
|
<InfoRow
|
|
385
485
|
label={t('admin.subscription.currentPeriod')}
|
|
386
486
|
value={[formatTime(data.current_period_start * 1000), formatTime(data.current_period_end * 1000)].join(
|
|
387
487
|
' ~ '
|
|
388
488
|
)}
|
|
389
|
-
direction={InfoDirection}
|
|
390
|
-
alignItems={InfoAlignItems}
|
|
391
489
|
/>
|
|
392
490
|
<InfoRow
|
|
393
491
|
label={t('admin.subscription.trialingPeriod')}
|
|
@@ -396,26 +494,17 @@ export default function CustomerSubscriptionDetail() {
|
|
|
396
494
|
? [formatTime(data.trial_start * 1000), formatTime(data.trial_end * 1000)].join(' ~ ')
|
|
397
495
|
: ''
|
|
398
496
|
}
|
|
399
|
-
direction={InfoDirection}
|
|
400
|
-
alignItems={InfoAlignItems}
|
|
401
|
-
/>
|
|
402
|
-
<InfoRow
|
|
403
|
-
label={t('admin.subscription.discount')}
|
|
404
|
-
value={data.discount_id ? data.discount_id : ''}
|
|
405
|
-
direction={InfoDirection}
|
|
406
|
-
alignItems={InfoAlignItems}
|
|
407
|
-
/>
|
|
408
|
-
<InfoRow
|
|
409
|
-
label={t('admin.subscription.collectionMethod')}
|
|
410
|
-
value={data.collection_method}
|
|
411
|
-
direction={InfoDirection}
|
|
412
|
-
alignItems={InfoAlignItems}
|
|
413
497
|
/>
|
|
498
|
+
{data.status === 'paused' && !!data.pause_collection?.resumes_at && (
|
|
499
|
+
<InfoRow label={t('common.resumesAt')} value={formatTime(data.pause_collection.resumes_at * 1000)} />
|
|
500
|
+
)}
|
|
501
|
+
|
|
502
|
+
<InfoRow label={t('admin.subscription.collectionMethod')} value={data.collection_method} />
|
|
503
|
+
<InfoRow label={t('admin.subscription.discount')} value={data.discount_id ? data.discount_id : ''} />
|
|
504
|
+
|
|
414
505
|
<InfoRow
|
|
415
506
|
label={t('admin.paymentMethod._name')}
|
|
416
507
|
value={<Currency logo={data.paymentMethod?.logo} name={data.paymentMethod?.name} />}
|
|
417
|
-
alignItems={InfoAlignItems}
|
|
418
|
-
direction={InfoDirection}
|
|
419
508
|
/>
|
|
420
509
|
<InfoRow
|
|
421
510
|
label={t('admin.paymentCurrency.name')}
|
|
@@ -439,15 +528,12 @@ export default function CustomerSubscriptionDetail() {
|
|
|
439
528
|
)}
|
|
440
529
|
</Stack>
|
|
441
530
|
}
|
|
442
|
-
direction={InfoDirection}
|
|
443
|
-
alignItems={InfoAlignItems}
|
|
444
531
|
/>
|
|
532
|
+
|
|
445
533
|
{data.payment_details && hasDelegateTxHash(data.payment_details, data.paymentMethod) && (
|
|
446
534
|
<InfoRow
|
|
447
535
|
label={t('common.delegateTxHash')}
|
|
448
536
|
value={<TxLink details={data.payment_details} method={data.paymentMethod} />}
|
|
449
|
-
direction={InfoDirection}
|
|
450
|
-
alignItems={InfoAlignItems}
|
|
451
537
|
/>
|
|
452
538
|
)}
|
|
453
539
|
{data.paymentMethod?.type === 'arcblock' && data.payment_details?.arcblock?.staking?.tx_hash && (
|
|
@@ -466,8 +552,6 @@ export default function CustomerSubscriptionDetail() {
|
|
|
466
552
|
/>
|
|
467
553
|
</Box>
|
|
468
554
|
}
|
|
469
|
-
direction={InfoDirection}
|
|
470
|
-
alignItems={InfoAlignItems}
|
|
471
555
|
/>
|
|
472
556
|
)}
|
|
473
557
|
{!!data.recovered_from && (
|
|
@@ -478,11 +562,9 @@ export default function CustomerSubscriptionDetail() {
|
|
|
478
562
|
{data.recovered_from}
|
|
479
563
|
</Link>
|
|
480
564
|
}
|
|
481
|
-
direction={InfoDirection}
|
|
482
|
-
alignItems={InfoAlignItems}
|
|
483
565
|
/>
|
|
484
566
|
)}
|
|
485
|
-
</
|
|
567
|
+
</InfoRowGroup>
|
|
486
568
|
</Box>
|
|
487
569
|
<Divider />
|
|
488
570
|
<Box className="section">
|