@blocklet/payment-react 1.24.4 → 1.25.0

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
package/lib/libs/util.js CHANGED
@@ -14,7 +14,10 @@ exports.formatCouponTerms = void 0;
14
14
  exports.formatCreditAmount = formatCreditAmount;
15
15
  exports.formatCreditForCheckout = formatCreditForCheckout;
16
16
  exports.formatDateTime = formatDateTime;
17
+ exports.formatDynamicPrice = formatDynamicPrice;
17
18
  exports.formatError = void 0;
19
+ exports.formatExchangeRate = formatExchangeRate;
20
+ exports.formatExchangeRateDisplay = formatExchangeRateDisplay;
18
21
  exports.formatLineItemPricing = formatLineItemPricing;
19
22
  exports.formatLinkWithLocale = formatLinkWithLocale;
20
23
  exports.formatLocale = void 0;
@@ -26,16 +29,19 @@ exports.formatQuantityInventory = formatQuantityInventory;
26
29
  exports.formatRecurring = formatRecurring;
27
30
  exports.formatSubscriptionProduct = formatSubscriptionProduct;
28
31
  exports.formatSubscriptionStatus = formatSubscriptionStatus;
32
+ exports.formatThenValue = formatThenValue;
29
33
  exports.formatTime = formatTime;
30
34
  exports.formatToDate = formatToDate;
31
35
  exports.formatToDatetime = formatToDatetime;
32
36
  exports.formatTotalPrice = formatTotalPrice;
33
37
  exports.formatUpsellSaving = formatUpsellSaving;
38
+ exports.formatUsdAmount = formatUsdAmount;
34
39
  exports.getCheckoutAmount = getCheckoutAmount;
35
40
  exports.getCustomerAvatar = getCustomerAvatar;
36
41
  exports.getFreeTrialTime = getFreeTrialTime;
37
42
  exports.getInvoiceDescriptionAndReason = getInvoiceDescriptionAndReason;
38
43
  exports.getInvoiceStatusColor = getInvoiceStatusColor;
44
+ exports.getLineItemAmounts = getLineItemAmounts;
39
45
  exports.getLineTimeInfo = void 0;
40
46
  exports.getPaymentIntentStatusColor = getPaymentIntentStatusColor;
41
47
  exports.getPaymentKitComponent = getPaymentKitComponent;
@@ -44,6 +50,7 @@ exports.getPrefix = void 0;
44
50
  exports.getPriceCurrencyOptions = getPriceCurrencyOptions;
45
51
  exports.getPriceUintAmountByCurrency = getPriceUintAmountByCurrency;
46
52
  exports.getQueryParams = getQueryParams;
53
+ exports.getQuoteLockInfo = getQuoteLockInfo;
47
54
  exports.getRecurringPeriod = getRecurringPeriod;
48
55
  exports.getRefundStatusColor = getRefundStatusColor;
49
56
  exports.getStatementDescriptor = getStatementDescriptor;
@@ -52,6 +59,8 @@ exports.getSubscriptionStatusColor = getSubscriptionStatusColor;
52
59
  exports.getSubscriptionTimeSummary = void 0;
53
60
  exports.getTokenBalanceLink = getTokenBalanceLink;
54
61
  exports.getTxLink = void 0;
62
+ exports.getUsdAmountFromBaseAmount = getUsdAmountFromBaseAmount;
63
+ exports.getUsdAmountFromTokenUnits = getUsdAmountFromTokenUnits;
55
64
  exports.getUserProfileLink = getUserProfileLink;
56
65
  exports.getWebhookStatusColor = getWebhookStatusColor;
57
66
  exports.getWordBreakStyle = getWordBreakStyle;
@@ -256,6 +265,87 @@ function formatNumber(n, precision = 6, trim = true, thousandSeparated = true) {
256
265
  const [left, right] = result.split(".");
257
266
  return right ? [left, (0, _trimEnd.default)(right, "0")].filter(Boolean).join(".") : left;
258
267
  }
268
+ function formatDynamicPrice(n, isDynamic, precision = 6, trim = true, thousandSeparated = true) {
269
+ if (!isDynamic) {
270
+ return formatNumber(n, precision, trim, thousandSeparated);
271
+ }
272
+ const num = Number(n);
273
+ if (!Number.isFinite(num)) {
274
+ return formatNumber(n, precision, trim, thousandSeparated);
275
+ }
276
+ const abs = Math.abs(num);
277
+ const targetPrecision = abs > 0 && abs < 0.01 ? precision : 2;
278
+ return formatNumber(n, targetPrecision, trim, thousandSeparated);
279
+ }
280
+ const USD_DECIMALS = 8;
281
+ function getUsdAmountFromBaseAmount(amount, quantity, scale = BASE_AMOUNT_SCALE) {
282
+ if (amount === void 0 || amount === null || !quantity || quantity <= 0) {
283
+ return null;
284
+ }
285
+ const scaled = toScaledBaseAmount(amount, quantity, scale);
286
+ return formatScaledAmount(scaled, scale);
287
+ }
288
+ function getUsdAmountFromTokenUnits(tokenAmount, tokenDecimals, exchangeRate) {
289
+ if (!exchangeRate && exchangeRate !== "0") {
290
+ return null;
291
+ }
292
+ if (tokenDecimals === void 0 || tokenDecimals === null) {
293
+ return null;
294
+ }
295
+ try {
296
+ const amountBN = tokenAmount instanceof _util.BN ? tokenAmount : new _util.BN(tokenAmount || "0");
297
+ const tokenScale = new _util.BN(10).pow(new _util.BN(tokenDecimals));
298
+ if (tokenScale.isZero()) {
299
+ return null;
300
+ }
301
+ const rateBN = new _util.BN(exchangeRate.replace(".", ""));
302
+ const rateDecimalPlaces = exchangeRate.includes(".") ? exchangeRate.split(".")[1]?.length || 0 : 0;
303
+ const rateScale = new _util.BN(10).pow(new _util.BN(rateDecimalPlaces));
304
+ const usdPrecisionScale = new _util.BN(10).pow(new _util.BN(USD_DECIMALS));
305
+ const usdUnit = amountBN.mul(rateBN).mul(usdPrecisionScale).div(tokenScale.mul(rateScale));
306
+ return (0, _util.fromUnitToToken)(usdUnit.toString(), USD_DECIMALS);
307
+ } catch {
308
+ return null;
309
+ }
310
+ }
311
+ function formatUsdAmount(amount, locale = "en") {
312
+ if (!amount && amount !== "0") {
313
+ return null;
314
+ }
315
+ const num = Number(amount);
316
+ if (!Number.isFinite(num)) {
317
+ return null;
318
+ }
319
+ return new Intl.NumberFormat(locale, {
320
+ minimumFractionDigits: 2,
321
+ maximumFractionDigits: 2
322
+ }).format(num);
323
+ }
324
+ function formatExchangeRate(amount) {
325
+ if (!amount && amount !== "0") {
326
+ return null;
327
+ }
328
+ const value = String(amount);
329
+ const num = Number(value);
330
+ if (!Number.isFinite(num)) {
331
+ return null;
332
+ }
333
+ return value;
334
+ }
335
+ function formatExchangeRateDisplay(rate, currency = "USD", decimals = 2) {
336
+ if (rate === null || rate === void 0) {
337
+ return null;
338
+ }
339
+ const num = Number(rate);
340
+ if (!Number.isFinite(num)) {
341
+ return null;
342
+ }
343
+ const formattedValue = num.toFixed(decimals);
344
+ if (currency === "USD") {
345
+ return `$${formattedValue}`;
346
+ }
347
+ return `${formattedValue} ${currency}`;
348
+ }
259
349
  const formatPrice = (price, currency, unit_label, quantity = 1, bn = true, locale = "en") => {
260
350
  if (!currency) {
261
351
  return "";
@@ -263,6 +353,22 @@ const formatPrice = (price, currency, unit_label, quantity = 1, bn = true, local
263
353
  if (price.custom_unit_amount) {
264
354
  return `Custom (${currency.symbol})`;
265
355
  }
356
+ if (price.pricing_type === "dynamic" && price.base_amount && price.base_currency === "USD") {
357
+ const baseAmount = price.base_amount;
358
+ const usdAmount = Number(baseAmount) * quantity;
359
+ const formattedUsd = formatUsdAmount(usdAmount.toString(), locale);
360
+ if (price?.type === "recurring" && price.recurring) {
361
+ const recurring = formatRecurring(price.recurring, false, "slash", locale);
362
+ if (unit_label) {
363
+ return `$${formattedUsd} / ${unit_label} ${recurring}`;
364
+ }
365
+ if (price.recurring.usage_type === "metered") {
366
+ return `$${formattedUsd} / unit ${recurring}`;
367
+ }
368
+ return `$${formattedUsd} ${recurring}`;
369
+ }
370
+ return `$${formattedUsd}`;
371
+ }
266
372
  const unit = getPriceUintAmountByCurrency(price, currency);
267
373
  const amount = bn ? (0, _util.fromUnitToToken)(new _util.BN(unit).mul(new _util.BN(quantity)), currency.decimal).toString() : +unit * quantity;
268
374
  if (price?.type === "recurring" && price.recurring) {
@@ -278,10 +384,25 @@ const formatPrice = (price, currency, unit_label, quantity = 1, bn = true, local
278
384
  return `${amount} ${currency.symbol}`;
279
385
  };
280
386
  exports.formatPrice = formatPrice;
281
- const formatPriceAmount = (price, currency, unit_label, quantity = 1, bn = true) => {
387
+ const formatPriceAmount = (price, currency, unit_label, quantity = 1, bn = true, locale = "en") => {
282
388
  if (!currency) {
283
389
  return "";
284
390
  }
391
+ if (price.pricing_type === "dynamic" && price.base_amount && price.base_currency === "USD") {
392
+ const baseAmount = price.base_amount;
393
+ const usdAmount = Number(baseAmount) * quantity;
394
+ const formattedUsd = formatUsdAmount(usdAmount.toString(), locale);
395
+ if (price?.type === "recurring" && price.recurring) {
396
+ if (unit_label) {
397
+ return `$${formattedUsd} / ${unit_label}`;
398
+ }
399
+ if (price.recurring.usage_type === "metered") {
400
+ return `$${formattedUsd} / unit`;
401
+ }
402
+ return `$${formattedUsd}`;
403
+ }
404
+ return `$${formattedUsd}`;
405
+ }
285
406
  const unit = getPriceUintAmountByCurrency(price, currency);
286
407
  const amount = bn ? (0, _util.fromUnitToToken)(new _util.BN(unit).mul(new _util.BN(quantity)), currency.decimal).toString() : +unit * quantity;
287
408
  if (price?.type === "recurring" && price.recurring) {
@@ -363,9 +484,113 @@ function getPriceCurrencyOptions(price) {
363
484
  tiers: null
364
485
  }];
365
486
  }
487
+ const BASE_AMOUNT_SCALE = 8;
488
+ function formatScaledAmount(value, scale = BASE_AMOUNT_SCALE) {
489
+ const isNegative = value.isNeg();
490
+ const absValue = value.abs();
491
+ const str = absValue.toString().padStart(scale + 1, "0");
492
+ const integerPart = str.slice(0, str.length - scale) || "0";
493
+ const fraction = str.slice(-scale).replace(/0+$/, "");
494
+ const formatted = fraction ? `${integerPart}.${fraction}` : integerPart;
495
+ return isNegative ? `-${formatted}` : formatted;
496
+ }
497
+ function toScaledBaseAmount(amount, quantity, scale = BASE_AMOUNT_SCALE) {
498
+ if (!amount || !quantity) {
499
+ return new _util.BN(0);
500
+ }
501
+ const normalized = String(amount);
502
+ const [integer = "0", fraction = ""] = normalized.split(".");
503
+ const frac = `${fraction}`.padEnd(scale, "0").slice(0, scale);
504
+ const digits = `${integer.replace("-", "")}${frac}` || "0";
505
+ const scaled = new _util.BN(digits).mul(new _util.BN(quantity));
506
+ return normalized.startsWith("-") ? scaled.neg() : scaled;
507
+ }
508
+ function getLineItemAmounts(item, currency, {
509
+ useUpsell = true,
510
+ exchangeRate = null
511
+ } = {}) {
512
+ if (!currency) {
513
+ return {
514
+ unitAmount: new _util.BN(0),
515
+ totalAmount: new _util.BN(0),
516
+ isDynamicQuote: false
517
+ };
518
+ }
519
+ const price = useUpsell ? item.upsell_price || item.price : item.price;
520
+ const quantity = new _util.BN(item.quantity || 0);
521
+ const quoteAmount = item?.quoted_amount;
522
+ const quoteCurrencyId = item?.quote_currency_id;
523
+ const isDynamicPrice = price?.pricing_type === "dynamic";
524
+ const isQuoteValidForCurrency = isDynamicPrice && quoteAmount && quoteCurrencyId === currency.id;
525
+ if (isQuoteValidForCurrency) {
526
+ const totalAmount = new _util.BN(quoteAmount || "0");
527
+ const unitAmount2 = quantity.gt(new _util.BN(0)) ? totalAmount.add(quantity).sub(new _util.BN(1)).div(quantity) : new _util.BN(0);
528
+ return {
529
+ unitAmount: unitAmount2,
530
+ totalAmount,
531
+ isDynamicQuote: true
532
+ };
533
+ }
534
+ if (isDynamicPrice && exchangeRate && price?.base_amount) {
535
+ const rate = Number(exchangeRate);
536
+ if (rate > 0 && Number.isFinite(rate)) {
537
+ const baseAmountUsd = Number(price.base_amount);
538
+ if (baseAmountUsd > 0 && Number.isFinite(baseAmountUsd)) {
539
+ const tokenAmount = baseAmountUsd / rate;
540
+ const unitAmount2 = (0, _util.fromTokenToUnit)(tokenAmount.toFixed(currency.decimal || 8), currency.decimal || 8);
541
+ return {
542
+ unitAmount: unitAmount2,
543
+ totalAmount: unitAmount2.mul(quantity),
544
+ isDynamicQuote: false
545
+ };
546
+ }
547
+ }
548
+ }
549
+ const unitAmount = new _util.BN(getPriceUintAmountByCurrency(price, currency));
550
+ return {
551
+ unitAmount,
552
+ totalAmount: unitAmount.mul(quantity),
553
+ isDynamicQuote: false
554
+ };
555
+ }
556
+ function getQuoteLockInfo(items, currency) {
557
+ if (!items?.length || !currency) {
558
+ return null;
559
+ }
560
+ const dynamicItems = items.filter(item => {
561
+ const price = item.upsell_price || item.price;
562
+ return price?.pricing_type === "dynamic" && item?.quoted_amount;
563
+ });
564
+ if (!dynamicItems.length) {
565
+ return null;
566
+ }
567
+ let totalBaseAmount = new _util.BN(0);
568
+ let totalTokenAmount = new _util.BN(0);
569
+ let expiresAt = null;
570
+ dynamicItems.forEach(item => {
571
+ const price = item.upsell_price || item.price;
572
+ const quoteAmount = new _util.BN(item?.quoted_amount || "0");
573
+ totalTokenAmount = totalTokenAmount.add(quoteAmount);
574
+ const baseAmount = price?.base_amount;
575
+ if (baseAmount) {
576
+ totalBaseAmount = totalBaseAmount.add(toScaledBaseAmount(baseAmount, item.quantity));
577
+ }
578
+ if (item?.expires_at) {
579
+ expiresAt = expiresAt === null ? item?.expires_at : Math.min(expiresAt, item?.expires_at);
580
+ }
581
+ });
582
+ return {
583
+ baseAmount: formatScaledAmount(totalBaseAmount, BASE_AMOUNT_SCALE),
584
+ baseCurrency: (dynamicItems[0]?.upsell_price || dynamicItems[0]?.price)?.base_currency || "USD",
585
+ tokenAmount: (0, _util.fromUnitToToken)(totalTokenAmount, currency.decimal).toString(),
586
+ tokenSymbol: currency.symbol,
587
+ expiresAt
588
+ };
589
+ }
366
590
  function formatLineItemPricing(item, currency, {
367
591
  trialEnd,
368
- trialInDays
592
+ trialInDays,
593
+ exchangeRate = null
369
594
  }, locale = "en") {
370
595
  if (!currency) {
371
596
  return {
@@ -375,15 +600,32 @@ function formatLineItemPricing(item, currency, {
375
600
  };
376
601
  }
377
602
  const price = item.upsell_price || item.price;
603
+ if (!price) {
604
+ return {
605
+ primary: "",
606
+ secondary: "",
607
+ quantity: ""
608
+ };
609
+ }
378
610
  let quantity = (0, _locales.t)("common.qty", locale, {
379
611
  count: item.quantity
380
612
  });
381
613
  if (price.recurring?.usage_type === "metered" || +item.quantity === 1) {
382
614
  quantity = "";
383
615
  }
384
- const unitValue = new _util.BN(getPriceUintAmountByCurrency(price, currency));
385
- const total = `${(0, _util.fromUnitToToken)(unitValue.mul(new _util.BN(item.quantity)), currency.decimal)} ${currency.symbol}`;
386
- const unit = `${(0, _util.fromUnitToToken)(unitValue, currency.decimal)} ${currency.symbol}`;
616
+ const {
617
+ unitAmount,
618
+ totalAmount
619
+ } = getLineItemAmounts(item, currency, {
620
+ exchangeRate
621
+ });
622
+ const isDynamic = price?.pricing_type === "dynamic";
623
+ const formatLineItemAmount = bn => {
624
+ const value = (0, _util.fromUnitToToken)(bn, currency.decimal);
625
+ return formatDynamicPrice(value, isDynamic, 6);
626
+ };
627
+ const total = `${formatLineItemAmount(totalAmount)} ${currency.symbol}`;
628
+ const unit = `${formatLineItemAmount(unitAmount)} ${currency.symbol}`;
387
629
  const trialResult = getFreeTrialTime({
388
630
  trialInDays,
389
631
  trialEnd
@@ -505,8 +747,13 @@ function getWebhookStatusColor(status) {
505
747
  return "default";
506
748
  }
507
749
  }
508
- function getCheckoutAmount(items, currency, trialing = false, upsell = true) {
509
- if (items.find(x => (x.upsell_price || x.price).custom_unit_amount) && items.length > 1) {
750
+ function getCheckoutAmount(items, currency, trialing = false, upsell = true, {
751
+ exchangeRate = null
752
+ } = {}) {
753
+ if (items.find(x => {
754
+ const price = upsell ? x.upsell_price || x.price : x.price;
755
+ return price?.custom_unit_amount;
756
+ }) && items.length > 1) {
510
757
  throw new Error("Multiple items with custom unit amount are not supported");
511
758
  }
512
759
  let renew = new _util.BN(0);
@@ -514,18 +761,20 @@ function getCheckoutAmount(items, currency, trialing = false, upsell = true) {
514
761
  const price = upsell ? x.upsell_price || x.price : x.price;
515
762
  return price != null;
516
763
  }).reduce((acc, x) => {
517
- if (x.custom_amount) {
764
+ const quoteCurrencyId = x?.quote_currency_id;
765
+ const isQuoteForDifferentCurrency = quoteCurrencyId && quoteCurrencyId !== currency.id;
766
+ if (x.custom_amount && !isQuoteForDifferentCurrency) {
518
767
  return acc.add(new _util.BN(x.custom_amount));
519
768
  }
520
769
  const price = upsell ? x.upsell_price || x.price : x.price;
521
- const unitPrice = getPriceUintAmountByCurrency(price, currency);
522
- if (price.custom_unit_amount) {
523
- if (unitPrice) {
524
- return acc.add(new _util.BN(unitPrice).mul(new _util.BN(x.quantity)));
525
- }
526
- }
770
+ const {
771
+ totalAmount
772
+ } = getLineItemAmounts(x, currency, {
773
+ useUpsell: upsell,
774
+ exchangeRate
775
+ });
527
776
  if (price?.type === "recurring") {
528
- renew = renew.add(new _util.BN(unitPrice).mul(new _util.BN(x.quantity)));
777
+ renew = renew.add(totalAmount);
529
778
  if (trialing) {
530
779
  return acc;
531
780
  }
@@ -533,12 +782,12 @@ function getCheckoutAmount(items, currency, trialing = false, upsell = true) {
533
782
  return acc;
534
783
  }
535
784
  }
536
- return acc.add(new _util.BN(unitPrice).mul(new _util.BN(x.quantity)));
785
+ return acc.add(totalAmount);
537
786
  }, new _util.BN(0)).toString();
538
787
  return {
539
788
  subtotal: total,
540
789
  total,
541
- renew: renew.toString(),
790
+ renew: formatDynamicPrice(renew.toString(), !!exchangeRate),
542
791
  discount: "0",
543
792
  shipping: "0",
544
793
  tax: "0"
@@ -569,7 +818,7 @@ function formatUpsellSaving(items, currency) {
569
818
  if (items[0]?.upsell_price_id) {
570
819
  return "0";
571
820
  }
572
- if (!items[0]?.price.upsell?.upsells_to) {
821
+ if (!items[0]?.price?.upsell?.upsells_to) {
573
822
  return "0";
574
823
  }
575
824
  const from = getCheckoutAmount(items, currency, false, false);
@@ -600,6 +849,14 @@ function formatMeteredThen(subscription, recurring, hasMetered, locale = "en") {
600
849
  recurring
601
850
  });
602
851
  }
852
+ function formatThenValue(subscription, recurring, hasMetered, locale = "en") {
853
+ if (hasMetered) {
854
+ return (0, _locales.t)("payment.checkout.metered", locale, {
855
+ recurring
856
+ });
857
+ }
858
+ return [subscription, recurring].filter(Boolean).join(" ");
859
+ }
603
860
  function formatPriceDisplay({
604
861
  amount,
605
862
  then,
@@ -665,11 +922,15 @@ function getFreeTrialTime({
665
922
  function formatCheckoutHeadlines(items, currency, {
666
923
  trialInDays,
667
924
  trialEnd
668
- }, locale = "en") {
925
+ }, locale = "en", {
926
+ exchangeRate = null
927
+ } = {}) {
669
928
  const brand = getStatementDescriptor(items);
670
929
  const {
671
930
  total
672
- } = getCheckoutAmount(items, currency, trialInDays > 0);
931
+ } = getCheckoutAmount(items, currency, trialInDays > 0, true, {
932
+ exchangeRate
933
+ });
673
934
  const actualAmount = (0, _util.fromUnitToToken)(total, currency.decimal);
674
935
  const amount = `${(0, _util.fromUnitToToken)(total, currency.decimal)} ${currency.symbol}`;
675
936
  const trialResult = getFreeTrialTime({
@@ -687,10 +948,10 @@ function formatCheckoutHeadlines(items, currency, {
687
948
  }
688
949
  const {
689
950
  name
690
- } = items[0]?.price.product || {
951
+ } = items[0]?.price?.product || {
691
952
  name: ""
692
953
  };
693
- if (items.every(x => x.price.type === "one_time")) {
954
+ if (items.every(x => x.price?.type === "one_time")) {
694
955
  const action = (0, _locales.t)("payment.checkout.pay", locale, {
695
956
  payee: brand
696
957
  });
@@ -710,19 +971,24 @@ function formatCheckoutHeadlines(items, currency, {
710
971
  priceDisplay: amount
711
972
  };
712
973
  }
713
- const item = items.find(x => x.price.type === "recurring");
974
+ const item = items.find(x => x.price?.type === "recurring");
714
975
  const recurring = formatRecurring((item?.upsell_price || item?.price)?.recurring, false, "per", locale);
715
- const hasMetered = items.some(x => x.price.type === "recurring" && x.price.recurring?.usage_type === "metered");
976
+ const hasMetered = items.some(x => x.price?.type === "recurring" && x.price?.recurring?.usage_type === "metered");
716
977
  const differentRecurring = hasMultipleRecurringIntervals(items);
717
- if (items.every(x => x.price.type === "recurring")) {
718
- const subscription2 = [hasMetered ? (0, _locales.t)("payment.checkout.least", locale) : "", (0, _util.fromUnitToToken)(items.reduce((acc, x) => {
719
- if (x.price.recurring?.usage_type === "metered") {
978
+ if (items.every(x => x.price?.type === "recurring")) {
979
+ const subscription2 = [hasMetered ? (0, _locales.t)("payment.checkout.least", locale) : "", formatDynamicPrice((0, _util.fromUnitToToken)(items.reduce((acc, x) => {
980
+ const price = x.upsell_price || x.price;
981
+ if (price?.recurring?.usage_type === "metered") {
720
982
  return acc;
721
983
  }
722
- return acc.add(new _util.BN(getPriceUintAmountByCurrency(x.upsell_price || x.price, currency)).mul(new _util.BN(x.quantity)));
723
- }, new _util.BN(0)), currency.decimal), currency.symbol].filter(Boolean).join(" ");
984
+ return acc.add(getLineItemAmounts(x, currency, {
985
+ exchangeRate
986
+ }).totalAmount);
987
+ }, new _util.BN(0)), currency.decimal), !!exchangeRate), currency.symbol].filter(Boolean).join(" ");
724
988
  if (items.length > 1) {
725
989
  if (trialResult.count > 0) {
990
+ const thenDisplay4 = formatMeteredThen(subscription2, recurring, hasMetered && Number(subscription2) === 0, locale);
991
+ const thenValue4 = formatThenValue(subscription2, recurring, hasMetered && Number(subscription2) === 0, locale);
726
992
  const result4 = {
727
993
  action: (0, _locales.t)("payment.checkout.try2", locale, {
728
994
  name,
@@ -732,33 +998,51 @@ function formatCheckoutHeadlines(items, currency, {
732
998
  count: trialResult.count,
733
999
  interval: trialResult.interval
734
1000
  }),
735
- then: formatMeteredThen(subscription2, recurring, hasMetered && Number(subscription2) === 0, locale),
1001
+ then: thenDisplay4,
1002
+ thenValue: thenValue4,
736
1003
  showThen: true,
737
1004
  actualAmount: "0"
738
1005
  };
739
1006
  return {
740
1007
  ...result4,
741
- priceDisplay: formatPriceDisplay(result4, recurring, hasMetered, locale)
1008
+ priceDisplay: formatPriceDisplay({
1009
+ amount: result4.amount,
1010
+ then: thenDisplay4,
1011
+ actualAmount: result4.actualAmount,
1012
+ showThen: result4.showThen
1013
+ }, recurring, hasMetered, locale)
742
1014
  };
743
1015
  }
1016
+ const thenDisplay3 = hasMetered ? (0, _locales.t)("payment.checkout.meteredThen", locale, {
1017
+ recurring
1018
+ }) : recurring;
1019
+ const thenValue3 = hasMetered ? (0, _locales.t)("payment.checkout.metered", locale, {
1020
+ recurring
1021
+ }) : recurring;
744
1022
  const result3 = {
745
1023
  action: (0, _locales.t)("payment.checkout.sub2", locale, {
746
1024
  name,
747
1025
  count: items.length - 1
748
1026
  }),
749
1027
  amount,
750
- then: hasMetered ? (0, _locales.t)("payment.checkout.meteredThen", locale, {
751
- recurring
752
- }) : recurring,
1028
+ then: thenDisplay3,
1029
+ thenValue: thenValue3,
753
1030
  showThen: hasMetered,
754
1031
  actualAmount
755
1032
  };
756
1033
  return {
757
1034
  ...result3,
758
- priceDisplay: formatPriceDisplay(result3, recurring, hasMetered, locale)
1035
+ priceDisplay: formatPriceDisplay({
1036
+ amount: result3.amount,
1037
+ then: thenDisplay3,
1038
+ actualAmount: result3.actualAmount,
1039
+ showThen: result3.showThen
1040
+ }, recurring, hasMetered, locale)
759
1041
  };
760
1042
  }
761
1043
  if (trialResult.count > 0) {
1044
+ const thenDisplay3 = formatMeteredThen(subscription2, recurring, hasMetered && Number(subscription2) === 0, locale);
1045
+ const thenValue3 = formatThenValue(subscription2, recurring, hasMetered && Number(subscription2) === 0, locale);
762
1046
  const result3 = {
763
1047
  action: (0, _locales.t)("payment.checkout.try1", locale, {
764
1048
  name
@@ -767,53 +1051,87 @@ function formatCheckoutHeadlines(items, currency, {
767
1051
  count: trialResult.count,
768
1052
  interval: trialResult.interval
769
1053
  }),
770
- then: formatMeteredThen(subscription2, recurring, hasMetered && Number(subscription2) === 0, locale),
1054
+ then: thenDisplay3,
1055
+ thenValue: thenValue3,
771
1056
  showThen: true,
772
1057
  actualAmount: "0"
773
1058
  };
774
1059
  return {
775
1060
  ...result3,
776
- priceDisplay: formatPriceDisplay(result3, recurring, hasMetered, locale)
1061
+ priceDisplay: formatPriceDisplay({
1062
+ amount: result3.amount,
1063
+ then: thenDisplay3,
1064
+ actualAmount: result3.actualAmount,
1065
+ showThen: result3.showThen
1066
+ }, recurring, hasMetered, locale)
777
1067
  };
778
1068
  }
1069
+ const thenDisplay2 = hasMetered ? (0, _locales.t)("payment.checkout.meteredThen", locale, {
1070
+ recurring
1071
+ }) : recurring;
1072
+ const thenValue2 = hasMetered ? (0, _locales.t)("payment.checkout.metered", locale, {
1073
+ recurring
1074
+ }) : recurring;
779
1075
  const result2 = {
780
1076
  action: (0, _locales.t)("payment.checkout.sub1", locale, {
781
1077
  name
782
1078
  }),
783
1079
  amount,
784
- then: hasMetered ? (0, _locales.t)("payment.checkout.meteredThen", locale, {
785
- recurring
786
- }) : recurring,
1080
+ then: thenDisplay2,
1081
+ thenValue: thenValue2,
787
1082
  showThen: hasMetered && !differentRecurring,
788
1083
  actualAmount
789
1084
  };
790
1085
  return {
791
1086
  ...result2,
792
- priceDisplay: formatPriceDisplay(result2, recurring, hasMetered, locale)
1087
+ priceDisplay: formatPriceDisplay({
1088
+ amount: result2.amount,
1089
+ then: thenDisplay2,
1090
+ actualAmount: result2.actualAmount,
1091
+ showThen: result2.showThen
1092
+ }, recurring, hasMetered, locale)
793
1093
  };
794
1094
  }
795
- const subscription = (0, _util.fromUnitToToken)(items.filter(x => x.price.type === "recurring").reduce((acc, x) => {
796
- if (x.price.recurring?.usage_type === "metered") {
1095
+ const subscription = (0, _util.fromUnitToToken)(items.filter(x => x.price?.type === "recurring").reduce((acc, x) => {
1096
+ if (x.price?.recurring?.usage_type === "metered") {
797
1097
  return acc;
798
1098
  }
799
- return acc.add(new _util.BN(getPriceUintAmountByCurrency(x.price, currency)).mul(new _util.BN(x.quantity)));
1099
+ return acc.add(getLineItemAmounts(x, currency, {
1100
+ useUpsell: false,
1101
+ exchangeRate
1102
+ }).totalAmount);
800
1103
  }, new _util.BN(0)), currency.decimal);
1104
+ const thenDisplay = formatMeteredThen(`${subscription} ${currency.symbol}`, recurring, hasMetered && Number(subscription) === 0, locale);
1105
+ const thenValue = formatThenValue(`${subscription} ${currency.symbol}`, recurring, hasMetered && Number(subscription) === 0, locale);
801
1106
  const result = {
802
1107
  action: (0, _locales.t)("payment.checkout.pay", locale, {
803
1108
  payee: brand
804
1109
  }),
805
1110
  amount,
806
- then: formatMeteredThen(`${subscription} ${currency.symbol}`, recurring, hasMetered && Number(subscription) === 0, locale),
1111
+ then: thenDisplay,
1112
+ thenValue,
807
1113
  showThen: !differentRecurring,
808
1114
  actualAmount
809
1115
  };
810
1116
  return {
811
1117
  ...result,
812
- priceDisplay: formatPriceDisplay(result, recurring, hasMetered, locale)
1118
+ priceDisplay: formatPriceDisplay({
1119
+ amount: result.amount,
1120
+ then: thenDisplay,
1121
+ actualAmount: result.actualAmount,
1122
+ showThen: result.showThen
1123
+ }, recurring, hasMetered, locale)
813
1124
  };
814
1125
  }
815
- function formatAmount(amount, decimals) {
816
- return (0, _util.fromUnitToToken)(amount, decimals);
1126
+ function formatAmount(amount, decimals, precision = 2) {
1127
+ const tokenAmount = (0, _util.fromUnitToToken)(amount, decimals);
1128
+ const numericValue = Number(tokenAmount);
1129
+ if (!Number.isFinite(numericValue)) {
1130
+ return formatBNStr(amount, decimals, precision, true, false);
1131
+ }
1132
+ const abs = Math.abs(numericValue);
1133
+ const targetPrecision = abs > 0 && abs < 0.01 ? decimals : 2;
1134
+ return formatNumber(tokenAmount, targetPrecision, true, false);
817
1135
  }
818
1136
  function findCurrency(methods, currencyId) {
819
1137
  for (const method of methods) {
@@ -1306,7 +1624,7 @@ function showStaking(method, currency, noStake) {
1306
1624
  if (noStake) {
1307
1625
  return false;
1308
1626
  }
1309
- if (method.type === "arcblock") {
1627
+ if (method && method.type === "arcblock") {
1310
1628
  return currency.type !== "credit";
1311
1629
  }
1312
1630
  return false;