@blocklet/payment-react 1.23.4 → 1.23.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/docs/components-ui-pricing-table.ja.md +4 -4
- package/docs/components-ui-pricing-table.md +4 -4
- package/docs/components-ui-pricing-table.zh-TW.md +4 -4
- package/docs/components-ui-pricing-table.zh.md +4 -4
- package/es/components/auto-topup/index.js +8 -5
- package/es/components/auto-topup/modal.js +2 -2
- package/es/components/auto-topup/product-card.js +7 -4
- package/es/history/credit/grants-list.js +4 -5
- package/es/history/credit/transactions-list.js +4 -10
- package/es/libs/util.d.ts +21 -0
- package/es/libs/util.js +29 -0
- package/es/locales/en.js +12 -10
- package/es/locales/zh.js +12 -10
- package/es/payment/product-item.js +19 -8
- package/lib/components/auto-topup/index.js +2 -2
- package/lib/components/auto-topup/modal.js +1 -1
- package/lib/components/auto-topup/product-card.js +3 -3
- package/lib/history/credit/grants-list.js +2 -2
- package/lib/history/credit/transactions-list.js +4 -4
- package/lib/libs/util.d.ts +21 -0
- package/lib/libs/util.js +31 -0
- package/lib/locales/en.js +12 -10
- package/lib/locales/zh.js +12 -10
- package/lib/payment/product-item.js +6 -8
- package/package.json +3 -3
- package/src/components/auto-topup/index.tsx +8 -5
- package/src/components/auto-topup/modal.tsx +6 -2
- package/src/components/auto-topup/product-card.tsx +7 -4
- package/src/history/credit/grants-list.tsx +5 -2
- package/src/history/credit/transactions-list.tsx +4 -6
- package/src/libs/util.ts +55 -0
- package/src/locales/en.tsx +12 -10
- package/src/locales/zh.tsx +12 -10
- package/src/payment/product-item.tsx +20 -8
|
@@ -41,7 +41,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
41
41
|
price: {
|
|
42
42
|
currency: 'usd',
|
|
43
43
|
unit_amount: '1000', // セント単位
|
|
44
|
-
recurring: { interval: 'month', interval_count: 1 },
|
|
44
|
+
recurring: { interval: 'month', interval_count: 1, usage_type: 'licensed' },
|
|
45
45
|
currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
|
|
46
46
|
},
|
|
47
47
|
is_highlight: false,
|
|
@@ -57,7 +57,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
57
57
|
price: {
|
|
58
58
|
currency: 'usd',
|
|
59
59
|
unit_amount: '2500',
|
|
60
|
-
recurring: { interval: 'month', interval_count: 1 },
|
|
60
|
+
recurring: { interval: 'month', interval_count: 1, usage_type: 'licensed' },
|
|
61
61
|
currency_options: [{ currency_id: 'usd', unit_amount: '2500' }],
|
|
62
62
|
},
|
|
63
63
|
is_highlight: true,
|
|
@@ -74,7 +74,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
74
74
|
price: {
|
|
75
75
|
currency: 'usd',
|
|
76
76
|
unit_amount: '10000',
|
|
77
|
-
recurring: { interval: 'year', interval_count: 1 },
|
|
77
|
+
recurring: { interval: 'year', interval_count: 1, usage_type: 'licensed' },
|
|
78
78
|
currency_options: [{ currency_id: 'usd', unit_amount: '10000' }],
|
|
79
79
|
},
|
|
80
80
|
is_highlight: false,
|
|
@@ -90,7 +90,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
90
90
|
price: {
|
|
91
91
|
currency: 'usd',
|
|
92
92
|
unit_amount: '25000',
|
|
93
|
-
recurring: { interval: 'year', interval_count: 1 },
|
|
93
|
+
recurring: { interval: 'year', interval_count: 1, usage_type: 'licensed' },
|
|
94
94
|
currency_options: [{ currency_id: 'usd', unit_amount: '25000' }],
|
|
95
95
|
},
|
|
96
96
|
is_highlight: true,
|
|
@@ -41,7 +41,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
41
41
|
price: {
|
|
42
42
|
currency: 'usd',
|
|
43
43
|
unit_amount: '1000', // in cents
|
|
44
|
-
recurring: { interval: 'month', interval_count: 1 },
|
|
44
|
+
recurring: { interval: 'month', interval_count: 1, usage_type: 'licensed' },
|
|
45
45
|
currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
|
|
46
46
|
},
|
|
47
47
|
is_highlight: false,
|
|
@@ -57,7 +57,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
57
57
|
price: {
|
|
58
58
|
currency: 'usd',
|
|
59
59
|
unit_amount: '2500',
|
|
60
|
-
recurring: { interval: 'month', interval_count: 1 },
|
|
60
|
+
recurring: { interval: 'month', interval_count: 1, usage_type: 'licensed' },
|
|
61
61
|
currency_options: [{ currency_id: 'usd', unit_amount: '2500' }],
|
|
62
62
|
},
|
|
63
63
|
is_highlight: true,
|
|
@@ -74,7 +74,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
74
74
|
price: {
|
|
75
75
|
currency: 'usd',
|
|
76
76
|
unit_amount: '10000',
|
|
77
|
-
recurring: { interval: 'year', interval_count: 1 },
|
|
77
|
+
recurring: { interval: 'year', interval_count: 1, usage_type: 'licensed' },
|
|
78
78
|
currency_options: [{ currency_id: 'usd', unit_amount: '10000' }],
|
|
79
79
|
},
|
|
80
80
|
is_highlight: false,
|
|
@@ -90,7 +90,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
90
90
|
price: {
|
|
91
91
|
currency: 'usd',
|
|
92
92
|
unit_amount: '25000',
|
|
93
|
-
recurring: { interval: 'year', interval_count: 1 },
|
|
93
|
+
recurring: { interval: 'year', interval_count: 1, usage_type: 'licensed' },
|
|
94
94
|
currency_options: [{ currency_id: 'usd', unit_amount: '25000' }],
|
|
95
95
|
},
|
|
96
96
|
is_highlight: true,
|
|
@@ -41,7 +41,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
41
41
|
price: {
|
|
42
42
|
currency: 'usd',
|
|
43
43
|
unit_amount: '1000', // 以分為單位
|
|
44
|
-
recurring: { interval: 'month', interval_count: 1 },
|
|
44
|
+
recurring: { interval: 'month', interval_count: 1, usage_type: 'licensed' },
|
|
45
45
|
currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
|
|
46
46
|
},
|
|
47
47
|
is_highlight: false,
|
|
@@ -57,7 +57,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
57
57
|
price: {
|
|
58
58
|
currency: 'usd',
|
|
59
59
|
unit_amount: '2500',
|
|
60
|
-
recurring: { interval: 'month', interval_count: 1 },
|
|
60
|
+
recurring: { interval: 'month', interval_count: 1, usage_type: 'licensed' },
|
|
61
61
|
currency_options: [{ currency_id: 'usd', unit_amount: '2500' }],
|
|
62
62
|
},
|
|
63
63
|
is_highlight: true,
|
|
@@ -74,7 +74,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
74
74
|
price: {
|
|
75
75
|
currency: 'usd',
|
|
76
76
|
unit_amount: '10000',
|
|
77
|
-
recurring: { interval: 'year', interval_count: 1 },
|
|
77
|
+
recurring: { interval: 'year', interval_count: 1, usage_type: 'licensed' },
|
|
78
78
|
currency_options: [{ currency_id: 'usd', unit_amount: '10000' }],
|
|
79
79
|
},
|
|
80
80
|
is_highlight: false,
|
|
@@ -90,7 +90,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
90
90
|
price: {
|
|
91
91
|
currency: 'usd',
|
|
92
92
|
unit_amount: '25000',
|
|
93
|
-
recurring: { interval: 'year', interval_count: 1 },
|
|
93
|
+
recurring: { interval: 'year', interval_count: 1, usage_type: 'licensed' },
|
|
94
94
|
currency_options: [{ currency_id: 'usd', unit_amount: '25000' }],
|
|
95
95
|
},
|
|
96
96
|
is_highlight: true,
|
|
@@ -41,7 +41,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
41
41
|
price: {
|
|
42
42
|
currency: 'usd',
|
|
43
43
|
unit_amount: '1000', // 单位为美分
|
|
44
|
-
recurring: { interval: 'month', interval_count: 1 },
|
|
44
|
+
recurring: { interval: 'month', interval_count: 1, usage_type: 'licensed' },
|
|
45
45
|
currency_options: [{ currency_id: 'usd', unit_amount: '1000' }],
|
|
46
46
|
},
|
|
47
47
|
is_highlight: false,
|
|
@@ -57,7 +57,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
57
57
|
price: {
|
|
58
58
|
currency: 'usd',
|
|
59
59
|
unit_amount: '2500',
|
|
60
|
-
recurring: { interval: 'month', interval_count: 1 },
|
|
60
|
+
recurring: { interval: 'month', interval_count: 1, usage_type: 'licensed' },
|
|
61
61
|
currency_options: [{ currency_id: 'usd', unit_amount: '2500' }],
|
|
62
62
|
},
|
|
63
63
|
is_highlight: true,
|
|
@@ -74,7 +74,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
74
74
|
price: {
|
|
75
75
|
currency: 'usd',
|
|
76
76
|
unit_amount: '10000',
|
|
77
|
-
recurring: { interval: 'year', interval_count: 1 },
|
|
77
|
+
recurring: { interval: 'year', interval_count: 1, usage_type: 'licensed' },
|
|
78
78
|
currency_options: [{ currency_id: 'usd', unit_amount: '10000' }],
|
|
79
79
|
},
|
|
80
80
|
is_highlight: false,
|
|
@@ -90,7 +90,7 @@ const pricingTableData: TPricingTableExpanded = {
|
|
|
90
90
|
price: {
|
|
91
91
|
currency: 'usd',
|
|
92
92
|
unit_amount: '25000',
|
|
93
|
-
recurring: { interval: 'year', interval_count: 1 },
|
|
93
|
+
recurring: { interval: 'year', interval_count: 1, usage_type: 'licensed' },
|
|
94
94
|
currency_options: [{ currency_id: 'usd', unit_amount: '25000' }],
|
|
95
95
|
},
|
|
96
96
|
is_highlight: true,
|
|
@@ -17,7 +17,7 @@ import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
|
17
17
|
import { useNavigate } from "react-router-dom";
|
|
18
18
|
import { joinURL } from "ufo";
|
|
19
19
|
import { useRequest } from "ahooks";
|
|
20
|
-
import { getPrefix, formatBNStr, formatNumber, formatPrice } from "../../libs/util.js";
|
|
20
|
+
import { getPrefix, formatBNStr, formatNumber, formatPrice, formatCreditAmount } from "../../libs/util.js";
|
|
21
21
|
import { createLink, handleNavigation } from "../../libs/navigation.js";
|
|
22
22
|
import { usePaymentContext } from "../../contexts/payment.js";
|
|
23
23
|
import api from "../../libs/api.js";
|
|
@@ -371,10 +371,13 @@ export default function AutoTopupCard({
|
|
|
371
371
|
),
|
|
372
372
|
config?.enabled ? /* @__PURE__ */ jsxs(Stack, { spacing: 1.5, className: "auto-topup-content", sx: { pt: 1.5 }, children: [
|
|
373
373
|
(() => {
|
|
374
|
-
const threshold = `${formatNumber(config.threshold)
|
|
375
|
-
const credits = `${
|
|
376
|
-
|
|
377
|
-
|
|
374
|
+
const threshold = `${formatCreditAmount(formatNumber(config.threshold), config.currency?.symbol || "")}`;
|
|
375
|
+
const credits = `${formatCreditAmount(
|
|
376
|
+
formatNumber(
|
|
377
|
+
Number(config.price.metadata?.credit_config?.credit_amount || 0) * Number(config.quantity)
|
|
378
|
+
),
|
|
379
|
+
config.currency?.symbol || ""
|
|
380
|
+
)}`;
|
|
378
381
|
return /* @__PURE__ */ jsxs(
|
|
379
382
|
Typography,
|
|
380
383
|
{
|
|
@@ -26,7 +26,7 @@ import pWaitFor from "p-wait-for";
|
|
|
26
26
|
import DidAddress from "@arcblock/ux/lib/DID";
|
|
27
27
|
import Switch from "../switch-button.js";
|
|
28
28
|
import api from "../../libs/api.js";
|
|
29
|
-
import { formatError, flattenPaymentMethods, getPrefix, formatBNStr } from "../../libs/util.js";
|
|
29
|
+
import { formatError, flattenPaymentMethods, getPrefix, formatBNStr, formatCreditAmount } from "../../libs/util.js";
|
|
30
30
|
import { usePaymentContext } from "../../contexts/payment.js";
|
|
31
31
|
import { createLink, handleNavigation } from "../../libs/navigation.js";
|
|
32
32
|
import Collapse from "../collapse.js";
|
|
@@ -560,7 +560,7 @@ export default function AutoTopup({
|
|
|
560
560
|
},
|
|
561
561
|
slotProps: {
|
|
562
562
|
input: {
|
|
563
|
-
endAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "end", children: config?.currency?.
|
|
563
|
+
endAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "end", children: formatCreditAmount("", config?.currency?.symbol) })
|
|
564
564
|
},
|
|
565
565
|
htmlInput: {
|
|
566
566
|
min: 0,
|
|
@@ -3,7 +3,7 @@ import { Stack, Typography, TextField, Card } from "@mui/material";
|
|
|
3
3
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
4
4
|
import { useState } from "react";
|
|
5
5
|
import ProductCard from "../../payment/product-card.js";
|
|
6
|
-
import { formatPrice, formatNumber } from "../../libs/util.js";
|
|
6
|
+
import { formatPrice, formatNumber, formatCreditForCheckout } from "../../libs/util.js";
|
|
7
7
|
export default function AutoTopupProductCard({
|
|
8
8
|
product,
|
|
9
9
|
price,
|
|
@@ -14,7 +14,7 @@ export default function AutoTopupProductCard({
|
|
|
14
14
|
minQuantity = 1,
|
|
15
15
|
creditCurrency
|
|
16
16
|
}) {
|
|
17
|
-
const { t } = useLocaleContext();
|
|
17
|
+
const { t, locale } = useLocaleContext();
|
|
18
18
|
const [localQuantity, setLocalQuantity] = useState(quantity);
|
|
19
19
|
const localQuantityNum = Number(localQuantity) || 0;
|
|
20
20
|
const handleQuantityChange = (newQuantity) => {
|
|
@@ -69,8 +69,11 @@ export default function AutoTopupProductCard({
|
|
|
69
69
|
{
|
|
70
70
|
name: product?.name || "",
|
|
71
71
|
description: t("payment.autoTopup.creditsIncluded", {
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
num: formatCreditForCheckout(
|
|
73
|
+
formatNumber(creditUnitAmount * localQuantityNum),
|
|
74
|
+
creditCurrency?.symbol || "",
|
|
75
|
+
locale
|
|
76
|
+
)
|
|
74
77
|
}),
|
|
75
78
|
logo: product?.images?.[0],
|
|
76
79
|
size: 40
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { jsx
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useLocaleContext } from "@arcblock/ux/lib/Locale/context";
|
|
3
3
|
import { Box, Typography, Chip } from "@mui/material";
|
|
4
4
|
import { useRequest } from "ahooks";
|
|
5
5
|
import React, { useEffect, useRef, useState } from "react";
|
|
6
6
|
import { useNavigate } from "react-router-dom";
|
|
7
7
|
import { styled } from "@mui/system";
|
|
8
|
-
import {
|
|
8
|
+
import { formatToDate, formatCreditAmount, formatBNStr } from "../../libs/util.js";
|
|
9
9
|
import { usePaymentContext } from "../../contexts/payment.js";
|
|
10
10
|
import api from "../../libs/api.js";
|
|
11
11
|
import Table from "../../components/table.js";
|
|
@@ -111,11 +111,10 @@ const GrantsTable = React.memo((props) => {
|
|
|
111
111
|
options: {
|
|
112
112
|
customBodyRenderLite: (_, index) => {
|
|
113
113
|
const grant = data?.list[index];
|
|
114
|
-
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleLinkClick(e, grant), children: /* @__PURE__ */
|
|
114
|
+
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleLinkClick(e, grant), children: /* @__PURE__ */ jsx(Typography, { variant: "body2", children: formatCreditAmount(
|
|
115
115
|
formatBNStr(grant.remaining_amount, grant.paymentCurrency.decimal),
|
|
116
|
-
" ",
|
|
117
116
|
grant.paymentCurrency.symbol
|
|
118
|
-
|
|
117
|
+
) }) });
|
|
119
118
|
}
|
|
120
119
|
}
|
|
121
120
|
},
|
|
@@ -7,7 +7,7 @@ import React, { useCallback, useEffect, useRef, useState } from "react";
|
|
|
7
7
|
import { styled } from "@mui/system";
|
|
8
8
|
import { joinURL } from "ufo";
|
|
9
9
|
import DateRangePicker from "../../components/date-range-picker.js";
|
|
10
|
-
import {
|
|
10
|
+
import { formatToDate, getPrefix, formatCreditAmount, formatBNStr } from "../../libs/util.js";
|
|
11
11
|
import { usePaymentContext } from "../../contexts/payment.js";
|
|
12
12
|
import api from "../../libs/api.js";
|
|
13
13
|
import Table from "../../components/table.js";
|
|
@@ -133,13 +133,9 @@ const TransactionsTable = React.memo((props) => {
|
|
|
133
133
|
const amount = isGrant ? item.amount : item.credit_amount;
|
|
134
134
|
const currency = item.paymentCurrency || item.currency;
|
|
135
135
|
const unit = !isGrant && item.meter?.unit ? item.meter.unit : currency?.symbol;
|
|
136
|
-
const displayAmount = formatBNStr(amount, currency?.decimal || 0);
|
|
136
|
+
const displayAmount = formatCreditAmount(formatBNStr(amount, currency?.decimal || 0), unit);
|
|
137
137
|
if (!includeGrants) {
|
|
138
|
-
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), children: /* @__PURE__ */
|
|
139
|
-
displayAmount,
|
|
140
|
-
" ",
|
|
141
|
-
unit
|
|
142
|
-
] }) });
|
|
138
|
+
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), children: /* @__PURE__ */ jsx(Typography, { children: displayAmount }) });
|
|
143
139
|
}
|
|
144
140
|
return /* @__PURE__ */ jsx(Box, { onClick: (e) => handleTransactionClick(e, item), children: /* @__PURE__ */ jsxs(
|
|
145
141
|
Typography,
|
|
@@ -150,9 +146,7 @@ const TransactionsTable = React.memo((props) => {
|
|
|
150
146
|
children: [
|
|
151
147
|
isGrant ? "+" : "-",
|
|
152
148
|
" ",
|
|
153
|
-
displayAmount
|
|
154
|
-
" ",
|
|
155
|
-
unit
|
|
149
|
+
displayAmount
|
|
156
150
|
]
|
|
157
151
|
}
|
|
158
152
|
) });
|
package/es/libs/util.d.ts
CHANGED
|
@@ -16,6 +16,27 @@ export declare const formatLocale: (locale?: string) => string;
|
|
|
16
16
|
export declare const formatPrettyMsLocale: (locale: string) => "zh_CN" | "en_US";
|
|
17
17
|
export declare const formatError: (err: any) => any;
|
|
18
18
|
export declare function formatBNStr(str?: string, decimals?: number, precision?: number, trim?: boolean, thousandSeparated?: boolean): string;
|
|
19
|
+
/**
|
|
20
|
+
* Format credit amount for display (data contexts: balances, transactions, notifications)
|
|
21
|
+
* - If currency has symbol mapping (USD -> $): shows "$20"
|
|
22
|
+
* - If no symbol mapping: shows "20 minutes" or "20 arcsphere credits"
|
|
23
|
+
*
|
|
24
|
+
* @param formattedAmount - Already formatted amount string (e.g., "20", "1,000.50")
|
|
25
|
+
* @param currencySymbol - Currency symbol (USD, EUR, minutes, etc.)
|
|
26
|
+
* @param showUnit - Whether to show the unit (default: true)
|
|
27
|
+
*/
|
|
28
|
+
export declare function formatCreditAmount(formattedAmount: string, currencySymbol: string, showUnit?: boolean): string;
|
|
29
|
+
/**
|
|
30
|
+
* Format credit amount for checkout/purchase scenarios
|
|
31
|
+
* - If currency has symbol mapping (USD -> $): shows "$20 of credits" (en) or "$20 的信用额度" (zh)
|
|
32
|
+
* - If no symbol mapping: shows "20 minutes" or "20 arcsphere credits"
|
|
33
|
+
*
|
|
34
|
+
* @param formattedAmount - Already formatted amount string (e.g., "20", "1,000.50")
|
|
35
|
+
* @param currencySymbol - Currency symbol (USD, EUR, minutes, etc.)
|
|
36
|
+
* @param locale - Locale for translation (default: 'en')
|
|
37
|
+
* @param showUnit - Whether to show the unit (default: true)
|
|
38
|
+
*/
|
|
39
|
+
export declare function formatCreditForCheckout(formattedAmount: string, currencySymbol: string, locale?: string, showUnit?: boolean): string;
|
|
19
40
|
export declare function formatNumber(n: number | string, precision?: number, trim?: boolean, thousandSeparated?: boolean): string;
|
|
20
41
|
export declare const formatPrice: (price: TPrice, currency: TPaymentCurrency, unit_label?: string, quantity?: number, bn?: boolean, locale?: string) => string;
|
|
21
42
|
export declare const formatPriceAmount: (price: TPrice, currency: TPaymentCurrency, unit_label?: string, quantity?: number, bn?: boolean) => string;
|
package/es/libs/util.js
CHANGED
|
@@ -122,6 +122,35 @@ export function formatBNStr(str = "", decimals = 18, precision = 6, trim = true,
|
|
|
122
122
|
}
|
|
123
123
|
return formatNumber(fromUnitToToken(str, decimals), precision, trim, thousandSeparated);
|
|
124
124
|
}
|
|
125
|
+
const CURRENCY_SYMBOLS = {
|
|
126
|
+
USD: "$",
|
|
127
|
+
EUR: "\u20AC",
|
|
128
|
+
GBP: "\xA3",
|
|
129
|
+
JPY: "\xA5",
|
|
130
|
+
CNY: "\xA5",
|
|
131
|
+
KRW: "\u20A9",
|
|
132
|
+
INR: "\u20B9",
|
|
133
|
+
AUD: "A$",
|
|
134
|
+
CAD: "C$",
|
|
135
|
+
CHF: "Fr",
|
|
136
|
+
HKD: "HK$",
|
|
137
|
+
SGD: "S$",
|
|
138
|
+
NZD: "NZ$"
|
|
139
|
+
};
|
|
140
|
+
export function formatCreditAmount(formattedAmount, currencySymbol, showUnit = true) {
|
|
141
|
+
const mappedSymbol = CURRENCY_SYMBOLS[currencySymbol];
|
|
142
|
+
if (mappedSymbol) {
|
|
143
|
+
return `${mappedSymbol}${formattedAmount}`;
|
|
144
|
+
}
|
|
145
|
+
return `${formattedAmount} ${showUnit ? currencySymbol : ""}`;
|
|
146
|
+
}
|
|
147
|
+
export function formatCreditForCheckout(formattedAmount, currencySymbol, locale = "en", showUnit = true) {
|
|
148
|
+
const mappedSymbol = CURRENCY_SYMBOLS[currencySymbol];
|
|
149
|
+
if (mappedSymbol) {
|
|
150
|
+
return `${mappedSymbol}${formattedAmount} ${showUnit ? t("common.credits", locale) : ""}`;
|
|
151
|
+
}
|
|
152
|
+
return `${formattedAmount} ${showUnit ? currencySymbol : ""}`;
|
|
153
|
+
}
|
|
125
154
|
export function formatNumber(n, precision = 6, trim = true, thousandSeparated = true) {
|
|
126
155
|
if (!n || n === "0") {
|
|
127
156
|
return "0";
|
package/es/locales/en.js
CHANGED
|
@@ -63,6 +63,8 @@ export default flat({
|
|
|
63
63
|
creditTransaction: "Credit Transaction",
|
|
64
64
|
creditAmount: "Credit Amount",
|
|
65
65
|
remainingBalance: "Remaining Balance",
|
|
66
|
+
credits: "credits",
|
|
67
|
+
ofCredits: "of credits",
|
|
66
68
|
transferStatus: "Transaction Status",
|
|
67
69
|
sourceData: "Source Data",
|
|
68
70
|
viewGrant: "View Grant",
|
|
@@ -251,17 +253,17 @@ export default flat({
|
|
|
251
253
|
},
|
|
252
254
|
credit: {
|
|
253
255
|
normal: {
|
|
254
|
-
oneTime: "
|
|
255
|
-
oneTimeWithExpiry: "
|
|
256
|
-
recurring: "
|
|
257
|
-
recurringWithExpiry: "
|
|
256
|
+
oneTime: "Purchase {amount}.",
|
|
257
|
+
oneTimeWithExpiry: "Purchase {amount} (valid for {duration} {unit}).",
|
|
258
|
+
recurring: "Subscription: {amount} {period}.",
|
|
259
|
+
recurringWithExpiry: "Subscription: {amount} {period} (valid for {duration} {unit})."
|
|
258
260
|
},
|
|
259
261
|
pending: {
|
|
260
|
-
notEnough: "
|
|
261
|
-
oneTimeEnough: "
|
|
262
|
-
oneTimeEnoughWithExpiry: "
|
|
263
|
-
recurringEnough: "
|
|
264
|
-
recurringEnoughWithExpiry: "
|
|
262
|
+
notEnough: "You have a usage overage of {amount}. To restore access, a minimum purchase of {quantity} units is required.",
|
|
263
|
+
oneTimeEnough: "You have a usage overage of {amount}. This purchase adds {totalAmount}. After covering the overage, your new available balance will be {availableAmount}.",
|
|
264
|
+
oneTimeEnoughWithExpiry: "You have a usage overage of {amount}. This purchase adds {totalAmount} (valid for {duration} {unit}). After covering the overage, your new available balance will be {availableAmount}.",
|
|
265
|
+
recurringEnough: "You have a usage overage of {amount}. This subscription adds {totalAmount} {period}. After covering the overage, your new available balance will be {availableAmount}.",
|
|
266
|
+
recurringEnoughWithExpiry: "You have a usage overage of {amount}. This subscription adds {totalAmount} {period} (valid for {duration} {unit}). After covering the overage, your new available balance will be {availableAmount}."
|
|
265
267
|
}
|
|
266
268
|
},
|
|
267
269
|
expired: {
|
|
@@ -337,7 +339,7 @@ export default flat({
|
|
|
337
339
|
thresholdDescription: "Credits remaining to trigger auto top-up",
|
|
338
340
|
thresholdMinError: "Threshold must be greater than 0",
|
|
339
341
|
thresholdFormatError: "Please enter a valid number",
|
|
340
|
-
creditsIncluded: "{num}
|
|
342
|
+
creditsIncluded: "{num} included",
|
|
341
343
|
purchaseBelow: "Purchase this package",
|
|
342
344
|
perPackage: "per package",
|
|
343
345
|
quantity: "Quantity",
|
package/es/locales/zh.js
CHANGED
|
@@ -63,6 +63,8 @@ export default flat({
|
|
|
63
63
|
creditTransaction: "\u4FE1\u7528\u4EA4\u6613",
|
|
64
64
|
creditAmount: "\u4FE1\u7528\u989D\u5EA6",
|
|
65
65
|
remainingBalance: "\u5269\u4F59\u4F59\u989D",
|
|
66
|
+
credits: "\u989D\u5EA6",
|
|
67
|
+
ofCredits: "\u989D\u5EA6",
|
|
66
68
|
transferStatus: "\u4EA4\u6613\u72B6\u6001",
|
|
67
69
|
sourceData: "\u6E90\u6570\u636E",
|
|
68
70
|
viewGrant: "\u67E5\u770B\u989D\u5EA6",
|
|
@@ -288,17 +290,17 @@ export default flat({
|
|
|
288
290
|
},
|
|
289
291
|
credit: {
|
|
290
292
|
normal: {
|
|
291
|
-
oneTime: "\
|
|
292
|
-
oneTimeWithExpiry: "\
|
|
293
|
-
recurring: "{period}
|
|
294
|
-
recurringWithExpiry: "{period}
|
|
293
|
+
oneTime: "\u8D2D\u4E70 {amount}\u3002",
|
|
294
|
+
oneTimeWithExpiry: "\u8D2D\u4E70 {amount}\uFF08\u6709\u6548\u671F {duration} {unit}\uFF09\u3002",
|
|
295
|
+
recurring: "\u8BA2\u9605\uFF1A{period}{amount}\u3002",
|
|
296
|
+
recurringWithExpiry: "\u8BA2\u9605\uFF1A{period}{amount}\uFF08\u6709\u6548\u671F {duration} {unit}\uFF09\u3002"
|
|
295
297
|
},
|
|
296
298
|
pending: {
|
|
297
|
-
notEnough: "\u60A8\
|
|
298
|
-
oneTimeEnough: "\u60A8\
|
|
299
|
-
oneTimeEnoughWithExpiry: "\u60A8\
|
|
300
|
-
recurringEnough: "\u60A8\
|
|
301
|
-
recurringEnoughWithExpiry: "\u60A8\
|
|
299
|
+
notEnough: "\u60A8\u6709 {amount} \u7684\u4F7F\u7528\u8D85\u989D\u3002\u8981\u6062\u590D\u8BBF\u95EE\uFF0C\u9700\u8981\u81F3\u5C11\u8D2D\u4E70 {quantity} \u6570\u91CF\u3002",
|
|
300
|
+
oneTimeEnough: "\u60A8\u6709 {amount} \u7684\u4F7F\u7528\u8D85\u989D\u3002\u672C\u6B21\u8D2D\u4E70\u5C06\u589E\u52A0 {totalAmount}\u3002\u6263\u9664\u8D85\u989D\u540E\uFF0C\u60A8\u7684\u65B0\u53EF\u7528\u4F59\u989D\u5C06\u4E3A {availableAmount}\u3002",
|
|
301
|
+
oneTimeEnoughWithExpiry: "\u60A8\u6709 {amount} \u7684\u4F7F\u7528\u8D85\u989D\u3002\u672C\u6B21\u8D2D\u4E70\u5C06\u589E\u52A0 {totalAmount}\uFF08\u6709\u6548\u671F {duration} {unit}\uFF09\u3002\u6263\u9664\u8D85\u989D\u540E\uFF0C\u60A8\u7684\u65B0\u53EF\u7528\u4F59\u989D\u5C06\u4E3A {availableAmount}\u3002",
|
|
302
|
+
recurringEnough: "\u60A8\u6709 {amount} \u7684\u4F7F\u7528\u8D85\u989D\u3002\u672C\u8BA2\u9605{period}\u5C06\u589E\u52A0 {totalAmount}\u3002\u6263\u9664\u8D85\u989D\u540E\uFF0C\u60A8\u7684\u65B0\u53EF\u7528\u4F59\u989D\u5C06\u4E3A {availableAmount}\u3002",
|
|
303
|
+
recurringEnoughWithExpiry: "\u60A8\u6709 {amount} \u7684\u4F7F\u7528\u8D85\u989D\u3002\u672C\u8BA2\u9605{period}\u5C06\u589E\u52A0 {totalAmount}\uFF08\u6709\u6548\u671F {duration} {unit}\uFF09\u3002\u6263\u9664\u8D85\u989D\u540E\uFF0C\u60A8\u7684\u65B0\u53EF\u7528\u4F59\u989D\u5C06\u4E3A {availableAmount}\u3002"
|
|
302
304
|
}
|
|
303
305
|
},
|
|
304
306
|
emptyItems: {
|
|
@@ -341,7 +343,7 @@ export default flat({
|
|
|
341
343
|
thresholdDescription: "\u89E6\u53D1\u81EA\u52A8\u5145\u503C\u7684\u5269\u4F59\u989D\u5EA6",
|
|
342
344
|
thresholdMinError: "\u9608\u503C\u5FC5\u987B\u5927\u4E8E0",
|
|
343
345
|
thresholdFormatError: "\u8BF7\u8F93\u5165\u6709\u6548\u6570\u5B57",
|
|
344
|
-
creditsIncluded: "\u5305\u542B {num}
|
|
346
|
+
creditsIncluded: "\u5305\u542B {num}",
|
|
345
347
|
purchaseBelow: "\u8D2D\u4E70\u6B64\u5957\u9910",
|
|
346
348
|
perPackage: "\u6BCF\u5305",
|
|
347
349
|
quantity: "\u6570\u91CF",
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
formatRecurring,
|
|
17
17
|
formatUpsellSaving,
|
|
18
18
|
formatAmount,
|
|
19
|
+
formatCreditForCheckout,
|
|
19
20
|
formatBNStr
|
|
20
21
|
} from "../libs/util.js";
|
|
21
22
|
import ProductCard from "./product-card.js";
|
|
@@ -157,12 +158,12 @@ export default function ProductItem({
|
|
|
157
158
|
if (!isCreditProduct) return null;
|
|
158
159
|
const totalCreditStr = formatNumber(creditAmount * (localQuantity || 0));
|
|
159
160
|
const currencySymbol = creditCurrency?.symbol || "Credits";
|
|
161
|
+
const formattedTotalCredit = formatCreditForCheckout(totalCreditStr, currencySymbol, locale);
|
|
160
162
|
const hasPendingAmount = pendingAmount && new BN(pendingAmount || "0").gt(new BN(0));
|
|
161
163
|
const isRecurring = item.price.type === "recurring";
|
|
162
164
|
const hasExpiry = validDuration && validDuration > 0;
|
|
163
165
|
const buildBaseParams = () => ({
|
|
164
|
-
amount:
|
|
165
|
-
symbol: currencySymbol,
|
|
166
|
+
amount: formattedTotalCredit,
|
|
166
167
|
...hasExpiry && {
|
|
167
168
|
duration: validDuration,
|
|
168
169
|
unit: t(`common.${validDurationUnit}`)
|
|
@@ -172,11 +173,18 @@ export default function ProductItem({
|
|
|
172
173
|
}
|
|
173
174
|
});
|
|
174
175
|
const buildPendingParams = (pendingBN, availableAmount) => ({
|
|
175
|
-
amount:
|
|
176
|
-
|
|
177
|
-
|
|
176
|
+
amount: formatCreditForCheckout(
|
|
177
|
+
formatBNStr(pendingBN.toString(), creditCurrency?.decimal || 2),
|
|
178
|
+
currencySymbol,
|
|
179
|
+
locale
|
|
180
|
+
),
|
|
181
|
+
totalAmount: formattedTotalCredit,
|
|
178
182
|
...availableAmount && {
|
|
179
|
-
availableAmount:
|
|
183
|
+
availableAmount: formatCreditForCheckout(
|
|
184
|
+
formatBNStr(availableAmount, creditCurrency?.decimal || 2),
|
|
185
|
+
currencySymbol,
|
|
186
|
+
locale
|
|
187
|
+
)
|
|
180
188
|
},
|
|
181
189
|
...hasExpiry && {
|
|
182
190
|
duration: validDuration,
|
|
@@ -201,8 +209,11 @@ export default function ProductItem({
|
|
|
201
209
|
const actualAvailable = currentPurchaseCreditBN.sub(pendingAmountBN).toString();
|
|
202
210
|
if (!new BN(actualAvailable).gt(new BN(0))) {
|
|
203
211
|
return t("payment.checkout.credit.pending.notEnough", {
|
|
204
|
-
amount:
|
|
205
|
-
|
|
212
|
+
amount: formatCreditForCheckout(
|
|
213
|
+
formatBNStr(pendingAmountBN.toString(), creditCurrency?.decimal || 2),
|
|
214
|
+
currencySymbol,
|
|
215
|
+
locale
|
|
216
|
+
),
|
|
206
217
|
quantity: formatNumber(minQuantityNeeded)
|
|
207
218
|
});
|
|
208
219
|
}
|
|
@@ -386,8 +386,8 @@ function AutoTopupCard({
|
|
|
386
386
|
pt: 1.5
|
|
387
387
|
},
|
|
388
388
|
children: [(() => {
|
|
389
|
-
const threshold = `${(0, _util.formatNumber)(config.threshold)
|
|
390
|
-
const credits = `${(0, _util.formatNumber)(Number(config.price.metadata?.credit_config?.credit_amount || 0) * Number(config.quantity))
|
|
389
|
+
const threshold = `${(0, _util.formatCreditAmount)((0, _util.formatNumber)(config.threshold), config.currency?.symbol || "")}`;
|
|
390
|
+
const credits = `${(0, _util.formatCreditAmount)((0, _util.formatNumber)(Number(config.price.metadata?.credit_config?.credit_amount || 0) * Number(config.quantity)), config.currency?.symbol || "")}`;
|
|
391
391
|
return /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Typography, {
|
|
392
392
|
variant: "body2",
|
|
393
393
|
sx: {
|
|
@@ -637,7 +637,7 @@ function AutoTopup({
|
|
|
637
637
|
input: {
|
|
638
638
|
endAdornment: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.InputAdornment, {
|
|
639
639
|
position: "end",
|
|
640
|
-
children: config?.currency?.
|
|
640
|
+
children: (0, _util.formatCreditAmount)("", config?.currency?.symbol)
|
|
641
641
|
})
|
|
642
642
|
},
|
|
643
643
|
htmlInput: {
|
|
@@ -22,7 +22,8 @@ function AutoTopupProductCard({
|
|
|
22
22
|
creditCurrency
|
|
23
23
|
}) {
|
|
24
24
|
const {
|
|
25
|
-
t
|
|
25
|
+
t,
|
|
26
|
+
locale
|
|
26
27
|
} = (0, _context.useLocaleContext)();
|
|
27
28
|
const [localQuantity, setLocalQuantity] = (0, _react.useState)(quantity);
|
|
28
29
|
const localQuantityNum = Number(localQuantity) || 0;
|
|
@@ -75,8 +76,7 @@ function AutoTopupProductCard({
|
|
|
75
76
|
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_productCard.default, {
|
|
76
77
|
name: product?.name || "",
|
|
77
78
|
description: t("payment.autoTopup.creditsIncluded", {
|
|
78
|
-
|
|
79
|
-
num: (0, _util.formatNumber)(creditUnitAmount * localQuantityNum)
|
|
79
|
+
num: (0, _util.formatCreditForCheckout)((0, _util.formatNumber)(creditUnitAmount * localQuantityNum), creditCurrency?.symbol || "", locale)
|
|
80
80
|
}),
|
|
81
81
|
logo: product?.images?.[0],
|
|
82
82
|
size: 40
|
|
@@ -154,9 +154,9 @@ const GrantsTable = _react.default.memo(props => {
|
|
|
154
154
|
const grant = data?.list[index];
|
|
155
155
|
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
|
|
156
156
|
onClick: e => handleLinkClick(e, grant),
|
|
157
|
-
children: /* @__PURE__ */(0, _jsxRuntime.
|
|
157
|
+
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
158
158
|
variant: "body2",
|
|
159
|
-
children:
|
|
159
|
+
children: (0, _util.formatCreditAmount)((0, _util.formatBNStr)(grant.remaining_amount, grant.paymentCurrency.decimal), grant.paymentCurrency.symbol)
|
|
160
160
|
})
|
|
161
161
|
});
|
|
162
162
|
}
|
|
@@ -157,12 +157,12 @@ const TransactionsTable = _react.default.memo(props => {
|
|
|
157
157
|
const amount = isGrant ? item.amount : item.credit_amount;
|
|
158
158
|
const currency = item.paymentCurrency || item.currency;
|
|
159
159
|
const unit = !isGrant && item.meter?.unit ? item.meter.unit : currency?.symbol;
|
|
160
|
-
const displayAmount = (0, _util.formatBNStr)(amount, currency?.decimal || 0);
|
|
160
|
+
const displayAmount = (0, _util.formatCreditAmount)((0, _util.formatBNStr)(amount, currency?.decimal || 0), unit);
|
|
161
161
|
if (!includeGrants) {
|
|
162
162
|
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Box, {
|
|
163
163
|
onClick: e => handleTransactionClick(e, item),
|
|
164
|
-
children: /* @__PURE__ */(0, _jsxRuntime.
|
|
165
|
-
children:
|
|
164
|
+
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
165
|
+
children: displayAmount
|
|
166
166
|
})
|
|
167
167
|
});
|
|
168
168
|
}
|
|
@@ -172,7 +172,7 @@ const TransactionsTable = _react.default.memo(props => {
|
|
|
172
172
|
sx: {
|
|
173
173
|
color: isGrant ? "success.main" : "error.main"
|
|
174
174
|
},
|
|
175
|
-
children: [isGrant ? "+" : "-", " ", displayAmount
|
|
175
|
+
children: [isGrant ? "+" : "-", " ", displayAmount]
|
|
176
176
|
})
|
|
177
177
|
});
|
|
178
178
|
}
|
package/lib/libs/util.d.ts
CHANGED
|
@@ -16,6 +16,27 @@ export declare const formatLocale: (locale?: string) => string;
|
|
|
16
16
|
export declare const formatPrettyMsLocale: (locale: string) => "zh_CN" | "en_US";
|
|
17
17
|
export declare const formatError: (err: any) => any;
|
|
18
18
|
export declare function formatBNStr(str?: string, decimals?: number, precision?: number, trim?: boolean, thousandSeparated?: boolean): string;
|
|
19
|
+
/**
|
|
20
|
+
* Format credit amount for display (data contexts: balances, transactions, notifications)
|
|
21
|
+
* - If currency has symbol mapping (USD -> $): shows "$20"
|
|
22
|
+
* - If no symbol mapping: shows "20 minutes" or "20 arcsphere credits"
|
|
23
|
+
*
|
|
24
|
+
* @param formattedAmount - Already formatted amount string (e.g., "20", "1,000.50")
|
|
25
|
+
* @param currencySymbol - Currency symbol (USD, EUR, minutes, etc.)
|
|
26
|
+
* @param showUnit - Whether to show the unit (default: true)
|
|
27
|
+
*/
|
|
28
|
+
export declare function formatCreditAmount(formattedAmount: string, currencySymbol: string, showUnit?: boolean): string;
|
|
29
|
+
/**
|
|
30
|
+
* Format credit amount for checkout/purchase scenarios
|
|
31
|
+
* - If currency has symbol mapping (USD -> $): shows "$20 of credits" (en) or "$20 的信用额度" (zh)
|
|
32
|
+
* - If no symbol mapping: shows "20 minutes" or "20 arcsphere credits"
|
|
33
|
+
*
|
|
34
|
+
* @param formattedAmount - Already formatted amount string (e.g., "20", "1,000.50")
|
|
35
|
+
* @param currencySymbol - Currency symbol (USD, EUR, minutes, etc.)
|
|
36
|
+
* @param locale - Locale for translation (default: 'en')
|
|
37
|
+
* @param showUnit - Whether to show the unit (default: true)
|
|
38
|
+
*/
|
|
39
|
+
export declare function formatCreditForCheckout(formattedAmount: string, currencySymbol: string, locale?: string, showUnit?: boolean): string;
|
|
19
40
|
export declare function formatNumber(n: number | string, precision?: number, trim?: boolean, thousandSeparated?: boolean): string;
|
|
20
41
|
export declare const formatPrice: (price: TPrice, currency: TPaymentCurrency, unit_label?: string, quantity?: number, bn?: boolean, locale?: string) => string;
|
|
21
42
|
export declare const formatPriceAmount: (price: TPrice, currency: TPaymentCurrency, unit_label?: string, quantity?: number, bn?: boolean) => string;
|
package/lib/libs/util.js
CHANGED
|
@@ -11,6 +11,8 @@ exports.formatAmountPrecisionLimit = formatAmountPrecisionLimit;
|
|
|
11
11
|
exports.formatBNStr = formatBNStr;
|
|
12
12
|
exports.formatCheckoutHeadlines = formatCheckoutHeadlines;
|
|
13
13
|
exports.formatCouponTerms = void 0;
|
|
14
|
+
exports.formatCreditAmount = formatCreditAmount;
|
|
15
|
+
exports.formatCreditForCheckout = formatCreditForCheckout;
|
|
14
16
|
exports.formatDateTime = formatDateTime;
|
|
15
17
|
exports.formatError = void 0;
|
|
16
18
|
exports.formatLineItemPricing = formatLineItemPricing;
|
|
@@ -207,6 +209,35 @@ function formatBNStr(str = "", decimals = 18, precision = 6, trim = true, thousa
|
|
|
207
209
|
}
|
|
208
210
|
return formatNumber((0, _util.fromUnitToToken)(str, decimals), precision, trim, thousandSeparated);
|
|
209
211
|
}
|
|
212
|
+
const CURRENCY_SYMBOLS = {
|
|
213
|
+
USD: "$",
|
|
214
|
+
EUR: "\u20AC",
|
|
215
|
+
GBP: "\xA3",
|
|
216
|
+
JPY: "\xA5",
|
|
217
|
+
CNY: "\xA5",
|
|
218
|
+
KRW: "\u20A9",
|
|
219
|
+
INR: "\u20B9",
|
|
220
|
+
AUD: "A$",
|
|
221
|
+
CAD: "C$",
|
|
222
|
+
CHF: "Fr",
|
|
223
|
+
HKD: "HK$",
|
|
224
|
+
SGD: "S$",
|
|
225
|
+
NZD: "NZ$"
|
|
226
|
+
};
|
|
227
|
+
function formatCreditAmount(formattedAmount, currencySymbol, showUnit = true) {
|
|
228
|
+
const mappedSymbol = CURRENCY_SYMBOLS[currencySymbol];
|
|
229
|
+
if (mappedSymbol) {
|
|
230
|
+
return `${mappedSymbol}${formattedAmount}`;
|
|
231
|
+
}
|
|
232
|
+
return `${formattedAmount} ${showUnit ? currencySymbol : ""}`;
|
|
233
|
+
}
|
|
234
|
+
function formatCreditForCheckout(formattedAmount, currencySymbol, locale = "en", showUnit = true) {
|
|
235
|
+
const mappedSymbol = CURRENCY_SYMBOLS[currencySymbol];
|
|
236
|
+
if (mappedSymbol) {
|
|
237
|
+
return `${mappedSymbol}${formattedAmount} ${showUnit ? (0, _locales.t)("common.credits", locale) : ""}`;
|
|
238
|
+
}
|
|
239
|
+
return `${formattedAmount} ${showUnit ? currencySymbol : ""}`;
|
|
240
|
+
}
|
|
210
241
|
function formatNumber(n, precision = 6, trim = true, thousandSeparated = true) {
|
|
211
242
|
if (!n || n === "0") {
|
|
212
243
|
return "0";
|
package/lib/locales/en.js
CHANGED
|
@@ -70,6 +70,8 @@ module.exports = (0, _flat.default)({
|
|
|
70
70
|
creditTransaction: "Credit Transaction",
|
|
71
71
|
creditAmount: "Credit Amount",
|
|
72
72
|
remainingBalance: "Remaining Balance",
|
|
73
|
+
credits: "credits",
|
|
74
|
+
ofCredits: "of credits",
|
|
73
75
|
transferStatus: "Transaction Status",
|
|
74
76
|
sourceData: "Source Data",
|
|
75
77
|
viewGrant: "View Grant",
|
|
@@ -258,17 +260,17 @@ module.exports = (0, _flat.default)({
|
|
|
258
260
|
},
|
|
259
261
|
credit: {
|
|
260
262
|
normal: {
|
|
261
|
-
oneTime: "
|
|
262
|
-
oneTimeWithExpiry: "
|
|
263
|
-
recurring: "
|
|
264
|
-
recurringWithExpiry: "
|
|
263
|
+
oneTime: "Purchase {amount}.",
|
|
264
|
+
oneTimeWithExpiry: "Purchase {amount} (valid for {duration} {unit}).",
|
|
265
|
+
recurring: "Subscription: {amount} {period}.",
|
|
266
|
+
recurringWithExpiry: "Subscription: {amount} {period} (valid for {duration} {unit})."
|
|
265
267
|
},
|
|
266
268
|
pending: {
|
|
267
|
-
notEnough: "
|
|
268
|
-
oneTimeEnough: "
|
|
269
|
-
oneTimeEnoughWithExpiry: "
|
|
270
|
-
recurringEnough: "
|
|
271
|
-
recurringEnoughWithExpiry: "
|
|
269
|
+
notEnough: "You have a usage overage of {amount}. To restore access, a minimum purchase of {quantity} units is required.",
|
|
270
|
+
oneTimeEnough: "You have a usage overage of {amount}. This purchase adds {totalAmount}. After covering the overage, your new available balance will be {availableAmount}.",
|
|
271
|
+
oneTimeEnoughWithExpiry: "You have a usage overage of {amount}. This purchase adds {totalAmount} (valid for {duration} {unit}). After covering the overage, your new available balance will be {availableAmount}.",
|
|
272
|
+
recurringEnough: "You have a usage overage of {amount}. This subscription adds {totalAmount} {period}. After covering the overage, your new available balance will be {availableAmount}.",
|
|
273
|
+
recurringEnoughWithExpiry: "You have a usage overage of {amount}. This subscription adds {totalAmount} {period} (valid for {duration} {unit}). After covering the overage, your new available balance will be {availableAmount}."
|
|
272
274
|
}
|
|
273
275
|
},
|
|
274
276
|
expired: {
|
|
@@ -344,7 +346,7 @@ module.exports = (0, _flat.default)({
|
|
|
344
346
|
thresholdDescription: "Credits remaining to trigger auto top-up",
|
|
345
347
|
thresholdMinError: "Threshold must be greater than 0",
|
|
346
348
|
thresholdFormatError: "Please enter a valid number",
|
|
347
|
-
creditsIncluded: "{num}
|
|
349
|
+
creditsIncluded: "{num} included",
|
|
348
350
|
purchaseBelow: "Purchase this package",
|
|
349
351
|
perPackage: "per package",
|
|
350
352
|
quantity: "Quantity",
|
package/lib/locales/zh.js
CHANGED
|
@@ -70,6 +70,8 @@ module.exports = (0, _flat.default)({
|
|
|
70
70
|
creditTransaction: "\u4FE1\u7528\u4EA4\u6613",
|
|
71
71
|
creditAmount: "\u4FE1\u7528\u989D\u5EA6",
|
|
72
72
|
remainingBalance: "\u5269\u4F59\u4F59\u989D",
|
|
73
|
+
credits: "\u989D\u5EA6",
|
|
74
|
+
ofCredits: "\u989D\u5EA6",
|
|
73
75
|
transferStatus: "\u4EA4\u6613\u72B6\u6001",
|
|
74
76
|
sourceData: "\u6E90\u6570\u636E",
|
|
75
77
|
viewGrant: "\u67E5\u770B\u989D\u5EA6",
|
|
@@ -295,17 +297,17 @@ module.exports = (0, _flat.default)({
|
|
|
295
297
|
},
|
|
296
298
|
credit: {
|
|
297
299
|
normal: {
|
|
298
|
-
oneTime: "\
|
|
299
|
-
oneTimeWithExpiry: "\
|
|
300
|
-
recurring: "{period}
|
|
301
|
-
recurringWithExpiry: "{period}
|
|
300
|
+
oneTime: "\u8D2D\u4E70 {amount}\u3002",
|
|
301
|
+
oneTimeWithExpiry: "\u8D2D\u4E70 {amount}\uFF08\u6709\u6548\u671F {duration} {unit}\uFF09\u3002",
|
|
302
|
+
recurring: "\u8BA2\u9605\uFF1A{period}{amount}\u3002",
|
|
303
|
+
recurringWithExpiry: "\u8BA2\u9605\uFF1A{period}{amount}\uFF08\u6709\u6548\u671F {duration} {unit}\uFF09\u3002"
|
|
302
304
|
},
|
|
303
305
|
pending: {
|
|
304
|
-
notEnough: "\u60A8\
|
|
305
|
-
oneTimeEnough: "\u60A8\
|
|
306
|
-
oneTimeEnoughWithExpiry: "\u60A8\
|
|
307
|
-
recurringEnough: "\u60A8\
|
|
308
|
-
recurringEnoughWithExpiry: "\u60A8\
|
|
306
|
+
notEnough: "\u60A8\u6709 {amount} \u7684\u4F7F\u7528\u8D85\u989D\u3002\u8981\u6062\u590D\u8BBF\u95EE\uFF0C\u9700\u8981\u81F3\u5C11\u8D2D\u4E70 {quantity} \u6570\u91CF\u3002",
|
|
307
|
+
oneTimeEnough: "\u60A8\u6709 {amount} \u7684\u4F7F\u7528\u8D85\u989D\u3002\u672C\u6B21\u8D2D\u4E70\u5C06\u589E\u52A0 {totalAmount}\u3002\u6263\u9664\u8D85\u989D\u540E\uFF0C\u60A8\u7684\u65B0\u53EF\u7528\u4F59\u989D\u5C06\u4E3A {availableAmount}\u3002",
|
|
308
|
+
oneTimeEnoughWithExpiry: "\u60A8\u6709 {amount} \u7684\u4F7F\u7528\u8D85\u989D\u3002\u672C\u6B21\u8D2D\u4E70\u5C06\u589E\u52A0 {totalAmount}\uFF08\u6709\u6548\u671F {duration} {unit}\uFF09\u3002\u6263\u9664\u8D85\u989D\u540E\uFF0C\u60A8\u7684\u65B0\u53EF\u7528\u4F59\u989D\u5C06\u4E3A {availableAmount}\u3002",
|
|
309
|
+
recurringEnough: "\u60A8\u6709 {amount} \u7684\u4F7F\u7528\u8D85\u989D\u3002\u672C\u8BA2\u9605{period}\u5C06\u589E\u52A0 {totalAmount}\u3002\u6263\u9664\u8D85\u989D\u540E\uFF0C\u60A8\u7684\u65B0\u53EF\u7528\u4F59\u989D\u5C06\u4E3A {availableAmount}\u3002",
|
|
310
|
+
recurringEnoughWithExpiry: "\u60A8\u6709 {amount} \u7684\u4F7F\u7528\u8D85\u989D\u3002\u672C\u8BA2\u9605{period}\u5C06\u589E\u52A0 {totalAmount}\uFF08\u6709\u6548\u671F {duration} {unit}\uFF09\u3002\u6263\u9664\u8D85\u989D\u540E\uFF0C\u60A8\u7684\u65B0\u53EF\u7528\u4F59\u989D\u5C06\u4E3A {availableAmount}\u3002"
|
|
309
311
|
}
|
|
310
312
|
},
|
|
311
313
|
emptyItems: {
|
|
@@ -348,7 +350,7 @@ module.exports = (0, _flat.default)({
|
|
|
348
350
|
thresholdDescription: "\u89E6\u53D1\u81EA\u52A8\u5145\u503C\u7684\u5269\u4F59\u989D\u5EA6",
|
|
349
351
|
thresholdMinError: "\u9608\u503C\u5FC5\u987B\u5927\u4E8E0",
|
|
350
352
|
thresholdFormatError: "\u8BF7\u8F93\u5165\u6709\u6548\u6570\u5B57",
|
|
351
|
-
creditsIncluded: "\u5305\u542B {num}
|
|
353
|
+
creditsIncluded: "\u5305\u542B {num}",
|
|
352
354
|
purchaseBelow: "\u8D2D\u4E70\u6B64\u5957\u9910",
|
|
353
355
|
perPackage: "\u6BCF\u5305",
|
|
354
356
|
quantity: "\u6570\u91CF",
|
|
@@ -166,12 +166,12 @@ function ProductItem({
|
|
|
166
166
|
if (!isCreditProduct) return null;
|
|
167
167
|
const totalCreditStr = (0, _util2.formatNumber)(creditAmount * (localQuantity || 0));
|
|
168
168
|
const currencySymbol = creditCurrency?.symbol || "Credits";
|
|
169
|
+
const formattedTotalCredit = (0, _util2.formatCreditForCheckout)(totalCreditStr, currencySymbol, locale);
|
|
169
170
|
const hasPendingAmount = pendingAmount && new _util.BN(pendingAmount || "0").gt(new _util.BN(0));
|
|
170
171
|
const isRecurring = item.price.type === "recurring";
|
|
171
172
|
const hasExpiry = validDuration && validDuration > 0;
|
|
172
173
|
const buildBaseParams = () => ({
|
|
173
|
-
amount:
|
|
174
|
-
symbol: currencySymbol,
|
|
174
|
+
amount: formattedTotalCredit,
|
|
175
175
|
...(hasExpiry && {
|
|
176
176
|
duration: validDuration,
|
|
177
177
|
unit: t(`common.${validDurationUnit}`)
|
|
@@ -181,11 +181,10 @@ function ProductItem({
|
|
|
181
181
|
})
|
|
182
182
|
});
|
|
183
183
|
const buildPendingParams = (pendingBN, availableAmount) => ({
|
|
184
|
-
amount: (0, _util2.formatBNStr)(pendingBN.toString(), creditCurrency?.decimal || 2),
|
|
185
|
-
|
|
186
|
-
totalAmount: totalCreditStr,
|
|
184
|
+
amount: (0, _util2.formatCreditForCheckout)((0, _util2.formatBNStr)(pendingBN.toString(), creditCurrency?.decimal || 2), currencySymbol, locale),
|
|
185
|
+
totalAmount: formattedTotalCredit,
|
|
187
186
|
...(availableAmount && {
|
|
188
|
-
availableAmount: (0, _util2.formatBNStr)(availableAmount, creditCurrency?.decimal || 2)
|
|
187
|
+
availableAmount: (0, _util2.formatCreditForCheckout)((0, _util2.formatBNStr)(availableAmount, creditCurrency?.decimal || 2), currencySymbol, locale)
|
|
189
188
|
}),
|
|
190
189
|
...(hasExpiry && {
|
|
191
190
|
duration: validDuration,
|
|
@@ -210,8 +209,7 @@ function ProductItem({
|
|
|
210
209
|
const actualAvailable = currentPurchaseCreditBN.sub(pendingAmountBN).toString();
|
|
211
210
|
if (!new _util.BN(actualAvailable).gt(new _util.BN(0))) {
|
|
212
211
|
return t("payment.checkout.credit.pending.notEnough", {
|
|
213
|
-
amount: (0, _util2.formatBNStr)(pendingAmountBN.toString(), creditCurrency?.decimal || 2),
|
|
214
|
-
symbol: currencySymbol,
|
|
212
|
+
amount: (0, _util2.formatCreditForCheckout)((0, _util2.formatBNStr)(pendingAmountBN.toString(), creditCurrency?.decimal || 2), currencySymbol, locale),
|
|
215
213
|
quantity: (0, _util2.formatNumber)(minQuantityNeeded)
|
|
216
214
|
});
|
|
217
215
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blocklet/payment-react",
|
|
3
|
-
"version": "1.23.
|
|
3
|
+
"version": "1.23.6",
|
|
4
4
|
"description": "Reusable react components for payment kit v2",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -96,7 +96,7 @@
|
|
|
96
96
|
"@babel/core": "^7.27.4",
|
|
97
97
|
"@babel/preset-env": "^7.27.2",
|
|
98
98
|
"@babel/preset-react": "^7.27.1",
|
|
99
|
-
"@blocklet/payment-types": "1.23.
|
|
99
|
+
"@blocklet/payment-types": "1.23.6",
|
|
100
100
|
"@storybook/addon-essentials": "^7.6.20",
|
|
101
101
|
"@storybook/addon-interactions": "^7.6.20",
|
|
102
102
|
"@storybook/addon-links": "^7.6.20",
|
|
@@ -127,5 +127,5 @@
|
|
|
127
127
|
"vite-plugin-babel": "^1.3.1",
|
|
128
128
|
"vite-plugin-node-polyfills": "^0.23.0"
|
|
129
129
|
},
|
|
130
|
-
"gitHead": "
|
|
130
|
+
"gitHead": "0026a41a10475159d72ade801561d6cbce0008fe"
|
|
131
131
|
}
|
|
@@ -22,7 +22,7 @@ import { joinURL } from 'ufo';
|
|
|
22
22
|
import type { AutoRechargeConfig } from '@blocklet/payment-types';
|
|
23
23
|
import { useRequest } from 'ahooks';
|
|
24
24
|
|
|
25
|
-
import { getPrefix, formatBNStr, formatNumber, formatPrice } from '../../libs/util';
|
|
25
|
+
import { getPrefix, formatBNStr, formatNumber, formatPrice, formatCreditAmount } from '../../libs/util';
|
|
26
26
|
import { createLink, handleNavigation } from '../../libs/navigation';
|
|
27
27
|
import { usePaymentContext } from '../../contexts/payment';
|
|
28
28
|
import api from '../../libs/api';
|
|
@@ -383,10 +383,13 @@ export default function AutoTopupCard({
|
|
|
383
383
|
<Stack spacing={1.5} className="auto-topup-content" sx={{ pt: 1.5 }}>
|
|
384
384
|
{/* Main Description */}
|
|
385
385
|
{(() => {
|
|
386
|
-
const threshold = `${formatNumber(config.threshold)
|
|
387
|
-
const credits = `${
|
|
388
|
-
|
|
389
|
-
|
|
386
|
+
const threshold = `${formatCreditAmount(formatNumber(config.threshold), config.currency?.symbol || '')}`;
|
|
387
|
+
const credits = `${formatCreditAmount(
|
|
388
|
+
formatNumber(
|
|
389
|
+
Number(config.price.metadata?.credit_config?.credit_amount || 0) * Number(config.quantity)
|
|
390
|
+
),
|
|
391
|
+
config.currency?.symbol || ''
|
|
392
|
+
)}`;
|
|
390
393
|
|
|
391
394
|
return (
|
|
392
395
|
<Typography
|
|
@@ -29,7 +29,7 @@ import type { AutoRechargeConfig, TCustomer, TPaymentCurrency, TPaymentMethod }
|
|
|
29
29
|
import DidAddress from '@arcblock/ux/lib/DID';
|
|
30
30
|
import Switch from '../switch-button';
|
|
31
31
|
import api from '../../libs/api';
|
|
32
|
-
import { formatError, flattenPaymentMethods, getPrefix, formatBNStr } from '../../libs/util';
|
|
32
|
+
import { formatError, flattenPaymentMethods, getPrefix, formatBNStr, formatCreditAmount } from '../../libs/util';
|
|
33
33
|
import { usePaymentContext } from '../../contexts/payment';
|
|
34
34
|
import { createLink, handleNavigation } from '../../libs/navigation';
|
|
35
35
|
|
|
@@ -612,7 +612,11 @@ export default function AutoTopup({
|
|
|
612
612
|
}}
|
|
613
613
|
slotProps={{
|
|
614
614
|
input: {
|
|
615
|
-
endAdornment:
|
|
615
|
+
endAdornment: (
|
|
616
|
+
<InputAdornment position="end">
|
|
617
|
+
{formatCreditAmount('', config?.currency?.symbol)}
|
|
618
|
+
</InputAdornment>
|
|
619
|
+
),
|
|
616
620
|
},
|
|
617
621
|
htmlInput: {
|
|
618
622
|
min: 0,
|
|
@@ -4,7 +4,7 @@ import { useState } from 'react';
|
|
|
4
4
|
|
|
5
5
|
import type { TPaymentCurrency } from '@blocklet/payment-types';
|
|
6
6
|
import ProductCard from '../../payment/product-card';
|
|
7
|
-
import { formatPrice, formatNumber } from '../../libs/util';
|
|
7
|
+
import { formatPrice, formatNumber, formatCreditForCheckout } from '../../libs/util';
|
|
8
8
|
|
|
9
9
|
interface AutoTopupProductCardProps {
|
|
10
10
|
product: any;
|
|
@@ -27,7 +27,7 @@ export default function AutoTopupProductCard({
|
|
|
27
27
|
minQuantity = 1,
|
|
28
28
|
creditCurrency,
|
|
29
29
|
}: AutoTopupProductCardProps) {
|
|
30
|
-
const { t } = useLocaleContext();
|
|
30
|
+
const { t, locale } = useLocaleContext();
|
|
31
31
|
const [localQuantity, setLocalQuantity] = useState<number | undefined>(quantity);
|
|
32
32
|
const localQuantityNum = Number(localQuantity) || 0;
|
|
33
33
|
|
|
@@ -79,8 +79,11 @@ export default function AutoTopupProductCard({
|
|
|
79
79
|
<ProductCard
|
|
80
80
|
name={product?.name || ''}
|
|
81
81
|
description={t('payment.autoTopup.creditsIncluded', {
|
|
82
|
-
|
|
83
|
-
|
|
82
|
+
num: formatCreditForCheckout(
|
|
83
|
+
formatNumber(creditUnitAmount * localQuantityNum),
|
|
84
|
+
creditCurrency?.symbol || '',
|
|
85
|
+
locale
|
|
86
|
+
),
|
|
84
87
|
})}
|
|
85
88
|
logo={product?.images?.[0] as string}
|
|
86
89
|
size={40}
|
|
@@ -13,7 +13,7 @@ import React, { useEffect, useRef, useState } from 'react';
|
|
|
13
13
|
import { useNavigate } from 'react-router-dom';
|
|
14
14
|
|
|
15
15
|
import { styled } from '@mui/system';
|
|
16
|
-
import {
|
|
16
|
+
import { formatToDate, formatCreditAmount, formatBNStr } from '../../libs/util';
|
|
17
17
|
import { usePaymentContext } from '../../contexts/payment';
|
|
18
18
|
import api from '../../libs/api';
|
|
19
19
|
import Table from '../../components/table';
|
|
@@ -156,7 +156,10 @@ const GrantsTable = React.memo((props: Props) => {
|
|
|
156
156
|
return (
|
|
157
157
|
<Box onClick={(e) => handleLinkClick(e, grant)}>
|
|
158
158
|
<Typography variant="body2">
|
|
159
|
-
{
|
|
159
|
+
{formatCreditAmount(
|
|
160
|
+
formatBNStr(grant.remaining_amount, grant.paymentCurrency.decimal),
|
|
161
|
+
grant.paymentCurrency.symbol
|
|
162
|
+
)}
|
|
160
163
|
</Typography>
|
|
161
164
|
</Box>
|
|
162
165
|
);
|
|
@@ -16,7 +16,7 @@ import { joinURL } from 'ufo';
|
|
|
16
16
|
import DateRangePicker, { type DateRangeValue } from '../../components/date-range-picker';
|
|
17
17
|
// eslint-disable-next-line import/no-extraneous-dependencies
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { formatToDate, getPrefix, formatCreditAmount, formatBNStr } from '../../libs/util';
|
|
20
20
|
import { usePaymentContext } from '../../contexts/payment';
|
|
21
21
|
import api from '../../libs/api';
|
|
22
22
|
import Table from '../../components/table';
|
|
@@ -184,14 +184,12 @@ const TransactionsTable = React.memo((props: Props) => {
|
|
|
184
184
|
const amount = isGrant ? item.amount : item.credit_amount;
|
|
185
185
|
const currency = item.paymentCurrency || item.currency;
|
|
186
186
|
const unit = !isGrant && item.meter?.unit ? item.meter.unit : currency?.symbol;
|
|
187
|
-
const displayAmount = formatBNStr(amount, currency?.decimal || 0);
|
|
187
|
+
const displayAmount = formatCreditAmount(formatBNStr(amount, currency?.decimal || 0), unit);
|
|
188
188
|
|
|
189
189
|
if (!includeGrants) {
|
|
190
190
|
return (
|
|
191
191
|
<Box onClick={(e) => handleTransactionClick(e, item)}>
|
|
192
|
-
<Typography>
|
|
193
|
-
{displayAmount} {unit}
|
|
194
|
-
</Typography>
|
|
192
|
+
<Typography>{displayAmount}</Typography>
|
|
195
193
|
</Box>
|
|
196
194
|
);
|
|
197
195
|
}
|
|
@@ -202,7 +200,7 @@ const TransactionsTable = React.memo((props: Props) => {
|
|
|
202
200
|
sx={{
|
|
203
201
|
color: isGrant ? 'success.main' : 'error.main',
|
|
204
202
|
}}>
|
|
205
|
-
{isGrant ? '+' : '-'} {displayAmount}
|
|
203
|
+
{isGrant ? '+' : '-'} {displayAmount}
|
|
206
204
|
</Typography>
|
|
207
205
|
</Box>
|
|
208
206
|
);
|
package/src/libs/util.ts
CHANGED
|
@@ -188,6 +188,61 @@ export function formatBNStr(
|
|
|
188
188
|
return formatNumber(fromUnitToToken(str, decimals), precision, trim, thousandSeparated);
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
const CURRENCY_SYMBOLS: Record<string, string> = {
|
|
192
|
+
USD: '$',
|
|
193
|
+
EUR: '€',
|
|
194
|
+
GBP: '£',
|
|
195
|
+
JPY: '¥',
|
|
196
|
+
CNY: '¥',
|
|
197
|
+
KRW: '₩',
|
|
198
|
+
INR: '₹',
|
|
199
|
+
AUD: 'A$',
|
|
200
|
+
CAD: 'C$',
|
|
201
|
+
CHF: 'Fr',
|
|
202
|
+
HKD: 'HK$',
|
|
203
|
+
SGD: 'S$',
|
|
204
|
+
NZD: 'NZ$',
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Format credit amount for display (data contexts: balances, transactions, notifications)
|
|
209
|
+
* - If currency has symbol mapping (USD -> $): shows "$20"
|
|
210
|
+
* - If no symbol mapping: shows "20 minutes" or "20 arcsphere credits"
|
|
211
|
+
*
|
|
212
|
+
* @param formattedAmount - Already formatted amount string (e.g., "20", "1,000.50")
|
|
213
|
+
* @param currencySymbol - Currency symbol (USD, EUR, minutes, etc.)
|
|
214
|
+
* @param showUnit - Whether to show the unit (default: true)
|
|
215
|
+
*/
|
|
216
|
+
export function formatCreditAmount(formattedAmount: string, currencySymbol: string, showUnit: boolean = true): string {
|
|
217
|
+
const mappedSymbol = CURRENCY_SYMBOLS[currencySymbol];
|
|
218
|
+
if (mappedSymbol) {
|
|
219
|
+
return `${mappedSymbol}${formattedAmount}`;
|
|
220
|
+
}
|
|
221
|
+
return `${formattedAmount} ${showUnit ? currencySymbol : ''}`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Format credit amount for checkout/purchase scenarios
|
|
226
|
+
* - If currency has symbol mapping (USD -> $): shows "$20 of credits" (en) or "$20 的信用额度" (zh)
|
|
227
|
+
* - If no symbol mapping: shows "20 minutes" or "20 arcsphere credits"
|
|
228
|
+
*
|
|
229
|
+
* @param formattedAmount - Already formatted amount string (e.g., "20", "1,000.50")
|
|
230
|
+
* @param currencySymbol - Currency symbol (USD, EUR, minutes, etc.)
|
|
231
|
+
* @param locale - Locale for translation (default: 'en')
|
|
232
|
+
* @param showUnit - Whether to show the unit (default: true)
|
|
233
|
+
*/
|
|
234
|
+
export function formatCreditForCheckout(
|
|
235
|
+
formattedAmount: string,
|
|
236
|
+
currencySymbol: string,
|
|
237
|
+
locale: string = 'en',
|
|
238
|
+
showUnit: boolean = true
|
|
239
|
+
): string {
|
|
240
|
+
const mappedSymbol = CURRENCY_SYMBOLS[currencySymbol];
|
|
241
|
+
if (mappedSymbol) {
|
|
242
|
+
return `${mappedSymbol}${formattedAmount} ${showUnit ? t('common.credits', locale) : ''}`;
|
|
243
|
+
}
|
|
244
|
+
return `${formattedAmount} ${showUnit ? currencySymbol : ''}`;
|
|
245
|
+
}
|
|
191
246
|
export function formatNumber(
|
|
192
247
|
n: number | string,
|
|
193
248
|
precision: number = 6,
|
package/src/locales/en.tsx
CHANGED
|
@@ -65,6 +65,8 @@ export default flat({
|
|
|
65
65
|
creditTransaction: 'Credit Transaction',
|
|
66
66
|
creditAmount: 'Credit Amount',
|
|
67
67
|
remainingBalance: 'Remaining Balance',
|
|
68
|
+
credits: 'credits',
|
|
69
|
+
ofCredits: 'of credits',
|
|
68
70
|
transferStatus: 'Transaction Status',
|
|
69
71
|
sourceData: 'Source Data',
|
|
70
72
|
viewGrant: 'View Grant',
|
|
@@ -257,22 +259,22 @@ export default flat({
|
|
|
257
259
|
},
|
|
258
260
|
credit: {
|
|
259
261
|
normal: {
|
|
260
|
-
oneTime: '
|
|
261
|
-
oneTimeWithExpiry: '
|
|
262
|
-
recurring: '
|
|
263
|
-
recurringWithExpiry: '
|
|
262
|
+
oneTime: 'Purchase {amount}.',
|
|
263
|
+
oneTimeWithExpiry: 'Purchase {amount} (valid for {duration} {unit}).',
|
|
264
|
+
recurring: 'Subscription: {amount} {period}.',
|
|
265
|
+
recurringWithExpiry: 'Subscription: {amount} {period} (valid for {duration} {unit}).',
|
|
264
266
|
},
|
|
265
267
|
pending: {
|
|
266
268
|
notEnough:
|
|
267
|
-
'
|
|
269
|
+
'You have a usage overage of {amount}. To restore access, a minimum purchase of {quantity} units is required.',
|
|
268
270
|
oneTimeEnough:
|
|
269
|
-
'
|
|
271
|
+
'You have a usage overage of {amount}. This purchase adds {totalAmount}. After covering the overage, your new available balance will be {availableAmount}.',
|
|
270
272
|
oneTimeEnoughWithExpiry:
|
|
271
|
-
'
|
|
273
|
+
'You have a usage overage of {amount}. This purchase adds {totalAmount} (valid for {duration} {unit}). After covering the overage, your new available balance will be {availableAmount}.',
|
|
272
274
|
recurringEnough:
|
|
273
|
-
'
|
|
275
|
+
'You have a usage overage of {amount}. This subscription adds {totalAmount} {period}. After covering the overage, your new available balance will be {availableAmount}.',
|
|
274
276
|
recurringEnoughWithExpiry:
|
|
275
|
-
'
|
|
277
|
+
'You have a usage overage of {amount}. This subscription adds {totalAmount} {period} (valid for {duration} {unit}). After covering the overage, your new available balance will be {availableAmount}.',
|
|
276
278
|
},
|
|
277
279
|
},
|
|
278
280
|
expired: {
|
|
@@ -353,7 +355,7 @@ export default flat({
|
|
|
353
355
|
thresholdDescription: 'Credits remaining to trigger auto top-up',
|
|
354
356
|
thresholdMinError: 'Threshold must be greater than 0',
|
|
355
357
|
thresholdFormatError: 'Please enter a valid number',
|
|
356
|
-
creditsIncluded: '{num}
|
|
358
|
+
creditsIncluded: '{num} included',
|
|
357
359
|
purchaseBelow: 'Purchase this package',
|
|
358
360
|
perPackage: 'per package',
|
|
359
361
|
quantity: 'Quantity',
|
package/src/locales/zh.tsx
CHANGED
|
@@ -65,6 +65,8 @@ export default flat({
|
|
|
65
65
|
creditTransaction: '信用交易',
|
|
66
66
|
creditAmount: '信用额度',
|
|
67
67
|
remainingBalance: '剩余余额',
|
|
68
|
+
credits: '额度',
|
|
69
|
+
ofCredits: '额度',
|
|
68
70
|
transferStatus: '交易状态',
|
|
69
71
|
sourceData: '源数据',
|
|
70
72
|
viewGrant: '查看额度',
|
|
@@ -291,21 +293,21 @@ export default flat({
|
|
|
291
293
|
},
|
|
292
294
|
credit: {
|
|
293
295
|
normal: {
|
|
294
|
-
oneTime: '
|
|
295
|
-
oneTimeWithExpiry: '
|
|
296
|
-
recurring: '{period}
|
|
297
|
-
recurringWithExpiry: '{period}
|
|
296
|
+
oneTime: '购买 {amount}。',
|
|
297
|
+
oneTimeWithExpiry: '购买 {amount}(有效期 {duration} {unit})。',
|
|
298
|
+
recurring: '订阅:{period}{amount}。',
|
|
299
|
+
recurringWithExpiry: '订阅:{period}{amount}(有效期 {duration} {unit})。',
|
|
298
300
|
},
|
|
299
301
|
pending: {
|
|
300
|
-
notEnough: '
|
|
302
|
+
notEnough: '您有 {amount} 的使用超额。要恢复访问,需要至少购买 {quantity} 数量。',
|
|
301
303
|
oneTimeEnough:
|
|
302
|
-
'
|
|
304
|
+
'您有 {amount} 的使用超额。本次购买将增加 {totalAmount}。扣除超额后,您的新可用余额将为 {availableAmount}。',
|
|
303
305
|
oneTimeEnoughWithExpiry:
|
|
304
|
-
'
|
|
306
|
+
'您有 {amount} 的使用超额。本次购买将增加 {totalAmount}(有效期 {duration} {unit})。扣除超额后,您的新可用余额将为 {availableAmount}。',
|
|
305
307
|
recurringEnough:
|
|
306
|
-
'
|
|
308
|
+
'您有 {amount} 的使用超额。本订阅{period}将增加 {totalAmount}。扣除超额后,您的新可用余额将为 {availableAmount}。',
|
|
307
309
|
recurringEnoughWithExpiry:
|
|
308
|
-
'
|
|
310
|
+
'您有 {amount} 的使用超额。本订阅{period}将增加 {totalAmount}(有效期 {duration} {unit})。扣除超额后,您的新可用余额将为 {availableAmount}。',
|
|
309
311
|
},
|
|
310
312
|
},
|
|
311
313
|
emptyItems: {
|
|
@@ -349,7 +351,7 @@ export default flat({
|
|
|
349
351
|
thresholdDescription: '触发自动充值的剩余额度',
|
|
350
352
|
thresholdMinError: '阈值必须大于0',
|
|
351
353
|
thresholdFormatError: '请输入有效数字',
|
|
352
|
-
creditsIncluded: '包含 {num}
|
|
354
|
+
creditsIncluded: '包含 {num}',
|
|
353
355
|
purchaseBelow: '购买此套餐',
|
|
354
356
|
perPackage: '每包',
|
|
355
357
|
quantity: '数量',
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
formatRecurring,
|
|
18
18
|
formatUpsellSaving,
|
|
19
19
|
formatAmount,
|
|
20
|
+
formatCreditForCheckout,
|
|
20
21
|
formatBNStr,
|
|
21
22
|
} from '../libs/util';
|
|
22
23
|
import ProductCard from './product-card';
|
|
@@ -206,13 +207,14 @@ export default function ProductItem({
|
|
|
206
207
|
|
|
207
208
|
const totalCreditStr = formatNumber(creditAmount * (localQuantity || 0));
|
|
208
209
|
const currencySymbol = creditCurrency?.symbol || 'Credits';
|
|
210
|
+
const formattedTotalCredit = formatCreditForCheckout(totalCreditStr, currencySymbol, locale);
|
|
211
|
+
|
|
209
212
|
const hasPendingAmount = pendingAmount && new BN(pendingAmount || '0').gt(new BN(0));
|
|
210
213
|
const isRecurring = item.price.type === 'recurring';
|
|
211
214
|
const hasExpiry = validDuration && validDuration > 0;
|
|
212
215
|
|
|
213
216
|
const buildBaseParams = () => ({
|
|
214
|
-
amount:
|
|
215
|
-
symbol: currencySymbol,
|
|
217
|
+
amount: formattedTotalCredit,
|
|
216
218
|
...(hasExpiry && {
|
|
217
219
|
duration: validDuration,
|
|
218
220
|
unit: t(`common.${validDurationUnit}`),
|
|
@@ -223,11 +225,18 @@ export default function ProductItem({
|
|
|
223
225
|
});
|
|
224
226
|
|
|
225
227
|
const buildPendingParams = (pendingBN: BN, availableAmount?: string) => ({
|
|
226
|
-
amount:
|
|
227
|
-
|
|
228
|
-
|
|
228
|
+
amount: formatCreditForCheckout(
|
|
229
|
+
formatBNStr(pendingBN.toString(), creditCurrency?.decimal || 2),
|
|
230
|
+
currencySymbol,
|
|
231
|
+
locale
|
|
232
|
+
),
|
|
233
|
+
totalAmount: formattedTotalCredit,
|
|
229
234
|
...(availableAmount && {
|
|
230
|
-
availableAmount:
|
|
235
|
+
availableAmount: formatCreditForCheckout(
|
|
236
|
+
formatBNStr(availableAmount, creditCurrency?.decimal || 2),
|
|
237
|
+
currencySymbol,
|
|
238
|
+
locale
|
|
239
|
+
),
|
|
231
240
|
}),
|
|
232
241
|
...(hasExpiry && {
|
|
233
242
|
duration: validDuration,
|
|
@@ -256,8 +265,11 @@ export default function ProductItem({
|
|
|
256
265
|
|
|
257
266
|
if (!new BN(actualAvailable).gt(new BN(0))) {
|
|
258
267
|
return t('payment.checkout.credit.pending.notEnough', {
|
|
259
|
-
amount:
|
|
260
|
-
|
|
268
|
+
amount: formatCreditForCheckout(
|
|
269
|
+
formatBNStr(pendingAmountBN.toString(), creditCurrency?.decimal || 2),
|
|
270
|
+
currencySymbol,
|
|
271
|
+
locale
|
|
272
|
+
),
|
|
261
273
|
quantity: formatNumber(minQuantityNeeded),
|
|
262
274
|
});
|
|
263
275
|
}
|