@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.
- package/es/components/auto-topup/modal.d.ts +2 -0
- package/es/components/auto-topup/modal.js +48 -6
- package/es/components/auto-topup/product-card.d.ts +16 -1
- package/es/components/auto-topup/product-card.js +97 -15
- package/es/components/dynamic-pricing-unavailable.d.ts +9 -0
- package/es/components/dynamic-pricing-unavailable.js +58 -0
- package/es/components/loading-amount.d.ts +17 -0
- package/es/components/loading-amount.js +46 -0
- package/es/components/price-change-confirm.d.ts +18 -0
- package/es/components/price-change-confirm.js +107 -0
- package/es/components/quote-details-panel.d.ts +21 -0
- package/es/components/quote-details-panel.js +170 -0
- package/es/components/quote-lock-banner.d.ts +7 -0
- package/es/components/quote-lock-banner.js +79 -0
- package/es/components/slippage-config.d.ts +20 -0
- package/es/components/slippage-config.js +261 -0
- package/es/history/invoice/list.js +125 -15
- package/es/hooks/dynamic-pricing.d.ts +102 -0
- package/es/hooks/dynamic-pricing.js +393 -0
- package/es/index.d.ts +6 -1
- package/es/index.js +9 -1
- package/es/libs/util.d.ts +42 -5
- package/es/libs/util.js +345 -57
- package/es/locales/en.js +114 -3
- package/es/locales/zh.js +114 -3
- package/es/payment/form/index.d.ts +4 -1
- package/es/payment/form/index.js +454 -22
- package/es/payment/index.d.ts +1 -1
- package/es/payment/index.js +279 -16
- package/es/payment/product-item.d.ts +26 -1
- package/es/payment/product-item.js +330 -51
- package/es/payment/summary-section/promotion-section.d.ts +32 -0
- package/es/payment/summary-section/promotion-section.js +143 -0
- package/es/payment/summary-section/total-section.d.ts +39 -0
- package/es/payment/summary-section/total-section.js +83 -0
- package/es/payment/summary.d.ts +17 -2
- package/es/payment/summary.js +300 -253
- package/es/types/index.d.ts +11 -0
- package/lib/components/auto-topup/modal.d.ts +2 -0
- package/lib/components/auto-topup/modal.js +54 -6
- package/lib/components/auto-topup/product-card.d.ts +16 -1
- package/lib/components/auto-topup/product-card.js +75 -7
- package/lib/components/dynamic-pricing-unavailable.d.ts +9 -0
- package/lib/components/dynamic-pricing-unavailable.js +81 -0
- package/lib/components/loading-amount.d.ts +17 -0
- package/lib/components/loading-amount.js +53 -0
- package/lib/components/price-change-confirm.d.ts +18 -0
- package/lib/components/price-change-confirm.js +157 -0
- package/lib/components/quote-details-panel.d.ts +21 -0
- package/lib/components/quote-details-panel.js +226 -0
- package/lib/components/quote-lock-banner.d.ts +7 -0
- package/lib/components/quote-lock-banner.js +93 -0
- package/lib/components/slippage-config.d.ts +20 -0
- package/lib/components/slippage-config.js +316 -0
- package/lib/history/invoice/list.js +167 -27
- package/lib/hooks/dynamic-pricing.d.ts +102 -0
- package/lib/hooks/dynamic-pricing.js +390 -0
- package/lib/index.d.ts +6 -1
- package/lib/index.js +32 -0
- package/lib/libs/util.d.ts +42 -5
- package/lib/libs/util.js +367 -49
- package/lib/locales/en.js +114 -3
- package/lib/locales/zh.js +114 -3
- package/lib/payment/form/index.d.ts +4 -1
- package/lib/payment/form/index.js +476 -20
- package/lib/payment/index.d.ts +1 -1
- package/lib/payment/index.js +308 -14
- package/lib/payment/product-item.d.ts +26 -1
- package/lib/payment/product-item.js +270 -35
- package/lib/payment/summary-section/promotion-section.d.ts +32 -0
- package/lib/payment/summary-section/promotion-section.js +133 -0
- package/lib/payment/summary-section/total-section.d.ts +39 -0
- package/lib/payment/summary-section/total-section.js +117 -0
- package/lib/payment/summary.d.ts +17 -2
- package/lib/payment/summary.js +205 -127
- package/lib/types/index.d.ts +11 -0
- package/package.json +3 -3
- package/src/components/auto-topup/modal.tsx +59 -6
- package/src/components/auto-topup/product-card.tsx +118 -11
- package/src/components/dynamic-pricing-unavailable.tsx +69 -0
- package/src/components/loading-amount.tsx +66 -0
- package/src/components/price-change-confirm.tsx +136 -0
- package/src/components/quote-details-panel.tsx +218 -0
- package/src/components/quote-lock-banner.tsx +99 -0
- package/src/components/slippage-config.tsx +336 -0
- package/src/history/invoice/list.tsx +143 -9
- package/src/hooks/dynamic-pricing.ts +617 -0
- package/src/index.ts +9 -0
- package/src/libs/util.ts +473 -58
- package/src/locales/en.tsx +117 -0
- package/src/locales/zh.tsx +111 -0
- package/src/payment/form/index.tsx +561 -19
- package/src/payment/index.tsx +349 -10
- package/src/payment/product-item.tsx +451 -37
- package/src/payment/summary-section/promotion-section.tsx +172 -0
- package/src/payment/summary-section/total-section.tsx +141 -0
- package/src/payment/summary.tsx +334 -192
- package/src/types/index.ts +15 -0
package/es/libs/util.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BN, fromUnitToToken } from "@ocap/util";
|
|
1
|
+
import { BN, fromUnitToToken, fromTokenToUnit } from "@ocap/util";
|
|
2
2
|
import omit from "lodash/omit";
|
|
3
3
|
import trimEnd from "lodash/trimEnd";
|
|
4
4
|
import numbro from "numbro";
|
|
@@ -167,6 +167,87 @@ export function formatNumber(n, precision = 6, trim = true, thousandSeparated =
|
|
|
167
167
|
const [left, right] = result.split(".");
|
|
168
168
|
return right ? [left, trimEnd(right, "0")].filter(Boolean).join(".") : left;
|
|
169
169
|
}
|
|
170
|
+
export function formatDynamicPrice(n, isDynamic, precision = 6, trim = true, thousandSeparated = true) {
|
|
171
|
+
if (!isDynamic) {
|
|
172
|
+
return formatNumber(n, precision, trim, thousandSeparated);
|
|
173
|
+
}
|
|
174
|
+
const num = Number(n);
|
|
175
|
+
if (!Number.isFinite(num)) {
|
|
176
|
+
return formatNumber(n, precision, trim, thousandSeparated);
|
|
177
|
+
}
|
|
178
|
+
const abs = Math.abs(num);
|
|
179
|
+
const targetPrecision = abs > 0 && abs < 0.01 ? precision : 2;
|
|
180
|
+
return formatNumber(n, targetPrecision, trim, thousandSeparated);
|
|
181
|
+
}
|
|
182
|
+
const USD_DECIMALS = 8;
|
|
183
|
+
export function getUsdAmountFromBaseAmount(amount, quantity, scale = BASE_AMOUNT_SCALE) {
|
|
184
|
+
if (amount === void 0 || amount === null || !quantity || quantity <= 0) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
const scaled = toScaledBaseAmount(amount, quantity, scale);
|
|
188
|
+
return formatScaledAmount(scaled, scale);
|
|
189
|
+
}
|
|
190
|
+
export function getUsdAmountFromTokenUnits(tokenAmount, tokenDecimals, exchangeRate) {
|
|
191
|
+
if (!exchangeRate && exchangeRate !== "0") {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
if (tokenDecimals === void 0 || tokenDecimals === null) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
const amountBN = tokenAmount instanceof BN ? tokenAmount : new BN(tokenAmount || "0");
|
|
199
|
+
const tokenScale = new BN(10).pow(new BN(tokenDecimals));
|
|
200
|
+
if (tokenScale.isZero()) {
|
|
201
|
+
return null;
|
|
202
|
+
}
|
|
203
|
+
const rateBN = new BN(exchangeRate.replace(".", ""));
|
|
204
|
+
const rateDecimalPlaces = exchangeRate.includes(".") ? exchangeRate.split(".")[1]?.length || 0 : 0;
|
|
205
|
+
const rateScale = new BN(10).pow(new BN(rateDecimalPlaces));
|
|
206
|
+
const usdPrecisionScale = new BN(10).pow(new BN(USD_DECIMALS));
|
|
207
|
+
const usdUnit = amountBN.mul(rateBN).mul(usdPrecisionScale).div(tokenScale.mul(rateScale));
|
|
208
|
+
return fromUnitToToken(usdUnit.toString(), USD_DECIMALS);
|
|
209
|
+
} catch {
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
export function formatUsdAmount(amount, locale = "en") {
|
|
214
|
+
if (!amount && amount !== "0") {
|
|
215
|
+
return null;
|
|
216
|
+
}
|
|
217
|
+
const num = Number(amount);
|
|
218
|
+
if (!Number.isFinite(num)) {
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
return new Intl.NumberFormat(locale, {
|
|
222
|
+
minimumFractionDigits: 2,
|
|
223
|
+
maximumFractionDigits: 2
|
|
224
|
+
}).format(num);
|
|
225
|
+
}
|
|
226
|
+
export function formatExchangeRate(amount) {
|
|
227
|
+
if (!amount && amount !== "0") {
|
|
228
|
+
return null;
|
|
229
|
+
}
|
|
230
|
+
const value = String(amount);
|
|
231
|
+
const num = Number(value);
|
|
232
|
+
if (!Number.isFinite(num)) {
|
|
233
|
+
return null;
|
|
234
|
+
}
|
|
235
|
+
return value;
|
|
236
|
+
}
|
|
237
|
+
export function formatExchangeRateDisplay(rate, currency = "USD", decimals = 2) {
|
|
238
|
+
if (rate === null || rate === void 0) {
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
const num = Number(rate);
|
|
242
|
+
if (!Number.isFinite(num)) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
const formattedValue = num.toFixed(decimals);
|
|
246
|
+
if (currency === "USD") {
|
|
247
|
+
return `$${formattedValue}`;
|
|
248
|
+
}
|
|
249
|
+
return `${formattedValue} ${currency}`;
|
|
250
|
+
}
|
|
170
251
|
export const formatPrice = (price, currency, unit_label, quantity = 1, bn = true, locale = "en") => {
|
|
171
252
|
if (!currency) {
|
|
172
253
|
return "";
|
|
@@ -174,6 +255,22 @@ export const formatPrice = (price, currency, unit_label, quantity = 1, bn = true
|
|
|
174
255
|
if (price.custom_unit_amount) {
|
|
175
256
|
return `Custom (${currency.symbol})`;
|
|
176
257
|
}
|
|
258
|
+
if (price.pricing_type === "dynamic" && price.base_amount && price.base_currency === "USD") {
|
|
259
|
+
const baseAmount = price.base_amount;
|
|
260
|
+
const usdAmount = Number(baseAmount) * quantity;
|
|
261
|
+
const formattedUsd = formatUsdAmount(usdAmount.toString(), locale);
|
|
262
|
+
if (price?.type === "recurring" && price.recurring) {
|
|
263
|
+
const recurring = formatRecurring(price.recurring, false, "slash", locale);
|
|
264
|
+
if (unit_label) {
|
|
265
|
+
return `$${formattedUsd} / ${unit_label} ${recurring}`;
|
|
266
|
+
}
|
|
267
|
+
if (price.recurring.usage_type === "metered") {
|
|
268
|
+
return `$${formattedUsd} / unit ${recurring}`;
|
|
269
|
+
}
|
|
270
|
+
return `$${formattedUsd} ${recurring}`;
|
|
271
|
+
}
|
|
272
|
+
return `$${formattedUsd}`;
|
|
273
|
+
}
|
|
177
274
|
const unit = getPriceUintAmountByCurrency(price, currency);
|
|
178
275
|
const amount = bn ? fromUnitToToken(new BN(unit).mul(new BN(quantity)), currency.decimal).toString() : +unit * quantity;
|
|
179
276
|
if (price?.type === "recurring" && price.recurring) {
|
|
@@ -188,10 +285,25 @@ export const formatPrice = (price, currency, unit_label, quantity = 1, bn = true
|
|
|
188
285
|
}
|
|
189
286
|
return `${amount} ${currency.symbol}`;
|
|
190
287
|
};
|
|
191
|
-
export const formatPriceAmount = (price, currency, unit_label, quantity = 1, bn = true) => {
|
|
288
|
+
export const formatPriceAmount = (price, currency, unit_label, quantity = 1, bn = true, locale = "en") => {
|
|
192
289
|
if (!currency) {
|
|
193
290
|
return "";
|
|
194
291
|
}
|
|
292
|
+
if (price.pricing_type === "dynamic" && price.base_amount && price.base_currency === "USD") {
|
|
293
|
+
const baseAmount = price.base_amount;
|
|
294
|
+
const usdAmount = Number(baseAmount) * quantity;
|
|
295
|
+
const formattedUsd = formatUsdAmount(usdAmount.toString(), locale);
|
|
296
|
+
if (price?.type === "recurring" && price.recurring) {
|
|
297
|
+
if (unit_label) {
|
|
298
|
+
return `$${formattedUsd} / ${unit_label}`;
|
|
299
|
+
}
|
|
300
|
+
if (price.recurring.usage_type === "metered") {
|
|
301
|
+
return `$${formattedUsd} / unit`;
|
|
302
|
+
}
|
|
303
|
+
return `$${formattedUsd}`;
|
|
304
|
+
}
|
|
305
|
+
return `$${formattedUsd}`;
|
|
306
|
+
}
|
|
195
307
|
const unit = getPriceUintAmountByCurrency(price, currency);
|
|
196
308
|
const amount = bn ? fromUnitToToken(new BN(unit).mul(new BN(quantity)), currency.decimal).toString() : +unit * quantity;
|
|
197
309
|
if (price?.type === "recurring" && price.recurring) {
|
|
@@ -272,18 +384,122 @@ export function getPriceCurrencyOptions(price) {
|
|
|
272
384
|
}
|
|
273
385
|
];
|
|
274
386
|
}
|
|
275
|
-
|
|
387
|
+
const BASE_AMOUNT_SCALE = 8;
|
|
388
|
+
function formatScaledAmount(value, scale = BASE_AMOUNT_SCALE) {
|
|
389
|
+
const isNegative = value.isNeg();
|
|
390
|
+
const absValue = value.abs();
|
|
391
|
+
const str = absValue.toString().padStart(scale + 1, "0");
|
|
392
|
+
const integerPart = str.slice(0, str.length - scale) || "0";
|
|
393
|
+
const fraction = str.slice(-scale).replace(/0+$/, "");
|
|
394
|
+
const formatted = fraction ? `${integerPart}.${fraction}` : integerPart;
|
|
395
|
+
return isNegative ? `-${formatted}` : formatted;
|
|
396
|
+
}
|
|
397
|
+
function toScaledBaseAmount(amount, quantity, scale = BASE_AMOUNT_SCALE) {
|
|
398
|
+
if (!amount || !quantity) {
|
|
399
|
+
return new BN(0);
|
|
400
|
+
}
|
|
401
|
+
const normalized = String(amount);
|
|
402
|
+
const [integer = "0", fraction = ""] = normalized.split(".");
|
|
403
|
+
const frac = `${fraction}`.padEnd(scale, "0").slice(0, scale);
|
|
404
|
+
const digits = `${integer.replace("-", "")}${frac}` || "0";
|
|
405
|
+
const scaled = new BN(digits).mul(new BN(quantity));
|
|
406
|
+
return normalized.startsWith("-") ? scaled.neg() : scaled;
|
|
407
|
+
}
|
|
408
|
+
export function getLineItemAmounts(item, currency, { useUpsell = true, exchangeRate = null } = {}) {
|
|
409
|
+
if (!currency) {
|
|
410
|
+
return { unitAmount: new BN(0), totalAmount: new BN(0), isDynamicQuote: false };
|
|
411
|
+
}
|
|
412
|
+
const price = useUpsell ? item.upsell_price || item.price : item.price;
|
|
413
|
+
const quantity = new BN(item.quantity || 0);
|
|
414
|
+
const quoteAmount = item?.quoted_amount;
|
|
415
|
+
const quoteCurrencyId = item?.quote_currency_id;
|
|
416
|
+
const isDynamicPrice = price?.pricing_type === "dynamic";
|
|
417
|
+
const isQuoteValidForCurrency = isDynamicPrice && quoteAmount && quoteCurrencyId === currency.id;
|
|
418
|
+
if (isQuoteValidForCurrency) {
|
|
419
|
+
const totalAmount = new BN(quoteAmount || "0");
|
|
420
|
+
const unitAmount2 = quantity.gt(new BN(0)) ? totalAmount.add(quantity).sub(new BN(1)).div(quantity) : new BN(0);
|
|
421
|
+
return { unitAmount: unitAmount2, totalAmount, isDynamicQuote: true };
|
|
422
|
+
}
|
|
423
|
+
if (isDynamicPrice && exchangeRate && price?.base_amount) {
|
|
424
|
+
const rate = Number(exchangeRate);
|
|
425
|
+
if (rate > 0 && Number.isFinite(rate)) {
|
|
426
|
+
const baseAmountUsd = Number(price.base_amount);
|
|
427
|
+
if (baseAmountUsd > 0 && Number.isFinite(baseAmountUsd)) {
|
|
428
|
+
const tokenAmount = baseAmountUsd / rate;
|
|
429
|
+
const unitAmount2 = fromTokenToUnit(tokenAmount.toFixed(currency.decimal || 8), currency.decimal || 8);
|
|
430
|
+
return {
|
|
431
|
+
unitAmount: unitAmount2,
|
|
432
|
+
totalAmount: unitAmount2.mul(quantity),
|
|
433
|
+
isDynamicQuote: false
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const unitAmount = new BN(getPriceUintAmountByCurrency(price, currency));
|
|
439
|
+
return {
|
|
440
|
+
unitAmount,
|
|
441
|
+
totalAmount: unitAmount.mul(quantity),
|
|
442
|
+
isDynamicQuote: false
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
export function getQuoteLockInfo(items, currency) {
|
|
446
|
+
if (!items?.length || !currency) {
|
|
447
|
+
return null;
|
|
448
|
+
}
|
|
449
|
+
const dynamicItems = items.filter((item) => {
|
|
450
|
+
const price = item.upsell_price || item.price;
|
|
451
|
+
return price?.pricing_type === "dynamic" && item?.quoted_amount;
|
|
452
|
+
});
|
|
453
|
+
if (!dynamicItems.length) {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
let totalBaseAmount = new BN(0);
|
|
457
|
+
let totalTokenAmount = new BN(0);
|
|
458
|
+
let expiresAt = null;
|
|
459
|
+
dynamicItems.forEach((item) => {
|
|
460
|
+
const price = item.upsell_price || item.price;
|
|
461
|
+
const quoteAmount = new BN(item?.quoted_amount || "0");
|
|
462
|
+
totalTokenAmount = totalTokenAmount.add(quoteAmount);
|
|
463
|
+
const baseAmount = price?.base_amount;
|
|
464
|
+
if (baseAmount) {
|
|
465
|
+
totalBaseAmount = totalBaseAmount.add(toScaledBaseAmount(baseAmount, item.quantity));
|
|
466
|
+
}
|
|
467
|
+
if (item?.expires_at) {
|
|
468
|
+
expiresAt = expiresAt === null ? item?.expires_at : Math.min(expiresAt, item?.expires_at);
|
|
469
|
+
}
|
|
470
|
+
});
|
|
471
|
+
return {
|
|
472
|
+
baseAmount: formatScaledAmount(totalBaseAmount, BASE_AMOUNT_SCALE),
|
|
473
|
+
baseCurrency: (dynamicItems[0]?.upsell_price || dynamicItems[0]?.price)?.base_currency || "USD",
|
|
474
|
+
tokenAmount: fromUnitToToken(totalTokenAmount, currency.decimal).toString(),
|
|
475
|
+
tokenSymbol: currency.symbol,
|
|
476
|
+
expiresAt
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
export function formatLineItemPricing(item, currency, {
|
|
480
|
+
trialEnd,
|
|
481
|
+
trialInDays,
|
|
482
|
+
exchangeRate = null
|
|
483
|
+
}, locale = "en") {
|
|
276
484
|
if (!currency) {
|
|
277
485
|
return { primary: "", secondary: "", quantity: "" };
|
|
278
486
|
}
|
|
279
487
|
const price = item.upsell_price || item.price;
|
|
488
|
+
if (!price) {
|
|
489
|
+
return { primary: "", secondary: "", quantity: "" };
|
|
490
|
+
}
|
|
280
491
|
let quantity = t("common.qty", locale, { count: item.quantity });
|
|
281
492
|
if (price.recurring?.usage_type === "metered" || +item.quantity === 1) {
|
|
282
493
|
quantity = "";
|
|
283
494
|
}
|
|
284
|
-
const
|
|
285
|
-
const
|
|
286
|
-
const
|
|
495
|
+
const { unitAmount, totalAmount } = getLineItemAmounts(item, currency, { exchangeRate });
|
|
496
|
+
const isDynamic = price?.pricing_type === "dynamic";
|
|
497
|
+
const formatLineItemAmount = (bn) => {
|
|
498
|
+
const value = fromUnitToToken(bn, currency.decimal);
|
|
499
|
+
return formatDynamicPrice(value, isDynamic, 6);
|
|
500
|
+
};
|
|
501
|
+
const total = `${formatLineItemAmount(totalAmount)} ${currency.symbol}`;
|
|
502
|
+
const unit = `${formatLineItemAmount(unitAmount)} ${currency.symbol}`;
|
|
287
503
|
const trialResult = getFreeTrialTime({ trialInDays, trialEnd }, locale);
|
|
288
504
|
const appendUnit = (v, alt) => {
|
|
289
505
|
if (price.product.unit_label) {
|
|
@@ -397,8 +613,11 @@ export function getWebhookStatusColor(status) {
|
|
|
397
613
|
return "default";
|
|
398
614
|
}
|
|
399
615
|
}
|
|
400
|
-
export function getCheckoutAmount(items, currency, trialing = false, upsell = true) {
|
|
401
|
-
if (items.find((x) =>
|
|
616
|
+
export function getCheckoutAmount(items, currency, trialing = false, upsell = true, { exchangeRate = null } = {}) {
|
|
617
|
+
if (items.find((x) => {
|
|
618
|
+
const price = upsell ? x.upsell_price || x.price : x.price;
|
|
619
|
+
return price?.custom_unit_amount;
|
|
620
|
+
}) && items.length > 1) {
|
|
402
621
|
throw new Error("Multiple items with custom unit amount are not supported");
|
|
403
622
|
}
|
|
404
623
|
let renew = new BN(0);
|
|
@@ -406,18 +625,15 @@ export function getCheckoutAmount(items, currency, trialing = false, upsell = tr
|
|
|
406
625
|
const price = upsell ? x.upsell_price || x.price : x.price;
|
|
407
626
|
return price != null;
|
|
408
627
|
}).reduce((acc, x) => {
|
|
409
|
-
|
|
628
|
+
const quoteCurrencyId = x?.quote_currency_id;
|
|
629
|
+
const isQuoteForDifferentCurrency = quoteCurrencyId && quoteCurrencyId !== currency.id;
|
|
630
|
+
if (x.custom_amount && !isQuoteForDifferentCurrency) {
|
|
410
631
|
return acc.add(new BN(x.custom_amount));
|
|
411
632
|
}
|
|
412
633
|
const price = upsell ? x.upsell_price || x.price : x.price;
|
|
413
|
-
const
|
|
414
|
-
if (price.custom_unit_amount) {
|
|
415
|
-
if (unitPrice) {
|
|
416
|
-
return acc.add(new BN(unitPrice).mul(new BN(x.quantity)));
|
|
417
|
-
}
|
|
418
|
-
}
|
|
634
|
+
const { totalAmount } = getLineItemAmounts(x, currency, { useUpsell: upsell, exchangeRate });
|
|
419
635
|
if (price?.type === "recurring") {
|
|
420
|
-
renew = renew.add(
|
|
636
|
+
renew = renew.add(totalAmount);
|
|
421
637
|
if (trialing) {
|
|
422
638
|
return acc;
|
|
423
639
|
}
|
|
@@ -425,9 +641,16 @@ export function getCheckoutAmount(items, currency, trialing = false, upsell = tr
|
|
|
425
641
|
return acc;
|
|
426
642
|
}
|
|
427
643
|
}
|
|
428
|
-
return acc.add(
|
|
644
|
+
return acc.add(totalAmount);
|
|
429
645
|
}, new BN(0)).toString();
|
|
430
|
-
return {
|
|
646
|
+
return {
|
|
647
|
+
subtotal: total,
|
|
648
|
+
total,
|
|
649
|
+
renew: formatDynamicPrice(renew.toString(), !!exchangeRate),
|
|
650
|
+
discount: "0",
|
|
651
|
+
shipping: "0",
|
|
652
|
+
tax: "0"
|
|
653
|
+
};
|
|
431
654
|
}
|
|
432
655
|
export function getRecurringPeriod(recurring) {
|
|
433
656
|
const { interval } = recurring;
|
|
@@ -452,7 +675,7 @@ export function formatUpsellSaving(items, currency) {
|
|
|
452
675
|
if (items[0]?.upsell_price_id) {
|
|
453
676
|
return "0";
|
|
454
677
|
}
|
|
455
|
-
if (!items[0]?.price
|
|
678
|
+
if (!items[0]?.price?.upsell?.upsells_to) {
|
|
456
679
|
return "0";
|
|
457
680
|
}
|
|
458
681
|
const from = getCheckoutAmount(items, currency, false, false);
|
|
@@ -482,6 +705,12 @@ export function formatMeteredThen(subscription, recurring, hasMetered, locale =
|
|
|
482
705
|
}
|
|
483
706
|
return t("payment.checkout.then", locale, { subscription, recurring });
|
|
484
707
|
}
|
|
708
|
+
export function formatThenValue(subscription, recurring, hasMetered, locale = "en") {
|
|
709
|
+
if (hasMetered) {
|
|
710
|
+
return t("payment.checkout.metered", locale, { recurring });
|
|
711
|
+
}
|
|
712
|
+
return [subscription, recurring].filter(Boolean).join(" ");
|
|
713
|
+
}
|
|
485
714
|
export function formatPriceDisplay({ amount, then, actualAmount, showThen }, recurring, hasMetered, locale = "en") {
|
|
486
715
|
if (Number(actualAmount) === 0 && hasMetered) {
|
|
487
716
|
return t("payment.checkout.metered", locale, { recurring });
|
|
@@ -534,9 +763,9 @@ export function getFreeTrialTime({ trialInDays, trialEnd }, locale = "en") {
|
|
|
534
763
|
interval: t("common.day", locale)
|
|
535
764
|
};
|
|
536
765
|
}
|
|
537
|
-
export function formatCheckoutHeadlines(items, currency, { trialInDays, trialEnd }, locale = "en") {
|
|
766
|
+
export function formatCheckoutHeadlines(items, currency, { trialInDays, trialEnd }, locale = "en", { exchangeRate = null } = {}) {
|
|
538
767
|
const brand = getStatementDescriptor(items);
|
|
539
|
-
const { total } = getCheckoutAmount(items, currency, trialInDays > 0);
|
|
768
|
+
const { total } = getCheckoutAmount(items, currency, trialInDays > 0, true, { exchangeRate });
|
|
540
769
|
const actualAmount = fromUnitToToken(total, currency.decimal);
|
|
541
770
|
const amount = `${fromUnitToToken(total, currency.decimal)} ${currency.symbol}`;
|
|
542
771
|
const trialResult = getFreeTrialTime({ trialInDays, trialEnd }, locale);
|
|
@@ -549,118 +778,177 @@ export function formatCheckoutHeadlines(items, currency, { trialInDays, trialEnd
|
|
|
549
778
|
priceDisplay: "0"
|
|
550
779
|
};
|
|
551
780
|
}
|
|
552
|
-
const { name } = items[0]?.price
|
|
553
|
-
if (items.every((x) => x.price
|
|
781
|
+
const { name } = items[0]?.price?.product || { name: "" };
|
|
782
|
+
if (items.every((x) => x.price?.type === "one_time")) {
|
|
554
783
|
const action = t("payment.checkout.pay", locale, { payee: brand });
|
|
555
784
|
if (items.length > 1) {
|
|
556
785
|
return { action, amount, actualAmount, priceDisplay: amount };
|
|
557
786
|
}
|
|
558
787
|
return { action, amount, then: "", actualAmount, priceDisplay: amount };
|
|
559
788
|
}
|
|
560
|
-
const item = items.find((x) => x.price
|
|
789
|
+
const item = items.find((x) => x.price?.type === "recurring");
|
|
561
790
|
const recurring = formatRecurring(
|
|
562
791
|
(item?.upsell_price || item?.price)?.recurring,
|
|
563
792
|
false,
|
|
564
793
|
"per",
|
|
565
794
|
locale
|
|
566
795
|
);
|
|
567
|
-
const hasMetered = items.some((x) => x.price
|
|
796
|
+
const hasMetered = items.some((x) => x.price?.type === "recurring" && x.price?.recurring?.usage_type === "metered");
|
|
568
797
|
const differentRecurring = hasMultipleRecurringIntervals(items);
|
|
569
|
-
if (items.every((x) => x.price
|
|
798
|
+
if (items.every((x) => x.price?.type === "recurring")) {
|
|
570
799
|
const subscription2 = [
|
|
571
800
|
hasMetered ? t("payment.checkout.least", locale) : "",
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
801
|
+
formatDynamicPrice(
|
|
802
|
+
fromUnitToToken(
|
|
803
|
+
items.reduce((acc, x) => {
|
|
804
|
+
const price = x.upsell_price || x.price;
|
|
805
|
+
if (price?.recurring?.usage_type === "metered") {
|
|
806
|
+
return acc;
|
|
807
|
+
}
|
|
808
|
+
return acc.add(getLineItemAmounts(x, currency, { exchangeRate }).totalAmount);
|
|
809
|
+
}, new BN(0)),
|
|
810
|
+
currency.decimal
|
|
811
|
+
),
|
|
812
|
+
!!exchangeRate
|
|
582
813
|
),
|
|
583
814
|
currency.symbol
|
|
584
815
|
].filter(Boolean).join(" ");
|
|
585
816
|
if (items.length > 1) {
|
|
586
817
|
if (trialResult.count > 0) {
|
|
818
|
+
const thenDisplay4 = formatMeteredThen(
|
|
819
|
+
subscription2,
|
|
820
|
+
recurring,
|
|
821
|
+
hasMetered && Number(subscription2) === 0,
|
|
822
|
+
locale
|
|
823
|
+
);
|
|
824
|
+
const thenValue4 = formatThenValue(subscription2, recurring, hasMetered && Number(subscription2) === 0, locale);
|
|
587
825
|
const result4 = {
|
|
588
826
|
action: t("payment.checkout.try2", locale, { name, count: items.length - 1 }),
|
|
589
827
|
amount: t("payment.checkout.free", locale, { count: trialResult.count, interval: trialResult.interval }),
|
|
590
|
-
then:
|
|
828
|
+
then: thenDisplay4,
|
|
829
|
+
thenValue: thenValue4,
|
|
591
830
|
showThen: true,
|
|
592
831
|
actualAmount: "0"
|
|
593
832
|
};
|
|
594
833
|
return {
|
|
595
834
|
...result4,
|
|
596
|
-
priceDisplay: formatPriceDisplay(
|
|
835
|
+
priceDisplay: formatPriceDisplay(
|
|
836
|
+
{ amount: result4.amount, then: thenDisplay4, actualAmount: result4.actualAmount, showThen: result4.showThen },
|
|
837
|
+
recurring,
|
|
838
|
+
hasMetered,
|
|
839
|
+
locale
|
|
840
|
+
)
|
|
597
841
|
};
|
|
598
842
|
}
|
|
843
|
+
const thenDisplay3 = hasMetered ? t("payment.checkout.meteredThen", locale, { recurring }) : recurring;
|
|
844
|
+
const thenValue3 = hasMetered ? t("payment.checkout.metered", locale, { recurring }) : recurring;
|
|
599
845
|
const result3 = {
|
|
600
846
|
action: t("payment.checkout.sub2", locale, { name, count: items.length - 1 }),
|
|
601
847
|
amount,
|
|
602
|
-
then:
|
|
848
|
+
then: thenDisplay3,
|
|
849
|
+
thenValue: thenValue3,
|
|
603
850
|
showThen: hasMetered,
|
|
604
851
|
actualAmount
|
|
605
852
|
};
|
|
606
853
|
return {
|
|
607
854
|
...result3,
|
|
608
|
-
priceDisplay: formatPriceDisplay(
|
|
855
|
+
priceDisplay: formatPriceDisplay(
|
|
856
|
+
{ amount: result3.amount, then: thenDisplay3, actualAmount: result3.actualAmount, showThen: result3.showThen },
|
|
857
|
+
recurring,
|
|
858
|
+
hasMetered,
|
|
859
|
+
locale
|
|
860
|
+
)
|
|
609
861
|
};
|
|
610
862
|
}
|
|
611
863
|
if (trialResult.count > 0) {
|
|
864
|
+
const thenDisplay3 = formatMeteredThen(subscription2, recurring, hasMetered && Number(subscription2) === 0, locale);
|
|
865
|
+
const thenValue3 = formatThenValue(subscription2, recurring, hasMetered && Number(subscription2) === 0, locale);
|
|
612
866
|
const result3 = {
|
|
613
867
|
action: t("payment.checkout.try1", locale, { name }),
|
|
614
868
|
amount: t("payment.checkout.free", locale, { count: trialResult.count, interval: trialResult.interval }),
|
|
615
|
-
then:
|
|
869
|
+
then: thenDisplay3,
|
|
870
|
+
thenValue: thenValue3,
|
|
616
871
|
showThen: true,
|
|
617
872
|
actualAmount: "0"
|
|
618
873
|
};
|
|
619
874
|
return {
|
|
620
875
|
...result3,
|
|
621
|
-
priceDisplay: formatPriceDisplay(
|
|
876
|
+
priceDisplay: formatPriceDisplay(
|
|
877
|
+
{ amount: result3.amount, then: thenDisplay3, actualAmount: result3.actualAmount, showThen: result3.showThen },
|
|
878
|
+
recurring,
|
|
879
|
+
hasMetered,
|
|
880
|
+
locale
|
|
881
|
+
)
|
|
622
882
|
};
|
|
623
883
|
}
|
|
884
|
+
const thenDisplay2 = hasMetered ? t("payment.checkout.meteredThen", locale, { recurring }) : recurring;
|
|
885
|
+
const thenValue2 = hasMetered ? t("payment.checkout.metered", locale, { recurring }) : recurring;
|
|
624
886
|
const result2 = {
|
|
625
887
|
action: t("payment.checkout.sub1", locale, { name }),
|
|
626
888
|
amount,
|
|
627
|
-
then:
|
|
889
|
+
then: thenDisplay2,
|
|
890
|
+
thenValue: thenValue2,
|
|
628
891
|
showThen: hasMetered && !differentRecurring,
|
|
629
892
|
actualAmount
|
|
630
893
|
};
|
|
631
894
|
return {
|
|
632
895
|
...result2,
|
|
633
|
-
priceDisplay: formatPriceDisplay(
|
|
896
|
+
priceDisplay: formatPriceDisplay(
|
|
897
|
+
{ amount: result2.amount, then: thenDisplay2, actualAmount: result2.actualAmount, showThen: result2.showThen },
|
|
898
|
+
recurring,
|
|
899
|
+
hasMetered,
|
|
900
|
+
locale
|
|
901
|
+
)
|
|
634
902
|
};
|
|
635
903
|
}
|
|
636
904
|
const subscription = fromUnitToToken(
|
|
637
|
-
items.filter((x) => x.price
|
|
638
|
-
if (x.price
|
|
905
|
+
items.filter((x) => x.price?.type === "recurring").reduce((acc, x) => {
|
|
906
|
+
if (x.price?.recurring?.usage_type === "metered") {
|
|
639
907
|
return acc;
|
|
640
908
|
}
|
|
641
|
-
return acc.add(
|
|
909
|
+
return acc.add(getLineItemAmounts(x, currency, { useUpsell: false, exchangeRate }).totalAmount);
|
|
642
910
|
}, new BN(0)),
|
|
643
911
|
currency.decimal
|
|
644
912
|
);
|
|
913
|
+
const thenDisplay = formatMeteredThen(
|
|
914
|
+
`${subscription} ${currency.symbol}`,
|
|
915
|
+
recurring,
|
|
916
|
+
hasMetered && Number(subscription) === 0,
|
|
917
|
+
locale
|
|
918
|
+
);
|
|
919
|
+
const thenValue = formatThenValue(
|
|
920
|
+
`${subscription} ${currency.symbol}`,
|
|
921
|
+
recurring,
|
|
922
|
+
hasMetered && Number(subscription) === 0,
|
|
923
|
+
locale
|
|
924
|
+
);
|
|
645
925
|
const result = {
|
|
646
926
|
action: t("payment.checkout.pay", locale, { payee: brand }),
|
|
647
927
|
amount,
|
|
648
|
-
then:
|
|
649
|
-
|
|
650
|
-
recurring,
|
|
651
|
-
hasMetered && Number(subscription) === 0,
|
|
652
|
-
locale
|
|
653
|
-
),
|
|
928
|
+
then: thenDisplay,
|
|
929
|
+
thenValue,
|
|
654
930
|
showThen: !differentRecurring,
|
|
655
931
|
actualAmount
|
|
656
932
|
};
|
|
657
933
|
return {
|
|
658
934
|
...result,
|
|
659
|
-
priceDisplay: formatPriceDisplay(
|
|
935
|
+
priceDisplay: formatPriceDisplay(
|
|
936
|
+
{ amount: result.amount, then: thenDisplay, actualAmount: result.actualAmount, showThen: result.showThen },
|
|
937
|
+
recurring,
|
|
938
|
+
hasMetered,
|
|
939
|
+
locale
|
|
940
|
+
)
|
|
660
941
|
};
|
|
661
942
|
}
|
|
662
|
-
export function formatAmount(amount, decimals) {
|
|
663
|
-
|
|
943
|
+
export function formatAmount(amount, decimals, precision = 2) {
|
|
944
|
+
const tokenAmount = fromUnitToToken(amount, decimals);
|
|
945
|
+
const numericValue = Number(tokenAmount);
|
|
946
|
+
if (!Number.isFinite(numericValue)) {
|
|
947
|
+
return formatBNStr(amount, decimals, precision, true, false);
|
|
948
|
+
}
|
|
949
|
+
const abs = Math.abs(numericValue);
|
|
950
|
+
const targetPrecision = abs > 0 && abs < 0.01 ? decimals : 2;
|
|
951
|
+
return formatNumber(tokenAmount, targetPrecision, true, false);
|
|
664
952
|
}
|
|
665
953
|
export function findCurrency(methods, currencyId) {
|
|
666
954
|
for (const method of methods) {
|
|
@@ -1063,7 +1351,7 @@ export function showStaking(method, currency, noStake) {
|
|
|
1063
1351
|
if (noStake) {
|
|
1064
1352
|
return false;
|
|
1065
1353
|
}
|
|
1066
|
-
if (method.type === "arcblock") {
|
|
1354
|
+
if (method && method.type === "arcblock") {
|
|
1067
1355
|
return currency.type !== "credit";
|
|
1068
1356
|
}
|
|
1069
1357
|
return false;
|