payment-kit 1.17.4 → 1.17.6
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/crons/currency.ts +1 -1
- package/api/src/integrations/arcblock/stake.ts +4 -3
- package/api/src/libs/constants.ts +3 -0
- package/api/src/libs/invoice.ts +6 -5
- package/api/src/libs/notification/template/subscription-renew-failed.ts +4 -3
- package/api/src/libs/notification/template/subscription-renewed.ts +4 -3
- package/api/src/libs/notification/template/subscription-succeeded.ts +4 -3
- package/api/src/libs/notification/template/subscription-trial-start.ts +11 -3
- package/api/src/libs/notification/template/subscription-upgraded.ts +2 -1
- package/api/src/libs/payment.ts +5 -4
- package/api/src/libs/product.ts +24 -1
- package/api/src/queues/payment.ts +7 -5
- package/api/src/queues/refund.ts +8 -6
- package/api/src/routes/connect/change-payment.ts +3 -2
- package/api/src/routes/connect/change-plan.ts +3 -2
- package/api/src/routes/connect/collect-batch.ts +5 -4
- package/api/src/routes/connect/collect.ts +6 -5
- package/api/src/routes/connect/pay.ts +9 -4
- package/api/src/routes/connect/recharge.ts +9 -4
- package/api/src/routes/connect/setup.ts +3 -2
- package/api/src/routes/connect/shared.ts +25 -7
- package/api/src/routes/connect/subscribe.ts +3 -2
- package/api/src/routes/payment-currencies.ts +71 -10
- package/api/src/routes/payment-methods.ts +37 -21
- package/api/src/routes/payment-stats.ts +9 -3
- package/api/src/routes/prices.ts +19 -1
- package/api/src/routes/products.ts +60 -28
- package/api/src/routes/subscriptions.ts +4 -3
- package/api/src/store/models/payment-currency.ts +31 -0
- package/api/src/store/models/payment-method.ts +11 -8
- package/api/src/store/models/types.ts +27 -1
- package/blocklet.yml +1 -1
- package/package.json +19 -19
- package/public/methods/base.png +0 -0
- package/src/components/payment-currency/add.tsx +1 -1
- package/src/components/payment-currency/edit.tsx +73 -0
- package/src/components/payment-currency/form.tsx +12 -1
- package/src/components/payment-method/base.tsx +79 -0
- package/src/components/payment-method/form.tsx +3 -0
- package/src/components/price/upsell-select.tsx +1 -0
- package/src/components/subscription/metrics.tsx +1 -1
- package/src/components/subscription/portal/actions.tsx +1 -1
- package/src/libs/util.ts +1 -1
- package/src/locales/en.tsx +30 -1
- package/src/locales/zh.tsx +28 -0
- package/src/pages/admin/billing/invoices/detail.tsx +1 -1
- package/src/pages/admin/customers/customers/detail.tsx +2 -2
- package/src/pages/admin/overview.tsx +15 -2
- package/src/pages/admin/payments/intents/detail.tsx +1 -1
- package/src/pages/admin/payments/payouts/detail.tsx +1 -1
- package/src/pages/admin/payments/refunds/detail.tsx +1 -1
- package/src/pages/admin/products/links/detail.tsx +1 -0
- package/src/pages/admin/products/prices/actions.tsx +2 -1
- package/src/pages/admin/products/prices/detail.tsx +1 -0
- package/src/pages/admin/products/products/detail.tsx +1 -0
- package/src/pages/admin/settings/payment-methods/create.tsx +7 -0
- package/src/pages/admin/settings/payment-methods/index.tsx +180 -20
- package/src/pages/customer/index.tsx +1 -1
- package/src/pages/customer/invoice/detail.tsx +1 -1
- package/src/pages/customer/recharge.tsx +1 -1
- package/src/pages/customer/refund/list.tsx +7 -3
- package/src/pages/customer/subscription/change-payment.tsx +1 -1
|
@@ -64,6 +64,7 @@ import { createUsageRecordQueryFn } from './usage-records';
|
|
|
64
64
|
import { SubscriptionWillCanceledSchedule } from '../crons/subscription-will-canceled';
|
|
65
65
|
import { getTokenByAddress } from '../integrations/arcblock/stake';
|
|
66
66
|
import { ensureOverdraftProtectionPrice } from '../libs/overdraft-protection';
|
|
67
|
+
import { CHARGE_SUPPORTED_CHAIN_TYPES } from '../libs/constants';
|
|
67
68
|
|
|
68
69
|
const router = Router();
|
|
69
70
|
const auth = authenticate<Subscription>({ component: true, roles: ['owner', 'admin'] });
|
|
@@ -1766,7 +1767,7 @@ router.get('/:id/cycle-amount', authPortal, async (req, res) => {
|
|
|
1766
1767
|
|
|
1767
1768
|
if (req.query?.overdraftProtection) {
|
|
1768
1769
|
const { price } = await ensureOverdraftProtectionPrice(subscription.livemode);
|
|
1769
|
-
const invoicePrice = price
|
|
1770
|
+
const invoicePrice = (price?.currency_options || []).find((x: any) => x.currency_id === subscription?.currency_id);
|
|
1770
1771
|
const gas = invoicePrice?.unit_amount;
|
|
1771
1772
|
return res.json({
|
|
1772
1773
|
amount: new BN(maxAmount).add(new BN(gas)).toString(),
|
|
@@ -1856,7 +1857,7 @@ router.get('/:id/payer-token', authMine, async (req, res) => {
|
|
|
1856
1857
|
|
|
1857
1858
|
// @ts-ignore
|
|
1858
1859
|
const paymentAddress = getSubscriptionPaymentAddress(subscription, paymentMethod.type);
|
|
1859
|
-
if (!paymentAddress &&
|
|
1860
|
+
if (!paymentAddress && CHARGE_SUPPORTED_CHAIN_TYPES.includes(paymentMethod.type)) {
|
|
1860
1861
|
return res.status(400).json({ error: `Payer not found on subscription payment detail: ${subscription.id}` });
|
|
1861
1862
|
}
|
|
1862
1863
|
|
|
@@ -2015,7 +2016,7 @@ router.get('/:id/overdraft-protection', authPortal, async (req, res) => {
|
|
|
2015
2016
|
await isSubscriptionOverdraftProtectionEnabled(subscription);
|
|
2016
2017
|
const upcoming = await getUpcomingInvoiceAmount(req.params.id as string);
|
|
2017
2018
|
const { price } = await ensureOverdraftProtectionPrice(subscription.livemode);
|
|
2018
|
-
const invoicePrice = price
|
|
2019
|
+
const invoicePrice = (price?.currency_options || []).find((x: any) => x.currency_id === subscription?.currency_id);
|
|
2019
2020
|
const gas = invoicePrice?.unit_amount;
|
|
2020
2021
|
return res.json({
|
|
2021
2022
|
enabled,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
InferCreationAttributes,
|
|
8
8
|
Model,
|
|
9
9
|
Op,
|
|
10
|
+
QueryTypes,
|
|
10
11
|
} from 'sequelize';
|
|
11
12
|
|
|
12
13
|
import { createIdGenerator } from '../../libs/util';
|
|
@@ -145,6 +146,36 @@ export class PaymentCurrency extends Model<InferAttributes<PaymentCurrency>, Inf
|
|
|
145
146
|
...options,
|
|
146
147
|
});
|
|
147
148
|
}
|
|
149
|
+
|
|
150
|
+
public async isLocked(): Promise<boolean> {
|
|
151
|
+
const { PaymentMethod } = this.sequelize.models;
|
|
152
|
+
const method = (await PaymentMethod!.findByPk(this.payment_method_id)) as any;
|
|
153
|
+
return this.locked || method?.default_currency_id === this.id;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
public async isUsed(): Promise<boolean> {
|
|
157
|
+
const { Price } = this.sequelize.models;
|
|
158
|
+
const price = await Price!.findOne({
|
|
159
|
+
where: {
|
|
160
|
+
currency_id: this.id,
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
if (price) {
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
// @ts-ignore
|
|
167
|
+
const [{ count }] = await this.sequelize.query(
|
|
168
|
+
`SELECT count(p.id) AS count
|
|
169
|
+
FROM prices AS p
|
|
170
|
+
JOIN json_each(p.currency_options) AS option
|
|
171
|
+
ON json_extract(option.value, '$.currency_id') = ?`,
|
|
172
|
+
{
|
|
173
|
+
replacements: [this.id],
|
|
174
|
+
type: QueryTypes.SELECT,
|
|
175
|
+
}
|
|
176
|
+
);
|
|
177
|
+
return count > 0;
|
|
178
|
+
}
|
|
148
179
|
}
|
|
149
180
|
|
|
150
181
|
export type TPaymentCurrency = InferAttributes<PaymentCurrency>;
|
|
@@ -10,6 +10,7 @@ import type { LiteralUnion } from 'type-fest';
|
|
|
10
10
|
import { STRIPE_API_VERSION, createIdGenerator } from '../../libs/util';
|
|
11
11
|
import { sequelize } from '../sequelize';
|
|
12
12
|
import type { PaymentMethodSettings } from './types';
|
|
13
|
+
import { CHARGE_SUPPORTED_CHAIN_TYPES, EVM_CHAIN_TYPES } from '../../libs/constants';
|
|
13
14
|
|
|
14
15
|
const nextId = createIdGenerator('pm', 24);
|
|
15
16
|
|
|
@@ -26,7 +27,7 @@ export class PaymentMethod extends Model<InferAttributes<PaymentMethod>, InferCr
|
|
|
26
27
|
declare livemode: boolean;
|
|
27
28
|
declare locked: boolean;
|
|
28
29
|
|
|
29
|
-
declare type: LiteralUnion<'stripe' | 'arcblock' | 'ethereum' | 'bitcoin', string>;
|
|
30
|
+
declare type: LiteralUnion<'stripe' | 'arcblock' | 'ethereum' | 'bitcoin' | 'base', string>;
|
|
30
31
|
|
|
31
32
|
declare name: string;
|
|
32
33
|
|
|
@@ -77,7 +78,7 @@ export class PaymentMethod extends Model<InferAttributes<PaymentMethod>, InferCr
|
|
|
77
78
|
defaultValue: false,
|
|
78
79
|
},
|
|
79
80
|
type: {
|
|
80
|
-
type: DataTypes.ENUM('stripe', 'arcblock', 'ethereum', 'bitcoin'),
|
|
81
|
+
type: DataTypes.ENUM('stripe', 'arcblock', 'ethereum', 'bitcoin', 'base'),
|
|
81
82
|
},
|
|
82
83
|
name: {
|
|
83
84
|
type: DataTypes.STRING(64),
|
|
@@ -209,11 +210,12 @@ export class PaymentMethod extends Model<InferAttributes<PaymentMethod>, InferCr
|
|
|
209
210
|
}
|
|
210
211
|
|
|
211
212
|
getEvmClient() {
|
|
212
|
-
if (this.type
|
|
213
|
-
throw new Error(
|
|
213
|
+
if (!EVM_CHAIN_TYPES.includes(this.type)) {
|
|
214
|
+
throw new Error(`payment method ${this.type} is not EVM compatible`);
|
|
214
215
|
}
|
|
215
|
-
|
|
216
|
-
|
|
216
|
+
|
|
217
|
+
if (!this.settings[this.type as keyof PaymentMethodSettings]) {
|
|
218
|
+
throw new Error(`payment method config insufficient for ${this.type}`);
|
|
217
219
|
}
|
|
218
220
|
|
|
219
221
|
if (evmClients.has(this.id)) {
|
|
@@ -221,7 +223,8 @@ export class PaymentMethod extends Model<InferAttributes<PaymentMethod>, InferCr
|
|
|
221
223
|
}
|
|
222
224
|
|
|
223
225
|
const settings = PaymentMethod.decryptSettings(this.settings);
|
|
224
|
-
|
|
226
|
+
// @ts-ignore
|
|
227
|
+
const client = new ethers.JsonRpcProvider(settings[this.type as keyof PaymentMethodSettings]?.api_host);
|
|
225
228
|
evmClients.set(this.id, client);
|
|
226
229
|
|
|
227
230
|
return client as JsonRpcProvider;
|
|
@@ -229,7 +232,7 @@ export class PaymentMethod extends Model<InferAttributes<PaymentMethod>, InferCr
|
|
|
229
232
|
|
|
230
233
|
public static async supportAutoCharge(id: string) {
|
|
231
234
|
const method = await PaymentMethod.findByPk(id);
|
|
232
|
-
return method &&
|
|
235
|
+
return method && CHARGE_SUPPORTED_CHAIN_TYPES.includes(method.type);
|
|
233
236
|
}
|
|
234
237
|
}
|
|
235
238
|
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/indent */
|
|
2
2
|
import type { LiteralUnion } from 'type-fest';
|
|
3
3
|
|
|
4
|
+
export type EVMChainType = 'ethereum' | 'base';
|
|
5
|
+
|
|
6
|
+
export type NFTMintChainType = 'arcblock' | EVMChainType;
|
|
7
|
+
|
|
8
|
+
export type ChainType = 'arcblock' | 'bitcoin' | 'stripe' | EVMChainType;
|
|
9
|
+
|
|
4
10
|
export type GroupedBN = { [currencyId: string]: string };
|
|
5
11
|
export type GroupedStrList = { [currencyId: string]: string[] };
|
|
6
12
|
|
|
@@ -242,6 +248,10 @@ export type PaymentMethodOptions = {
|
|
|
242
248
|
payer: string;
|
|
243
249
|
hash?: string;
|
|
244
250
|
};
|
|
251
|
+
base?: {
|
|
252
|
+
payer: string;
|
|
253
|
+
hash?: string;
|
|
254
|
+
};
|
|
245
255
|
stripe?: {
|
|
246
256
|
payer: string;
|
|
247
257
|
};
|
|
@@ -271,6 +281,13 @@ export type PaymentMethodSettings = {
|
|
|
271
281
|
api_host: string;
|
|
272
282
|
explorer_host: string;
|
|
273
283
|
};
|
|
284
|
+
base?: {
|
|
285
|
+
chain_id: string;
|
|
286
|
+
api_host: string;
|
|
287
|
+
explorer_host: string;
|
|
288
|
+
native_symbol: string;
|
|
289
|
+
confirmation: number;
|
|
290
|
+
};
|
|
274
291
|
};
|
|
275
292
|
|
|
276
293
|
export type PaymentSettings = {
|
|
@@ -304,6 +321,14 @@ export type PaymentDetails = {
|
|
|
304
321
|
gas_price: string;
|
|
305
322
|
type?: LiteralUnion<'transfer' | 'approve', string>;
|
|
306
323
|
};
|
|
324
|
+
base?: {
|
|
325
|
+
tx_hash: string;
|
|
326
|
+
payer: string;
|
|
327
|
+
block_height: string;
|
|
328
|
+
gas_used: string;
|
|
329
|
+
gas_price: string;
|
|
330
|
+
type?: LiteralUnion<'transfer' | 'approve', string>;
|
|
331
|
+
};
|
|
307
332
|
bitcoin?: {
|
|
308
333
|
tx_hash: string;
|
|
309
334
|
payer: string;
|
|
@@ -370,9 +395,10 @@ export interface NftMintItem {
|
|
|
370
395
|
}
|
|
371
396
|
|
|
372
397
|
export type NftMintDetails = {
|
|
373
|
-
type:
|
|
398
|
+
type: NFTMintChainType;
|
|
374
399
|
arcblock?: NftMintItem;
|
|
375
400
|
ethereum?: NftMintItem;
|
|
401
|
+
base?: NftMintItem;
|
|
376
402
|
};
|
|
377
403
|
|
|
378
404
|
export type SubscriptionData = {
|
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.6",
|
|
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.37",
|
|
47
|
-
"@arcblock/did": "^1.19.
|
|
47
|
+
"@arcblock/did": "^1.19.3",
|
|
48
48
|
"@arcblock/did-auth-storage-nedb": "^1.7.1",
|
|
49
|
-
"@arcblock/did-connect": "^2.11.
|
|
50
|
-
"@arcblock/did-util": "^1.19.
|
|
51
|
-
"@arcblock/jwt": "^1.19.
|
|
52
|
-
"@arcblock/ux": "^2.11.
|
|
53
|
-
"@arcblock/validator": "^1.19.
|
|
49
|
+
"@arcblock/did-connect": "^2.11.27",
|
|
50
|
+
"@arcblock/did-util": "^1.19.3",
|
|
51
|
+
"@arcblock/jwt": "^1.19.3",
|
|
52
|
+
"@arcblock/ux": "^2.11.27",
|
|
53
|
+
"@arcblock/validator": "^1.19.3",
|
|
54
54
|
"@blocklet/js-sdk": "^1.16.37",
|
|
55
55
|
"@blocklet/logger": "^1.16.37",
|
|
56
|
-
"@blocklet/payment-react": "1.17.
|
|
56
|
+
"@blocklet/payment-react": "1.17.6",
|
|
57
57
|
"@blocklet/sdk": "^1.16.37",
|
|
58
|
-
"@blocklet/ui-react": "^2.11.
|
|
59
|
-
"@blocklet/uploader": "^0.1.
|
|
60
|
-
"@blocklet/xss": "^0.1.
|
|
58
|
+
"@blocklet/ui-react": "^2.11.27",
|
|
59
|
+
"@blocklet/uploader": "^0.1.64",
|
|
60
|
+
"@blocklet/xss": "^0.1.21",
|
|
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.19.
|
|
66
|
-
"@ocap/client": "^1.19.
|
|
67
|
-
"@ocap/mcrypto": "^1.19.
|
|
68
|
-
"@ocap/util": "^1.19.
|
|
69
|
-
"@ocap/wallet": "^1.19.
|
|
65
|
+
"@ocap/asset": "^1.19.3",
|
|
66
|
+
"@ocap/client": "^1.19.3",
|
|
67
|
+
"@ocap/mcrypto": "^1.19.3",
|
|
68
|
+
"@ocap/util": "^1.19.3",
|
|
69
|
+
"@ocap/wallet": "^1.19.3",
|
|
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.37",
|
|
122
122
|
"@arcblock/eslint-config-ts": "^0.3.3",
|
|
123
|
-
"@blocklet/payment-types": "1.17.
|
|
123
|
+
"@blocklet/payment-types": "1.17.6",
|
|
124
124
|
"@types/cookie-parser": "^1.4.7",
|
|
125
125
|
"@types/cors": "^2.8.17",
|
|
126
126
|
"@types/debug": "^4.1.12",
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
"vite": "^5.3.5",
|
|
151
151
|
"vite-node": "^2.0.4",
|
|
152
152
|
"vite-plugin-babel-import": "^2.0.5",
|
|
153
|
-
"vite-plugin-blocklet": "^0.9.
|
|
153
|
+
"vite-plugin-blocklet": "^0.9.16",
|
|
154
154
|
"vite-plugin-node-polyfills": "^0.21.0",
|
|
155
155
|
"vite-plugin-svgr": "^4.2.0",
|
|
156
156
|
"vite-tsconfig-paths": "^4.3.2",
|
|
@@ -166,5 +166,5 @@
|
|
|
166
166
|
"parser": "typescript"
|
|
167
167
|
}
|
|
168
168
|
},
|
|
169
|
-
"gitHead": "
|
|
169
|
+
"gitHead": "50e93ab4340be53c9d9bd18718c6db26048578c5"
|
|
170
170
|
}
|
|
Binary file
|
|
@@ -12,7 +12,7 @@ import { dispatch } from 'use-bus';
|
|
|
12
12
|
import DrawerForm from '../drawer-form';
|
|
13
13
|
import PaymentCurrencyForm from './form';
|
|
14
14
|
|
|
15
|
-
export default function PaymentCurrencyAdd({ method, onClose }: { method: TPaymentMethod; onClose:
|
|
15
|
+
export default function PaymentCurrencyAdd({ method, onClose }: { method: TPaymentMethod; onClose: () => void }) {
|
|
16
16
|
const { t } = useLocaleContext();
|
|
17
17
|
const [state, setState] = useSetState({ loading: false });
|
|
18
18
|
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/* eslint-disable no-nested-ternary */
|
|
2
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
+
import Toast from '@arcblock/ux/lib/Toast';
|
|
4
|
+
import { api, formatError } from '@blocklet/payment-react';
|
|
5
|
+
import type { TPaymentCurrency, TPaymentMethod } from '@blocklet/payment-types';
|
|
6
|
+
import { AddOutlined } from '@mui/icons-material';
|
|
7
|
+
import { Button, CircularProgress } from '@mui/material';
|
|
8
|
+
import { useSetState } from 'ahooks';
|
|
9
|
+
import { FormProvider, useForm } from 'react-hook-form';
|
|
10
|
+
import { dispatch } from 'use-bus';
|
|
11
|
+
|
|
12
|
+
import DrawerForm from '../drawer-form';
|
|
13
|
+
import PaymentCurrencyForm from './form';
|
|
14
|
+
|
|
15
|
+
export default function PaymentCurrencyEdit({
|
|
16
|
+
method,
|
|
17
|
+
onClose,
|
|
18
|
+
value,
|
|
19
|
+
}: {
|
|
20
|
+
method: TPaymentMethod;
|
|
21
|
+
onClose: () => void;
|
|
22
|
+
value: TPaymentCurrency;
|
|
23
|
+
}) {
|
|
24
|
+
const { t } = useLocaleContext();
|
|
25
|
+
const [state, setState] = useSetState({ loading: false });
|
|
26
|
+
|
|
27
|
+
const methods = useForm<TPaymentCurrency>({
|
|
28
|
+
defaultValues: {
|
|
29
|
+
payment_method_id: method.id,
|
|
30
|
+
name: value?.name,
|
|
31
|
+
description: value?.description,
|
|
32
|
+
logo: value?.logo,
|
|
33
|
+
contract: value?.contract,
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
const { handleSubmit } = methods;
|
|
37
|
+
|
|
38
|
+
const onSubmit = (data: TPaymentCurrency) => {
|
|
39
|
+
setState({ loading: true });
|
|
40
|
+
api
|
|
41
|
+
.put(`/api/payment-currencies/${value.id}`, data)
|
|
42
|
+
.then(() => {
|
|
43
|
+
setState({ loading: false });
|
|
44
|
+
Toast.success(t('admin.paymentCurrency.saved'));
|
|
45
|
+
methods.reset();
|
|
46
|
+
dispatch('drawer.submitted');
|
|
47
|
+
dispatch('paymentCurrency.updated');
|
|
48
|
+
})
|
|
49
|
+
.catch((err) => {
|
|
50
|
+
setState({ loading: false });
|
|
51
|
+
console.error(err);
|
|
52
|
+
Toast.error(formatError(err));
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<DrawerForm
|
|
58
|
+
open
|
|
59
|
+
icon={<AddOutlined />}
|
|
60
|
+
onClose={onClose}
|
|
61
|
+
text={t('admin.paymentCurrency.edit')}
|
|
62
|
+
width={640}
|
|
63
|
+
addons={
|
|
64
|
+
<Button variant="contained" size="small" onClick={handleSubmit(onSubmit)} disabled={state.loading}>
|
|
65
|
+
{state.loading ? <CircularProgress size="small" /> : t('admin.paymentCurrency.save')}
|
|
66
|
+
</Button>
|
|
67
|
+
}>
|
|
68
|
+
<FormProvider {...methods}>
|
|
69
|
+
<PaymentCurrencyForm disableKeys={['contract']} />
|
|
70
|
+
</FormProvider>
|
|
71
|
+
</DrawerForm>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
@@ -6,7 +6,15 @@ import { useFormContext, useWatch } from 'react-hook-form';
|
|
|
6
6
|
|
|
7
7
|
import Uploader from '../uploader';
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
type TPaymentCurrencyFormProps = {
|
|
10
|
+
disableKeys?: string[];
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
PaymentCurrencyForm.defaultProps = {
|
|
14
|
+
disableKeys: [],
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export default function PaymentCurrencyForm({ disableKeys = [] }: TPaymentCurrencyFormProps) {
|
|
10
18
|
const { t } = useLocaleContext();
|
|
11
19
|
const { control, setValue } = useFormContext();
|
|
12
20
|
const logo = useWatch({ control, name: 'logo' });
|
|
@@ -29,6 +37,7 @@ export default function PaymentCurrencyForm() {
|
|
|
29
37
|
rules={{ required: true }}
|
|
30
38
|
label={t('admin.paymentMethod.name.label')}
|
|
31
39
|
placeholder={t('admin.paymentMethod.name.tip')}
|
|
40
|
+
disabled={disableKeys.includes('name')}
|
|
32
41
|
/>
|
|
33
42
|
<FormInput
|
|
34
43
|
key="description"
|
|
@@ -37,6 +46,7 @@ export default function PaymentCurrencyForm() {
|
|
|
37
46
|
rules={{ required: true }}
|
|
38
47
|
label={t('admin.paymentMethod.description.label')}
|
|
39
48
|
placeholder={t('admin.paymentMethod.description.tip')}
|
|
49
|
+
disabled={disableKeys.includes('description')}
|
|
40
50
|
/>
|
|
41
51
|
<FormInput
|
|
42
52
|
key="contract"
|
|
@@ -45,6 +55,7 @@ export default function PaymentCurrencyForm() {
|
|
|
45
55
|
rules={{ required: true }}
|
|
46
56
|
label={t('admin.paymentCurrency.contract.label')}
|
|
47
57
|
placeholder={t('admin.paymentCurrency.contract.tip')}
|
|
58
|
+
disabled={disableKeys.includes('contract')}
|
|
48
59
|
/>
|
|
49
60
|
<Stack direction="column">
|
|
50
61
|
<Typography mb={1}>{t('admin.paymentCurrency.logo.label')}</Typography>
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/* eslint-disable no-nested-ternary */
|
|
2
|
+
import { useLocaleContext } from '@arcblock/ux/lib/Locale/context';
|
|
3
|
+
import { FormInput } from '@blocklet/payment-react';
|
|
4
|
+
import { Stack, Typography } from '@mui/material';
|
|
5
|
+
import { useFormContext, useWatch } from 'react-hook-form';
|
|
6
|
+
|
|
7
|
+
import Uploader from '../uploader';
|
|
8
|
+
|
|
9
|
+
export default function BaseMethodForm() {
|
|
10
|
+
const { t } = useLocaleContext();
|
|
11
|
+
const { control, setValue } = useFormContext();
|
|
12
|
+
const logo = useWatch({ control, name: 'logo' });
|
|
13
|
+
|
|
14
|
+
const onUploaded = (result: any) => {
|
|
15
|
+
if (!result.url) {
|
|
16
|
+
setValue('logo', '');
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
const tmp = new URL(result.url);
|
|
20
|
+
setValue('logo', tmp.pathname);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<>
|
|
25
|
+
<FormInput
|
|
26
|
+
key="name"
|
|
27
|
+
name="name"
|
|
28
|
+
type="text"
|
|
29
|
+
rules={{ required: true }}
|
|
30
|
+
label={t('admin.paymentMethod.name.label')}
|
|
31
|
+
placeholder={t('admin.paymentMethod.name.tip')}
|
|
32
|
+
/>
|
|
33
|
+
<FormInput
|
|
34
|
+
key="description"
|
|
35
|
+
name="description"
|
|
36
|
+
type="text"
|
|
37
|
+
rules={{ required: true }}
|
|
38
|
+
label={t('admin.paymentMethod.description.label')}
|
|
39
|
+
placeholder={t('admin.paymentMethod.description.tip')}
|
|
40
|
+
/>
|
|
41
|
+
<FormInput
|
|
42
|
+
key="api_host"
|
|
43
|
+
name="settings.base.api_host"
|
|
44
|
+
type="text"
|
|
45
|
+
rules={{ required: true }}
|
|
46
|
+
label={t('admin.paymentMethod.base.api_host.label')}
|
|
47
|
+
placeholder={t('admin.paymentMethod.base.api_host.tip')}
|
|
48
|
+
/>
|
|
49
|
+
<FormInput
|
|
50
|
+
key="explorer_host"
|
|
51
|
+
name="settings.base.explorer_host"
|
|
52
|
+
type="text"
|
|
53
|
+
rules={{ required: true }}
|
|
54
|
+
label={t('admin.paymentMethod.base.explorer_host.label')}
|
|
55
|
+
placeholder={t('admin.paymentMethod.base.explorer_host.tip')}
|
|
56
|
+
/>
|
|
57
|
+
<FormInput
|
|
58
|
+
key="native_symbol"
|
|
59
|
+
name="settings.base.native_symbol"
|
|
60
|
+
type="text"
|
|
61
|
+
rules={{ required: true }}
|
|
62
|
+
label={t('admin.paymentMethod.base.native_symbol.label')}
|
|
63
|
+
placeholder={t('admin.paymentMethod.base.native_symbol.tip')}
|
|
64
|
+
/>
|
|
65
|
+
<FormInput
|
|
66
|
+
key="confirmation"
|
|
67
|
+
name="settings.base.confirmation"
|
|
68
|
+
type="number"
|
|
69
|
+
rules={{ required: true }}
|
|
70
|
+
label={t('admin.paymentMethod.base.confirmation.label')}
|
|
71
|
+
placeholder={t('admin.paymentMethod.base.confirmation.tip')}
|
|
72
|
+
/>
|
|
73
|
+
<Stack direction="column">
|
|
74
|
+
<Typography mb={1}>{t('admin.paymentCurrency.logo.label')}</Typography>
|
|
75
|
+
<Uploader onUploaded={onUploaded} preview={logo} />
|
|
76
|
+
</Stack>
|
|
77
|
+
</>
|
|
78
|
+
);
|
|
79
|
+
}
|
|
@@ -7,6 +7,7 @@ import ArcBlockMethodForm from './arcblock';
|
|
|
7
7
|
import BitcoinMethodForm from './bitcoin';
|
|
8
8
|
import EthereumMethodForm from './ethereum';
|
|
9
9
|
import StripeMethodForm from './stripe';
|
|
10
|
+
import BaseMethodForm from './base';
|
|
10
11
|
|
|
11
12
|
export default function PaymentMethodForm() {
|
|
12
13
|
const { t } = useLocaleContext();
|
|
@@ -31,6 +32,7 @@ export default function PaymentMethodForm() {
|
|
|
31
32
|
<ToggleButton value="arcblock">ArcBlock</ToggleButton>
|
|
32
33
|
<ToggleButton value="stripe">Stripe</ToggleButton>
|
|
33
34
|
<ToggleButton value="ethereum">Ethereum</ToggleButton>
|
|
35
|
+
<ToggleButton value="base">Base</ToggleButton>
|
|
34
36
|
<ToggleButton value="bitcoin" disabled>
|
|
35
37
|
Bitcoin
|
|
36
38
|
</ToggleButton>
|
|
@@ -43,6 +45,7 @@ export default function PaymentMethodForm() {
|
|
|
43
45
|
{type === 'stripe' && <StripeMethodForm />}
|
|
44
46
|
{type === 'arcblock' && <ArcBlockMethodForm />}
|
|
45
47
|
{type === 'ethereum' && <EthereumMethodForm />}
|
|
48
|
+
{type === 'base' && <BaseMethodForm />}
|
|
46
49
|
{type === 'bitcoin' && <BitcoinMethodForm />}
|
|
47
50
|
</Root>
|
|
48
51
|
);
|
|
@@ -32,7 +32,7 @@ export default function SubscriptionMetrics({ subscription, showBalance = true }
|
|
|
32
32
|
ready: showBalance,
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
const supportShowBalance = showBalance && ['arcblock', 'ethereum'].includes(subscription.paymentMethod.type);
|
|
35
|
+
const supportShowBalance = showBalance && ['arcblock', 'ethereum', 'base'].includes(subscription.paymentMethod.type);
|
|
36
36
|
// let scheduleToCancelTime = 0;
|
|
37
37
|
// if (['active', 'trialing', 'past_due'].includes(subscription.status) && subscription.cancel_at) {
|
|
38
38
|
// scheduleToCancelTime = subscription.cancel_at * 1000;
|
|
@@ -121,7 +121,7 @@ const fetchExtraActions = async ({
|
|
|
121
121
|
const supportRecharge = (subscription: TSubscriptionExpanded) => {
|
|
122
122
|
return (
|
|
123
123
|
['active', 'trialing', 'past_due'].includes(subscription?.status) &&
|
|
124
|
-
['arcblock', 'ethereum'].includes(subscription?.paymentMethod?.type)
|
|
124
|
+
['arcblock', 'ethereum', 'base'].includes(subscription?.paymentMethod?.type)
|
|
125
125
|
);
|
|
126
126
|
};
|
|
127
127
|
|
package/src/libs/util.ts
CHANGED
|
@@ -310,7 +310,7 @@ export function getInvoiceUsageReportStartEnd(invoice: TInvoiceExpanded, showPre
|
|
|
310
310
|
}
|
|
311
311
|
const cycle = getRecurringPeriod(subscription.pending_invoice_item_interval);
|
|
312
312
|
let offset = 0;
|
|
313
|
-
if (['arcblock', 'ethereum'].includes(paymentMethod.type)) {
|
|
313
|
+
if (['arcblock', 'ethereum', 'base'].includes(paymentMethod.type)) {
|
|
314
314
|
switch (invoice?.billing_reason) {
|
|
315
315
|
case 'subscription_cycle':
|
|
316
316
|
offset = cycle / 1000;
|
package/src/locales/en.tsx
CHANGED
|
@@ -325,6 +325,9 @@ export default flat({
|
|
|
325
325
|
save: 'Save payment method',
|
|
326
326
|
saved: 'Payment method successfully saved',
|
|
327
327
|
settings: 'Settings',
|
|
328
|
+
gasTip:
|
|
329
|
+
'Ensure your account on the {chain} network has sufficient balance to cover transaction fees when using {method}.',
|
|
330
|
+
showQR: 'Show QR Code',
|
|
328
331
|
props: {
|
|
329
332
|
type: 'Type',
|
|
330
333
|
confirmation: 'Confirmation',
|
|
@@ -395,12 +398,38 @@ export default flat({
|
|
|
395
398
|
tip: 'How many blocks since transaction execution',
|
|
396
399
|
},
|
|
397
400
|
},
|
|
401
|
+
base: {
|
|
402
|
+
chain_id: {
|
|
403
|
+
label: 'Chain ID',
|
|
404
|
+
tip: 'Must be a valid EVM chain id, usually an integer, https://chainlist.org',
|
|
405
|
+
},
|
|
406
|
+
api_host: {
|
|
407
|
+
label: 'RPC Endpoint',
|
|
408
|
+
tip: 'The RPC endpoint to send transaction to',
|
|
409
|
+
},
|
|
410
|
+
explorer_host: {
|
|
411
|
+
label: 'Explorer Host',
|
|
412
|
+
tip: 'The webapp endpoint to view transaction details',
|
|
413
|
+
},
|
|
414
|
+
native_symbol: {
|
|
415
|
+
label: 'Native Symbol',
|
|
416
|
+
tip: 'The symbol for native token on this chain',
|
|
417
|
+
},
|
|
418
|
+
confirmation: {
|
|
419
|
+
label: 'Confirmation Count',
|
|
420
|
+
tip: 'How many blocks since transaction execution',
|
|
421
|
+
},
|
|
422
|
+
},
|
|
398
423
|
},
|
|
399
424
|
paymentCurrency: {
|
|
400
425
|
name: 'Payment Currency',
|
|
401
|
-
add: 'Add
|
|
426
|
+
add: 'Add Currency',
|
|
427
|
+
edit: 'Edit Currency',
|
|
402
428
|
save: 'Save payment currency',
|
|
403
429
|
saved: 'Payment currency successfully saved',
|
|
430
|
+
delete: 'Delete payment currency',
|
|
431
|
+
deleteConfirm: 'Are you sure you want to delete this payment currency? Once deleted, it cannot be recovered',
|
|
432
|
+
deleted: 'Payment currency successfully deleted',
|
|
404
433
|
logo: {
|
|
405
434
|
label: 'Logo',
|
|
406
435
|
tip: 'Displayed on payment page',
|
package/src/locales/zh.tsx
CHANGED
|
@@ -315,6 +315,8 @@ export default flat({
|
|
|
315
315
|
save: '保存支付方式',
|
|
316
316
|
saved: '支付方式已成功保存',
|
|
317
317
|
settings: '设置',
|
|
318
|
+
gasTip: '使用 {method} 支付需保证账户在 {chain} 链上有余额支付手续费',
|
|
319
|
+
showQR: '显示二维码',
|
|
318
320
|
props: {
|
|
319
321
|
type: '类型',
|
|
320
322
|
confirmation: '确认',
|
|
@@ -385,12 +387,38 @@ export default flat({
|
|
|
385
387
|
tip: '交易标记为确认需要的区块数',
|
|
386
388
|
},
|
|
387
389
|
},
|
|
390
|
+
base: {
|
|
391
|
+
chain_id: {
|
|
392
|
+
label: '链 ID',
|
|
393
|
+
tip: '必须是有效的EVM链ID,通常是一个整数,https://chainlist.org',
|
|
394
|
+
},
|
|
395
|
+
api_host: {
|
|
396
|
+
label: 'RPC 端点',
|
|
397
|
+
tip: '发送交易的RPC端点',
|
|
398
|
+
},
|
|
399
|
+
explorer_host: {
|
|
400
|
+
label: '区块浏览器',
|
|
401
|
+
tip: '查看交易详情的区块浏览器',
|
|
402
|
+
},
|
|
403
|
+
native_symbol: {
|
|
404
|
+
label: '货币符号',
|
|
405
|
+
tip: '链上主货币符号',
|
|
406
|
+
},
|
|
407
|
+
confirmation: {
|
|
408
|
+
label: '确认区块数',
|
|
409
|
+
tip: '交易标记为确认需要的区块数',
|
|
410
|
+
},
|
|
411
|
+
},
|
|
388
412
|
},
|
|
389
413
|
paymentCurrency: {
|
|
390
414
|
name: '支付货币',
|
|
391
415
|
add: '添加货币',
|
|
416
|
+
edit: '编辑货币',
|
|
392
417
|
save: '保存货币',
|
|
393
418
|
saved: '货币已成功保存',
|
|
419
|
+
delete: '删除货币',
|
|
420
|
+
deleteConfirm: '确定要删除此货币吗?一旦删除,将无法恢复',
|
|
421
|
+
deleted: '货币已成功删除',
|
|
394
422
|
logo: {
|
|
395
423
|
label: 'Logo',
|
|
396
424
|
tip: '在支付页面显示',
|
|
@@ -304,7 +304,7 @@ export default function InvoiceDetail(props: { id: string }) {
|
|
|
304
304
|
direction={InfoDirection}
|
|
305
305
|
alignItems={InfoAlignItems}
|
|
306
306
|
/>
|
|
307
|
-
{!!data.paymentIntent?.payment_details?.ethereum && (
|
|
307
|
+
{(!!data.paymentIntent?.payment_details?.ethereum || !!data.paymentIntent?.payment_details?.base) && (
|
|
308
308
|
<InfoRow
|
|
309
309
|
label={t('common.txGas')}
|
|
310
310
|
value={<TxGas details={data.paymentIntent.payment_details as any} method={data.paymentMethod} />}
|