payment-kit 1.13.111 → 1.13.113
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/api/src/libs/notification/template/subscription-trial-will-end.ts +1 -1
- package/api/src/libs/notification/template/subscription-will-renew.ts +1 -1
- package/api/src/routes/products.ts +1 -0
- package/api/src/routes/subscriptions.ts +2 -0
- package/blocklet.yml +1 -1
- package/package.json +6 -6
- package/src/components/actions.tsx +1 -2
- package/src/components/blockchain/tx.tsx +1 -1
- package/src/components/click-boundary.tsx +1 -2
- package/src/components/customer/actions.tsx +2 -3
- package/src/components/customer/edit.tsx +3 -3
- package/src/components/customer/form.tsx +22 -24
- package/src/components/drawer-form.tsx +2 -4
- package/src/components/event/list.tsx +2 -3
- package/src/components/invoice/action.tsx +2 -3
- package/src/components/invoice/list.tsx +3 -4
- package/src/components/invoice/table.tsx +7 -7
- package/src/components/metadata/form.tsx +3 -4
- package/src/components/passport/actions.tsx +1 -2
- package/src/components/passport/assign.tsx +1 -1
- package/src/components/payment-intent/actions.tsx +2 -3
- package/src/components/payment-intent/list.tsx +3 -4
- package/src/components/payment-link/actions.tsx +2 -3
- package/src/components/payment-link/item.tsx +3 -5
- package/src/components/payment-link/product-select.tsx +3 -4
- package/src/components/payment-link/rename.tsx +3 -4
- package/src/components/payment-method/arcblock.tsx +1 -2
- package/src/components/payment-method/bitcoin.tsx +1 -2
- package/src/components/payment-method/ethereum.tsx +1 -2
- package/src/components/payment-method/stripe.tsx +1 -2
- package/src/components/portal/invoice/list.tsx +4 -5
- package/src/components/portal/subscription/actions.tsx +7 -8
- package/src/components/portal/subscription/cancel.tsx +13 -12
- package/src/components/portal/subscription/list.tsx +4 -10
- package/src/components/price/currency-select.tsx +2 -2
- package/src/components/price/form.tsx +3 -4
- package/src/components/price/upsell-select.tsx +2 -3
- package/src/components/price/upsell.tsx +3 -5
- package/src/components/pricing-table/actions.tsx +2 -3
- package/src/components/pricing-table/payment-settings.tsx +4 -4
- package/src/components/pricing-table/price-item.tsx +4 -7
- package/src/components/pricing-table/product-item.tsx +3 -3
- package/src/components/pricing-table/rename.tsx +3 -4
- package/src/components/product/actions.tsx +2 -3
- package/src/components/product/add-price.tsx +2 -2
- package/src/components/product/create.tsx +2 -4
- package/src/components/product/cross-sell-select.tsx +3 -3
- package/src/components/product/cross-sell.tsx +4 -5
- package/src/components/product/edit.tsx +1 -1
- package/src/components/product/form.tsx +6 -6
- package/src/components/relative-time.tsx +1 -2
- package/src/components/subscription/actions/cancel.tsx +2 -3
- package/src/components/subscription/actions/index.tsx +2 -3
- package/src/components/subscription/items/actions.tsx +1 -1
- package/src/components/subscription/items/index.tsx +2 -2
- package/src/components/subscription/items/usage-records.tsx +2 -3
- package/src/components/subscription/list.tsx +3 -4
- package/src/components/subscription/metrics.tsx +2 -2
- package/src/components/subscription/status.tsx +3 -3
- package/src/components/webhook/attempts.tsx +3 -3
- package/src/contexts/products.tsx +2 -3
- package/src/libs/api.ts +2 -19
- package/src/libs/dayjs.ts +1 -15
- package/src/libs/util.ts +24 -555
- package/src/locales/en.tsx +0 -139
- package/src/locales/index.tsx +5 -23
- package/src/locales/zh.tsx +0 -136
- package/src/pages/admin/billing/index.tsx +4 -2
- package/src/pages/admin/billing/invoices/detail.tsx +3 -4
- package/src/pages/admin/billing/subscriptions/detail.tsx +3 -3
- package/src/pages/admin/customers/customers/detail.tsx +3 -5
- package/src/pages/admin/customers/customers/index.tsx +2 -3
- package/src/pages/admin/customers/index.tsx +4 -2
- package/src/pages/admin/developers/events/detail.tsx +2 -3
- package/src/pages/admin/developers/index.tsx +4 -2
- package/src/pages/admin/developers/webhooks/detail.tsx +3 -4
- package/src/pages/admin/developers/webhooks/index.tsx +3 -4
- package/src/pages/admin/index.tsx +10 -7
- package/src/pages/admin/payments/index.tsx +4 -2
- package/src/pages/admin/payments/intents/detail.tsx +4 -6
- package/src/pages/admin/payments/links/create.tsx +3 -5
- package/src/pages/admin/payments/links/detail.tsx +4 -5
- package/src/pages/admin/payments/links/index.tsx +4 -6
- package/src/pages/admin/products/passports/index.tsx +1 -1
- package/src/pages/admin/products/prices/actions.tsx +2 -3
- package/src/pages/admin/products/prices/detail.tsx +2 -3
- package/src/pages/admin/products/prices/list.tsx +2 -4
- package/src/pages/admin/products/pricing-tables/create.tsx +2 -3
- package/src/pages/admin/products/pricing-tables/detail.tsx +4 -5
- package/src/pages/admin/products/pricing-tables/index.tsx +2 -4
- package/src/pages/admin/products/products/create.tsx +2 -4
- package/src/pages/admin/products/products/detail.tsx +4 -5
- package/src/pages/admin/products/products/index.tsx +4 -6
- package/src/pages/admin/settings/index.tsx +4 -2
- package/src/pages/admin/settings/payment-methods/create.tsx +3 -5
- package/src/pages/admin/settings/payment-methods/index.tsx +2 -3
- package/src/pages/checkout/index.tsx +6 -3
- package/src/pages/checkout/pay.tsx +5 -69
- package/src/pages/checkout/pricing-table.tsx +3 -7
- package/src/pages/customer/index.tsx +7 -7
- package/src/pages/customer/invoice.tsx +10 -11
- package/src/pages/customer/subscription/detail.tsx +4 -4
- package/src/pages/customer/subscription/update.tsx +19 -19
- package/src/components/checkout/amount.tsx +0 -24
- package/src/components/checkout/error.tsx +0 -30
- package/src/components/checkout/footer.tsx +0 -12
- package/src/components/checkout/form/address.tsx +0 -119
- package/src/components/checkout/form/index.tsx +0 -400
- package/src/components/checkout/form/phone.tsx +0 -103
- package/src/components/checkout/form/stripe.tsx +0 -195
- package/src/components/checkout/form/user-buttons.tsx +0 -24
- package/src/components/checkout/header.tsx +0 -40
- package/src/components/checkout/pay.tsx +0 -385
- package/src/components/checkout/pricing-table.tsx +0 -205
- package/src/components/checkout/product-card.tsx +0 -52
- package/src/components/checkout/product-item.tsx +0 -111
- package/src/components/checkout/skeleton/overview.tsx +0 -21
- package/src/components/checkout/skeleton/payment.tsx +0 -35
- package/src/components/checkout/success.tsx +0 -183
- package/src/components/checkout/summary.tsx +0 -198
- package/src/components/input.tsx +0 -58
- package/src/components/livemode.tsx +0 -23
- package/src/components/pricing-table/product-skeleton.tsx +0 -39
- package/src/components/status.tsx +0 -19
- package/src/components/switch.tsx +0 -48
- package/src/contexts/settings.tsx +0 -54
package/src/libs/util.ts
CHANGED
|
@@ -1,125 +1,27 @@
|
|
|
1
1
|
/* eslint-disable no-nested-ternary */
|
|
2
2
|
/* eslint-disable @typescript-eslint/indent */
|
|
3
|
+
import {
|
|
4
|
+
formatCheckoutHeadlines,
|
|
5
|
+
formatPrice,
|
|
6
|
+
getCheckoutAmount,
|
|
7
|
+
getPriceCurrencyOptions,
|
|
8
|
+
} from '@blocklet/payment-react';
|
|
3
9
|
import type {
|
|
4
10
|
LineItem,
|
|
5
|
-
PriceCurrency,
|
|
6
|
-
PriceRecurring,
|
|
7
|
-
TCheckoutSessionExpanded,
|
|
8
11
|
TLineItemExpanded,
|
|
9
12
|
TPaymentCurrency,
|
|
10
13
|
TPaymentLinkExpanded,
|
|
11
14
|
TPaymentMethodExpanded,
|
|
12
15
|
TPrice,
|
|
13
16
|
TProductExpanded,
|
|
14
|
-
TSubscriptionExpanded,
|
|
15
17
|
TSubscriptionItemExpanded,
|
|
16
|
-
} from '@
|
|
17
|
-
import { BN, fromUnitToToken } from '@ocap/util';
|
|
18
|
+
} from '@blocklet/payment-types';
|
|
18
19
|
import cloneDeep from 'lodash/cloneDeep';
|
|
19
20
|
import isEqual from 'lodash/isEqual';
|
|
20
|
-
import { defaultCountries } from 'react-international-phone';
|
|
21
21
|
|
|
22
22
|
import { t } from '../locales/index';
|
|
23
23
|
import dayjs from './dayjs';
|
|
24
24
|
|
|
25
|
-
export function getExplorerLink(chainHost: string, did: string, type: string) {
|
|
26
|
-
if (!chainHost) return undefined;
|
|
27
|
-
try {
|
|
28
|
-
const chainUrl = new URL(chainHost);
|
|
29
|
-
switch (type) {
|
|
30
|
-
case 'asset':
|
|
31
|
-
chainUrl.pathname = `/explorer/assets/${did}`;
|
|
32
|
-
break;
|
|
33
|
-
case 'account':
|
|
34
|
-
chainUrl.pathname = `/explorer/accounts/${did}`;
|
|
35
|
-
break;
|
|
36
|
-
case 'tx':
|
|
37
|
-
chainUrl.pathname = `/explorer/txs/${did}`;
|
|
38
|
-
break;
|
|
39
|
-
case 'token':
|
|
40
|
-
chainUrl.pathname = `/explorer/tokens/${did}`;
|
|
41
|
-
break;
|
|
42
|
-
case 'factory':
|
|
43
|
-
chainUrl.pathname = `/explorer/factories/${did}`;
|
|
44
|
-
break;
|
|
45
|
-
case 'bridge':
|
|
46
|
-
chainUrl.pathname = `/explorer/bridges/${did}`;
|
|
47
|
-
break;
|
|
48
|
-
default:
|
|
49
|
-
chainUrl.pathname = '/';
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return chainUrl.href;
|
|
53
|
-
} catch {
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function formatToDate(date: Date | string | number, locale = 'en') {
|
|
59
|
-
if (!date) {
|
|
60
|
-
return '-';
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return dayjs(date).locale(formatLocale(locale)).format('YYYY-MM-DD HH:mm:ss');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function formatToDatetime(date: Date | string | number, locale = 'en') {
|
|
67
|
-
if (!date) {
|
|
68
|
-
return '-';
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return dayjs(date).locale(formatLocale(locale)).format('lll');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export function formatTime(date: Date | string | number, format = 'YYYY-MM-DD HH:mm:ss', locale = 'en') {
|
|
75
|
-
if (!date) {
|
|
76
|
-
return '-';
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return dayjs(date).locale(formatLocale(locale)).format(format);
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function formatDateTime(date: Date | string | number, locale = 'en') {
|
|
83
|
-
return dayjs(date).locale(formatLocale(locale)).format('YYYY-MM-DD HH:mm');
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export const formatLocale = (locale = 'en') => {
|
|
87
|
-
if (locale === 'tw') {
|
|
88
|
-
return 'zh';
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return locale;
|
|
92
|
-
};
|
|
93
|
-
|
|
94
|
-
export const formatPrettyMsLocale = (locale: string) => (locale === 'zh' ? 'zh_CN' : 'en_US');
|
|
95
|
-
|
|
96
|
-
export const formatError = (err: any) => {
|
|
97
|
-
const { details, errors, response } = err;
|
|
98
|
-
|
|
99
|
-
// graphql error
|
|
100
|
-
if (Array.isArray(errors)) {
|
|
101
|
-
return errors.map((x) => x.message).join('\n');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
// joi validate error
|
|
105
|
-
if (Array.isArray(details)) {
|
|
106
|
-
const formatted = details.map((e) => {
|
|
107
|
-
const errorMessage = e.message.replace(/["]/g, "'");
|
|
108
|
-
const errorPath = e.path.join('.');
|
|
109
|
-
return `${errorPath}: ${errorMessage}`;
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
return `Validate failed: ${formatted.join(';')}`;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// axios error
|
|
116
|
-
if (response) {
|
|
117
|
-
return response.data.error || `${err.message}: ${JSON.stringify(response.data)}`;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return err.message;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
25
|
export const formatProductPrice = (
|
|
124
26
|
{ prices, unit_label }: { prices: TPrice[]; unit_label: string },
|
|
125
27
|
currency: TPaymentCurrency,
|
|
@@ -136,59 +38,6 @@ export const formatProductPrice = (
|
|
|
136
38
|
return t('admin.price.empty', locale);
|
|
137
39
|
};
|
|
138
40
|
|
|
139
|
-
export const formatPrice = (
|
|
140
|
-
price: TPrice,
|
|
141
|
-
currency: TPaymentCurrency,
|
|
142
|
-
unit_label?: string,
|
|
143
|
-
quantity: number = 1,
|
|
144
|
-
bn: boolean = true,
|
|
145
|
-
locale: string = 'en'
|
|
146
|
-
) => {
|
|
147
|
-
const unit = getPriceUintAmountByCurrency(price, currency);
|
|
148
|
-
const amount = bn
|
|
149
|
-
? fromUnitToToken(new BN(unit).mul(new BN(quantity)), currency.decimal).toString()
|
|
150
|
-
: +unit * quantity;
|
|
151
|
-
if (price?.type === 'recurring' && price.recurring) {
|
|
152
|
-
const recurring = formatRecurring(price.recurring, false, 'slash', locale);
|
|
153
|
-
|
|
154
|
-
if (unit_label) {
|
|
155
|
-
return `${amount} ${currency.symbol} / ${unit_label} ${recurring}`;
|
|
156
|
-
}
|
|
157
|
-
if (price.recurring.usage_type === 'metered') {
|
|
158
|
-
return `${amount} ${currency.symbol} / unit ${recurring}`;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
return `${amount} ${currency.symbol} ${recurring}`;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
return `${amount} ${currency.symbol}`;
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
export const formatPriceAmount = (
|
|
168
|
-
price: TPrice,
|
|
169
|
-
currency: TPaymentCurrency,
|
|
170
|
-
unit_label?: string,
|
|
171
|
-
quantity: number = 1,
|
|
172
|
-
bn: boolean = true
|
|
173
|
-
) => {
|
|
174
|
-
const unit = getPriceUintAmountByCurrency(price, currency);
|
|
175
|
-
const amount = bn
|
|
176
|
-
? fromUnitToToken(new BN(unit).mul(new BN(quantity)), currency.decimal).toString()
|
|
177
|
-
: +unit * quantity;
|
|
178
|
-
if (price?.type === 'recurring' && price.recurring) {
|
|
179
|
-
if (unit_label) {
|
|
180
|
-
return `${amount} ${currency.symbol} / ${unit_label}`;
|
|
181
|
-
}
|
|
182
|
-
if (price.recurring.usage_type === 'metered') {
|
|
183
|
-
return `${amount} ${currency.symbol} / unit`;
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return `${amount} ${currency.symbol}`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return `${amount} ${currency.symbol}`;
|
|
190
|
-
};
|
|
191
|
-
|
|
192
41
|
export function getPricingModel(price: TPrice) {
|
|
193
42
|
if (price.billing_scheme === 'tiered') {
|
|
194
43
|
return price.tiers_mode;
|
|
@@ -214,141 +63,6 @@ export function getPriceFromProducts(products: TProductExpanded[], priceId: stri
|
|
|
214
63
|
return product?.prices.find((x) => x.id === priceId);
|
|
215
64
|
}
|
|
216
65
|
|
|
217
|
-
export function getStatementDescriptor(items: any[]) {
|
|
218
|
-
for (const item of items) {
|
|
219
|
-
if (item.price?.product?.statement_descriptor) {
|
|
220
|
-
return item.price?.product?.statement_descriptor;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return window.blocklet.appName;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export function formatRecurring(
|
|
228
|
-
recurring: PriceRecurring,
|
|
229
|
-
translate: boolean = true,
|
|
230
|
-
separator: string = 'per',
|
|
231
|
-
locale: string = 'en'
|
|
232
|
-
) {
|
|
233
|
-
const intervals = {
|
|
234
|
-
hour: 'hourly',
|
|
235
|
-
day: 'daily',
|
|
236
|
-
week: 'weekly',
|
|
237
|
-
month: 'monthly',
|
|
238
|
-
year: 'yearly',
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
if (+recurring.interval_count === 1) {
|
|
242
|
-
const interval = t(`common.${recurring.interval}`, locale);
|
|
243
|
-
// @ts-ignore
|
|
244
|
-
return translate ? t(`common.${intervals[recurring.interval]}`, locale) : separator ? t(`common.${separator}`, locale, { interval }) : interval; // prettier-ignore
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return t('common.recurring', locale, {
|
|
248
|
-
count: recurring.interval_count,
|
|
249
|
-
interval: t(`common.${recurring.interval}s`, locale),
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
export function getPriceUintAmountByCurrency(price: TPrice, currency: TPaymentCurrency) {
|
|
254
|
-
const options = getPriceCurrencyOptions(price);
|
|
255
|
-
const option = options.find((x) => x.currency_id === currency.id);
|
|
256
|
-
if (option) {
|
|
257
|
-
return option.unit_amount;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
return price.unit_amount;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
export function getPriceCurrencyOptions(price: TPrice): PriceCurrency[] {
|
|
264
|
-
if (Array.isArray(price.currency_options)) {
|
|
265
|
-
return price.currency_options;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
return [{ currency_id: price.currency_id, unit_amount: price.unit_amount, tiers: null, custom_unit_amount: null }];
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
export function formatLineItemPricing(
|
|
272
|
-
item: TLineItemExpanded,
|
|
273
|
-
currency: TPaymentCurrency,
|
|
274
|
-
trial: number,
|
|
275
|
-
locale: string = 'en'
|
|
276
|
-
): { primary: string; secondary?: string; quantity: string } {
|
|
277
|
-
const price = item.upsell_price || item.price;
|
|
278
|
-
|
|
279
|
-
let quantity = t('common.qty', locale, { count: item.quantity });
|
|
280
|
-
if (price.recurring?.usage_type === 'metered' || +item.quantity === 1) {
|
|
281
|
-
quantity = '';
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
const total = `${fromUnitToToken(
|
|
285
|
-
new BN(getPriceUintAmountByCurrency(price, currency)).mul(new BN(item.quantity)),
|
|
286
|
-
currency.decimal
|
|
287
|
-
)} ${currency.symbol}`;
|
|
288
|
-
const unit = `${fromUnitToToken(getPriceUintAmountByCurrency(price, currency))} ${currency.symbol}`;
|
|
289
|
-
|
|
290
|
-
const appendUnit = (v: string, alt: string) => {
|
|
291
|
-
if (price.product.unit_label) {
|
|
292
|
-
return `${v}/${price.product.unit_label}`;
|
|
293
|
-
}
|
|
294
|
-
if (price.recurring?.usage_type === 'metered' || item.quantity === 1) {
|
|
295
|
-
return alt;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
return quantity ? t('common.each', locale, { unit }) : '';
|
|
299
|
-
};
|
|
300
|
-
|
|
301
|
-
if (price.type === 'recurring' && price.recurring) {
|
|
302
|
-
if (trial > 0) {
|
|
303
|
-
return {
|
|
304
|
-
primary: t('common.trial', locale, { count: trial }),
|
|
305
|
-
secondary: `${appendUnit(total, total)} ${formatRecurring(price.recurring, false, 'slash', locale)}`,
|
|
306
|
-
quantity,
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
return {
|
|
311
|
-
primary: total,
|
|
312
|
-
secondary: appendUnit(total, ''),
|
|
313
|
-
quantity,
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
return {
|
|
318
|
-
primary: total,
|
|
319
|
-
secondary: appendUnit(total, ''),
|
|
320
|
-
quantity,
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
export function getCheckoutAmount(
|
|
325
|
-
items: TLineItemExpanded[],
|
|
326
|
-
currency: TPaymentCurrency,
|
|
327
|
-
includeFreeTrial = false,
|
|
328
|
-
upsell = true
|
|
329
|
-
) {
|
|
330
|
-
let renew = new BN(0);
|
|
331
|
-
|
|
332
|
-
const total = items
|
|
333
|
-
.reduce((acc, x) => {
|
|
334
|
-
const price = upsell ? x.upsell_price || x.price : x.price;
|
|
335
|
-
if (price.type === 'recurring') {
|
|
336
|
-
renew = renew.add(new BN(getPriceUintAmountByCurrency(price, currency)).mul(new BN(x.quantity)));
|
|
337
|
-
|
|
338
|
-
if (includeFreeTrial) {
|
|
339
|
-
return acc;
|
|
340
|
-
}
|
|
341
|
-
if (price.recurring?.usage_type === 'metered') {
|
|
342
|
-
return acc;
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
return acc.add(new BN(getPriceUintAmountByCurrency(price, currency)).mul(new BN(x.quantity)));
|
|
346
|
-
}, new BN(0))
|
|
347
|
-
.toString();
|
|
348
|
-
|
|
349
|
-
return { subtotal: total, total, renew: renew.toString(), discount: '0', shipping: '0', tax: '0' };
|
|
350
|
-
}
|
|
351
|
-
|
|
352
66
|
export function formatPaymentLinkPricing(link: TPaymentLinkExpanded, currency: TPaymentCurrency) {
|
|
353
67
|
const amount = getCheckoutAmount(link.line_items, currency, !!link.subscription_data?.trial_period_days);
|
|
354
68
|
return formatCheckoutHeadlines(
|
|
@@ -365,176 +79,6 @@ export function formatPaymentLinkPricing(link: TPaymentLinkExpanded, currency: T
|
|
|
365
79
|
currency
|
|
366
80
|
);
|
|
367
81
|
}
|
|
368
|
-
export function getRecurringPeriod(recurring: PriceRecurring) {
|
|
369
|
-
const { interval } = recurring;
|
|
370
|
-
const count = +recurring.interval_count || 1;
|
|
371
|
-
const dayInMs = 24 * 60 * 60 * 1000;
|
|
372
|
-
|
|
373
|
-
switch (interval) {
|
|
374
|
-
case 'hour':
|
|
375
|
-
return 60 * 60 * 1000;
|
|
376
|
-
case 'day':
|
|
377
|
-
return count * dayInMs;
|
|
378
|
-
case 'week':
|
|
379
|
-
return count * 7 * dayInMs;
|
|
380
|
-
case 'month':
|
|
381
|
-
return count * 30 * dayInMs;
|
|
382
|
-
case 'year':
|
|
383
|
-
return count * 365 * dayInMs;
|
|
384
|
-
default:
|
|
385
|
-
throw new Error(`Unsupported recurring interval: ${interval}`);
|
|
386
|
-
}
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
export function formatUpsellSaving(session: TCheckoutSessionExpanded, currency: TPaymentCurrency) {
|
|
390
|
-
const items = session.line_items as TLineItemExpanded[];
|
|
391
|
-
if (items[0]?.upsell_price_id) {
|
|
392
|
-
return '0';
|
|
393
|
-
}
|
|
394
|
-
if (!items[0]?.price.upsell?.upsells_to) {
|
|
395
|
-
return '0';
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
const from = getCheckoutAmount(items, currency, false, false);
|
|
399
|
-
const to = getCheckoutAmount(
|
|
400
|
-
items.map((x) => ({
|
|
401
|
-
...x,
|
|
402
|
-
upsell_price_id: x.price.upsell?.upsells_to_id,
|
|
403
|
-
upsell_price: x.price.upsell?.upsells_to,
|
|
404
|
-
})) as TLineItemExpanded[],
|
|
405
|
-
currency,
|
|
406
|
-
false,
|
|
407
|
-
true
|
|
408
|
-
);
|
|
409
|
-
|
|
410
|
-
const fromRecurring = items[0].price?.recurring as PriceRecurring;
|
|
411
|
-
const toRecurring = items[0].price?.upsell?.upsells_to?.recurring as PriceRecurring;
|
|
412
|
-
if (!fromRecurring || !toRecurring) {
|
|
413
|
-
return '0';
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
const factor = Math.floor(getRecurringPeriod(toRecurring) / getRecurringPeriod(fromRecurring));
|
|
417
|
-
|
|
418
|
-
const before = new BN(from.total).mul(new BN(factor));
|
|
419
|
-
const after = new BN(to.total);
|
|
420
|
-
|
|
421
|
-
return Number(before.sub(after).mul(new BN(100)).div(before).toString()).toFixed(0);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
export function formatCheckoutHeadlines(
|
|
425
|
-
session: TCheckoutSessionExpanded,
|
|
426
|
-
currency: TPaymentCurrency,
|
|
427
|
-
locale: string = 'en'
|
|
428
|
-
): {
|
|
429
|
-
action: string;
|
|
430
|
-
amount: string;
|
|
431
|
-
then?: string;
|
|
432
|
-
secondary?: string;
|
|
433
|
-
} {
|
|
434
|
-
const items = session.line_items as TLineItemExpanded[];
|
|
435
|
-
const trial = session.subscription_data?.trial_period_days || 0;
|
|
436
|
-
|
|
437
|
-
const brand = getStatementDescriptor(items);
|
|
438
|
-
const { total } = getCheckoutAmount(items, currency, !!trial);
|
|
439
|
-
const amount = `${fromUnitToToken(total, currency.decimal)} ${currency.symbol}`;
|
|
440
|
-
|
|
441
|
-
// empty
|
|
442
|
-
if (items.length === 0) {
|
|
443
|
-
return {
|
|
444
|
-
action: t('checkout.empty', locale),
|
|
445
|
-
amount: '0',
|
|
446
|
-
then: '',
|
|
447
|
-
};
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
const { name } = items[0]?.price.product || { name: '' };
|
|
451
|
-
|
|
452
|
-
// all one time
|
|
453
|
-
if (items.every((x) => x.price.type === 'one_time')) {
|
|
454
|
-
const action = t('checkout.pay', locale, { payee: brand });
|
|
455
|
-
if (items.length > 1) {
|
|
456
|
-
return { action, amount };
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
return { action, amount, then: '' };
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
const item = items.find((x) => x.price.type === 'recurring');
|
|
463
|
-
const recurring = formatRecurring(
|
|
464
|
-
(item?.upsell_price || item?.price)?.recurring as PriceRecurring,
|
|
465
|
-
false,
|
|
466
|
-
'per',
|
|
467
|
-
locale
|
|
468
|
-
);
|
|
469
|
-
|
|
470
|
-
// all recurring
|
|
471
|
-
if (items.every((x) => x.price.type === 'recurring')) {
|
|
472
|
-
const hasMetered = items.some((x) => x.price.type === 'recurring' && x.price.recurring?.usage_type === 'metered');
|
|
473
|
-
const subscription = [
|
|
474
|
-
hasMetered ? t('checkout.least', locale) : '',
|
|
475
|
-
fromUnitToToken(
|
|
476
|
-
items.reduce((acc, x) => {
|
|
477
|
-
if (x.price.recurring?.usage_type === 'metered') {
|
|
478
|
-
return acc;
|
|
479
|
-
}
|
|
480
|
-
return acc.add(new BN(getPriceUintAmountByCurrency(x.price, currency)).mul(new BN(x.quantity)));
|
|
481
|
-
}, new BN(0)),
|
|
482
|
-
currency.decimal
|
|
483
|
-
),
|
|
484
|
-
currency.symbol,
|
|
485
|
-
]
|
|
486
|
-
.filter(Boolean)
|
|
487
|
-
.join(' ');
|
|
488
|
-
if (items.length > 1) {
|
|
489
|
-
if (trial > 0) {
|
|
490
|
-
return {
|
|
491
|
-
action: t('checkout.try2', locale, { name, count: items.length - 1 }),
|
|
492
|
-
amount: t('checkout.free', locale, { count: trial }),
|
|
493
|
-
then: t('checkout.then', locale, { subscription, recurring }),
|
|
494
|
-
};
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
return {
|
|
498
|
-
action: t('checkout.sub2', locale, { name, count: items.length - 1 }),
|
|
499
|
-
amount,
|
|
500
|
-
then: recurring,
|
|
501
|
-
};
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
if (trial > 0) {
|
|
505
|
-
return {
|
|
506
|
-
action: t('checkout.try1', locale, { name }),
|
|
507
|
-
amount: t('checkout.free', locale, { count: trial }),
|
|
508
|
-
then: t('checkout.then', locale, { subscription, recurring }),
|
|
509
|
-
};
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
return {
|
|
513
|
-
action: t('checkout.sub1', locale, { name }),
|
|
514
|
-
amount,
|
|
515
|
-
then: recurring,
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// mixed
|
|
520
|
-
const subscription = fromUnitToToken(
|
|
521
|
-
items
|
|
522
|
-
.filter((x) => x.price.type === 'recurring')
|
|
523
|
-
.reduce((acc, x) => {
|
|
524
|
-
if (x.price.recurring?.usage_type === 'metered') {
|
|
525
|
-
return acc;
|
|
526
|
-
}
|
|
527
|
-
return acc.add(new BN(getPriceUintAmountByCurrency(x.price, currency)).mul(new BN(x.quantity)));
|
|
528
|
-
}, new BN(0)),
|
|
529
|
-
currency.decimal
|
|
530
|
-
);
|
|
531
|
-
|
|
532
|
-
return {
|
|
533
|
-
action: t('checkout.pay', locale, { payee: brand }),
|
|
534
|
-
amount,
|
|
535
|
-
then: t('checkout.then', locale, { subscription: `${subscription} ${currency.symbol}`, recurring }),
|
|
536
|
-
};
|
|
537
|
-
}
|
|
538
82
|
|
|
539
83
|
export function getSubscriptionStatusColor(status: string) {
|
|
540
84
|
switch (status) {
|
|
@@ -666,53 +210,6 @@ export function formatSubscriptionProduct(items: TSubscriptionItemExpanded[], ma
|
|
|
666
210
|
);
|
|
667
211
|
}
|
|
668
212
|
|
|
669
|
-
export function formatAmount(amount: string, decimals: number) {
|
|
670
|
-
return fromUnitToToken(amount, decimals);
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
export function findCurrency(methods: TPaymentMethodExpanded[], currencyId: string) {
|
|
674
|
-
for (const method of methods) {
|
|
675
|
-
for (const currency of method.payment_currencies) {
|
|
676
|
-
if (currency.id === currencyId) {
|
|
677
|
-
return currency;
|
|
678
|
-
}
|
|
679
|
-
}
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
return null;
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
export function filterCurrencies(method: TPaymentMethodExpanded, hasSelected: (currency: any) => boolean) {
|
|
686
|
-
method.payment_currencies = method.payment_currencies.filter((x) => !hasSelected(x));
|
|
687
|
-
return method;
|
|
688
|
-
}
|
|
689
|
-
|
|
690
|
-
export function getSupportedPaymentMethods(methods: TPaymentMethodExpanded[], hasSelected: (currency: any) => boolean) {
|
|
691
|
-
const filtered = cloneDeep(methods).map((x) => filterCurrencies(x, hasSelected));
|
|
692
|
-
return filtered.filter((x) => x.payment_currencies.length);
|
|
693
|
-
}
|
|
694
|
-
|
|
695
|
-
export function getSupportedPaymentCurrencies(items: TLineItemExpanded[]) {
|
|
696
|
-
const currencies = items.reduce((acc, x: any) => {
|
|
697
|
-
return acc.concat(x.price.currency_options.map((c: any) => c.currency_id));
|
|
698
|
-
}, []);
|
|
699
|
-
return Array.from(new Set(currencies));
|
|
700
|
-
}
|
|
701
|
-
|
|
702
|
-
export function isValidCountry(code: string) {
|
|
703
|
-
return defaultCountries.some((x) => x[1] === code);
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
export function stopEvent(e: React.SyntheticEvent<any>) {
|
|
707
|
-
try {
|
|
708
|
-
e.stopPropagation();
|
|
709
|
-
e.preventDefault();
|
|
710
|
-
// eslint-disable-next-line no-empty
|
|
711
|
-
} catch {
|
|
712
|
-
// Do nothing
|
|
713
|
-
}
|
|
714
|
-
}
|
|
715
|
-
|
|
716
213
|
export function groupPricingTableItems(items: any[]) {
|
|
717
214
|
const grouped: { [key: string]: any[] } = {};
|
|
718
215
|
items.forEach((x: any, index) => {
|
|
@@ -726,54 +223,26 @@ export function groupPricingTableItems(items: any[]) {
|
|
|
726
223
|
return Object.entries(grouped);
|
|
727
224
|
}
|
|
728
225
|
|
|
729
|
-
export function sleep(ms: number) {
|
|
730
|
-
return new Promise((resolve) => {
|
|
731
|
-
setTimeout(resolve, ms);
|
|
732
|
-
});
|
|
733
|
-
}
|
|
734
|
-
|
|
735
226
|
export function isSuccessAttempt(code: number) {
|
|
736
227
|
return code >= 200 && code < 300;
|
|
737
228
|
}
|
|
738
229
|
|
|
739
|
-
export
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
lines.push(`will cancel on ${formatToDate(subscription.cancel_at * 1000)}`);
|
|
744
|
-
} else if (subscription.cancel_at_period_end) {
|
|
745
|
-
lines.push(`will cancel on ${formatToDate(subscription.current_period_end * 1000)}`);
|
|
746
|
-
} else {
|
|
747
|
-
lines.push(`will renew on ${formatToDate(subscription.current_period_end * 1000)}`);
|
|
748
|
-
}
|
|
749
|
-
} else if (subscription.status === 'past_due') {
|
|
750
|
-
lines.push(`will cancel on ${formatToDate(subscription.current_period_end * 1000)}`);
|
|
751
|
-
} else if (subscription.status === 'canceled') {
|
|
752
|
-
lines.push(`canceled on ${formatToDate(subscription.canceled_at * 1000)}`);
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
return lines.join(', ');
|
|
756
|
-
};
|
|
757
|
-
|
|
758
|
-
export const getSubscriptionAction = (subscription: TSubscriptionExpanded) => {
|
|
759
|
-
if (subscription.status !== 'canceled' && subscription.cancel_at_period_end) {
|
|
760
|
-
return { action: 'recover', variant: 'contained', color: 'primary' };
|
|
761
|
-
}
|
|
762
|
-
|
|
763
|
-
if (subscription.status === 'active' || subscription.status === 'trialing') {
|
|
764
|
-
if (subscription.cancel_at) {
|
|
765
|
-
return null;
|
|
766
|
-
}
|
|
767
|
-
if (subscription.cancel_at_period_end) {
|
|
768
|
-
return { action: 'recover', variant: 'contained', color: 'primary' };
|
|
769
|
-
}
|
|
770
|
-
|
|
771
|
-
return { action: 'cancel', variant: 'outlined', color: 'inherit' };
|
|
772
|
-
}
|
|
230
|
+
export function filterCurrencies(method: TPaymentMethodExpanded, hasSelected: (currency: TPaymentCurrency) => boolean) {
|
|
231
|
+
method.payment_currencies = method.payment_currencies.filter((x) => hasSelected(x));
|
|
232
|
+
return method;
|
|
233
|
+
}
|
|
773
234
|
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
235
|
+
export function getSupportedPaymentMethods(
|
|
236
|
+
methods: TPaymentMethodExpanded[],
|
|
237
|
+
hasSelected: (currency: TPaymentCurrency) => boolean
|
|
238
|
+
) {
|
|
239
|
+
const filtered = cloneDeep(methods).map((x) => filterCurrencies(x, hasSelected));
|
|
240
|
+
return filtered.filter((x) => x.payment_currencies.length);
|
|
241
|
+
}
|
|
777
242
|
|
|
778
|
-
|
|
779
|
-
|
|
243
|
+
export function getSupportedPaymentCurrencies(items: TLineItemExpanded[]) {
|
|
244
|
+
const currencies = items.reduce((acc: string[], x: any) => {
|
|
245
|
+
return acc.concat(getPriceCurrencyOptions(x.price).map((c: any) => c.currency_id));
|
|
246
|
+
}, []);
|
|
247
|
+
return Array.from(new Set(currencies));
|
|
248
|
+
}
|