@blocklet/payment-react 1.23.4 → 1.23.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.
@@ -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)} ${config.currency?.symbol || ""}`;
375
- const credits = `${formatNumber(
376
- Number(config.price.metadata?.credit_config?.credit_amount || 0) * Number(config.quantity)
377
- )} ${config.currency?.name || ""}`;
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?.name })
563
+ endAdornment: /* @__PURE__ */ jsx(InputAdornment, { position: "end", children: formatCreditAmount("", config?.currency?.symbol) })
564
564
  },
565
565
  htmlInput: {
566
566
  min: 0,
@@ -1,11 +1,11 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
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 { formatBNStr, formatToDate } from "../../libs/util.js";
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__ */ jsxs(Typography, { variant: "body2", children: [
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 { formatBNStr, formatToDate, getPrefix } from "../../libs/util.js";
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__ */ jsxs(Typography, { children: [
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: "Top up {amount} {symbol} credit.",
255
- oneTimeWithExpiry: "Top up {amount} {symbol} credit, valid for {duration} {unit}.",
256
- recurring: "Top up {amount} {symbol} credit {period}.",
257
- recurringWithExpiry: "Top up {amount} {symbol} credit {period}, valid for {duration} {unit}."
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: "Your outstanding balance is {amount} {symbol}. To settle it, a minimum purchase of {quantity} units is required.",
261
- oneTimeEnough: "Your outstanding balance is {amount} {symbol}. Upon payment, you will receive {totalAmount} {symbol}, resulting in a net balance of {availableAmount} {symbol} after the deduction.",
262
- oneTimeEnoughWithExpiry: "Your outstanding balance is {amount} {symbol}. Upon payment, you will receive {totalAmount} {symbol} (valid for {duration} {unit}), resulting in a net balance of {availableAmount} {symbol} after the deduction.",
263
- recurringEnough: "Your outstanding balance is {amount} {symbol}. Upon payment, you will receive {totalAmount} {symbol} {period}, resulting in a net balance of {availableAmount} {symbol} after the deduction.",
264
- recurringEnoughWithExpiry: "Your outstanding balance is {amount} {symbol}. Upon payment, you will receive {totalAmount} {symbol} {period} (valid for {duration} {unit}), resulting in a net balance of {availableAmount} {symbol} after the deduction."
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: {
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: "\u5145\u503C {amount} {symbol} \u989D\u5EA6\u3002",
292
- oneTimeWithExpiry: "\u5145\u503C {amount} {symbol} \u989D\u5EA6\uFF0C\u6709\u6548\u671F {duration} {unit}\u3002",
293
- recurring: "{period}\u5145\u503C {amount} {symbol} \u989D\u5EA6\u3002",
294
- recurringWithExpiry: "{period}\u5145\u503C {amount} {symbol} \u989D\u5EA6\uFF0C\u6709\u6548\u671F {duration} {unit}\u3002"
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\u5F53\u524D\u6B20\u8D39 {amount} {symbol}\uFF0C\u81F3\u5C11\u9700\u8981\u8D2D\u4E70 {quantity} \u6570\u91CF\u3002",
298
- oneTimeEnough: "\u60A8\u5F53\u524D\u6B20\u8D39 {amount} {symbol}\uFF0C\u4ED8\u6B3E\u540E\u5C06\u83B7\u5F97 {totalAmount} {symbol} \u989D\u5EA6\uFF0C\u6263\u9664\u6B20\u8D39\u540E\u51C0\u4F59\u989D\u4E3A {availableAmount} {symbol}\u3002",
299
- oneTimeEnoughWithExpiry: "\u60A8\u5F53\u524D\u6B20\u8D39 {amount} {symbol}\uFF0C\u4ED8\u6B3E\u540E\u5C06\u83B7\u5F97 {totalAmount} {symbol} \u989D\u5EA6\uFF08\u6709\u6548\u671F {duration} {unit}\uFF09\uFF0C\u6263\u9664\u6B20\u8D39\u540E\u51C0\u4F59\u989D\u4E3A {availableAmount} {symbol}\u3002",
300
- recurringEnough: "\u60A8\u5F53\u524D\u6B20\u8D39 {amount} {symbol}\uFF0C\u4ED8\u6B3E\u540E{period}\u5C06\u83B7\u5F97 {totalAmount} {symbol} \u989D\u5EA6\uFF0C\u6263\u9664\u6B20\u8D39\u540E\u51C0\u4F59\u989D\u4E3A {availableAmount} {symbol}\u3002",
301
- recurringEnoughWithExpiry: "\u60A8\u5F53\u524D\u6B20\u8D39 {amount} {symbol}\uFF0C\u4ED8\u6B3E\u540E{period}\u5C06\u83B7\u5F97 {totalAmount} {symbol} \u989D\u5EA6\uFF08\u6709\u6548\u671F {duration} {unit}\uFF09\uFF0C\u6263\u9664\u6B20\u8D39\u540E\u51C0\u4F59\u989D\u4E3A {availableAmount} {symbol}\u3002"
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: {
@@ -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: totalCreditStr,
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: formatBNStr(pendingBN.toString(), creditCurrency?.decimal || 2),
176
- symbol: currencySymbol,
177
- totalAmount: totalCreditStr,
176
+ amount: formatCreditForCheckout(
177
+ formatBNStr(pendingBN.toString(), creditCurrency?.decimal || 2),
178
+ currencySymbol,
179
+ locale
180
+ ),
181
+ totalAmount: formattedTotalCredit,
178
182
  ...availableAmount && {
179
- availableAmount: formatBNStr(availableAmount, creditCurrency?.decimal || 2)
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: formatBNStr(pendingAmountBN.toString(), creditCurrency?.decimal || 2),
205
- symbol: currencySymbol,
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)} ${config.currency?.symbol || ""}`;
390
- const credits = `${(0, _util.formatNumber)(Number(config.price.metadata?.credit_config?.credit_amount || 0) * Number(config.quantity))} ${config.currency?.name || ""}`;
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?.name
640
+ children: (0, _util.formatCreditAmount)("", config?.currency?.symbol)
641
641
  })
642
642
  },
643
643
  htmlInput: {
@@ -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.jsxs)(_material.Typography, {
157
+ children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
158
158
  variant: "body2",
159
- children: [(0, _util.formatBNStr)(grant.remaining_amount, grant.paymentCurrency.decimal), " ", grant.paymentCurrency.symbol]
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.jsxs)(_material.Typography, {
165
- children: [displayAmount, " ", unit]
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, " ", unit]
175
+ children: [isGrant ? "+" : "-", " ", displayAmount]
176
176
  })
177
177
  });
178
178
  }
@@ -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: "Top up {amount} {symbol} credit.",
262
- oneTimeWithExpiry: "Top up {amount} {symbol} credit, valid for {duration} {unit}.",
263
- recurring: "Top up {amount} {symbol} credit {period}.",
264
- recurringWithExpiry: "Top up {amount} {symbol} credit {period}, valid for {duration} {unit}."
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: "Your outstanding balance is {amount} {symbol}. To settle it, a minimum purchase of {quantity} units is required.",
268
- oneTimeEnough: "Your outstanding balance is {amount} {symbol}. Upon payment, you will receive {totalAmount} {symbol}, resulting in a net balance of {availableAmount} {symbol} after the deduction.",
269
- oneTimeEnoughWithExpiry: "Your outstanding balance is {amount} {symbol}. Upon payment, you will receive {totalAmount} {symbol} (valid for {duration} {unit}), resulting in a net balance of {availableAmount} {symbol} after the deduction.",
270
- recurringEnough: "Your outstanding balance is {amount} {symbol}. Upon payment, you will receive {totalAmount} {symbol} {period}, resulting in a net balance of {availableAmount} {symbol} after the deduction.",
271
- recurringEnoughWithExpiry: "Your outstanding balance is {amount} {symbol}. Upon payment, you will receive {totalAmount} {symbol} {period} (valid for {duration} {unit}), resulting in a net balance of {availableAmount} {symbol} after the deduction."
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: {
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: "\u5145\u503C {amount} {symbol} \u989D\u5EA6\u3002",
299
- oneTimeWithExpiry: "\u5145\u503C {amount} {symbol} \u989D\u5EA6\uFF0C\u6709\u6548\u671F {duration} {unit}\u3002",
300
- recurring: "{period}\u5145\u503C {amount} {symbol} \u989D\u5EA6\u3002",
301
- recurringWithExpiry: "{period}\u5145\u503C {amount} {symbol} \u989D\u5EA6\uFF0C\u6709\u6548\u671F {duration} {unit}\u3002"
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\u5F53\u524D\u6B20\u8D39 {amount} {symbol}\uFF0C\u81F3\u5C11\u9700\u8981\u8D2D\u4E70 {quantity} \u6570\u91CF\u3002",
305
- oneTimeEnough: "\u60A8\u5F53\u524D\u6B20\u8D39 {amount} {symbol}\uFF0C\u4ED8\u6B3E\u540E\u5C06\u83B7\u5F97 {totalAmount} {symbol} \u989D\u5EA6\uFF0C\u6263\u9664\u6B20\u8D39\u540E\u51C0\u4F59\u989D\u4E3A {availableAmount} {symbol}\u3002",
306
- oneTimeEnoughWithExpiry: "\u60A8\u5F53\u524D\u6B20\u8D39 {amount} {symbol}\uFF0C\u4ED8\u6B3E\u540E\u5C06\u83B7\u5F97 {totalAmount} {symbol} \u989D\u5EA6\uFF08\u6709\u6548\u671F {duration} {unit}\uFF09\uFF0C\u6263\u9664\u6B20\u8D39\u540E\u51C0\u4F59\u989D\u4E3A {availableAmount} {symbol}\u3002",
307
- recurringEnough: "\u60A8\u5F53\u524D\u6B20\u8D39 {amount} {symbol}\uFF0C\u4ED8\u6B3E\u540E{period}\u5C06\u83B7\u5F97 {totalAmount} {symbol} \u989D\u5EA6\uFF0C\u6263\u9664\u6B20\u8D39\u540E\u51C0\u4F59\u989D\u4E3A {availableAmount} {symbol}\u3002",
308
- recurringEnoughWithExpiry: "\u60A8\u5F53\u524D\u6B20\u8D39 {amount} {symbol}\uFF0C\u4ED8\u6B3E\u540E{period}\u5C06\u83B7\u5F97 {totalAmount} {symbol} \u989D\u5EA6\uFF08\u6709\u6548\u671F {duration} {unit}\uFF09\uFF0C\u6263\u9664\u6B20\u8D39\u540E\u51C0\u4F59\u989D\u4E3A {availableAmount} {symbol}\u3002"
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: {
@@ -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: totalCreditStr,
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
- symbol: currencySymbol,
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.4",
3
+ "version": "1.23.5",
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.4",
99
+ "@blocklet/payment-types": "1.23.5",
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": "12cd3cecedb8aa3331a4a24fa093f62cc6630d5b"
130
+ "gitHead": "486456ed522207cc4efa54cd14b1f65c6433d179"
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)} ${config.currency?.symbol || ''}`;
387
- const credits = `${formatNumber(
388
- Number(config.price.metadata?.credit_config?.credit_amount || 0) * Number(config.quantity)
389
- )} ${config.currency?.name || ''}`;
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: <InputAdornment position="end">{config?.currency?.name}</InputAdornment>,
615
+ endAdornment: (
616
+ <InputAdornment position="end">
617
+ {formatCreditAmount('', config?.currency?.symbol)}
618
+ </InputAdornment>
619
+ ),
616
620
  },
617
621
  htmlInput: {
618
622
  min: 0,
@@ -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 { formatBNStr, formatToDate } from '../../libs/util';
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
- {formatBNStr(grant.remaining_amount, grant.paymentCurrency.decimal)} {grant.paymentCurrency.symbol}
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 { formatBNStr, formatToDate, getPrefix } from '../../libs/util';
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} {unit}
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,
@@ -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: 'Top up {amount} {symbol} credit.',
261
- oneTimeWithExpiry: 'Top up {amount} {symbol} credit, valid for {duration} {unit}.',
262
- recurring: 'Top up {amount} {symbol} credit {period}.',
263
- recurringWithExpiry: 'Top up {amount} {symbol} credit {period}, valid for {duration} {unit}.',
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
- 'Your outstanding balance is {amount} {symbol}. To settle it, a minimum purchase of {quantity} units is required.',
269
+ 'You have a usage overage of {amount}. To restore access, a minimum purchase of {quantity} units is required.',
268
270
  oneTimeEnough:
269
- 'Your outstanding balance is {amount} {symbol}. Upon payment, you will receive {totalAmount} {symbol}, resulting in a net balance of {availableAmount} {symbol} after the deduction.',
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
- 'Your outstanding balance is {amount} {symbol}. Upon payment, you will receive {totalAmount} {symbol} (valid for {duration} {unit}), resulting in a net balance of {availableAmount} {symbol} after the deduction.',
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
- 'Your outstanding balance is {amount} {symbol}. Upon payment, you will receive {totalAmount} {symbol} {period}, resulting in a net balance of {availableAmount} {symbol} after the deduction.',
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
- 'Your outstanding balance is {amount} {symbol}. Upon payment, you will receive {totalAmount} {symbol} {period} (valid for {duration} {unit}), resulting in a net balance of {availableAmount} {symbol} after the deduction.',
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: {
@@ -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: '充值 {amount} {symbol} 额度。',
295
- oneTimeWithExpiry: '充值 {amount} {symbol} 额度,有效期 {duration} {unit}',
296
- recurring: '{period}充值 {amount} {symbol} 额度。',
297
- recurringWithExpiry: '{period}充值 {amount} {symbol} 额度,有效期 {duration} {unit}',
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: '您当前欠费 {amount} {symbol},至少需要购买 {quantity} 数量。',
302
+ notEnough: '您有 {amount} 的使用超额。要恢复访问,需要至少购买 {quantity} 数量。',
301
303
  oneTimeEnough:
302
- '您当前欠费 {amount} {symbol},付款后将获得 {totalAmount} {symbol} 额度,扣除欠费后净余额为 {availableAmount} {symbol}。',
304
+ '您有 {amount} 的使用超额。本次购买将增加 {totalAmount}。扣除超额后,您的新可用余额将为 {availableAmount}。',
303
305
  oneTimeEnoughWithExpiry:
304
- '您当前欠费 {amount} {symbol},付款后将获得 {totalAmount} {symbol} 额度(有效期 {duration} {unit}),扣除欠费后净余额为 {availableAmount} {symbol}。',
306
+ '您有 {amount} 的使用超额。本次购买将增加 {totalAmount}(有效期 {duration} {unit})。扣除超额后,您的新可用余额将为 {availableAmount}。',
305
307
  recurringEnough:
306
- '您当前欠费 {amount} {symbol},付款后{period}将获得 {totalAmount} {symbol} 额度,扣除欠费后净余额为 {availableAmount} {symbol}。',
308
+ '您有 {amount} 的使用超额。本订阅{period}将增加 {totalAmount}。扣除超额后,您的新可用余额将为 {availableAmount}。',
307
309
  recurringEnoughWithExpiry:
308
- '您当前欠费 {amount} {symbol},付款后{period}将获得 {totalAmount} {symbol} 额度(有效期 {duration} {unit}),扣除欠费后净余额为 {availableAmount} {symbol}。',
310
+ '您有 {amount} 的使用超额。本订阅{period}将增加 {totalAmount}(有效期 {duration} {unit})。扣除超额后,您的新可用余额将为 {availableAmount}。',
309
311
  },
310
312
  },
311
313
  emptyItems: {
@@ -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: totalCreditStr,
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: formatBNStr(pendingBN.toString(), creditCurrency?.decimal || 2),
227
- symbol: currencySymbol,
228
- totalAmount: totalCreditStr,
228
+ amount: formatCreditForCheckout(
229
+ formatBNStr(pendingBN.toString(), creditCurrency?.decimal || 2),
230
+ currencySymbol,
231
+ locale
232
+ ),
233
+ totalAmount: formattedTotalCredit,
229
234
  ...(availableAmount && {
230
- availableAmount: formatBNStr(availableAmount, creditCurrency?.decimal || 2),
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: formatBNStr(pendingAmountBN.toString(), creditCurrency?.decimal || 2),
260
- symbol: currencySymbol,
268
+ amount: formatCreditForCheckout(
269
+ formatBNStr(pendingAmountBN.toString(), creditCurrency?.decimal || 2),
270
+ currencySymbol,
271
+ locale
272
+ ),
261
273
  quantity: formatNumber(minQuantityNeeded),
262
274
  });
263
275
  }