@blocklet/payment-react 1.24.3 → 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.
- 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/credit/transactions-list.js +11 -1
- 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/credit/transactions-list.js +11 -1
- 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/credit/transactions-list.tsx +14 -1
- 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
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
exports.useDynamicPricing = useDynamicPricing;
|
|
8
|
+
var _react = require("react");
|
|
9
|
+
var _util = require("@ocap/util");
|
|
10
|
+
var _util2 = require("../libs/util");
|
|
11
|
+
function extractQuoteMeta(items) {
|
|
12
|
+
if (!items?.length) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
let exchangeRate = null;
|
|
16
|
+
let baseCurrency = null;
|
|
17
|
+
let expiresAt = null;
|
|
18
|
+
let providerName = null;
|
|
19
|
+
let providerId = null;
|
|
20
|
+
let rateTimestampMs = null;
|
|
21
|
+
let slippagePercent = null;
|
|
22
|
+
items.forEach(item => {
|
|
23
|
+
const price = item.upsell_price || item.price;
|
|
24
|
+
const rate = item?.exchange_rate;
|
|
25
|
+
if (!exchangeRate && rate) {
|
|
26
|
+
exchangeRate = rate;
|
|
27
|
+
}
|
|
28
|
+
const base = price?.base_currency;
|
|
29
|
+
if (!baseCurrency && base) {
|
|
30
|
+
baseCurrency = base;
|
|
31
|
+
}
|
|
32
|
+
const expires = item?.expires_at;
|
|
33
|
+
if (expires) {
|
|
34
|
+
expiresAt = expiresAt === null ? expires : Math.min(expiresAt, expires);
|
|
35
|
+
}
|
|
36
|
+
const itemProviderName = item?.rate_provider_name;
|
|
37
|
+
if (!providerName && itemProviderName) {
|
|
38
|
+
providerName = itemProviderName;
|
|
39
|
+
}
|
|
40
|
+
const itemProviderId = item?.rate_provider_id;
|
|
41
|
+
if (!providerId && itemProviderId) {
|
|
42
|
+
providerId = itemProviderId;
|
|
43
|
+
}
|
|
44
|
+
const itemRateTimestamp = item?.rate_timestamp_ms;
|
|
45
|
+
if (!rateTimestampMs && Number.isFinite(Number(itemRateTimestamp))) {
|
|
46
|
+
rateTimestampMs = Number(itemRateTimestamp);
|
|
47
|
+
}
|
|
48
|
+
const itemSlippage = item?.slippage_percent;
|
|
49
|
+
if (slippagePercent === null && Number.isFinite(Number(itemSlippage))) {
|
|
50
|
+
slippagePercent = Number(itemSlippage);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return {
|
|
54
|
+
exchangeRate,
|
|
55
|
+
baseCurrency: baseCurrency || "USD",
|
|
56
|
+
expiresAt,
|
|
57
|
+
providerName,
|
|
58
|
+
providerId,
|
|
59
|
+
rateTimestampMs,
|
|
60
|
+
slippagePercent
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function calculateTokenAmount(items, rate, currencyDecimal, trialing = false) {
|
|
64
|
+
const usdTotal = items.reduce((acc, item) => {
|
|
65
|
+
const price = item.upsell_price || item.price;
|
|
66
|
+
if (trialing && price?.type === "recurring") {
|
|
67
|
+
return acc;
|
|
68
|
+
}
|
|
69
|
+
if (price?.type === "recurring" && price?.recurring?.usage_type === "metered") {
|
|
70
|
+
return acc;
|
|
71
|
+
}
|
|
72
|
+
const baseAmount = price?.base_amount;
|
|
73
|
+
if (baseAmount) {
|
|
74
|
+
const quantity = item.quantity || 1;
|
|
75
|
+
return acc + Number(baseAmount) * quantity;
|
|
76
|
+
}
|
|
77
|
+
return acc;
|
|
78
|
+
}, 0);
|
|
79
|
+
if (usdTotal <= 0) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
const rateNum = Number(rate);
|
|
83
|
+
if (rateNum <= 0) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
const tokenAmount = usdTotal / rateNum;
|
|
87
|
+
const tokenAmountUnit = (0, _util.fromTokenToUnit)(tokenAmount.toFixed(currencyDecimal || 8), currencyDecimal || 8);
|
|
88
|
+
return tokenAmountUnit.toString();
|
|
89
|
+
}
|
|
90
|
+
function extractQuoteLockedAt(paymentIntent, checkoutSession) {
|
|
91
|
+
if (paymentIntent?.quote_locked_at) {
|
|
92
|
+
const lockedAtMs = new Date(paymentIntent.quote_locked_at).getTime();
|
|
93
|
+
if (Number.isFinite(lockedAtMs)) {
|
|
94
|
+
return Math.floor(lockedAtMs / 1e3);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const metaLockedAt = checkoutSession?.metadata?.quote_locked_at;
|
|
98
|
+
if (typeof metaLockedAt === "number") {
|
|
99
|
+
return metaLockedAt;
|
|
100
|
+
}
|
|
101
|
+
if (typeof metaLockedAt === "string") {
|
|
102
|
+
const parsed = Number(metaLockedAt);
|
|
103
|
+
if (Number.isFinite(parsed)) {
|
|
104
|
+
return parsed;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
function useDynamicPricing(options) {
|
|
110
|
+
const {
|
|
111
|
+
items,
|
|
112
|
+
currency,
|
|
113
|
+
liveRate,
|
|
114
|
+
liveQuoteSnapshot,
|
|
115
|
+
checkoutSession,
|
|
116
|
+
paymentIntent,
|
|
117
|
+
locale = "en",
|
|
118
|
+
isStripePayment = false,
|
|
119
|
+
isSubscription = checkoutSession?.mode === "subscription" || checkoutSession?.mode === "setup",
|
|
120
|
+
slippageConfig,
|
|
121
|
+
trialInDays = 0,
|
|
122
|
+
trialEnd = 0,
|
|
123
|
+
discounts
|
|
124
|
+
} = options;
|
|
125
|
+
const currentTime = Math.floor(Date.now() / 1e3);
|
|
126
|
+
const trialing = trialInDays > 0 || trialEnd > currentTime;
|
|
127
|
+
const hasDynamicPricing = (0, _react.useMemo)(() => items.some(item => (item.upsell_price || item.price)?.pricing_type === "dynamic"), [items]);
|
|
128
|
+
const quoteMeta = (0, _react.useMemo)(() => hasDynamicPricing ? extractQuoteMeta(items) : null, [items, hasDynamicPricing]);
|
|
129
|
+
const rateInfo = (0, _react.useMemo)(() => ({
|
|
130
|
+
exchangeRate: isStripePayment ? null : liveRate?.rate || quoteMeta?.exchangeRate || null,
|
|
131
|
+
baseCurrency: liveRate?.base_currency || quoteMeta?.baseCurrency || "USD",
|
|
132
|
+
providerName: isStripePayment ? null : liveRate?.provider_name || quoteMeta?.providerName || null,
|
|
133
|
+
providerId: isStripePayment ? null : liveRate?.provider_id || quoteMeta?.providerId || null,
|
|
134
|
+
timestampMs: isStripePayment ? null : liveRate?.timestamp_ms || quoteMeta?.rateTimestampMs || null,
|
|
135
|
+
fetchedAt: isStripePayment ? null : liveRate?.fetched_at || null
|
|
136
|
+
// When we fetched the data
|
|
137
|
+
}), [liveRate, quoteMeta, isStripePayment]);
|
|
138
|
+
const calculatedTokenAmount = (0, _react.useMemo)(() => {
|
|
139
|
+
if (isStripePayment || !hasDynamicPricing || !liveRate?.rate) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
return calculateTokenAmount(items, liveRate.rate, currency.decimal, trialing);
|
|
143
|
+
}, [hasDynamicPricing, liveRate?.rate, items, currency.decimal, isStripePayment, trialing]);
|
|
144
|
+
const calculatedDiscountAmount = (0, _react.useMemo)(() => {
|
|
145
|
+
if (!discounts?.length) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
const couponDetails = discounts[0]?.coupon_details;
|
|
149
|
+
if (!couponDetails) {
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
if (isStripePayment) {
|
|
153
|
+
const discountableSubtotalCents = items.reduce((sum, item) => {
|
|
154
|
+
if (!item.discountable) {
|
|
155
|
+
return sum;
|
|
156
|
+
}
|
|
157
|
+
const price = item.upsell_price || item.price;
|
|
158
|
+
if (trialing && price?.type === "recurring") {
|
|
159
|
+
return sum;
|
|
160
|
+
}
|
|
161
|
+
if (price?.type === "recurring" && price?.recurring?.usage_type === "metered") {
|
|
162
|
+
return sum;
|
|
163
|
+
}
|
|
164
|
+
const isDynamic = price?.pricing_type === "dynamic";
|
|
165
|
+
const baseAmount = price?.base_amount;
|
|
166
|
+
let amountCents;
|
|
167
|
+
if (isDynamic && baseAmount !== void 0 && baseAmount !== null) {
|
|
168
|
+
amountCents = Number(baseAmount) * 100;
|
|
169
|
+
} else {
|
|
170
|
+
const unitAmount = price?.unit_amount;
|
|
171
|
+
if (!unitAmount) {
|
|
172
|
+
return sum;
|
|
173
|
+
}
|
|
174
|
+
amountCents = Number(unitAmount);
|
|
175
|
+
}
|
|
176
|
+
const quantity = item.quantity || 1;
|
|
177
|
+
return sum + amountCents * quantity;
|
|
178
|
+
}, 0);
|
|
179
|
+
if (discountableSubtotalCents <= 0) {
|
|
180
|
+
return null;
|
|
181
|
+
}
|
|
182
|
+
if (couponDetails.percent_off && couponDetails.percent_off > 0) {
|
|
183
|
+
const discountCents = Math.round(discountableSubtotalCents * couponDetails.percent_off / 100);
|
|
184
|
+
return discountCents.toString();
|
|
185
|
+
}
|
|
186
|
+
if (couponDetails.amount_off) {
|
|
187
|
+
const amountOffCents = Number(couponDetails.amount_off);
|
|
188
|
+
if (Number.isFinite(amountOffCents) && amountOffCents > 0) {
|
|
189
|
+
return Math.min(amountOffCents, discountableSubtotalCents).toString();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
if (!hasDynamicPricing || !liveRate?.rate) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
const rateNum = Number(liveRate.rate);
|
|
198
|
+
if (rateNum <= 0) {
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
const discountableSubtotal = items.reduce((sum, item) => {
|
|
202
|
+
if (!item.discountable) {
|
|
203
|
+
return sum;
|
|
204
|
+
}
|
|
205
|
+
const price = item.upsell_price || item.price;
|
|
206
|
+
if (trialing && price?.type === "recurring") {
|
|
207
|
+
return sum;
|
|
208
|
+
}
|
|
209
|
+
if (price?.type === "recurring" && price?.recurring?.usage_type === "metered") {
|
|
210
|
+
return sum;
|
|
211
|
+
}
|
|
212
|
+
const baseAmount = price?.base_amount;
|
|
213
|
+
if (!baseAmount) {
|
|
214
|
+
return sum;
|
|
215
|
+
}
|
|
216
|
+
const quantity = item.quantity || 1;
|
|
217
|
+
const tokenAmount = Number(baseAmount) * quantity / rateNum;
|
|
218
|
+
return sum + tokenAmount;
|
|
219
|
+
}, 0);
|
|
220
|
+
if (discountableSubtotal <= 0) {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
if (couponDetails.percent_off && couponDetails.percent_off > 0) {
|
|
224
|
+
const discountAmount = discountableSubtotal * couponDetails.percent_off / 100;
|
|
225
|
+
const discountAmountUnit = (0, _util.fromTokenToUnit)(discountAmount.toFixed(currency.decimal || 8), currency.decimal || 8);
|
|
226
|
+
return discountAmountUnit.toString();
|
|
227
|
+
}
|
|
228
|
+
if (couponDetails.amount_off) {
|
|
229
|
+
const amountOff = couponDetails.currency_id === currency.id ? couponDetails.amount_off : couponDetails.currency_options?.[currency.id]?.amount_off;
|
|
230
|
+
if (!amountOff) {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
const subtotalUnit = (0, _util.fromTokenToUnit)(discountableSubtotal.toFixed(currency.decimal || 8), currency.decimal || 8);
|
|
234
|
+
const amountOffBN = new _util.BN(amountOff);
|
|
235
|
+
const subtotalBN = new _util.BN(subtotalUnit);
|
|
236
|
+
return _util.BN.min(amountOffBN, subtotalBN).toString();
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}, [isStripePayment, hasDynamicPricing, liveRate?.rate, discounts, items, trialing, currency.decimal, currency.id]);
|
|
240
|
+
const rateDisplay = (0, _react.useMemo)(() => {
|
|
241
|
+
if (!rateInfo.exchangeRate) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
const formattedRate = (0, _util2.formatExchangeRate)(rateInfo.exchangeRate);
|
|
245
|
+
if (!formattedRate) {
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
if (rateInfo.baseCurrency === "USD") {
|
|
249
|
+
return `$${formattedRate}`;
|
|
250
|
+
}
|
|
251
|
+
return `${formattedRate} ${rateInfo.baseCurrency}`;
|
|
252
|
+
}, [rateInfo]);
|
|
253
|
+
const quoteLockedAt = (0, _react.useMemo)(() => extractQuoteLockedAt(paymentIntent, checkoutSession), [paymentIntent, checkoutSession]);
|
|
254
|
+
const isPriceLocked = (0, _react.useMemo)(() => {
|
|
255
|
+
if (!quoteLockedAt) return false;
|
|
256
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
257
|
+
const lockRemaining = quoteLockedAt + 180 - now;
|
|
258
|
+
return lockRemaining > 0;
|
|
259
|
+
}, [quoteLockedAt]);
|
|
260
|
+
const lockExpired = (0, _react.useMemo)(() => {
|
|
261
|
+
if (!quoteLockedAt) return false;
|
|
262
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
263
|
+
const lockRemaining = quoteLockedAt + 180 - now;
|
|
264
|
+
return lockRemaining <= 0;
|
|
265
|
+
}, [quoteLockedAt]);
|
|
266
|
+
const currentSlippagePercent = (0, _react.useMemo)(() => {
|
|
267
|
+
let slippageValue = quoteMeta?.slippagePercent;
|
|
268
|
+
if (slippageValue === null || slippageValue === void 0) {
|
|
269
|
+
slippageValue = slippageConfig?.percent ?? checkoutSession?.metadata?.slippage?.percent ?? checkoutSession?.slippage_percent;
|
|
270
|
+
}
|
|
271
|
+
if (slippageValue === null || slippageValue === void 0) {
|
|
272
|
+
slippageValue = 0.5;
|
|
273
|
+
}
|
|
274
|
+
return slippageValue;
|
|
275
|
+
}, [quoteMeta?.slippagePercent, slippageConfig?.percent, checkoutSession]);
|
|
276
|
+
const providerDisplay = (0, _react.useMemo)(() => {
|
|
277
|
+
const fallback = "\u2014";
|
|
278
|
+
if (liveRate?.provider_display) {
|
|
279
|
+
return liveRate.provider_display;
|
|
280
|
+
}
|
|
281
|
+
if (!rateInfo.providerName && !rateInfo.providerId) {
|
|
282
|
+
return fallback;
|
|
283
|
+
}
|
|
284
|
+
return rateInfo.providerName || rateInfo.providerId || fallback;
|
|
285
|
+
}, [liveRate?.provider_display, rateInfo.providerName, rateInfo.providerId]);
|
|
286
|
+
const formatTotalDisplay = (totalAmountValue, fallback = "\u2014") => {
|
|
287
|
+
if (hasDynamicPricing && calculatedTokenAmount) {
|
|
288
|
+
const displayAmount = (0, _util.fromUnitToToken)(calculatedTokenAmount, currency.decimal);
|
|
289
|
+
const numericValue2 = Number(displayAmount);
|
|
290
|
+
if (Number.isFinite(numericValue2)) {
|
|
291
|
+
return (0, _util2.formatDynamicPrice)(displayAmount, true, 6);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (hasDynamicPricing && liveQuoteSnapshot?.quoted_amount) {
|
|
295
|
+
const snapshotAmount = (0, _util.fromUnitToToken)(liveQuoteSnapshot.quoted_amount, currency.decimal);
|
|
296
|
+
const numericValue2 = Number(snapshotAmount);
|
|
297
|
+
if (Number.isFinite(numericValue2)) {
|
|
298
|
+
return (0, _util2.formatDynamicPrice)(snapshotAmount, true, 6);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
if (totalAmountValue === null || totalAmountValue === void 0 || totalAmountValue === "") {
|
|
302
|
+
return fallback;
|
|
303
|
+
}
|
|
304
|
+
const numericValue = Number(totalAmountValue);
|
|
305
|
+
if (!Number.isFinite(numericValue)) {
|
|
306
|
+
return fallback;
|
|
307
|
+
}
|
|
308
|
+
return (0, _util2.formatDynamicPrice)(totalAmountValue, hasDynamicPricing, 6);
|
|
309
|
+
};
|
|
310
|
+
const calculateUsdDisplay = totalAmountValue => {
|
|
311
|
+
const fallback = "\u2014";
|
|
312
|
+
if (!hasDynamicPricing || !totalAmountValue || totalAmountValue === fallback) {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
const numericValue = Number(totalAmountValue);
|
|
316
|
+
if (!Number.isFinite(numericValue) || numericValue < 0) {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
if (numericValue === 0) {
|
|
320
|
+
return (0, _util2.formatUsdAmount)("0", locale);
|
|
321
|
+
}
|
|
322
|
+
const {
|
|
323
|
+
exchangeRate
|
|
324
|
+
} = rateInfo;
|
|
325
|
+
if (!exchangeRate) {
|
|
326
|
+
return null;
|
|
327
|
+
}
|
|
328
|
+
const totalAmountInUnits = (0, _util.fromTokenToUnit)(totalAmountValue, currency.decimal);
|
|
329
|
+
const totalUsd = (0, _util2.getUsdAmountFromTokenUnits)(new _util.BN(totalAmountInUnits), currency.decimal, exchangeRate);
|
|
330
|
+
if (!totalUsd) {
|
|
331
|
+
return null;
|
|
332
|
+
}
|
|
333
|
+
return (0, _util2.formatUsdAmount)(totalUsd, locale);
|
|
334
|
+
};
|
|
335
|
+
const buildQuoteDetailRows = t => {
|
|
336
|
+
if (!hasDynamicPricing) {
|
|
337
|
+
return [];
|
|
338
|
+
}
|
|
339
|
+
const rows = [];
|
|
340
|
+
const fallback = "\u2014";
|
|
341
|
+
if (rateDisplay) {
|
|
342
|
+
const displayTimestamp = rateInfo.fetchedAt || rateInfo.timestampMs;
|
|
343
|
+
const updatedAt = displayTimestamp ? (0, _util2.formatDateTime)(displayTimestamp) : fallback;
|
|
344
|
+
rows.push({
|
|
345
|
+
label: t("payment.checkout.quote.detailProvider"),
|
|
346
|
+
value: providerDisplay
|
|
347
|
+
}, {
|
|
348
|
+
label: t("payment.checkout.quote.detailUpdatedAt"),
|
|
349
|
+
value: updatedAt
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
if (isSubscription) {
|
|
353
|
+
let slippageDisplay = `${currentSlippagePercent}%`;
|
|
354
|
+
if (slippageConfig?.mode === "rate" && slippageConfig.min_acceptable_rate) {
|
|
355
|
+
const formattedRate = (0, _util2.formatExchangeRate)(slippageConfig.min_acceptable_rate);
|
|
356
|
+
const displayRate = formattedRate || slippageConfig.min_acceptable_rate;
|
|
357
|
+
const displayCurrency = slippageConfig.base_currency || rateInfo.baseCurrency || "USD";
|
|
358
|
+
const displayText = displayCurrency === "USD" ? `$${displayRate}` : `${displayRate} ${displayCurrency}`;
|
|
359
|
+
slippageDisplay = `${displayText}`;
|
|
360
|
+
}
|
|
361
|
+
rows.push({
|
|
362
|
+
label: t("payment.checkout.quote.detailSlippage"),
|
|
363
|
+
value: slippageDisplay,
|
|
364
|
+
isSlippage: true,
|
|
365
|
+
tooltip: t("payment.checkout.quote.slippage.tooltip")
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
return rows;
|
|
369
|
+
};
|
|
370
|
+
return {
|
|
371
|
+
// Flags
|
|
372
|
+
hasDynamicPricing,
|
|
373
|
+
isPriceLocked,
|
|
374
|
+
lockExpired,
|
|
375
|
+
// Data
|
|
376
|
+
quoteMeta,
|
|
377
|
+
rateInfo,
|
|
378
|
+
quoteLockedAt,
|
|
379
|
+
calculatedTokenAmount,
|
|
380
|
+
calculatedDiscountAmount,
|
|
381
|
+
currentSlippagePercent,
|
|
382
|
+
// Display helpers
|
|
383
|
+
rateDisplay,
|
|
384
|
+
providerDisplay,
|
|
385
|
+
formatTotalDisplay,
|
|
386
|
+
calculateUsdDisplay,
|
|
387
|
+
buildQuoteDetailRows
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
module.exports = useDynamicPricing;
|
package/lib/index.d.ts
CHANGED
|
@@ -26,6 +26,8 @@ import StripeForm from './payment/form/stripe';
|
|
|
26
26
|
import Payment from './payment/index';
|
|
27
27
|
import ProductSkeleton from './payment/product-skeleton';
|
|
28
28
|
import PaymentSummary from './payment/summary';
|
|
29
|
+
import PromotionSection from './payment/summary-section/promotion-section';
|
|
30
|
+
import TotalSection from './payment/summary-section/total-section';
|
|
29
31
|
import PricingItem from './components/pricing-item';
|
|
30
32
|
import CountrySelect from './components/country-select';
|
|
31
33
|
import TruncatedText from './components/truncated-text';
|
|
@@ -42,6 +44,8 @@ import AutoTopup from './components/auto-topup';
|
|
|
42
44
|
import Collapse from './components/collapse';
|
|
43
45
|
import PromotionCode from './components/promotion-code';
|
|
44
46
|
import SourceDataViewer from './components/source-data-viewer';
|
|
47
|
+
import SlippageConfig from './components/slippage-config';
|
|
48
|
+
import DynamicPricingUnavailable from './components/dynamic-pricing-unavailable';
|
|
45
49
|
export { PaymentThemeProvider } from './theme';
|
|
46
50
|
export * from './libs/util';
|
|
47
51
|
export * from './libs/connect';
|
|
@@ -56,6 +60,7 @@ export * from './hooks/scroll';
|
|
|
56
60
|
export * from './hooks/keyboard';
|
|
57
61
|
export * from './libs/validator';
|
|
58
62
|
export { translations, createTranslator } from './locales';
|
|
59
|
-
export { createLazyComponent, api, dayjs, FormInput, FormLabel, PhoneInput, AddressForm, StripeForm, Status, Livemode, Switch, ConfirmDialog, CheckoutForm, CheckoutTable, CheckoutDonate, CurrencySelector, Payment, PaymentSummary, PricingTable, ProductSkeleton, Amount, CustomerInvoiceList, CustomerPaymentList, TxLink, TxGas, SafeGuard, PricingItem, CountrySelect, Table, TruncatedText, Link, OverdueInvoicePayment, StripePaymentAction, PaymentBeneficiaries, LoadingButton, DonateDetails, ResumeSubscription, CreditGrantsList, CreditTransactionsList, DateRangePicker, CreditStatusChip, AutoTopupModal, AutoTopup, Collapse, PromotionCode, SourceDataViewer, };
|
|
63
|
+
export { createLazyComponent, api, dayjs, FormInput, FormLabel, PhoneInput, AddressForm, StripeForm, Status, Livemode, Switch, ConfirmDialog, CheckoutForm, CheckoutTable, CheckoutDonate, CurrencySelector, Payment, PaymentSummary, PricingTable, ProductSkeleton, Amount, CustomerInvoiceList, CustomerPaymentList, TxLink, TxGas, SafeGuard, PricingItem, CountrySelect, Table, TruncatedText, Link, OverdueInvoicePayment, StripePaymentAction, PaymentBeneficiaries, LoadingButton, DonateDetails, ResumeSubscription, CreditGrantsList, CreditTransactionsList, DateRangePicker, CreditStatusChip, AutoTopupModal, AutoTopup, Collapse, PromotionCode, SourceDataViewer, SlippageConfig, DynamicPricingUnavailable, PromotionSection, TotalSection, };
|
|
60
64
|
export type { CountrySelectProps } from './components/country-select';
|
|
61
65
|
export type { StripePaymentActionProps } from './components/stripe-payment-action';
|
|
66
|
+
export type { SlippageConfigValue, SlippageConfigProps } from './components/slippage-config';
|
package/lib/index.js
CHANGED
|
@@ -34,6 +34,8 @@ var _exportNames = {
|
|
|
34
34
|
Payment: true,
|
|
35
35
|
ProductSkeleton: true,
|
|
36
36
|
PaymentSummary: true,
|
|
37
|
+
PromotionSection: true,
|
|
38
|
+
TotalSection: true,
|
|
37
39
|
PricingItem: true,
|
|
38
40
|
CountrySelect: true,
|
|
39
41
|
TruncatedText: true,
|
|
@@ -50,6 +52,8 @@ var _exportNames = {
|
|
|
50
52
|
Collapse: true,
|
|
51
53
|
PromotionCode: true,
|
|
52
54
|
SourceDataViewer: true,
|
|
55
|
+
SlippageConfig: true,
|
|
56
|
+
DynamicPricingUnavailable: true,
|
|
53
57
|
PaymentThemeProvider: true,
|
|
54
58
|
translations: true,
|
|
55
59
|
createTranslator: true
|
|
@@ -162,6 +166,12 @@ Object.defineProperty(exports, "DonateDetails", {
|
|
|
162
166
|
return _donate.DonateDetails;
|
|
163
167
|
}
|
|
164
168
|
});
|
|
169
|
+
Object.defineProperty(exports, "DynamicPricingUnavailable", {
|
|
170
|
+
enumerable: true,
|
|
171
|
+
get: function () {
|
|
172
|
+
return _dynamicPricingUnavailable.default;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
165
175
|
Object.defineProperty(exports, "FormInput", {
|
|
166
176
|
enumerable: true,
|
|
167
177
|
get: function () {
|
|
@@ -252,6 +262,12 @@ Object.defineProperty(exports, "PromotionCode", {
|
|
|
252
262
|
return _promotionCode.default;
|
|
253
263
|
}
|
|
254
264
|
});
|
|
265
|
+
Object.defineProperty(exports, "PromotionSection", {
|
|
266
|
+
enumerable: true,
|
|
267
|
+
get: function () {
|
|
268
|
+
return _promotionSection.default;
|
|
269
|
+
}
|
|
270
|
+
});
|
|
255
271
|
Object.defineProperty(exports, "ResumeSubscription", {
|
|
256
272
|
enumerable: true,
|
|
257
273
|
get: function () {
|
|
@@ -264,6 +280,12 @@ Object.defineProperty(exports, "SafeGuard", {
|
|
|
264
280
|
return _safeGuard.default;
|
|
265
281
|
}
|
|
266
282
|
});
|
|
283
|
+
Object.defineProperty(exports, "SlippageConfig", {
|
|
284
|
+
enumerable: true,
|
|
285
|
+
get: function () {
|
|
286
|
+
return _slippageConfig.default;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
267
289
|
Object.defineProperty(exports, "SourceDataViewer", {
|
|
268
290
|
enumerable: true,
|
|
269
291
|
get: function () {
|
|
@@ -300,6 +322,12 @@ Object.defineProperty(exports, "Table", {
|
|
|
300
322
|
return _table2.default;
|
|
301
323
|
}
|
|
302
324
|
});
|
|
325
|
+
Object.defineProperty(exports, "TotalSection", {
|
|
326
|
+
enumerable: true,
|
|
327
|
+
get: function () {
|
|
328
|
+
return _totalSection.default;
|
|
329
|
+
}
|
|
330
|
+
});
|
|
303
331
|
Object.defineProperty(exports, "TruncatedText", {
|
|
304
332
|
enumerable: true,
|
|
305
333
|
get: function () {
|
|
@@ -376,6 +404,8 @@ var _stripe = _interopRequireDefault(require("./payment/form/stripe"));
|
|
|
376
404
|
var _index = _interopRequireDefault(require("./payment/index"));
|
|
377
405
|
var _productSkeleton = _interopRequireDefault(require("./payment/product-skeleton"));
|
|
378
406
|
var _summary = _interopRequireDefault(require("./payment/summary"));
|
|
407
|
+
var _promotionSection = _interopRequireDefault(require("./payment/summary-section/promotion-section"));
|
|
408
|
+
var _totalSection = _interopRequireDefault(require("./payment/summary-section/total-section"));
|
|
379
409
|
var _pricingItem = _interopRequireDefault(require("./components/pricing-item"));
|
|
380
410
|
var _countrySelect = _interopRequireDefault(require("./components/country-select"));
|
|
381
411
|
var _truncatedText = _interopRequireDefault(require("./components/truncated-text"));
|
|
@@ -392,6 +422,8 @@ var _autoTopup = _interopRequireDefault(require("./components/auto-topup"));
|
|
|
392
422
|
var _collapse = _interopRequireDefault(require("./components/collapse"));
|
|
393
423
|
var _promotionCode = _interopRequireDefault(require("./components/promotion-code"));
|
|
394
424
|
var _sourceDataViewer = _interopRequireDefault(require("./components/source-data-viewer"));
|
|
425
|
+
var _slippageConfig = _interopRequireDefault(require("./components/slippage-config"));
|
|
426
|
+
var _dynamicPricingUnavailable = _interopRequireDefault(require("./components/dynamic-pricing-unavailable"));
|
|
395
427
|
var _theme = require("./theme");
|
|
396
428
|
var _util = require("./libs/util");
|
|
397
429
|
Object.keys(_util).forEach(function (key) {
|
package/lib/libs/util.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { PaymentDetails, PriceCurrency, PriceRecurring, TCoupon, TInvoiceExpanded, TLineItemExpanded, TPaymentCurrency, TPaymentCurrencyExpanded, TPaymentMethod, TPaymentMethodExpanded, TPrice, TProductExpanded, TSubscriptionExpanded, TSubscriptionItemExpanded } from '@blocklet/payment-types';
|
|
2
|
+
import { BN } from '@ocap/util';
|
|
2
3
|
import type { ActionProps, PricingRenderProps } from '../types';
|
|
3
4
|
export declare const PAYMENT_KIT_DID = "z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk";
|
|
4
5
|
/**
|
|
@@ -38,15 +39,45 @@ export declare function formatCreditAmount(formattedAmount: string, currencySymb
|
|
|
38
39
|
*/
|
|
39
40
|
export declare function formatCreditForCheckout(formattedAmount: string, currencySymbol: string, locale?: string, showUnit?: boolean): string;
|
|
40
41
|
export declare function formatNumber(n: number | string, precision?: number, trim?: boolean, thousandSeparated?: boolean): string;
|
|
42
|
+
export declare function formatDynamicPrice(n: number | string, isDynamic: boolean, precision?: number, trim?: boolean, thousandSeparated?: boolean): string;
|
|
43
|
+
export declare function getUsdAmountFromBaseAmount(amount: string | number | undefined, quantity: number, scale?: number): string | null;
|
|
44
|
+
export declare function getUsdAmountFromTokenUnits(tokenAmount: BN | string, tokenDecimals: number, exchangeRate?: string | null): string | null;
|
|
45
|
+
export declare function formatUsdAmount(amount: string | null, locale?: string): string | null;
|
|
46
|
+
export declare function formatExchangeRate(amount: string | null): string | null;
|
|
47
|
+
/**
|
|
48
|
+
* Format exchange rate with currency symbol for display
|
|
49
|
+
* @param rate - The exchange rate value
|
|
50
|
+
* @param currency - The currency code (default: 'USD')
|
|
51
|
+
* @param decimals - Number of decimal places (default: 2, use 4 for live exchange rates)
|
|
52
|
+
* @returns Formatted string like "$0.12" for USD, or "0.12 EUR" for other currencies
|
|
53
|
+
*/
|
|
54
|
+
export declare function formatExchangeRateDisplay(rate: string | number | null | undefined, currency?: string, decimals?: number): string | null;
|
|
41
55
|
export declare const formatPrice: (price: TPrice, currency: TPaymentCurrency, unit_label?: string, quantity?: number, bn?: boolean, locale?: string) => string;
|
|
42
|
-
export declare const formatPriceAmount: (price: TPrice, currency: TPaymentCurrency, unit_label?: string, quantity?: number, bn?: boolean) => string;
|
|
56
|
+
export declare const formatPriceAmount: (price: TPrice, currency: TPaymentCurrency, unit_label?: string, quantity?: number, bn?: boolean, locale?: string) => string;
|
|
43
57
|
export declare function getStatementDescriptor(items: any[]): any;
|
|
44
58
|
export declare function formatRecurring(recurring: PriceRecurring, translate?: boolean, separator?: string, locale?: string): string;
|
|
45
59
|
export declare function getPriceUintAmountByCurrency(price: TPrice, currency: TPaymentCurrency): string;
|
|
46
60
|
export declare function getPriceCurrencyOptions(price: TPrice): PriceCurrency[];
|
|
47
|
-
export declare function
|
|
61
|
+
export declare function getLineItemAmounts(item: TLineItemExpanded, currency: TPaymentCurrency, { useUpsell, exchangeRate }?: {
|
|
62
|
+
useUpsell?: boolean;
|
|
63
|
+
exchangeRate?: string | null;
|
|
64
|
+
}): {
|
|
65
|
+
unitAmount: BN;
|
|
66
|
+
totalAmount: BN;
|
|
67
|
+
isDynamicQuote: boolean;
|
|
68
|
+
};
|
|
69
|
+
export type QuoteLockInfo = {
|
|
70
|
+
baseAmount: string;
|
|
71
|
+
baseCurrency: string;
|
|
72
|
+
tokenAmount: string;
|
|
73
|
+
tokenSymbol: string;
|
|
74
|
+
expiresAt: number | null;
|
|
75
|
+
};
|
|
76
|
+
export declare function getQuoteLockInfo(items: TLineItemExpanded[], currency?: TPaymentCurrency | null): QuoteLockInfo | null;
|
|
77
|
+
export declare function formatLineItemPricing(item: TLineItemExpanded, currency: TPaymentCurrency, { trialEnd, trialInDays, exchangeRate, }: {
|
|
48
78
|
trialEnd: number;
|
|
49
79
|
trialInDays: number;
|
|
80
|
+
exchangeRate?: string | null;
|
|
50
81
|
}, locale?: string): {
|
|
51
82
|
primary: string;
|
|
52
83
|
secondary?: string;
|
|
@@ -58,7 +89,9 @@ export declare function getRefundStatusColor(status: string): "default" | "succe
|
|
|
58
89
|
export declare function getPayoutStatusColor(status: string): "default" | "success" | "warning";
|
|
59
90
|
export declare function getInvoiceStatusColor(status: string): "default" | "success" | "warning" | "secondary";
|
|
60
91
|
export declare function getWebhookStatusColor(status: string): "default" | "success";
|
|
61
|
-
export declare function getCheckoutAmount(items: TLineItemExpanded[], currency: TPaymentCurrency, trialing?: boolean, upsell?: boolean
|
|
92
|
+
export declare function getCheckoutAmount(items: TLineItemExpanded[], currency: TPaymentCurrency, trialing?: boolean, upsell?: boolean, { exchangeRate }?: {
|
|
93
|
+
exchangeRate?: string | null;
|
|
94
|
+
}): {
|
|
62
95
|
subtotal: any;
|
|
63
96
|
total: any;
|
|
64
97
|
renew: string;
|
|
@@ -69,6 +102,7 @@ export declare function getCheckoutAmount(items: TLineItemExpanded[], currency:
|
|
|
69
102
|
export declare function getRecurringPeriod(recurring: PriceRecurring): number;
|
|
70
103
|
export declare function formatUpsellSaving(items: TLineItemExpanded[], currency: TPaymentCurrency): string;
|
|
71
104
|
export declare function formatMeteredThen(subscription: string, recurring: string, hasMetered: boolean, locale?: string): string;
|
|
105
|
+
export declare function formatThenValue(subscription: string, recurring: string, hasMetered: boolean, locale?: string): string;
|
|
72
106
|
export declare function formatPriceDisplay({ amount, then, actualAmount, showThen }: {
|
|
73
107
|
amount: string;
|
|
74
108
|
then?: string;
|
|
@@ -86,16 +120,19 @@ export declare function getFreeTrialTime({ trialInDays, trialEnd }: {
|
|
|
86
120
|
export declare function formatCheckoutHeadlines(items: TLineItemExpanded[], currency: TPaymentCurrency, { trialInDays, trialEnd }: {
|
|
87
121
|
trialInDays: number;
|
|
88
122
|
trialEnd: number;
|
|
89
|
-
}, locale?: string
|
|
123
|
+
}, locale?: string, { exchangeRate }?: {
|
|
124
|
+
exchangeRate?: string | null;
|
|
125
|
+
}): {
|
|
90
126
|
action: string;
|
|
91
127
|
amount: string;
|
|
92
128
|
then?: string;
|
|
129
|
+
thenValue?: string;
|
|
93
130
|
secondary?: string;
|
|
94
131
|
showThen?: boolean;
|
|
95
132
|
actualAmount: string;
|
|
96
133
|
priceDisplay: string;
|
|
97
134
|
};
|
|
98
|
-
export declare function formatAmount(amount: string, decimals: number): string;
|
|
135
|
+
export declare function formatAmount(amount: string, decimals: number, precision?: number): string;
|
|
99
136
|
export declare function findCurrency(methods: TPaymentMethodExpanded[], currencyId: string): TPaymentCurrencyExpanded | null;
|
|
100
137
|
export declare function isValidCountry(code: string): boolean;
|
|
101
138
|
export declare function stopEvent(e: React.SyntheticEvent<any>): void;
|