@blocklet/payment-react 1.24.4 → 1.25.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/es/components/auto-topup/modal.d.ts +2 -0
  2. package/es/components/auto-topup/modal.js +48 -6
  3. package/es/components/auto-topup/product-card.d.ts +16 -1
  4. package/es/components/auto-topup/product-card.js +97 -15
  5. package/es/components/dynamic-pricing-unavailable.d.ts +9 -0
  6. package/es/components/dynamic-pricing-unavailable.js +58 -0
  7. package/es/components/loading-amount.d.ts +17 -0
  8. package/es/components/loading-amount.js +46 -0
  9. package/es/components/price-change-confirm.d.ts +18 -0
  10. package/es/components/price-change-confirm.js +107 -0
  11. package/es/components/quote-details-panel.d.ts +21 -0
  12. package/es/components/quote-details-panel.js +170 -0
  13. package/es/components/quote-lock-banner.d.ts +7 -0
  14. package/es/components/quote-lock-banner.js +79 -0
  15. package/es/components/slippage-config.d.ts +20 -0
  16. package/es/components/slippage-config.js +261 -0
  17. package/es/history/invoice/list.js +125 -15
  18. package/es/hooks/dynamic-pricing.d.ts +102 -0
  19. package/es/hooks/dynamic-pricing.js +393 -0
  20. package/es/index.d.ts +6 -1
  21. package/es/index.js +9 -1
  22. package/es/libs/util.d.ts +42 -5
  23. package/es/libs/util.js +345 -57
  24. package/es/locales/en.js +114 -3
  25. package/es/locales/zh.js +114 -3
  26. package/es/payment/form/index.d.ts +4 -1
  27. package/es/payment/form/index.js +454 -22
  28. package/es/payment/index.d.ts +1 -1
  29. package/es/payment/index.js +279 -16
  30. package/es/payment/product-item.d.ts +26 -1
  31. package/es/payment/product-item.js +330 -51
  32. package/es/payment/summary-section/promotion-section.d.ts +32 -0
  33. package/es/payment/summary-section/promotion-section.js +143 -0
  34. package/es/payment/summary-section/total-section.d.ts +39 -0
  35. package/es/payment/summary-section/total-section.js +83 -0
  36. package/es/payment/summary.d.ts +17 -2
  37. package/es/payment/summary.js +300 -253
  38. package/es/types/index.d.ts +11 -0
  39. package/lib/components/auto-topup/modal.d.ts +2 -0
  40. package/lib/components/auto-topup/modal.js +54 -6
  41. package/lib/components/auto-topup/product-card.d.ts +16 -1
  42. package/lib/components/auto-topup/product-card.js +75 -7
  43. package/lib/components/dynamic-pricing-unavailable.d.ts +9 -0
  44. package/lib/components/dynamic-pricing-unavailable.js +81 -0
  45. package/lib/components/loading-amount.d.ts +17 -0
  46. package/lib/components/loading-amount.js +53 -0
  47. package/lib/components/price-change-confirm.d.ts +18 -0
  48. package/lib/components/price-change-confirm.js +157 -0
  49. package/lib/components/quote-details-panel.d.ts +21 -0
  50. package/lib/components/quote-details-panel.js +226 -0
  51. package/lib/components/quote-lock-banner.d.ts +7 -0
  52. package/lib/components/quote-lock-banner.js +93 -0
  53. package/lib/components/slippage-config.d.ts +20 -0
  54. package/lib/components/slippage-config.js +316 -0
  55. package/lib/history/invoice/list.js +167 -27
  56. package/lib/hooks/dynamic-pricing.d.ts +102 -0
  57. package/lib/hooks/dynamic-pricing.js +390 -0
  58. package/lib/index.d.ts +6 -1
  59. package/lib/index.js +32 -0
  60. package/lib/libs/util.d.ts +42 -5
  61. package/lib/libs/util.js +367 -49
  62. package/lib/locales/en.js +114 -3
  63. package/lib/locales/zh.js +114 -3
  64. package/lib/payment/form/index.d.ts +4 -1
  65. package/lib/payment/form/index.js +476 -20
  66. package/lib/payment/index.d.ts +1 -1
  67. package/lib/payment/index.js +308 -14
  68. package/lib/payment/product-item.d.ts +26 -1
  69. package/lib/payment/product-item.js +270 -35
  70. package/lib/payment/summary-section/promotion-section.d.ts +32 -0
  71. package/lib/payment/summary-section/promotion-section.js +133 -0
  72. package/lib/payment/summary-section/total-section.d.ts +39 -0
  73. package/lib/payment/summary-section/total-section.js +117 -0
  74. package/lib/payment/summary.d.ts +17 -2
  75. package/lib/payment/summary.js +205 -127
  76. package/lib/types/index.d.ts +11 -0
  77. package/package.json +3 -3
  78. package/src/components/auto-topup/modal.tsx +59 -6
  79. package/src/components/auto-topup/product-card.tsx +118 -11
  80. package/src/components/dynamic-pricing-unavailable.tsx +69 -0
  81. package/src/components/loading-amount.tsx +66 -0
  82. package/src/components/price-change-confirm.tsx +136 -0
  83. package/src/components/quote-details-panel.tsx +218 -0
  84. package/src/components/quote-lock-banner.tsx +99 -0
  85. package/src/components/slippage-config.tsx +336 -0
  86. package/src/history/invoice/list.tsx +143 -9
  87. package/src/hooks/dynamic-pricing.ts +617 -0
  88. package/src/index.ts +9 -0
  89. package/src/libs/util.ts +473 -58
  90. package/src/locales/en.tsx +117 -0
  91. package/src/locales/zh.tsx +111 -0
  92. package/src/payment/form/index.tsx +561 -19
  93. package/src/payment/index.tsx +349 -10
  94. package/src/payment/product-item.tsx +451 -37
  95. package/src/payment/summary-section/promotion-section.tsx +172 -0
  96. package/src/payment/summary-section/total-section.tsx +141 -0
  97. package/src/payment/summary.tsx +334 -192
  98. package/src/types/index.ts +15 -0
@@ -16,21 +16,26 @@ import React, { useEffect, useRef, useState } from 'react';
16
16
  import { useNavigate } from 'react-router-dom';
17
17
 
18
18
  import debounce from 'lodash/debounce';
19
+ import { BN } from '@ocap/util';
19
20
  import Status from '../../components/status';
20
21
  import { usePaymentContext } from '../../contexts/payment';
21
22
  import { useSubscription } from '../../hooks/subscription';
22
23
  import api from '../../libs/api';
23
24
  import StripePaymentAction from '../../components/stripe-payment-action';
24
25
  import {
25
- formatBNStr,
26
26
  formatCreditAmount,
27
27
  formatError,
28
28
  formatToDate,
29
29
  formatToDatetime,
30
+ formatTime,
31
+ formatExchangeRate,
30
32
  getInvoiceDescriptionAndReason,
31
33
  getInvoiceStatusColor,
32
34
  getTxLink,
33
35
  isCrossOrigin,
36
+ getUsdAmountFromTokenUnits,
37
+ formatUsdAmount,
38
+ formatAmount,
34
39
  } from '../../libs/util';
35
40
  import Table from '../../components/table';
36
41
  import { createLink, handleNavigation, LinkInfo } from '../../libs/navigation';
@@ -51,14 +56,26 @@ const groupByDate = (items: TInvoiceExpanded[]) => {
51
56
 
52
57
  const fetchData = (params: Record<string, any> = {}): Promise<Result> => {
53
58
  const search = new URLSearchParams();
54
- Object.keys(params).forEach((key) => {
55
- if (params[key]) {
56
- search.set(key, String(params[key]));
59
+ const mergedParams: Record<string, any> = { include_quote: true, ...params };
60
+ Object.keys(mergedParams).forEach((key) => {
61
+ if (mergedParams[key]) {
62
+ search.set(key, String(mergedParams[key]));
57
63
  }
58
64
  });
59
65
  return api.get(`/api/invoices?${search.toString()}`).then((res: any) => res.data);
60
66
  };
61
67
 
68
+ const getInvoiceQuoteInfo = (invoice: TInvoiceExpanded) => {
69
+ const lines = (invoice as any).lines || [];
70
+ for (const line of lines) {
71
+ const quote = (line.metadata as any)?.quote;
72
+ if (quote?.exchange_rate) {
73
+ return quote;
74
+ }
75
+ }
76
+ return null;
77
+ };
78
+
62
79
  type Props = {
63
80
  customer_id?: string;
64
81
  subscription_id?: string;
@@ -199,12 +216,119 @@ const InvoiceTable = React.memo((props: Props & { onPay: (invoiceId: string) =>
199
216
  customBodyRenderLite: (_: string, index: number) => {
200
217
  const invoice = data?.list[index] as TInvoiceExpanded;
201
218
  const isVoid = invoice.status === 'void';
219
+ const quoteInfo = getInvoiceQuoteInfo(invoice);
220
+ const providers = quoteInfo?.providers || [];
221
+ const providerNames = providers.map((provider: any) => provider.provider_name).filter(Boolean);
222
+ const providerDisplay =
223
+ providerNames.length > 0
224
+ ? providerNames.join(', ')
225
+ : quoteInfo?.rate_provider_name || quoteInfo?.rate_provider_id || '—';
226
+ const providerRates =
227
+ quoteInfo?.providers
228
+ ?.map((provider: any) => {
229
+ const name = provider.provider_name || provider.provider_id || '—';
230
+ return provider.rate ? `${name}` : name;
231
+ })
232
+ .filter(Boolean) || [];
233
+ const rateTimestamp = quoteInfo?.rate_timestamp_ms ? formatTime(quoteInfo.rate_timestamp_ms) : '—';
234
+ const formattedRate = formatExchangeRate(quoteInfo?.exchange_rate || null);
235
+ const rateLine = formattedRate
236
+ ? (() => {
237
+ const currencyMap = {
238
+ USD: '$',
239
+ CNY: '¥',
240
+ };
241
+ const currencySymbol = currencyMap[quoteInfo?.base_currency as keyof typeof currencyMap];
242
+ return `1 ${invoice.paymentCurrency.symbol} ≈ ${
243
+ currencySymbol
244
+ ? `${currencySymbol}${formattedRate}`
245
+ : `${formattedRate} ${quoteInfo?.base_currency || 'USD'}`
246
+ }`;
247
+ })()
248
+ : null;
249
+
250
+ let usdAmount: string | null = null;
251
+ if (quoteInfo?.base_amount) {
252
+ usdAmount = formatUsdAmount(quoteInfo.base_amount, locale);
253
+ } else if (quoteInfo?.exchange_rate && invoice.total) {
254
+ const calculatedUsd = getUsdAmountFromTokenUnits(
255
+ new BN(invoice.total),
256
+ invoice.paymentCurrency.decimal,
257
+ quoteInfo.exchange_rate
258
+ );
259
+ if (calculatedUsd) {
260
+ usdAmount = formatUsdAmount(calculatedUsd, locale);
261
+ }
262
+ }
263
+
264
+ const tooltipContent = quoteInfo ? (
265
+ <Stack spacing={0.5} sx={{ p: 1 }}>
266
+ <Stack direction="row" justifyContent="space-between" spacing={2}>
267
+ <Typography variant="caption" sx={{ color: 'text.secondary' }}>
268
+ {t('payment.customer.invoice.quote.providers')}:
269
+ </Typography>
270
+ <Typography variant="caption" sx={{ color: 'text.primary' }}>
271
+ {(providerRates.length > 0 ? providerRates.join(', ') : providerDisplay) || '—'}
272
+ </Typography>
273
+ </Stack>
274
+ {rateLine && (
275
+ <Stack direction="row" justifyContent="space-between" spacing={2}>
276
+ <Typography variant="caption" sx={{ color: 'text.secondary' }}>
277
+ {t('payment.customer.invoice.quote.exchangeRate')}:
278
+ </Typography>
279
+ <Typography variant="caption" sx={{ color: 'text.primary' }}>
280
+ {rateLine}
281
+ </Typography>
282
+ </Stack>
283
+ )}
284
+ <Stack direction="row" justifyContent="space-between" spacing={2}>
285
+ <Typography variant="caption" sx={{ color: 'text.secondary' }}>
286
+ {t('payment.customer.invoice.quote.rateTimestamp')}:
287
+ </Typography>
288
+ <Typography variant="caption" sx={{ color: 'text.primary' }}>
289
+ {rateTimestamp}
290
+ </Typography>
291
+ </Stack>
292
+ </Stack>
293
+ ) : null;
294
+
202
295
  return (
203
296
  <Box onClick={(e) => handleLinkClick(e, invoice)} sx={linkStyle}>
204
- <Typography sx={isVoid ? { textDecoration: 'line-through' } : {}}>
205
- {formatBNStr(invoice.total, invoice.paymentCurrency.decimal)}&nbsp;
206
- {invoice.paymentCurrency.symbol}
207
- </Typography>
297
+ <Stack spacing={0.25} alignItems="flex-end">
298
+ <Typography sx={isVoid ? { textDecoration: 'line-through' } : {}}>
299
+ {formatAmount(invoice.total, invoice.paymentCurrency.decimal)}&nbsp;
300
+ {invoice.paymentCurrency.symbol}
301
+ </Typography>
302
+ {(usdAmount || rateLine) && (
303
+ <Tooltip
304
+ title={tooltipContent}
305
+ placement="top"
306
+ arrow
307
+ slotProps={{
308
+ tooltip: {
309
+ sx: {
310
+ backgroundColor: 'background.paper',
311
+ boxShadow: 1,
312
+ },
313
+ },
314
+ }}>
315
+ <Stack spacing={0.25} alignItems="flex-end">
316
+ {usdAmount && (
317
+ <Typography
318
+ variant="caption"
319
+ sx={{
320
+ color: 'text.secondary',
321
+ fontSize: '0.75rem',
322
+ fontWeight: 400,
323
+ lineHeight: 1.2,
324
+ }}>
325
+ ≈ ${usdAmount}
326
+ </Typography>
327
+ )}
328
+ </Stack>
329
+ </Tooltip>
330
+ )}
331
+ </Stack>
208
332
  </Box>
209
333
  );
210
334
  },
@@ -638,6 +762,11 @@ const InvoiceList = React.memo((props: Props & { onPay: (invoiceId: string) => v
638
762
  {invoices.map((invoice) => {
639
763
  const { link, connect } = getInvoiceLink(invoice, action);
640
764
  const isVoid = invoice.status === 'void';
765
+ const quoteInfo = getInvoiceQuoteInfo(invoice);
766
+ const formattedRate = formatExchangeRate(quoteInfo?.exchange_rate || null);
767
+ const rateLine = formattedRate
768
+ ? `1 ${invoice.paymentCurrency.symbol} ≈ ${formattedRate} ${quoteInfo?.base_currency || 'USD'}`
769
+ : null;
641
770
  return (
642
771
  <Stack
643
772
  key={invoice.id}
@@ -687,9 +816,14 @@ const InvoiceList = React.memo((props: Props & { onPay: (invoiceId: string) => v
687
816
  textAlign: 'right',
688
817
  }}>
689
818
  <Typography sx={isVoid ? { textDecoration: 'line-through' } : {}}>
690
- {formatBNStr(invoice.total, invoice.paymentCurrency.decimal)}&nbsp;
819
+ {formatAmount(invoice.total, invoice.paymentCurrency.decimal)}&nbsp;
691
820
  {invoice.paymentCurrency.symbol}
692
821
  </Typography>
822
+ {rateLine && (
823
+ <Typography variant="caption" sx={{ color: 'text.secondary', display: 'block' }}>
824
+ {rateLine}
825
+ </Typography>
826
+ )}
693
827
  </Box>
694
828
  <Box
695
829
  sx={{