payment-kit 1.17.1 → 1.17.3
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/env.ts +0 -2
- package/api/src/libs/overdraft-protection.ts +4 -1
- package/api/src/libs/subscription.ts +1 -1
- package/api/src/locales/en.ts +2 -2
- package/api/src/locales/zh.ts +2 -2
- package/api/src/queues/notification.ts +1 -1
- package/api/src/queues/subscription.ts +1 -1
- package/api/src/routes/connect/overdraft-protection.ts +4 -1
- package/api/src/routes/connect/shared.ts +12 -7
- package/api/src/routes/invoices.ts +21 -5
- package/api/src/routes/subscriptions.ts +1 -1
- package/blocklet.yml +1 -1
- package/package.json +15 -15
- package/src/components/customer/overdraft-protection.tsx +132 -18
- package/src/components/subscription/metrics.tsx +83 -7
- package/src/components/subscription/portal/actions.tsx +188 -153
- package/src/components/subscription/portal/list.tsx +1 -0
- package/src/libs/dayjs.ts +132 -0
- package/src/locales/en.tsx +18 -11
- package/src/locales/zh.tsx +17 -10
- package/src/pages/admin/billing/subscriptions/detail.tsx +1 -1
- package/src/pages/customer/recharge.tsx +63 -27
- package/src/pages/customer/subscription/detail.tsx +153 -10
- package/src/pages/customer/subscription/embed.tsx +2 -1
- /package/api/src/libs/notification/template/{subscription.overdraft-protection.exhausted.ts → subscription-overdraft-protection-exhausted.ts} +0 -0
package/api/src/libs/env.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import env from '@blocklet/sdk/lib/env';
|
|
2
|
-
import { env as configEnv } from '@blocklet/sdk/lib/config';
|
|
3
2
|
|
|
4
3
|
export const paymentStatCronTime: string = '0 1 0 * * *'; // 默认每天一次,计算前一天的
|
|
5
4
|
export const subscriptionCronTime: string = process.env.SUBSCRIPTION_CRON_TIME || '0 */30 * * * *'; // 默认每 30 min 行一次
|
|
@@ -13,7 +12,6 @@ export const revokeStakeCronTime: string = process.env.REVOKE_STAKE_CRON_TIME ||
|
|
|
13
12
|
export const daysUntilCancel: string | undefined = process.env.DAYS_UNTIL_CANCEL;
|
|
14
13
|
export const meteringSubscriptionDetectionCronTime: string =
|
|
15
14
|
process.env.METERING_SUBSCRIPTION_DETECTION_CRON_TIME || '0 0 10 * * *'; // 默认每天 10:00 执行
|
|
16
|
-
export const overdraftProtectionMaxCount: number = Number(configEnv?.preferences?.overdraftProtectionMaxCount || 10); // 透支保护次数上限
|
|
17
15
|
|
|
18
16
|
// sequelize 配置相关
|
|
19
17
|
export const sequelizeOptionsPoolMin: number = process.env.SEQUELIZE_OPTIONS_POOL_MIN
|
|
@@ -10,6 +10,9 @@ export async function ensureOverdraftProtectionPrice(livemode = true) {
|
|
|
10
10
|
const exist = await Price.findOne({ where: { lookup_key: lookUpKey, livemode } });
|
|
11
11
|
if (exist) {
|
|
12
12
|
const product = await Product.findByPk(exist.product_id);
|
|
13
|
+
if (product?.name === 'Overdraft Protection') {
|
|
14
|
+
product.update({ name: 'SubGuard Service' });
|
|
15
|
+
}
|
|
13
16
|
return {
|
|
14
17
|
product,
|
|
15
18
|
price: exist,
|
|
@@ -27,7 +30,7 @@ export async function ensureOverdraftProtectionPrice(livemode = true) {
|
|
|
27
30
|
|
|
28
31
|
const result = await createProductAndPrices({
|
|
29
32
|
type: 'service',
|
|
30
|
-
name: '
|
|
33
|
+
name: 'SubGuard Service',
|
|
31
34
|
description:
|
|
32
35
|
'If you purchase this service, the overdue subscription will be protected and not be ended due to overdue',
|
|
33
36
|
images: [],
|
|
@@ -1296,7 +1296,7 @@ export async function returnOverdraftProtectionStake(
|
|
|
1296
1296
|
const item = await Refund.create({
|
|
1297
1297
|
type: 'stake_return',
|
|
1298
1298
|
amount: unused,
|
|
1299
|
-
description: '
|
|
1299
|
+
description: 'stake_return_for_subscription_guard',
|
|
1300
1300
|
status: 'pending',
|
|
1301
1301
|
reason: refundReason || 'requested_by_admin',
|
|
1302
1302
|
subscription_id: subscription.id,
|
package/api/src/locales/en.ts
CHANGED
|
@@ -194,8 +194,8 @@ export default flat({
|
|
|
194
194
|
},
|
|
195
195
|
|
|
196
196
|
overdraftProtectionExhausted: {
|
|
197
|
-
title: 'Insufficient Credit for
|
|
198
|
-
body: 'Your subscription to {productName} has insufficient staked credit for
|
|
197
|
+
title: 'Insufficient Credit for SubGuard',
|
|
198
|
+
body: 'Your subscription to {productName} has insufficient staked credit for SubGuard. Please increase your stake to maintain the service or disable it if no longer needed.',
|
|
199
199
|
},
|
|
200
200
|
},
|
|
201
201
|
});
|
package/api/src/locales/zh.ts
CHANGED
|
@@ -187,8 +187,8 @@ export default flat({
|
|
|
187
187
|
},
|
|
188
188
|
|
|
189
189
|
overdraftProtectionExhausted: {
|
|
190
|
-
title: '
|
|
191
|
-
body: '您订阅的 {productName}
|
|
190
|
+
title: '订阅守护服务额度不足',
|
|
191
|
+
body: '您订阅的 {productName} 订阅守护服务额度不足,为了避免影响您的使用,请及时充值。如不再需要订阅守护服务,可关闭该功能。',
|
|
192
192
|
},
|
|
193
193
|
},
|
|
194
194
|
});
|
|
@@ -68,7 +68,7 @@ import {
|
|
|
68
68
|
import {
|
|
69
69
|
OverdraftProtectionExhaustedEmailTemplate,
|
|
70
70
|
OverdraftProtectionExhaustedEmailTemplateOptions,
|
|
71
|
-
} from '../libs/notification/template/subscription
|
|
71
|
+
} from '../libs/notification/template/subscription-overdraft-protection-exhausted';
|
|
72
72
|
|
|
73
73
|
export type NotificationQueueJobOptions = any;
|
|
74
74
|
|
|
@@ -1211,7 +1211,7 @@ events.on('customer.stake.revoked', async ({ subscriptionId, tx }: { subscriptio
|
|
|
1211
1211
|
feedback: 'other',
|
|
1212
1212
|
comment: `Revoked by ${tx.tx.from} with tx ${tx.hash}`,
|
|
1213
1213
|
},
|
|
1214
|
-
//
|
|
1214
|
+
// 关闭订阅守护
|
|
1215
1215
|
// @ts-ignore
|
|
1216
1216
|
overdraft_protection: {
|
|
1217
1217
|
...(subscription.overdraft_protection || {}),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { CallbackArgs } from '../../libs/auth';
|
|
2
2
|
import { isSubscriptionOverdraftProtectionEnabled } from '../../libs/subscription';
|
|
3
|
-
import { Lock } from '../../store/models';
|
|
3
|
+
import { Lock, TLineItemExpanded } from '../../store/models';
|
|
4
4
|
import {
|
|
5
5
|
ensureSubscriptionForOverdraftProtection,
|
|
6
6
|
executeOcapTransactions,
|
|
@@ -35,6 +35,8 @@ export default {
|
|
|
35
35
|
);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
// @ts-ignore
|
|
39
|
+
const items = subscription!.items as TLineItemExpanded[];
|
|
38
40
|
const claims: { [type: string]: [string, object] } = {};
|
|
39
41
|
|
|
40
42
|
if (paymentMethod.type === 'arcblock') {
|
|
@@ -48,6 +50,7 @@ export default {
|
|
|
48
50
|
paymentMethod,
|
|
49
51
|
stakeAmount,
|
|
50
52
|
subscription,
|
|
53
|
+
items,
|
|
51
54
|
}),
|
|
52
55
|
];
|
|
53
56
|
|
|
@@ -692,6 +692,7 @@ export async function getOverdraftProtectionStakeTxClaim({
|
|
|
692
692
|
paymentCurrency,
|
|
693
693
|
paymentMethod,
|
|
694
694
|
stakeAmount,
|
|
695
|
+
items,
|
|
695
696
|
}: {
|
|
696
697
|
userDid: string;
|
|
697
698
|
userPk: string;
|
|
@@ -699,6 +700,7 @@ export async function getOverdraftProtectionStakeTxClaim({
|
|
|
699
700
|
paymentCurrency: PaymentCurrency;
|
|
700
701
|
paymentMethod: PaymentMethod;
|
|
701
702
|
stakeAmount: string;
|
|
703
|
+
items: TLineItemExpanded[];
|
|
702
704
|
}) {
|
|
703
705
|
// create staking amount
|
|
704
706
|
logger.info('getStakeTxClaim', {
|
|
@@ -725,9 +727,11 @@ export async function getOverdraftProtectionStakeTxClaim({
|
|
|
725
727
|
),
|
|
726
728
|
};
|
|
727
729
|
|
|
730
|
+
const setup = getSubscriptionCreateSetup(items, paymentCurrency.id, 0, 0);
|
|
731
|
+
|
|
728
732
|
return {
|
|
729
733
|
type: 'StakeTx',
|
|
730
|
-
description: `Stake to complete subscription ${subscription.id}
|
|
734
|
+
description: `Stake to complete subscription ${subscription.id} SubGuard`,
|
|
731
735
|
partialTx: {
|
|
732
736
|
from: userDid,
|
|
733
737
|
pk: userPk,
|
|
@@ -735,8 +739,8 @@ export async function getOverdraftProtectionStakeTxClaim({
|
|
|
735
739
|
address,
|
|
736
740
|
receiver: wallet.address,
|
|
737
741
|
slashers: [wallet.address],
|
|
738
|
-
revokeWaitingPeriod:
|
|
739
|
-
message: `Stake for subscription ${subscription.id}
|
|
742
|
+
revokeWaitingPeriod: setup.cycle.duration / 1000, // wait for at least 1 billing cycle
|
|
743
|
+
message: `Stake for subscription ${subscription.id} SubGuard`,
|
|
740
744
|
nonce,
|
|
741
745
|
inputs: [],
|
|
742
746
|
data,
|
|
@@ -946,12 +950,13 @@ export async function ensureSubscriptionForCollectBatch(subscriptionId: string,
|
|
|
946
950
|
export async function ensureSubscriptionForOverdraftProtection(subscriptionId: string, amount: string) {
|
|
947
951
|
const subscription = await Subscription.findByPk(subscriptionId);
|
|
948
952
|
if (!subscription) {
|
|
949
|
-
throw new Error(`Subscription ${subscriptionId} not found when prepare
|
|
953
|
+
throw new Error(`Subscription ${subscriptionId} not found when prepare SubGuard`);
|
|
950
954
|
}
|
|
951
|
-
|
|
955
|
+
// @ts-ignore
|
|
956
|
+
subscription.items = await expandSubscriptionItems(subscription.id);
|
|
952
957
|
const paymentCurrency = await PaymentCurrency.findByPk(subscription.currency_id);
|
|
953
958
|
if (!paymentCurrency) {
|
|
954
|
-
throw new Error(`PaymentCurrency ${subscription.currency_id} not found when prepare
|
|
959
|
+
throw new Error(`PaymentCurrency ${subscription.currency_id} not found when prepare SubGuard`);
|
|
955
960
|
}
|
|
956
961
|
|
|
957
962
|
const paymentMethod = await PaymentMethod.findByPk(paymentCurrency.payment_method_id);
|
|
@@ -960,7 +965,7 @@ export async function ensureSubscriptionForOverdraftProtection(subscriptionId: s
|
|
|
960
965
|
throw new Error(`Payment method not found for subscription ${subscriptionId}`);
|
|
961
966
|
}
|
|
962
967
|
if (paymentMethod.type !== 'arcblock') {
|
|
963
|
-
throw new Error(`Payment method ${paymentMethod.type} not supported for
|
|
968
|
+
throw new Error(`Payment method ${paymentMethod.type} not supported for SubGuard`);
|
|
964
969
|
}
|
|
965
970
|
|
|
966
971
|
const customer = await Customer.findByPk(subscription.customer_id);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/naming-convention */
|
|
1
2
|
import { isValid } from '@arcblock/did';
|
|
2
3
|
import { Router } from 'express';
|
|
3
4
|
import Joi from 'joi';
|
|
@@ -43,6 +44,7 @@ const schema = createListParamSchema<{
|
|
|
43
44
|
ignore_zero?: boolean;
|
|
44
45
|
include_staking?: boolean;
|
|
45
46
|
include_return_staking?: boolean;
|
|
47
|
+
include_overdraft_protection?: boolean;
|
|
46
48
|
include_recovered_from?: boolean;
|
|
47
49
|
}>({
|
|
48
50
|
status: Joi.string().empty(''),
|
|
@@ -53,15 +55,25 @@ const schema = createListParamSchema<{
|
|
|
53
55
|
ignore_zero: Joi.boolean().empty(false),
|
|
54
56
|
include_staking: Joi.boolean().empty(false),
|
|
55
57
|
include_return_staking: Joi.boolean().empty(false),
|
|
58
|
+
include_overdraft_protection: Joi.boolean().default(true),
|
|
56
59
|
include_recovered_from: Joi.boolean().empty(false),
|
|
57
60
|
});
|
|
58
61
|
router.get('/', authMine, async (req, res) => {
|
|
59
62
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
60
|
-
const {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
const {
|
|
64
|
+
page,
|
|
65
|
+
pageSize,
|
|
66
|
+
livemode,
|
|
67
|
+
status,
|
|
68
|
+
ignore_zero,
|
|
69
|
+
include_staking,
|
|
70
|
+
include_return_staking,
|
|
71
|
+
include_overdraft_protection = true,
|
|
72
|
+
...query
|
|
73
|
+
} = await schema.validateAsync(req.query, {
|
|
74
|
+
stripUnknown: false,
|
|
75
|
+
allowUnknown: true,
|
|
76
|
+
});
|
|
65
77
|
const where = getWhereFromKvQuery(query.q);
|
|
66
78
|
|
|
67
79
|
if (status) {
|
|
@@ -107,7 +119,11 @@ router.get('/', authMine, async (req, res) => {
|
|
|
107
119
|
// @ts-ignore
|
|
108
120
|
where[key] = query[key];
|
|
109
121
|
});
|
|
122
|
+
|
|
110
123
|
const excludeBillingReasons = ['recharge'];
|
|
124
|
+
if (!include_overdraft_protection) {
|
|
125
|
+
excludeBillingReasons.push('stake_overdraft_protection');
|
|
126
|
+
}
|
|
111
127
|
if (!!(include_staking && query.subscription_id) || !include_staking) {
|
|
112
128
|
excludeBillingReasons.push('stake');
|
|
113
129
|
}
|
|
@@ -1760,7 +1760,7 @@ router.get('/:id/cycle-amount', authPortal, async (req, res) => {
|
|
|
1760
1760
|
const pastMaxAmount = await getPastInvoicesAmount(subscription.id, 'max');
|
|
1761
1761
|
|
|
1762
1762
|
// return max amount
|
|
1763
|
-
const nextAmount = new BN(result.amount === '0' ? result.minExpectedAmount : result.amount);
|
|
1763
|
+
const nextAmount = new BN(result.amount === '0' ? result.minExpectedAmount : result.amount).toString();
|
|
1764
1764
|
|
|
1765
1765
|
const maxAmount = new BN(pastMaxAmount.amount).lte(new BN(nextAmount)) ? nextAmount : pastMaxAmount.amount;
|
|
1766
1766
|
|
package/blocklet.yml
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "payment-kit",
|
|
3
|
-
"version": "1.17.
|
|
3
|
+
"version": "1.17.3",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"dev": "blocklet dev --open",
|
|
6
6
|
"eject": "vite eject",
|
|
@@ -44,29 +44,29 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"@abtnode/cron": "^1.16.36",
|
|
47
|
-
"@arcblock/did": "^1.18.
|
|
47
|
+
"@arcblock/did": "^1.18.166",
|
|
48
48
|
"@arcblock/did-auth-storage-nedb": "^1.7.1",
|
|
49
49
|
"@arcblock/did-connect": "^2.11.15",
|
|
50
|
-
"@arcblock/did-util": "^1.18.
|
|
51
|
-
"@arcblock/jwt": "^1.18.
|
|
50
|
+
"@arcblock/did-util": "^1.18.166",
|
|
51
|
+
"@arcblock/jwt": "^1.18.166",
|
|
52
52
|
"@arcblock/ux": "^2.11.15",
|
|
53
|
-
"@arcblock/validator": "^1.18.
|
|
53
|
+
"@arcblock/validator": "^1.18.166",
|
|
54
54
|
"@blocklet/js-sdk": "^1.16.36",
|
|
55
55
|
"@blocklet/logger": "^1.16.36",
|
|
56
|
-
"@blocklet/payment-react": "1.17.
|
|
56
|
+
"@blocklet/payment-react": "1.17.3",
|
|
57
57
|
"@blocklet/sdk": "^1.16.36",
|
|
58
58
|
"@blocklet/ui-react": "^2.11.15",
|
|
59
|
-
"@blocklet/uploader": "^0.1.
|
|
60
|
-
"@blocklet/xss": "^0.1.
|
|
59
|
+
"@blocklet/uploader": "^0.1.62",
|
|
60
|
+
"@blocklet/xss": "^0.1.19",
|
|
61
61
|
"@mui/icons-material": "^5.16.6",
|
|
62
62
|
"@mui/lab": "^5.0.0-alpha.173",
|
|
63
63
|
"@mui/material": "^5.16.6",
|
|
64
64
|
"@mui/system": "^5.16.6",
|
|
65
|
-
"@ocap/asset": "^1.18.
|
|
66
|
-
"@ocap/client": "^1.18.
|
|
67
|
-
"@ocap/mcrypto": "^1.18.
|
|
68
|
-
"@ocap/util": "^1.18.
|
|
69
|
-
"@ocap/wallet": "^1.18.
|
|
65
|
+
"@ocap/asset": "^1.18.166",
|
|
66
|
+
"@ocap/client": "^1.18.166",
|
|
67
|
+
"@ocap/mcrypto": "^1.18.166",
|
|
68
|
+
"@ocap/util": "^1.18.166",
|
|
69
|
+
"@ocap/wallet": "^1.18.166",
|
|
70
70
|
"@stripe/react-stripe-js": "^2.7.3",
|
|
71
71
|
"@stripe/stripe-js": "^2.4.0",
|
|
72
72
|
"ahooks": "^3.8.0",
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
"devDependencies": {
|
|
121
121
|
"@abtnode/types": "^1.16.36",
|
|
122
122
|
"@arcblock/eslint-config-ts": "^0.3.3",
|
|
123
|
-
"@blocklet/payment-types": "1.17.
|
|
123
|
+
"@blocklet/payment-types": "1.17.3",
|
|
124
124
|
"@types/cookie-parser": "^1.4.7",
|
|
125
125
|
"@types/cors": "^2.8.17",
|
|
126
126
|
"@types/debug": "^4.1.12",
|
|
@@ -166,5 +166,5 @@
|
|
|
166
166
|
"parser": "typescript"
|
|
167
167
|
}
|
|
168
168
|
},
|
|
169
|
-
"gitHead": "
|
|
169
|
+
"gitHead": "bc8adef7ff5a032048eef64dc1753bcee88acb98"
|
|
170
170
|
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
FormControl,
|
|
17
17
|
RadioGroup,
|
|
18
18
|
Radio,
|
|
19
|
+
Link,
|
|
19
20
|
} from '@mui/material';
|
|
20
21
|
import Dialog from '@arcblock/ux/lib/Dialog';
|
|
21
22
|
import { EventHandler, useState } from 'react';
|
|
@@ -23,7 +24,10 @@ import { api, formatAmountPrecisionLimit, Switch, useMobile } from '@blocklet/pa
|
|
|
23
24
|
import { useRequest } from 'ahooks';
|
|
24
25
|
import { BN, fromTokenToUnit, fromUnitToToken } from '@ocap/util';
|
|
25
26
|
import type { TPaymentCurrency, TSubscriptionExpanded } from '@blocklet/payment-types';
|
|
27
|
+
import { joinURL } from 'ufo';
|
|
28
|
+
import { OpenInNewOutlined } from '@mui/icons-material';
|
|
26
29
|
import Currency from '../currency';
|
|
30
|
+
import { formatSmartDuration, TimeUnit } from '../../libs/dayjs';
|
|
27
31
|
|
|
28
32
|
const fetchCycleAmount = (
|
|
29
33
|
subscriptionId: string,
|
|
@@ -58,7 +62,8 @@ type OverdraftProtectionDialogProps = {
|
|
|
58
62
|
onSave: (data: { enabled: boolean; additionalCount?: number; returnRemaining?: boolean }) => Promise<void>;
|
|
59
63
|
onCancel: EventHandler<any>;
|
|
60
64
|
open: boolean;
|
|
61
|
-
|
|
65
|
+
stakingAddress?: string;
|
|
66
|
+
payerAddress?: string;
|
|
62
67
|
currency: {
|
|
63
68
|
symbol: string;
|
|
64
69
|
logo: string;
|
|
@@ -66,10 +71,16 @@ type OverdraftProtectionDialogProps = {
|
|
|
66
71
|
maximum_precision?: number;
|
|
67
72
|
};
|
|
68
73
|
subscription: TSubscriptionExpanded;
|
|
74
|
+
initValues?: {
|
|
75
|
+
enabled: boolean;
|
|
76
|
+
return_stake: boolean;
|
|
77
|
+
} | null;
|
|
69
78
|
};
|
|
70
79
|
|
|
71
80
|
OverdraftProtectionDialog.defaultProps = {
|
|
72
|
-
|
|
81
|
+
payerAddress: '',
|
|
82
|
+
stakingAddress: '',
|
|
83
|
+
initValues: null,
|
|
73
84
|
};
|
|
74
85
|
|
|
75
86
|
export default function OverdraftProtectionDialog({
|
|
@@ -78,14 +89,16 @@ export default function OverdraftProtectionDialog({
|
|
|
78
89
|
onSave,
|
|
79
90
|
onCancel,
|
|
80
91
|
open,
|
|
81
|
-
|
|
92
|
+
stakingAddress,
|
|
93
|
+
payerAddress,
|
|
82
94
|
currency,
|
|
83
95
|
subscription,
|
|
96
|
+
initValues,
|
|
84
97
|
}: OverdraftProtectionDialogProps) {
|
|
85
98
|
const { t, locale } = useLocaleContext();
|
|
86
99
|
const { isMobile } = useMobile();
|
|
87
100
|
const [customAmount, setCustomAmount] = useState(false);
|
|
88
|
-
const [presetAmounts, setPresetAmounts] = useState<{ amount: string; cycles: number }[]>([]);
|
|
101
|
+
const [presetAmounts, setPresetAmounts] = useState<{ amount: string; cycles: number; label: string }[]>([]);
|
|
89
102
|
|
|
90
103
|
const {
|
|
91
104
|
data: cycleAmount = {
|
|
@@ -100,10 +113,47 @@ export default function OverdraftProtectionDialog({
|
|
|
100
113
|
{
|
|
101
114
|
refreshDeps: [subscription.id],
|
|
102
115
|
onSuccess: (data) => {
|
|
103
|
-
const presets =
|
|
116
|
+
const presets = (() => {
|
|
117
|
+
const { interval, interval_count: intervalCount } = subscription.pending_invoice_item_interval;
|
|
118
|
+
|
|
119
|
+
switch (interval) {
|
|
120
|
+
case 'hour':
|
|
121
|
+
return [
|
|
122
|
+
{ cycles: Math.ceil(24 / intervalCount), label: '1 day' },
|
|
123
|
+
{ cycles: Math.ceil((24 * 7) / intervalCount), label: '1 week' },
|
|
124
|
+
{ cycles: Math.ceil((24 * 30) / intervalCount), label: '1 month' },
|
|
125
|
+
{ cycles: Math.ceil((24 * 90) / intervalCount), label: '3 months' },
|
|
126
|
+
{ cycles: Math.ceil((24 * 180) / intervalCount), label: '6 months' },
|
|
127
|
+
];
|
|
128
|
+
case 'day':
|
|
129
|
+
return [
|
|
130
|
+
{ cycles: Math.ceil(7 / intervalCount), label: '1 week' },
|
|
131
|
+
{ cycles: Math.ceil(30 / intervalCount), label: '1 month' },
|
|
132
|
+
{ cycles: Math.ceil(90 / intervalCount), label: '3 months' },
|
|
133
|
+
{ cycles: Math.ceil(180 / intervalCount), label: '6 months' },
|
|
134
|
+
{ cycles: Math.ceil(365 / intervalCount), label: '1 year' },
|
|
135
|
+
];
|
|
136
|
+
default:
|
|
137
|
+
return [
|
|
138
|
+
{ cycles: 1, label: '1x' },
|
|
139
|
+
{ cycles: 2, label: '2x' },
|
|
140
|
+
{ cycles: 3, label: '6x' },
|
|
141
|
+
{ cycles: 6, label: '6x' },
|
|
142
|
+
{ cycles: 12, label: '12x' },
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
})();
|
|
146
|
+
|
|
104
147
|
const getCycleAmount = (cycles: number) =>
|
|
105
148
|
fromUnitToToken(new BN(data.amount).mul(new BN(cycles)).toString(), data?.currency?.decimal);
|
|
106
|
-
|
|
149
|
+
|
|
150
|
+
setPresetAmounts(
|
|
151
|
+
presets.map(({ cycles, label }) => ({
|
|
152
|
+
amount: getCycleAmount(cycles),
|
|
153
|
+
cycles,
|
|
154
|
+
label,
|
|
155
|
+
}))
|
|
156
|
+
);
|
|
107
157
|
},
|
|
108
158
|
}
|
|
109
159
|
);
|
|
@@ -117,6 +167,7 @@ export default function OverdraftProtectionDialog({
|
|
|
117
167
|
enabled: !!subscription.overdraft_protection?.enabled,
|
|
118
168
|
return_stake: false,
|
|
119
169
|
amount: '0',
|
|
170
|
+
...(initValues || {}),
|
|
120
171
|
},
|
|
121
172
|
mode: 'onChange',
|
|
122
173
|
});
|
|
@@ -146,16 +197,28 @@ export default function OverdraftProtectionDialog({
|
|
|
146
197
|
const totalIntervals = cycles * intervalCount;
|
|
147
198
|
const availableUnitKeys = ['hour', 'day', 'week', 'month', 'year'];
|
|
148
199
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
200
|
+
if (!availableUnitKeys.includes(interval)) {
|
|
201
|
+
return t('customer.overdraftProtection.estimatedDuration', {
|
|
202
|
+
duration: totalIntervals,
|
|
203
|
+
unit: t('customer.overdraftProtection.intervals').toLowerCase(),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return t('common.estimatedDuration', {
|
|
207
|
+
duration: formatSmartDuration(totalIntervals, interval as TimeUnit, {
|
|
208
|
+
t,
|
|
209
|
+
}),
|
|
156
210
|
});
|
|
157
211
|
};
|
|
158
212
|
|
|
213
|
+
const getStakingAddressURL = () => {
|
|
214
|
+
return joinURL(
|
|
215
|
+
subscription.paymentMethod?.settings.arcblock?.explorer_host as string,
|
|
216
|
+
'/stakes',
|
|
217
|
+
stakingAddress as string,
|
|
218
|
+
'/tokens'
|
|
219
|
+
);
|
|
220
|
+
};
|
|
221
|
+
|
|
159
222
|
return (
|
|
160
223
|
<Dialog
|
|
161
224
|
open={open}
|
|
@@ -207,16 +270,67 @@ export default function OverdraftProtectionDialog({
|
|
|
207
270
|
|
|
208
271
|
{isEnabled ? (
|
|
209
272
|
<Stack gap={1} sx={{ mt: '-8px' }}>
|
|
210
|
-
{
|
|
211
|
-
<Stack
|
|
273
|
+
{payerAddress && (
|
|
274
|
+
<Stack
|
|
275
|
+
sx={
|
|
276
|
+
isMobile
|
|
277
|
+
? {
|
|
278
|
+
flexDirection: 'column',
|
|
279
|
+
gap: 1,
|
|
280
|
+
}
|
|
281
|
+
: {
|
|
282
|
+
flexDirection: 'row',
|
|
283
|
+
alignItems: 'center',
|
|
284
|
+
gap: 2,
|
|
285
|
+
}
|
|
286
|
+
}>
|
|
212
287
|
<Typography variant="subtitle2" color="text.secondary">
|
|
213
|
-
{t('customer.overdraftProtection.
|
|
288
|
+
{t('customer.overdraftProtection.payerAddress')}
|
|
214
289
|
</Typography>
|
|
215
290
|
<Typography variant="body2" sx={{ wordBreak: 'break-all', fontFamily: 'monospace', fontWeight: '700' }}>
|
|
216
|
-
{
|
|
291
|
+
{payerAddress}
|
|
217
292
|
</Typography>
|
|
218
293
|
</Stack>
|
|
219
294
|
)}
|
|
295
|
+
|
|
296
|
+
{stakingAddress && (
|
|
297
|
+
<Stack
|
|
298
|
+
sx={
|
|
299
|
+
isMobile
|
|
300
|
+
? {
|
|
301
|
+
flexDirection: 'column',
|
|
302
|
+
gap: 1,
|
|
303
|
+
}
|
|
304
|
+
: {
|
|
305
|
+
flexDirection: 'row',
|
|
306
|
+
alignItems: 'center',
|
|
307
|
+
gap: 2,
|
|
308
|
+
}
|
|
309
|
+
}>
|
|
310
|
+
<Typography variant="subtitle2" color="text.secondary">
|
|
311
|
+
{t('customer.overdraftProtection.stakingAddress')}
|
|
312
|
+
</Typography>
|
|
313
|
+
<Link
|
|
314
|
+
href={getStakingAddressURL()}
|
|
315
|
+
target="_blank"
|
|
316
|
+
rel="noopener noreferrer"
|
|
317
|
+
sx={{
|
|
318
|
+
wordBreak: 'break-all',
|
|
319
|
+
fontFamily: 'monospace',
|
|
320
|
+
fontWeight: '700',
|
|
321
|
+
textDecoration: 'none',
|
|
322
|
+
color: 'text.link',
|
|
323
|
+
display: 'flex',
|
|
324
|
+
flexWrap: 'wrap',
|
|
325
|
+
alignItems: 'center',
|
|
326
|
+
gap: 1,
|
|
327
|
+
}}>
|
|
328
|
+
{stakingAddress}
|
|
329
|
+
{!isMobile && <OpenInNewOutlined fontSize="small" />}
|
|
330
|
+
</Link>
|
|
331
|
+
</Stack>
|
|
332
|
+
)}
|
|
333
|
+
|
|
220
334
|
<Typography variant="body2" color="text.secondary">
|
|
221
335
|
{(() => {
|
|
222
336
|
if (Number(dueAmount) > 0 && !value.enabled) {
|
|
@@ -346,7 +460,7 @@ export default function OverdraftProtectionDialog({
|
|
|
346
460
|
</FormControl>
|
|
347
461
|
</Stack>
|
|
348
462
|
)}
|
|
349
|
-
{amount && Number(amount) > 0 && !methods.formState.errors.amount && (
|
|
463
|
+
{amount && Number(amount) > 0 && Number(estimateAmount) > 0 && !methods.formState.errors.amount && (
|
|
350
464
|
<Typography variant="body2" sx={{ color: 'text.lighter', mt: '8px !important' }} fontSize={12}>
|
|
351
465
|
{t('customer.overdraftProtection.total', {
|
|
352
466
|
total: safeAdd(currency, amount, availableAmount),
|