payment-kit 1.19.8 → 1.19.10
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/index.ts +15 -10
- package/api/src/libs/env.ts +4 -0
- package/api/src/libs/notification/index.ts +28 -1
- package/api/src/libs/payment.ts +17 -5
- package/api/src/libs/setting.ts +20 -1
- package/api/src/locales/zh.ts +3 -3
- package/api/src/routes/checkout-sessions.ts +70 -33
- package/api/src/routes/connect/change-payment.ts +2 -2
- package/api/src/routes/connect/change-plan.ts +2 -2
- package/api/src/routes/connect/setup.ts +6 -8
- package/api/src/routes/connect/shared.ts +142 -157
- package/api/src/routes/connect/subscribe.ts +21 -6
- package/api/src/routes/meters.ts +1 -0
- package/api/src/routes/payment-currencies.ts +11 -2
- package/api/src/routes/subscriptions.ts +8 -3
- package/blocklet.yml +1 -1
- package/package.json +9 -8
- package/src/components/customer/credit-overview.tsx +11 -8
- package/src/components/payment-link/product-select.tsx +14 -1
- package/src/components/pricing-table/product-item.tsx +8 -1
- package/src/contexts/products.tsx +6 -1
- package/src/pages/admin/products/links/detail.tsx +14 -1
- package/src/pages/admin/products/prices/actions.tsx +4 -0
- package/src/pages/admin/products/prices/detail.tsx +2 -0
- package/src/pages/admin/products/products/create.tsx +1 -1
- package/src/pages/admin/products/products/detail.tsx +3 -0
- package/src/pages/admin/products/products/index.tsx +3 -1
|
@@ -1052,10 +1052,11 @@ router.put('/:id', authPortal, async (req, res) => {
|
|
|
1052
1052
|
if (due === '0') {
|
|
1053
1053
|
hasNext = false;
|
|
1054
1054
|
} else {
|
|
1055
|
+
const payer = getSubscriptionPaymentAddress(subscription, paymentMethod.type);
|
|
1055
1056
|
const delegation = await isDelegationSufficientForPayment({
|
|
1056
1057
|
paymentMethod,
|
|
1057
1058
|
paymentCurrency,
|
|
1058
|
-
userDid:
|
|
1059
|
+
userDid: payer,
|
|
1059
1060
|
amount: setup.amount.setup,
|
|
1060
1061
|
});
|
|
1061
1062
|
if (delegation.sufficient) {
|
|
@@ -1657,10 +1658,11 @@ router.post('/:id/change-payment', authPortal, async (req, res) => {
|
|
|
1657
1658
|
}
|
|
1658
1659
|
} else {
|
|
1659
1660
|
// changing from crypto to crypto: just update the subscription
|
|
1661
|
+
const payer = getSubscriptionPaymentAddress(subscription, paymentMethod.type);
|
|
1660
1662
|
delegation = await isDelegationSufficientForPayment({
|
|
1661
1663
|
paymentMethod,
|
|
1662
1664
|
paymentCurrency,
|
|
1663
|
-
userDid:
|
|
1665
|
+
userDid: payer,
|
|
1664
1666
|
amount: getFastCheckoutAmount(lineItems, 'subscription', paymentCurrency.id, false),
|
|
1665
1667
|
});
|
|
1666
1668
|
const noStake = subscription.billing_thresholds?.no_stake;
|
|
@@ -1679,7 +1681,10 @@ router.post('/:id/change-payment', authPortal, async (req, res) => {
|
|
|
1679
1681
|
default_payment_method_id: paymentMethod.id,
|
|
1680
1682
|
payment_settings: {
|
|
1681
1683
|
payment_method_types: [paymentMethod.type],
|
|
1682
|
-
payment_method_options: {
|
|
1684
|
+
payment_method_options: {
|
|
1685
|
+
...(subscription.payment_settings?.payment_method_options || {}),
|
|
1686
|
+
[paymentMethod.type]: { payer },
|
|
1687
|
+
},
|
|
1683
1688
|
},
|
|
1684
1689
|
});
|
|
1685
1690
|
logger.info('Subscription payment change done on delegation enough', {
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.10",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"lint": "tsc --noEmit && eslint src api/src --ext .mjs,.js,.jsx,.ts,.tsx",
|
|
@@ -46,17 +46,18 @@
|
|
|
46
46
|
"@abtnode/cron": "^1.16.46",
|
|
47
47
|
"@arcblock/did": "^1.21.0",
|
|
48
48
|
"@arcblock/did-auth-storage-nedb": "^1.7.1",
|
|
49
|
-
"@arcblock/did-connect": "^3.0.
|
|
49
|
+
"@arcblock/did-connect": "^3.0.36",
|
|
50
50
|
"@arcblock/did-util": "^1.21.0",
|
|
51
51
|
"@arcblock/jwt": "^1.21.0",
|
|
52
|
-
"@arcblock/ux": "^3.0.
|
|
52
|
+
"@arcblock/ux": "^3.0.36",
|
|
53
53
|
"@arcblock/validator": "^1.21.0",
|
|
54
|
-
"@blocklet/did-space-js": "^1.1.
|
|
54
|
+
"@blocklet/did-space-js": "^1.1.10",
|
|
55
|
+
"@blocklet/error": "^0.2.5",
|
|
55
56
|
"@blocklet/js-sdk": "^1.16.46",
|
|
56
57
|
"@blocklet/logger": "^1.16.46",
|
|
57
|
-
"@blocklet/payment-react": "1.19.
|
|
58
|
+
"@blocklet/payment-react": "1.19.10",
|
|
58
59
|
"@blocklet/sdk": "^1.16.46",
|
|
59
|
-
"@blocklet/ui-react": "^3.0.
|
|
60
|
+
"@blocklet/ui-react": "^3.0.36",
|
|
60
61
|
"@blocklet/uploader": "^0.2.4",
|
|
61
62
|
"@blocklet/xss": "^0.2.2",
|
|
62
63
|
"@mui/icons-material": "^7.1.2",
|
|
@@ -123,7 +124,7 @@
|
|
|
123
124
|
"devDependencies": {
|
|
124
125
|
"@abtnode/types": "^1.16.46",
|
|
125
126
|
"@arcblock/eslint-config-ts": "^0.3.3",
|
|
126
|
-
"@blocklet/payment-types": "1.19.
|
|
127
|
+
"@blocklet/payment-types": "1.19.10",
|
|
127
128
|
"@types/cookie-parser": "^1.4.9",
|
|
128
129
|
"@types/cors": "^2.8.19",
|
|
129
130
|
"@types/debug": "^4.1.12",
|
|
@@ -169,5 +170,5 @@
|
|
|
169
170
|
"parser": "typescript"
|
|
170
171
|
}
|
|
171
172
|
},
|
|
172
|
-
"gitHead": "
|
|
173
|
+
"gitHead": "96665501b84f130e074df9a3d70f9b6f48cc3309"
|
|
173
174
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { formatBNStr, CreditGrantsList, CreditTransactionsList, api } from '@blocklet/payment-react';
|
|
2
2
|
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
-
import {
|
|
3
|
+
import { Box, Card, CardContent, Stack, Typography, Tabs, Tab } from '@mui/material';
|
|
4
4
|
import { useMemo, useState } from 'react';
|
|
5
5
|
import type { TPaymentCurrency } from '@blocklet/payment-types';
|
|
6
6
|
import { useRequest } from 'ahooks';
|
|
@@ -103,13 +103,16 @@ export default function CreditOverview({ customerId, settings, mode = 'portal' }
|
|
|
103
103
|
<CardContent sx={{ flexGrow: 1 }}>
|
|
104
104
|
<Stack spacing={2}>
|
|
105
105
|
{/* 货币信息 */}
|
|
106
|
+
|
|
106
107
|
<Stack
|
|
107
|
-
direction="
|
|
108
|
-
spacing={
|
|
108
|
+
direction="column"
|
|
109
|
+
spacing={0.5}
|
|
109
110
|
sx={{
|
|
110
|
-
alignItems: '
|
|
111
|
+
alignItems: 'flex-start',
|
|
112
|
+
borderBottom: '1px solid',
|
|
113
|
+
borderColor: 'divider',
|
|
114
|
+
pb: 2,
|
|
111
115
|
}}>
|
|
112
|
-
<Avatar src={currency.logo} alt={currency.symbol} sx={{ width: 24, height: 24 }} />
|
|
113
116
|
<Typography variant="h6" component="div">
|
|
114
117
|
{currency.name}
|
|
115
118
|
</Typography>
|
|
@@ -127,11 +130,11 @@ export default function CreditOverview({ customerId, settings, mode = 'portal' }
|
|
|
127
130
|
</Typography>
|
|
128
131
|
<Typography variant="h5" component="div" sx={{ fontWeight: 'normal' }}>
|
|
129
132
|
{totalAmount === '0' && remainingAmount === '0' ? (
|
|
130
|
-
<>0
|
|
133
|
+
<>0 </>
|
|
131
134
|
) : (
|
|
132
135
|
<>
|
|
133
136
|
{formatBNStr(remainingAmount, currency.decimal, 6, true)} /{' '}
|
|
134
|
-
{formatBNStr(totalAmount, currency.decimal, 6, true)}
|
|
137
|
+
{formatBNStr(totalAmount, currency.decimal, 6, true)}
|
|
135
138
|
</>
|
|
136
139
|
)}
|
|
137
140
|
</Typography>
|
|
@@ -153,7 +156,7 @@ export default function CreditOverview({ customerId, settings, mode = 'portal' }
|
|
|
153
156
|
sx={{
|
|
154
157
|
color: 'error.main',
|
|
155
158
|
}}>
|
|
156
|
-
{formatBNStr(pendingAmount, currency.decimal, 6, true)}
|
|
159
|
+
{formatBNStr(pendingAmount, currency.decimal, 6, true)}
|
|
157
160
|
</Typography>
|
|
158
161
|
</Box>
|
|
159
162
|
)}
|
|
@@ -4,10 +4,11 @@ import type { TProductExpanded } from '@blocklet/payment-types';
|
|
|
4
4
|
import { AddOutlined } from '@mui/icons-material';
|
|
5
5
|
import { Avatar, Box, ListSubheader, MenuItem, Select, Stack, Typography } from '@mui/material';
|
|
6
6
|
import cloneDeep from 'lodash/cloneDeep';
|
|
7
|
-
import { useState } from 'react';
|
|
7
|
+
import { useRef, useState } from 'react';
|
|
8
8
|
import type { LiteralUnion } from 'type-fest';
|
|
9
9
|
|
|
10
10
|
import Empty from '@arcblock/ux/lib/Empty';
|
|
11
|
+
import useBus, { EventAction } from 'use-bus';
|
|
11
12
|
import { useProductsContext } from '../../contexts/products';
|
|
12
13
|
|
|
13
14
|
type Props = {
|
|
@@ -53,14 +54,26 @@ export default function ProductSelect({
|
|
|
53
54
|
const { products } = useProductsContext();
|
|
54
55
|
const { settings } = usePaymentContext();
|
|
55
56
|
const [value, setValue] = useState('');
|
|
57
|
+
const valueRef = useRef(value);
|
|
56
58
|
const size = { width: 16, height: 16 };
|
|
57
59
|
|
|
58
60
|
const handleSelect = (e: any) => {
|
|
59
61
|
setValue(e.target.value);
|
|
62
|
+
valueRef.current = e.target.value;
|
|
60
63
|
setMode('waiting');
|
|
61
64
|
onSelect(e.target.value);
|
|
62
65
|
};
|
|
63
66
|
|
|
67
|
+
useBus(
|
|
68
|
+
(event: EventAction) => event.type === 'product.select.price.removed',
|
|
69
|
+
(event: EventAction) => {
|
|
70
|
+
if (valueRef.current === event.priceId) {
|
|
71
|
+
setValue('');
|
|
72
|
+
valueRef.current = '';
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
);
|
|
76
|
+
|
|
64
77
|
const items = (callback?: any) => {
|
|
65
78
|
const filteredProducts = filterProducts(products, hasSelected, filterPrice);
|
|
66
79
|
|
|
@@ -5,6 +5,7 @@ import type { PricingTableItem, TProduct, TProductExpanded } from '@blocklet/pay
|
|
|
5
5
|
import { Box, Stack } from '@mui/material';
|
|
6
6
|
import { useSetState } from 'ahooks';
|
|
7
7
|
|
|
8
|
+
import { dispatch } from 'use-bus';
|
|
8
9
|
import { useProductsContext } from '../../contexts/products';
|
|
9
10
|
import { getPriceFromProducts } from '../../libs/util';
|
|
10
11
|
import Actions from '../actions';
|
|
@@ -74,7 +75,13 @@ export default function ProductItem({ product, prices, valid, onUpdate, onRemove
|
|
|
74
75
|
key={x.index}
|
|
75
76
|
prefix={`items.${x.index}`}
|
|
76
77
|
price={getPriceFromProducts(products, x.price_id) as any}
|
|
77
|
-
onRemove={() =>
|
|
78
|
+
onRemove={() => {
|
|
79
|
+
onRemove(x.index);
|
|
80
|
+
dispatch({
|
|
81
|
+
type: 'product.select.price.removed',
|
|
82
|
+
priceId: x.price_id,
|
|
83
|
+
});
|
|
84
|
+
}}
|
|
78
85
|
/>
|
|
79
86
|
))}
|
|
80
87
|
</Stack>
|
|
@@ -33,7 +33,12 @@ const fetchProducts = (forceRefresh = false): Promise<{ list: TProductExpanded[]
|
|
|
33
33
|
// eslint-disable-next-line react/prop-types
|
|
34
34
|
function ProductsProvider({ children }: { children: any }): JSX.Element {
|
|
35
35
|
const { data, error, run, loading } = useRequest((forceRefresh = false) => fetchProducts(forceRefresh));
|
|
36
|
-
useBus('
|
|
36
|
+
useBus('product.created', () => run(true), []);
|
|
37
|
+
useBus('product.updated', () => run(true), []);
|
|
38
|
+
useBus('product.deleted', () => run(true), []);
|
|
39
|
+
useBus('price.created', () => run(true), []);
|
|
40
|
+
useBus('price.updated', () => run(true), []);
|
|
41
|
+
useBus('price.deleted', () => run(true), []);
|
|
37
42
|
if (error) {
|
|
38
43
|
return <Alert severity="error">{error.message}</Alert>;
|
|
39
44
|
}
|
|
@@ -102,6 +102,16 @@ export default function PaymentLinkDetail(props: { id: string }) {
|
|
|
102
102
|
setState((prev) => ({ editing: { ...prev.editing, metadata: true } }));
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
+
const renderConfirmPage = (record: any) => {
|
|
106
|
+
if (record.type === 'hosted_confirmation') {
|
|
107
|
+
return <Typography>{record.hosted_confirmation.custom_message || t('common.none')}</Typography>;
|
|
108
|
+
}
|
|
109
|
+
if (record.type === 'redirect' && record.redirect.url) {
|
|
110
|
+
return <Link to={record.redirect.url}>{record.redirect.url}</Link>;
|
|
111
|
+
}
|
|
112
|
+
return t('common.none');
|
|
113
|
+
};
|
|
114
|
+
|
|
105
115
|
return (
|
|
106
116
|
<Grid container spacing={4} sx={{ mb: 4 }}>
|
|
107
117
|
<Grid size={12}>
|
|
@@ -362,7 +372,10 @@ export default function PaymentLinkDetail(props: { id: string }) {
|
|
|
362
372
|
value={data.phone_number_collection?.enabled ? t('common.yes') : t('common.no')}
|
|
363
373
|
/>
|
|
364
374
|
|
|
365
|
-
<InfoRow
|
|
375
|
+
<InfoRow
|
|
376
|
+
label={t('admin.paymentLink.showConfirmPage')}
|
|
377
|
+
value={renderConfirmPage(data.after_completion)}
|
|
378
|
+
/>
|
|
366
379
|
<InfoRow label={t('admin.paymentLink.mintNft')} value={data.nft_mint_settings?.factory || ''} />
|
|
367
380
|
<InfoRow label={t('common.createdAt')} value={formatTime(data.created_at)} />
|
|
368
381
|
<InfoRow label={t('common.updatedAt')} value={formatTime(data.updated_at)} />
|
|
@@ -6,6 +6,7 @@ import { useSetState } from 'ahooks';
|
|
|
6
6
|
import noop from 'lodash/noop';
|
|
7
7
|
import { useNavigate } from 'react-router-dom';
|
|
8
8
|
|
|
9
|
+
import { dispatch } from 'use-bus';
|
|
9
10
|
import Actions from '../../../../components/actions';
|
|
10
11
|
import EditPrice from '../../../../components/product/edit-price';
|
|
11
12
|
|
|
@@ -45,6 +46,7 @@ export default function PriceActions({
|
|
|
45
46
|
setState({ loading: true });
|
|
46
47
|
await api.put(`/api/prices/${data.id}`, updates).then((res) => res.data);
|
|
47
48
|
Toast.success(t('common.saved'));
|
|
49
|
+
dispatch('price.updated');
|
|
48
50
|
onChange(state.action);
|
|
49
51
|
} catch (err) {
|
|
50
52
|
console.error(err);
|
|
@@ -59,6 +61,7 @@ export default function PriceActions({
|
|
|
59
61
|
setState({ loading: true });
|
|
60
62
|
await api.put(`/api/prices/${data.id}/archive`).then((res) => res.data);
|
|
61
63
|
Toast.success(t('common.saved'));
|
|
64
|
+
dispatch('price.updated');
|
|
62
65
|
onChange(state.action);
|
|
63
66
|
} catch (err) {
|
|
64
67
|
console.error(err);
|
|
@@ -72,6 +75,7 @@ export default function PriceActions({
|
|
|
72
75
|
setState({ loading: true });
|
|
73
76
|
await api.delete(`/api/prices/${data.id}`).then((res) => res.data);
|
|
74
77
|
Toast.success(t('common.removed'));
|
|
78
|
+
dispatch('price.deleted');
|
|
75
79
|
onChange(state.action);
|
|
76
80
|
} catch (err) {
|
|
77
81
|
console.error(err);
|
|
@@ -17,6 +17,7 @@ import { styled } from '@mui/system';
|
|
|
17
17
|
import { useRequest, useSetState } from 'ahooks';
|
|
18
18
|
import { Link, useNavigate } from 'react-router-dom';
|
|
19
19
|
|
|
20
|
+
import { dispatch } from 'use-bus';
|
|
20
21
|
import Copyable from '../../../../components/copyable';
|
|
21
22
|
import Currency from '../../../../components/currency';
|
|
22
23
|
import EventList from '../../../../components/event/list';
|
|
@@ -68,6 +69,7 @@ export default function PriceDetail(props: { id: string }) {
|
|
|
68
69
|
setState((prev) => ({ loading: { ...prev.loading, [key]: true } }));
|
|
69
70
|
await api.put(`/api/prices/${props.id}`, updates).then((res) => res.data);
|
|
70
71
|
Toast.success(t('common.saved'));
|
|
72
|
+
dispatch('price.updated');
|
|
71
73
|
runAsync();
|
|
72
74
|
} catch (err) {
|
|
73
75
|
console.error(err);
|
|
@@ -159,7 +159,7 @@ export default function ProductsCreate({
|
|
|
159
159
|
Toast.success(t('admin.product.saved'));
|
|
160
160
|
methods.reset();
|
|
161
161
|
dispatch('drawer.submitted');
|
|
162
|
-
dispatch('
|
|
162
|
+
dispatch('product.created');
|
|
163
163
|
if (onSubmitCallback) {
|
|
164
164
|
onSubmitCallback();
|
|
165
165
|
}
|
|
@@ -20,6 +20,7 @@ import { useRequest, useSetState } from 'ahooks';
|
|
|
20
20
|
import { useNavigate } from 'react-router-dom';
|
|
21
21
|
|
|
22
22
|
import { isEmpty } from 'lodash';
|
|
23
|
+
import { dispatch } from 'use-bus';
|
|
23
24
|
import Copyable from '../../../../components/copyable';
|
|
24
25
|
import EventList from '../../../../components/event/list';
|
|
25
26
|
import InfoMetric from '../../../../components/info-metric';
|
|
@@ -78,6 +79,7 @@ export default function ProductDetail(props: { id: string }) {
|
|
|
78
79
|
setState((prev) => ({ loading: { ...prev.loading, [key]: true } }));
|
|
79
80
|
await api.put(`/api/products/${props.id}`, updates).then((res) => res.data);
|
|
80
81
|
Toast.success(t('common.saved'));
|
|
82
|
+
dispatch('product.updated');
|
|
81
83
|
runAsync();
|
|
82
84
|
} catch (err) {
|
|
83
85
|
console.error(err);
|
|
@@ -92,6 +94,7 @@ export default function ProductDetail(props: { id: string }) {
|
|
|
92
94
|
setState((prev) => ({ loading: { ...prev.loading, price: true } }));
|
|
93
95
|
await api.post('/api/prices', { ...price, product_id: props.id });
|
|
94
96
|
Toast.success(t('common.saved'));
|
|
97
|
+
dispatch('price.created');
|
|
95
98
|
runAsync();
|
|
96
99
|
} catch (err) {
|
|
97
100
|
console.error(err);
|
|
@@ -60,7 +60,9 @@ export default function ProductsList() {
|
|
|
60
60
|
setData(res);
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
-
useBus('
|
|
63
|
+
useBus('product.created', () => refresh(), []);
|
|
64
|
+
useBus('product.updated', () => refresh(), []);
|
|
65
|
+
useBus('product.deleted', () => refresh(), []);
|
|
64
66
|
|
|
65
67
|
useEffect(() => {
|
|
66
68
|
refresh();
|