payment-kit 1.17.3 → 1.17.5

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.
Files changed (63) hide show
  1. package/api/src/crons/currency.ts +1 -1
  2. package/api/src/integrations/arcblock/nft.ts +1 -1
  3. package/api/src/integrations/arcblock/stake.ts +4 -3
  4. package/api/src/libs/constants.ts +3 -0
  5. package/api/src/libs/invoice.ts +6 -5
  6. package/api/src/libs/middleware.ts +2 -2
  7. package/api/src/libs/notification/template/subscription-renew-failed.ts +4 -3
  8. package/api/src/libs/notification/template/subscription-renewed.ts +4 -3
  9. package/api/src/libs/notification/template/subscription-succeeded.ts +4 -3
  10. package/api/src/libs/notification/template/subscription-trial-start.ts +11 -3
  11. package/api/src/libs/notification/template/subscription-upgraded.ts +2 -1
  12. package/api/src/libs/payment.ts +5 -4
  13. package/api/src/libs/product.ts +24 -1
  14. package/api/src/libs/security.ts +2 -2
  15. package/api/src/locales/en.ts +2 -2
  16. package/api/src/queues/payment.ts +7 -5
  17. package/api/src/queues/refund.ts +8 -6
  18. package/api/src/routes/connect/change-payment.ts +3 -2
  19. package/api/src/routes/connect/change-plan.ts +3 -2
  20. package/api/src/routes/connect/collect-batch.ts +5 -4
  21. package/api/src/routes/connect/collect.ts +6 -5
  22. package/api/src/routes/connect/pay.ts +9 -4
  23. package/api/src/routes/connect/recharge.ts +9 -4
  24. package/api/src/routes/connect/setup.ts +3 -2
  25. package/api/src/routes/connect/shared.ts +25 -7
  26. package/api/src/routes/connect/subscribe.ts +3 -2
  27. package/api/src/routes/payment-currencies.ts +11 -10
  28. package/api/src/routes/payment-methods.ts +35 -19
  29. package/api/src/routes/payment-stats.ts +9 -3
  30. package/api/src/routes/prices.ts +19 -1
  31. package/api/src/routes/products.ts +60 -28
  32. package/api/src/routes/subscriptions.ts +4 -3
  33. package/api/src/store/models/payment-method.ts +11 -8
  34. package/api/src/store/models/types.ts +27 -1
  35. package/blocklet.yml +1 -1
  36. package/package.json +24 -24
  37. package/public/methods/base.png +0 -0
  38. package/src/components/customer/overdraft-protection.tsx +87 -56
  39. package/src/components/payment-method/base.tsx +79 -0
  40. package/src/components/payment-method/form.tsx +3 -0
  41. package/src/components/price/upsell-select.tsx +1 -0
  42. package/src/components/subscription/metrics.tsx +1 -1
  43. package/src/components/subscription/portal/actions.tsx +1 -1
  44. package/src/libs/util.ts +1 -1
  45. package/src/locales/en.tsx +36 -11
  46. package/src/locales/zh.tsx +24 -0
  47. package/src/pages/admin/billing/invoices/detail.tsx +1 -1
  48. package/src/pages/admin/customers/customers/detail.tsx +2 -2
  49. package/src/pages/admin/overview.tsx +15 -2
  50. package/src/pages/admin/payments/intents/detail.tsx +1 -1
  51. package/src/pages/admin/payments/payouts/detail.tsx +1 -1
  52. package/src/pages/admin/payments/refunds/detail.tsx +1 -1
  53. package/src/pages/admin/products/links/detail.tsx +1 -0
  54. package/src/pages/admin/products/prices/actions.tsx +2 -1
  55. package/src/pages/admin/products/prices/detail.tsx +1 -0
  56. package/src/pages/admin/products/products/detail.tsx +1 -0
  57. package/src/pages/admin/settings/payment-methods/create.tsx +7 -0
  58. package/src/pages/admin/settings/payment-methods/index.tsx +99 -11
  59. package/src/pages/customer/index.tsx +1 -1
  60. package/src/pages/customer/invoice/detail.tsx +1 -1
  61. package/src/pages/customer/recharge.tsx +1 -1
  62. package/src/pages/customer/refund/list.tsx +7 -3
  63. 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.currency_options.find((x: any) => x.currency_id === subscription?.currency_id);
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 && ['ethereum', 'arcblock'].includes(paymentMethod.type)) {
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.currency_options.find((x: any) => x.currency_id === subscription?.currency_id);
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,
@@ -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 !== 'ethereum') {
213
- throw new Error('payment method is not ethereum');
213
+ if (!EVM_CHAIN_TYPES.includes(this.type)) {
214
+ throw new Error(`payment method ${this.type} is not EVM compatible`);
214
215
  }
215
- if (!this.settings.ethereum) {
216
- throw new Error('payment method config insufficient for ethereum');
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
- const client = new ethers.JsonRpcProvider(settings.ethereum?.api_host);
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 && ['arcblock', 'ethereum'].includes(method.type);
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: LiteralUnion<'arcblock' | 'ethereum' | 'bitcoin', string>;
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
@@ -14,7 +14,7 @@ repository:
14
14
  type: git
15
15
  url: git+https://github.com/blocklet/payment-kit.git
16
16
  specVersion: 1.2.8
17
- version: 1.17.3
17
+ version: 1.17.5
18
18
  logo: logo.png
19
19
  files:
20
20
  - dist
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "payment-kit",
3
- "version": "1.17.3",
3
+ "version": "1.17.5",
4
4
  "scripts": {
5
5
  "dev": "blocklet dev --open",
6
6
  "eject": "vite eject",
@@ -43,30 +43,30 @@
43
43
  ]
44
44
  },
45
45
  "dependencies": {
46
- "@abtnode/cron": "^1.16.36",
47
- "@arcblock/did": "^1.18.166",
46
+ "@abtnode/cron": "^1.16.37",
47
+ "@arcblock/did": "^1.19.3",
48
48
  "@arcblock/did-auth-storage-nedb": "^1.7.1",
49
- "@arcblock/did-connect": "^2.11.15",
50
- "@arcblock/did-util": "^1.18.166",
51
- "@arcblock/jwt": "^1.18.166",
52
- "@arcblock/ux": "^2.11.15",
53
- "@arcblock/validator": "^1.18.166",
54
- "@blocklet/js-sdk": "^1.16.36",
55
- "@blocklet/logger": "^1.16.36",
56
- "@blocklet/payment-react": "1.17.3",
57
- "@blocklet/sdk": "^1.16.36",
58
- "@blocklet/ui-react": "^2.11.15",
59
- "@blocklet/uploader": "^0.1.62",
60
- "@blocklet/xss": "^0.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
+ "@blocklet/js-sdk": "^1.16.37",
55
+ "@blocklet/logger": "^1.16.37",
56
+ "@blocklet/payment-react": "1.17.5",
57
+ "@blocklet/sdk": "^1.16.37",
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.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",
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",
@@ -118,9 +118,9 @@
118
118
  "validator": "^13.12.0"
119
119
  },
120
120
  "devDependencies": {
121
- "@abtnode/types": "^1.16.36",
121
+ "@abtnode/types": "^1.16.37",
122
122
  "@arcblock/eslint-config-ts": "^0.3.3",
123
- "@blocklet/payment-types": "1.17.3",
123
+ "@blocklet/payment-types": "1.17.5",
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.14",
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": "bc8adef7ff5a032048eef64dc1753bcee88acb98"
169
+ "gitHead": "7713be8272f1056796820a9d52f742ed63899900"
170
170
  }
Binary file
@@ -17,6 +17,7 @@ import {
17
17
  RadioGroup,
18
18
  Radio,
19
19
  Link,
20
+ Skeleton,
20
21
  } from '@mui/material';
21
22
  import Dialog from '@arcblock/ux/lib/Dialog';
22
23
  import { EventHandler, useState } from 'react';
@@ -105,6 +106,7 @@ export default function OverdraftProtectionDialog({
105
106
  amount: '0',
106
107
  gas: '0',
107
108
  },
109
+ loading: cycleAmountLoading,
108
110
  } = useRequest(
109
111
  () =>
110
112
  fetchCycleAmount(subscription.id, {
@@ -356,62 +358,91 @@ export default function OverdraftProtectionDialog({
356
358
  </Typography>
357
359
 
358
360
  <Grid container spacing={2} ml={-2} sx={{ mt: -1 }}>
359
- {presetAmounts.map(({ amount: presetAmount, cycles }) => (
360
- <Grid item xs={6} sm={4} key={presetAmount}>
361
- <Card
362
- variant="outlined"
363
- sx={{
364
- height: '100%',
365
- transition: 'all 0.3s',
366
- cursor: 'pointer',
367
- '&:hover': {
368
- transform: 'translateY(-4px)',
369
- boxShadow: 3,
370
- },
371
- ...(amount === presetAmount && !customAmount
372
- ? { borderColor: 'primary.main', borderWidth: 2 }
373
- : {}),
374
- }}>
375
- <CardActionArea
376
- onClick={() => {
377
- methods.setValue('amount', presetAmount);
378
- setCustomAmount(false);
379
- }}
380
- sx={{ height: '100%', p: 1 }}>
381
- <Stack spacing={1} alignItems="center">
382
- <Typography variant="h6" sx={{ fontWeight: 600 }}>
383
- {presetAmount} {currency.symbol}
384
- </Typography>
385
- <Typography variant="caption" color="text.secondary">
386
- {formatEstimatedDuration(cycles)}
387
- </Typography>
388
- </Stack>
389
- </CardActionArea>
390
- </Card>
391
- </Grid>
392
- ))}
393
- <Grid item xs={6} sm={4}>
394
- <Card
395
- variant="outlined"
396
- sx={{
397
- height: '100%',
398
- transition: 'all 0.3s',
399
- cursor: 'pointer',
400
- '&:hover': {
401
- transform: 'translateY(-4px)',
402
- boxShadow: 3,
403
- },
404
- ...(customAmount ? { borderColor: 'primary.main', borderWidth: 2 } : {}),
405
- }}>
406
- <CardActionArea onClick={handleCustomSelect} sx={{ height: '100%', p: 2 }}>
407
- <Stack spacing={1} alignItems="center">
408
- <Typography variant="h6" sx={{ fontWeight: 600 }}>
409
- {t('common.custom')}
410
- </Typography>
411
- </Stack>
412
- </CardActionArea>
413
- </Card>
414
- </Grid>
361
+ {cycleAmountLoading ? (
362
+ // 加载状态的占位
363
+ <>
364
+ {[1, 2, 3, 4, 5].map((key) => (
365
+ <Grid item xs={6} sm={4} key={key}>
366
+ <Card variant="outlined" sx={{ height: '100%' }}>
367
+ <CardActionArea sx={{ height: '100%', p: 1 }}>
368
+ <Stack spacing={1} alignItems="center">
369
+ <Skeleton variant="rectangular" width={80} height={32} />
370
+ <Skeleton width={100} />
371
+ </Stack>
372
+ </CardActionArea>
373
+ </Card>
374
+ </Grid>
375
+ ))}
376
+ <Grid item xs={6} sm={4}>
377
+ <Card variant="outlined" sx={{ height: '100%' }}>
378
+ <CardActionArea sx={{ height: '100%', p: 2 }}>
379
+ <Stack spacing={1} alignItems="center">
380
+ <Skeleton variant="rectangular" width={80} height={24} />
381
+ </Stack>
382
+ </CardActionArea>
383
+ </Card>
384
+ </Grid>
385
+ </>
386
+ ) : (
387
+ <>
388
+ {presetAmounts.map(({ amount: presetAmount, cycles }) => (
389
+ <Grid item xs={6} sm={4} key={presetAmount}>
390
+ <Card
391
+ variant="outlined"
392
+ sx={{
393
+ height: '100%',
394
+ transition: 'all 0.3s',
395
+ cursor: 'pointer',
396
+ '&:hover': {
397
+ transform: 'translateY(-4px)',
398
+ boxShadow: 3,
399
+ },
400
+ ...(amount === presetAmount && !customAmount
401
+ ? { borderColor: 'primary.main', borderWidth: 2 }
402
+ : {}),
403
+ }}>
404
+ <CardActionArea
405
+ onClick={() => {
406
+ methods.setValue('amount', presetAmount);
407
+ setCustomAmount(false);
408
+ }}
409
+ sx={{ height: '100%', p: 1 }}>
410
+ <Stack spacing={1} alignItems="center">
411
+ <Typography variant="h6" sx={{ fontWeight: 600 }}>
412
+ {presetAmount} {currency.symbol}
413
+ </Typography>
414
+ <Typography variant="caption" color="text.secondary">
415
+ {formatEstimatedDuration(cycles)}
416
+ </Typography>
417
+ </Stack>
418
+ </CardActionArea>
419
+ </Card>
420
+ </Grid>
421
+ ))}
422
+ <Grid item xs={6} sm={4}>
423
+ <Card
424
+ variant="outlined"
425
+ sx={{
426
+ height: '100%',
427
+ transition: 'all 0.3s',
428
+ cursor: 'pointer',
429
+ '&:hover': {
430
+ transform: 'translateY(-4px)',
431
+ boxShadow: 3,
432
+ },
433
+ ...(customAmount ? { borderColor: 'primary.main', borderWidth: 2 } : {}),
434
+ }}>
435
+ <CardActionArea onClick={handleCustomSelect} sx={{ height: '100%', p: 2 }}>
436
+ <Stack spacing={1} alignItems="center">
437
+ <Typography variant="h6" sx={{ fontWeight: 600 }}>
438
+ {t('common.custom')}
439
+ </Typography>
440
+ </Stack>
441
+ </CardActionArea>
442
+ </Card>
443
+ </Grid>
444
+ </>
445
+ )}
415
446
  </Grid>
416
447
 
417
448
  {customAmount && (
@@ -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
  );
@@ -66,6 +66,7 @@ export default function UpsellSelect({ price, onSelect, onAdd }: Props) {
66
66
  } catch (err) {
67
67
  console.error(err);
68
68
  Toast.error(formatError(err));
69
+ throw err;
69
70
  } finally {
70
71
  setState({ adding: false });
71
72
  }
@@ -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;