payment-kit 1.17.2 → 1.17.4

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.
@@ -14,7 +14,7 @@ import type { TSubscriptionExpanded } from '@blocklet/payment-types';
14
14
  import { Button, Link, Stack, Tooltip } from '@mui/material';
15
15
  import { useRequest, useSetState } from 'ahooks';
16
16
  import isEmpty from 'lodash/isEmpty';
17
- import { useState } from 'react';
17
+ import { useEffect, useState } from 'react';
18
18
  import { FormProvider, useForm, useFormContext } from 'react-hook-form';
19
19
  import { useNavigate } from 'react-router-dom';
20
20
  import { joinURL } from 'ufo';
@@ -23,6 +23,22 @@ import OverdraftProtectionDialog from '../../customer/overdraft-protection';
23
23
  import Actions from '../../actions';
24
24
  import { useUnpaidInvoicesCheckForSubscription } from '../../../hooks/subscription';
25
25
 
26
+ interface ActionConfig {
27
+ key: string;
28
+ show: boolean;
29
+ label: string;
30
+ tooltip?: string;
31
+ onClick: (e?: React.MouseEvent) => void;
32
+ variant?: 'text' | 'outlined' | 'contained';
33
+ color?: 'inherit' | 'primary' | 'secondary' | 'error';
34
+ sx?: any;
35
+ primary?: boolean;
36
+ component?: any;
37
+ href?: string;
38
+ target?: string;
39
+ divider?: boolean;
40
+ }
41
+
26
42
  type ActionProps = {
27
43
  [key: string]: {
28
44
  color?: string;
@@ -33,6 +49,15 @@ type ActionProps = {
33
49
  text?: string;
34
50
  };
35
51
  };
52
+ export interface ProtectionInitValues {
53
+ enabled?: boolean;
54
+ return_stake?: boolean;
55
+ }
56
+ export interface ActionMethods {
57
+ openOverdraftProtection: (initValues?: ProtectionInitValues) => void;
58
+ }
59
+
60
+ export type ActionDisplayMode = 'all-buttons' | 'primary-buttons' | 'menu-only';
36
61
 
37
62
  type Props = {
38
63
  subscription: TSubscriptionExpanded;
@@ -45,9 +70,11 @@ type Props = {
45
70
  onChange: () => any | Promise<any>;
46
71
  };
47
72
  showDelegation?: boolean;
73
+ showUnsubscribe?: boolean;
48
74
  onChange?: (action?: string) => any | Promise<any>;
49
75
  actionProps?: ActionProps;
50
- mode?: 'menu' | 'btn';
76
+ mode?: ActionDisplayMode;
77
+ setUp?: (methods: ActionMethods) => void;
51
78
  };
52
79
 
53
80
  SubscriptionActions.defaultProps = {
@@ -57,7 +84,9 @@ SubscriptionActions.defaultProps = {
57
84
  showDelegation: false,
58
85
  onChange: null,
59
86
  actionProps: {},
60
- mode: 'btn',
87
+ mode: 'all-buttons',
88
+ setUp: null,
89
+ showUnsubscribe: true,
61
90
  };
62
91
  const fetchExtraActions = async ({
63
92
  id,
@@ -102,9 +131,11 @@ export function SubscriptionActionsInner({
102
131
  showRecharge,
103
132
  showOverdraftProtection,
104
133
  showDelegation,
134
+ showUnsubscribe,
105
135
  onChange,
106
136
  actionProps,
107
137
  mode,
138
+ setUp,
108
139
  }: Props) {
109
140
  const { t, locale } = useLocaleContext();
110
141
  const { reset, getValues } = useFormContext();
@@ -122,6 +153,7 @@ export function SubscriptionActionsInner({
122
153
  loading: false,
123
154
  openProtection: false,
124
155
  protectionLoading: false,
156
+ protectionInitValues: null,
125
157
  });
126
158
 
127
159
  const shouldFetchDelegation = showDelegation && ['active', 'trialing', 'past_due'].includes(subscription?.status);
@@ -146,6 +178,19 @@ export function SubscriptionActionsInner({
146
178
  refreshDeps: [subscription.id, shouldFetchOverdraftProtection],
147
179
  });
148
180
 
181
+ useEffect(() => {
182
+ if (setUp) {
183
+ setUp({
184
+ openOverdraftProtection: (initValues?: ProtectionInitValues) =>
185
+ setState({
186
+ openProtection: true,
187
+ // @ts-ignore
188
+ protectionInitValues: initValues,
189
+ }),
190
+ });
191
+ }
192
+ }, [setUp]);
193
+
149
194
  const handleCancel = async () => {
150
195
  try {
151
196
  const result = await checkUnpaidInvoices();
@@ -273,162 +318,148 @@ export function SubscriptionActionsInner({
273
318
  };
274
319
 
275
320
  const renderActions = () => {
276
- if (mode === 'menu') {
277
- const actions = [
278
- showDelegation &&
279
- noDelegation && {
280
- label: t('customer.delegation.btn'),
281
- handler: handleDelegate,
282
- color: 'primary',
283
- divider: true,
284
- },
285
- shouldFetchOverdraftProtection && {
286
- label: t('customer.overdraftProtection.title'),
287
- handler: () => setState({ openProtection: true }),
288
- color: 'primary',
321
+ const supportUnsubscribe = action?.action === 'cancel' && showUnsubscribe;
322
+ const supportAction = action && (action?.action !== 'cancel' || supportUnsubscribe);
323
+
324
+ const serviceActions = subscription.service_actions?.filter((x: any) => x?.type !== 'notification') || [];
325
+ const actionConfigs: ActionConfig[] = [
326
+ {
327
+ key: 'delegation',
328
+ show: showDelegation && noDelegation,
329
+ label: t('customer.delegation.btn'),
330
+ tooltip: t('customer.delegation.title'),
331
+ onClick: handleDelegate,
332
+ variant: 'outlined',
333
+ color: 'primary',
334
+ primary: true,
335
+ },
336
+ {
337
+ key: 'protection',
338
+ show: shouldFetchOverdraftProtection,
339
+ label: t('customer.overdraftProtection.setting'),
340
+ onClick: () => setState({ openProtection: true, protectionInitValues: null }),
341
+ variant: 'outlined',
342
+ color: 'primary',
343
+ },
344
+ {
345
+ key: 'recharge',
346
+ show: !!(showRecharge && supportRecharge(subscription)),
347
+ label: t('customer.recharge.title'),
348
+ onClick: (e) => {
349
+ e?.stopPropagation();
350
+ navigate(`/customer/subscription/${subscription.id}/recharge`);
289
351
  },
290
- showRecharge &&
291
- supportRecharge(subscription) && {
292
- label: t('customer.recharge.title'),
293
- handler: () => navigate(`/customer/subscription/${subscription.id}/recharge`),
294
- color: 'primary',
295
- divider: true,
296
- },
297
- !extraActions?.batchPay &&
298
- action && {
299
- label: action?.text || t(`payment.customer.${action.action}.button`),
300
- handler: () => {
301
- if (action.action === 'pastDue') {
302
- navigate(`/customer/invoice/past-due?subscription=${subscription.id}`);
303
- } else {
304
- setState({
305
- action: action.action,
306
- subscription: subscription.id,
307
- });
308
- }
309
- },
310
- color: action.color as any,
311
- },
312
- extraActions?.changePlan && {
313
- label: action?.text || t('payment.customer.changePlan.button'),
314
- handler: () => navigate(`/customer/subscription/${subscription.id}/change-plan`),
315
- color: 'primary',
352
+ variant: 'outlined',
353
+ color: 'primary',
354
+ primary: true,
355
+ },
356
+ {
357
+ key: 'changePlan',
358
+ show: !!extraActions?.changePlan,
359
+ label: action?.text || t('payment.customer.changePlan.button'),
360
+ onClick: (e) => {
361
+ e?.stopPropagation();
362
+ navigate(`/customer/subscription/${subscription.id}/change-plan`);
363
+ },
364
+ variant: 'contained',
365
+ color: 'primary',
366
+ sx: action?.sx,
367
+ },
368
+ {
369
+ key: 'batchPay',
370
+ show: !!extraActions?.batchPay,
371
+ label: action?.text || t('admin.subscription.batchPay.button'),
372
+ onClick: (e) => {
373
+ e?.stopPropagation();
374
+ navigate(`/customer/invoice/past-due?subscription=${subscription.id}&currency=${extraActions?.batchPay}`);
316
375
  },
317
- !!extraActions?.batchPay && {
318
- label: action?.text || t('admin.subscription.batchPay.button'),
319
- handler: () =>
320
- navigate(`/customer/invoice/past-due?subscription=${subscription.id}&currency=${extraActions.batchPay}`),
321
- color: 'error',
376
+ variant: 'outlined',
377
+ color: 'error',
378
+ sx: action?.sx,
379
+ primary: true,
380
+ },
381
+ {
382
+ key: 'mainAction',
383
+ show: !!(!extraActions?.batchPay && supportAction),
384
+ label: action?.text || t(`payment.customer.${action?.action}.button`),
385
+ onClick: (e) => {
386
+ e?.stopPropagation();
387
+ if (action?.action === 'pastDue') {
388
+ navigate(`/customer/invoice/past-due?subscription=${subscription.id}`);
389
+ } else {
390
+ // @ts-ignore
391
+ setState({ action: action?.action, subscription: subscription.id });
392
+ }
322
393
  },
323
- ...(subscription.service_actions
324
- ?.filter((x: any) => x?.type !== 'notification')
325
- .map((x) => ({
326
- label: x.text[locale] || x.text.en || x.name,
327
- handler: () => {
328
- window.open(x.link, '_blank');
329
- },
330
- color: x?.color || 'primary',
331
- })) || []),
332
- ].filter(Boolean);
394
+ // @ts-ignore
395
+ variant: action?.variant || 'outlined',
396
+ // @ts-ignore
397
+ color: action?.color || 'primary',
398
+ sx: action?.sx,
399
+ divider: serviceActions.length > 0,
400
+ },
401
+ // @ts-ignore
402
+ ...serviceActions.map((x) => ({
403
+ key: x.name,
404
+ show: true,
405
+ label: x.text[locale] || x.text.en || x.name,
406
+ onClick: () => window.open(x.link, '_blank'),
407
+ variant: x?.variant || 'contained',
408
+ color: x?.color || 'primary',
409
+ component: Link,
410
+ href: x.link,
411
+ target: '_blank',
412
+ sx: { textDecoration: 'none !important' },
413
+ })),
414
+ ];
333
415
 
334
- return (
335
- <Actions
336
- // @ts-ignore
337
- actions={actions}
338
- />
339
- );
340
- }
341
- return (
342
- <>
343
- {showDelegation && noDelegation && (
344
- <Tooltip title={t('customer.delegation.title')}>
345
- <Button variant="outlined" color="primary" onClick={handleDelegate}>
346
- {t('customer.delegation.btn')}
347
- </Button>
348
- </Tooltip>
349
- )}
416
+ // 过滤出要显示的操作
417
+ const visibleActions = actionConfigs.filter((a) => a.show);
350
418
 
351
- {shouldFetchOverdraftProtection && (
352
- <Button variant="outlined" color="primary" onClick={() => setState({ openProtection: true })}>
353
- {t('customer.overdraftProtection.title')}
354
- </Button>
355
- )}
419
+ // 转换为菜单项
420
+ const toMenuItem = (item: any) => ({
421
+ label: item.label,
422
+ handler: item.onClick,
423
+ color: item.color,
424
+ divider: item.divider,
425
+ });
356
426
 
357
- {showRecharge && supportRecharge(subscription) && (
358
- <Button
359
- variant="outlined"
360
- color="primary"
361
- onClick={(e) => {
362
- e.stopPropagation();
363
- navigate(`/customer/subscription/${subscription.id}/recharge`);
364
- }}>
365
- {t('customer.recharge.title')}
366
- </Button>
367
- )}
368
- {!extraActions?.batchPay && action && (
369
- <Button
370
- variant={action.variant as any}
371
- color={action.color as any}
372
- size="small"
373
- sx={action?.sx as any}
374
- onClick={(e) => {
375
- e.stopPropagation();
376
- if (action.action === 'pastDue') {
377
- navigate(`/customer/invoice/past-due?subscription=${subscription.id}`);
378
- } else {
379
- setState({
380
- action: action.action,
381
- subscription: subscription.id,
382
- });
383
- }
384
- }}>
385
- {action?.text || t(`payment.customer.${action.action}.button`)}
386
- </Button>
387
- )}
388
- {extraActions?.changePlan && (
389
- <Button
390
- variant="contained"
391
- color="primary"
392
- size="small"
393
- sx={action?.sx as any}
394
- onClick={(e) => {
395
- e.stopPropagation();
396
- navigate(`/customer/subscription/${subscription.id}/change-plan`);
397
- }}>
398
- {action?.text || t('payment.customer.changePlan.button')}
399
- </Button>
400
- )}
401
- {!!extraActions?.batchPay && (
402
- <Button
403
- variant="outlined"
404
- color="error"
405
- size="small"
406
- sx={action?.sx as any}
407
- onClick={(e) => {
408
- e.stopPropagation();
409
- navigate(`/customer/invoice/past-due?subscription=${subscription.id}&currency=${extraActions.batchPay}`);
410
- }}>
411
- {action?.text || t('admin.subscription.batchPay.button')}
412
- </Button>
427
+ const toButton = (item: ActionConfig) => (
428
+ <Button
429
+ key={item.key}
430
+ variant={item.variant}
431
+ color={item.color}
432
+ onClick={item.onClick}
433
+ component={item.component}
434
+ href={item.href}
435
+ target={item.target}
436
+ sx={item.sx}
437
+ size="small">
438
+ {item.tooltip ? (
439
+ <Tooltip title={item.tooltip}>
440
+ <span>{item.label}</span>
441
+ </Tooltip>
442
+ ) : (
443
+ item.label
413
444
  )}
414
- {subscription.service_actions
415
- ?.filter((x: any) => x?.type !== 'notification')
416
- .map((x) => (
417
- // @ts-ignore
418
- <Button
419
- component={Link}
420
- key={x.name}
421
- variant={x?.variant || 'contained'}
422
- color={x?.color || 'primary'}
423
- href={x.link}
424
- size="small"
425
- target="_blank"
426
- sx={{ textDecoration: 'none !important' }}>
427
- {x.text[locale] || x.text.en || x.name}
428
- </Button>
429
- ))}
430
- </>
445
+ </Button>
431
446
  );
447
+ if (mode === 'menu-only') {
448
+ return <Actions actions={visibleActions.map(toMenuItem)} />;
449
+ }
450
+
451
+ if (mode === 'primary-buttons') {
452
+ const primaryButtons = visibleActions.filter((a) => a.primary);
453
+ const menuItems = visibleActions.filter((a) => !a.primary);
454
+ return (
455
+ <>
456
+ {primaryButtons.map(toButton)}
457
+ {menuItems.length > 0 && <Actions actions={menuItems.map(toMenuItem)} />}
458
+ </>
459
+ );
460
+ }
461
+
462
+ return <>{visibleActions.map(toButton)}</>;
432
463
  };
433
464
 
434
465
  return (
@@ -466,11 +497,13 @@ export function SubscriptionActionsInner({
466
497
  value={overdraftProtection}
467
498
  onSave={handleOverdraftProtection}
468
499
  open={state.openProtection}
469
- paymentAddress={subscription.overdraft_protection?.payment_details?.arcblock?.payer}
500
+ payerAddress={subscription.overdraft_protection?.payment_details?.arcblock?.payer}
501
+ stakingAddress={subscription.overdraft_protection?.payment_details?.arcblock?.staking?.address}
470
502
  currency={subscription.paymentCurrency}
471
503
  subscription={subscription}
472
504
  loading={state.protectionLoading}
473
505
  onCancel={() => setState({ openProtection: false })}
506
+ initValues={state.protectionInitValues}
474
507
  />
475
508
  )}
476
509
  </Stack>
@@ -500,7 +533,9 @@ SubscriptionActionsInner.defaultProps = {
500
533
  showRecharge: false,
501
534
  showOverdraftProtection: false,
502
535
  showDelegation: false,
536
+ showUnsubscribe: true,
503
537
  onChange: null,
504
538
  actionProps: {},
505
- mode: 'btn',
539
+ mode: 'all-buttons',
540
+ setUp: null,
506
541
  };
@@ -192,6 +192,7 @@ export default function CurrentSubscriptions({
192
192
  onChange(v);
193
193
  }
194
194
  }}
195
+ showUnsubscribe={false}
195
196
  showRecharge
196
197
  actionProps={{
197
198
  cancel: {
package/src/libs/dayjs.ts CHANGED
@@ -1,3 +1,135 @@
1
1
  import { dayjs } from '@blocklet/payment-react';
2
2
 
3
3
  export default dayjs;
4
+
5
+ /**
6
+ * Time unit type definition
7
+ */
8
+ export type TimeUnit = 'hour' | 'day' | 'week' | 'month' | 'year';
9
+
10
+ /**
11
+ * Format options interface
12
+ */
13
+ interface FormatDurationOptions {
14
+ t: (key: string, options?: Record<string, any>) => string;
15
+ pluralSuffix?: string;
16
+ separator?: string;
17
+ }
18
+
19
+ /**
20
+ * Conversion rule type
21
+ */
22
+ export type ConversionRule = {
23
+ threshold: number;
24
+ convert: (value: number) => { main: number; remainder: number };
25
+ format: (main: number, remainder: number, value: number) => Array<[TimeUnit, number]>;
26
+ };
27
+ /**
28
+ * Smart duration formatter optimized for subscription periods
29
+ *
30
+ * Display Rules:
31
+ * 1. Hours:
32
+ * - < 24h: show hours (e.g., "5 hours")
33
+ * - 24-47h: show days and hours (e.g., "1 day 5 hours")
34
+ * - >= 48h: convert to days
35
+ *
36
+ * 2. Days:
37
+ * - < 7 days: show days (e.g., "5 days")
38
+ * - 7-13 days: show 1 week + days (e.g., "1 week 3 days")
39
+ * - 14+ days: show weeks if divisible, else days (e.g., "2 weeks" or "15 days")
40
+ * - 30+ days: show months if divisible, else weeks/days (e.g., "1 month" or "5 weeks")
41
+ *
42
+ * 3. Weeks:
43
+ * - < 4 weeks: show weeks (e.g., "3 weeks")
44
+ * - 4-7 weeks: show 1 month + weeks (e.g., "1 month 2 weeks")
45
+ * - 8+ weeks: show months if divisible, else weeks (e.g., "2 months" or "9 weeks")
46
+ *
47
+ * 4. Months:
48
+ * - < 12: show months (e.g., "6 months")
49
+ * - 12+: show years if divisible, else months (e.g., "1 year" or "15 months")
50
+ */
51
+ export const formatSmartDuration = (
52
+ value: number,
53
+ unit: TimeUnit,
54
+ { t, separator = ' ' }: FormatDurationOptions
55
+ ): string => {
56
+ // Format single unit
57
+ const formatUnit = (val: number, unitType: TimeUnit): string =>
58
+ `${val} ${t(`common.${unitType}${val > 1 ? 's' : ''}`).toLowerCase()}`;
59
+
60
+ // Convert to largest possible unit
61
+ const convertToLargest = (val: number, fromUnit: TimeUnit): [TimeUnit, number][] => {
62
+ switch (fromUnit) {
63
+ case 'hour': {
64
+ if (val < 24) return [['hour', val]];
65
+ if (val < 48)
66
+ return [
67
+ ['day', Math.floor(val / 24)],
68
+ ['hour', val % 24],
69
+ ];
70
+ const days = Math.floor(val / 24);
71
+ return convertToLargest(days, 'day');
72
+ }
73
+ case 'day': {
74
+ if (val < 7) return [['day', val]];
75
+ if (val < 14)
76
+ return [
77
+ ['week', Math.floor(val / 7)],
78
+ ['day', val % 7],
79
+ ];
80
+ if (val < 30) return val % 7 === 0 ? [['week', val / 7]] : [['day', val]];
81
+ const years = Math.floor(val / 365);
82
+ const remainingDays = val % 365;
83
+ const months = Math.floor(remainingDays / 30);
84
+ if (years > 0) {
85
+ return months > 0
86
+ ? [
87
+ ['year', years],
88
+ ['month', months],
89
+ ]
90
+ : [['year', years]];
91
+ }
92
+ return months > 0
93
+ ? [
94
+ ['month', months],
95
+ ['week', Math.floor((remainingDays % 30) / 7)],
96
+ ]
97
+ : [['week', Math.floor(val / 7)]];
98
+ }
99
+ case 'week': {
100
+ if (val < 4) return [['week', val]];
101
+ if (val < 8)
102
+ return [
103
+ ['month', Math.floor(val / 4)],
104
+ ['week', val % 4],
105
+ ];
106
+ const months = Math.floor(val / 4);
107
+ return convertToLargest(months, 'month');
108
+ }
109
+ case 'month': {
110
+ const years = Math.floor(val / 12);
111
+ const months = val % 12;
112
+ if (years > 0) {
113
+ if (months > 0) {
114
+ return [
115
+ ['year', years],
116
+ ['month', months],
117
+ ];
118
+ }
119
+ return [['year', years]];
120
+ }
121
+ return [['month', val]];
122
+ }
123
+ case 'year':
124
+ return [['year', val]];
125
+ default:
126
+ return [[fromUnit, val]];
127
+ }
128
+ };
129
+
130
+ // Get units and filter out zero values
131
+ const units = convertToLargest(value, unit).filter(([, val]) => val > 0);
132
+
133
+ // Format all units
134
+ return units.map(([u, val]) => formatUnit(val, u)).join(separator);
135
+ };
@@ -25,6 +25,7 @@ export default flat({
25
25
  rechargeTime: 'Recharge Time',
26
26
  submit: 'Submit',
27
27
  custom: 'Custom',
28
+ estimatedDuration: '{duration} est.',
28
29
  },
29
30
  admin: {
30
31
  balances: 'Balances',
@@ -456,6 +457,9 @@ export default flat({
456
457
  resume: 'Resume payment collection',
457
458
  resumeTip:
458
459
  'Are you sure you want to resume collecting payments? Any future invoices for this subscription will resume payment collection.',
460
+ paymentAddress: 'Payment Address',
461
+ currentBalance: 'Current Balance',
462
+ insufficientBalance: 'Insufficient Balance, please add funds',
459
463
  cancel: {
460
464
  schedule: 'Scheduled to cancel',
461
465
  title: 'Cancel subscription',
@@ -648,38 +652,41 @@ export default flat({
648
652
  error: 'Delegate failed',
649
653
  },
650
654
  overdraftProtection: {
651
- title: 'Overdraft Protection',
652
- setting: 'Overdraft Protection Setting',
653
- tip: 'To avoid service interruption due to unpaid invoices, you can enable overdraft protection by staking. Timely payment will not incur additional fees. Please settle your invoices promptly. If your available stake is insufficient or payment is overdue, we will deduct the amount from your stake and charge a service fee.',
655
+ title: 'SubGuard™',
656
+ setting: 'Set SubGuard™',
657
+ tip: 'To avoid service interruption due to unpaid invoices, you can enable SubGuard™ by staking. Timely payment will not incur additional fees. Please settle your invoices promptly. If your available stake is insufficient or payment is overdue, we will deduct the amount from your stake and charge a service fee.',
654
658
  enabled: 'Enabled',
655
659
  disabled: 'Disabled',
656
660
  returnRemaining: 'Return Remaining Stake',
657
661
  returnRemainingTip:
658
- 'Once the remaining stake is returned, the overdraft protection will be automatically disabled. Please confirm the action.',
662
+ 'Once the remaining stake is returned, the SubGuard™ will be automatically disabled. Please confirm the action.',
659
663
  applyRemainingSuccess: 'Stake return application successful',
660
664
  remaining:
661
665
  'Your current remaining stake: {amount} {symbol}, estimated required stake per cycle: {estimateAmount} {symbol}.',
662
666
  noRemaining:
663
- 'No remaining stake available. Please stake at least {estimateAmount} {symbol} as soon as possible to ensure overdraft protection is enabled.',
667
+ 'No remaining stake available. Please stake at least {estimateAmount} {symbol} as soon as possible to ensure SubGuard™ is enabled.',
664
668
  remainingNotEnough:
665
669
  'You have unpaid invoices totaling {due} {symbol}. If not paid, your remaining stake will be insufficient to cover the next invoice. Available stake: {unused} {symbol}. Please stake at least {min} {symbol}.',
666
670
  due: 'Please pay the outstanding amount first',
667
- insufficient: 'Insufficient Stake to cover the next invoice',
668
- insufficientTip: 'Insufficient Stake, please stake to ensure overdraft protection is enabled.',
671
+ insufficient: 'Insufficient Stake to cover the next invoice, please add stake',
672
+ insufficientTip: 'Insufficient Stake, please stake to ensure SubGuard™ is enabled.',
669
673
  intervals: 'cycles',
670
674
  estimatedDuration: '{duration} {unit} est.',
671
675
  rule: 'Rule: N * ( P + Fee )',
672
676
  ruleTip:
673
- 'N is the number of cycles, P is the subscription bill amount, Fee is the overdraft protection service fee, the single fee is {gas} {symbol}',
677
+ 'N is the number of cycles, P is the subscription bill amount, Fee is the SubGuard™ service fee, the single fee is {gas} {symbol}',
674
678
  min: 'The amount must be greater or equal to {min} {symbol}',
675
- settingSuccess: 'Overdraft protection setting successful',
676
- settingError: 'Overdraft protection setting failed',
677
- keepStake: 'Not Return',
679
+ settingSuccess: 'Set SubGuard™ Successful',
680
+ settingError: 'Set SubGuard™ Failed',
681
+ keepStake: 'Keep Remaining Stake For SubGuard™',
678
682
  returnStake: 'Return Remaining Stake',
679
683
  stake: 'Stake',
680
684
  address: 'Staking Address',
681
685
  total: 'Total Stake: {total} {symbol}, ',
682
686
  disableConfirm: 'You currently have unpaid invoices, please settle your invoices first.',
687
+ open: 'Enable SubGuard™',
688
+ payerAddress: 'Payer',
689
+ stakingAddress: 'Staking Address',
683
690
  },
684
691
  unpaidInvoicesWarning: 'You currently have unpaid invoices, please settle your invoices first.',
685
692
  unpaidInvoicesWarningTip: 'You currently have unpaid invoices, please settle your invoices promptly.',