@blocklet/payment-react 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.
Files changed (173) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +29 -0
  3. package/babel.config.es.js +8 -0
  4. package/build.config.ts +29 -0
  5. package/es/api.d.ts +2 -0
  6. package/es/api.js +18 -0
  7. package/es/checkout/index.d.ts +15 -0
  8. package/es/checkout/index.js +61 -0
  9. package/es/components/input.d.ts +23 -0
  10. package/es/components/input.js +44 -0
  11. package/es/components/livemode.d.ts +2 -0
  12. package/es/components/livemode.js +24 -0
  13. package/es/components/pricing-table.d.ts +18 -0
  14. package/es/components/pricing-table.js +175 -0
  15. package/es/components/status.d.ts +3 -0
  16. package/es/components/status.js +20 -0
  17. package/es/components/switch.d.ts +6 -0
  18. package/es/components/switch.js +42 -0
  19. package/es/contexts/payment.d.ts +29 -0
  20. package/es/contexts/payment.js +45 -0
  21. package/es/dayjs.d.ts +2 -0
  22. package/es/dayjs.js +14 -0
  23. package/es/index.d.ts +16 -0
  24. package/es/index.js +29 -0
  25. package/es/locales/en.d.ts +2 -0
  26. package/es/locales/en.js +213 -0
  27. package/es/locales/index.d.ts +10 -0
  28. package/es/locales/index.js +20 -0
  29. package/es/locales/zh.d.ts +2 -0
  30. package/es/locales/zh.js +213 -0
  31. package/es/payment/amount.d.ts +12 -0
  32. package/es/payment/amount.js +22 -0
  33. package/es/payment/error.d.ts +13 -0
  34. package/es/payment/error.js +12 -0
  35. package/es/payment/footer.d.ts +4 -0
  36. package/es/payment/footer.js +9 -0
  37. package/es/payment/form/addon.d.ts +2 -0
  38. package/es/payment/form/addon.js +14 -0
  39. package/es/payment/form/address.d.ts +7 -0
  40. package/es/payment/form/address.js +119 -0
  41. package/es/payment/form/index.d.ts +9 -0
  42. package/es/payment/form/index.js +337 -0
  43. package/es/payment/form/phone.d.ts +4 -0
  44. package/es/payment/form/phone.js +97 -0
  45. package/es/payment/form/stripe.d.ts +13 -0
  46. package/es/payment/form/stripe.js +158 -0
  47. package/es/payment/header.d.ts +7 -0
  48. package/es/payment/header.js +29 -0
  49. package/es/payment/index.d.ts +28 -0
  50. package/es/payment/index.js +327 -0
  51. package/es/payment/product-card.d.ts +21 -0
  52. package/es/payment/product-card.js +34 -0
  53. package/es/payment/product-item.d.ts +19 -0
  54. package/es/payment/product-item.js +107 -0
  55. package/es/payment/product-skeleton.d.ts +4 -0
  56. package/es/payment/product-skeleton.js +34 -0
  57. package/es/payment/skeleton/overview.d.ts +2 -0
  58. package/es/payment/skeleton/overview.js +13 -0
  59. package/es/payment/skeleton/payment.d.ts +2 -0
  60. package/es/payment/skeleton/payment.js +19 -0
  61. package/es/payment/success.d.ts +8 -0
  62. package/es/payment/success.js +164 -0
  63. package/es/payment/summary.d.ts +12 -0
  64. package/es/payment/summary.js +178 -0
  65. package/es/theme.d.ts +1 -0
  66. package/es/theme.js +17 -0
  67. package/es/types/index.d.ts +19 -0
  68. package/es/types/index.js +0 -0
  69. package/es/types/shims.d.ts +18 -0
  70. package/es/util.d.ts +52 -0
  71. package/es/util.js +390 -0
  72. package/lib/api.d.ts +2 -0
  73. package/lib/api.js +26 -0
  74. package/lib/checkout/index.d.ts +15 -0
  75. package/lib/checkout/index.js +83 -0
  76. package/lib/components/input.d.ts +23 -0
  77. package/lib/components/input.js +72 -0
  78. package/lib/components/livemode.d.ts +2 -0
  79. package/lib/components/livemode.js +29 -0
  80. package/lib/components/pricing-table.d.ts +18 -0
  81. package/lib/components/pricing-table.js +232 -0
  82. package/lib/components/status.d.ts +3 -0
  83. package/lib/components/status.js +23 -0
  84. package/lib/components/switch.d.ts +6 -0
  85. package/lib/components/switch.js +51 -0
  86. package/lib/contexts/payment.d.ts +29 -0
  87. package/lib/contexts/payment.js +73 -0
  88. package/lib/dayjs.d.ts +2 -0
  89. package/lib/dayjs.js +21 -0
  90. package/lib/index.d.ts +16 -0
  91. package/lib/index.js +143 -0
  92. package/lib/locales/en.d.ts +2 -0
  93. package/lib/locales/en.js +220 -0
  94. package/lib/locales/index.d.ts +10 -0
  95. package/lib/locales/index.js +33 -0
  96. package/lib/locales/zh.d.ts +2 -0
  97. package/lib/locales/zh.js +220 -0
  98. package/lib/payment/amount.d.ts +12 -0
  99. package/lib/payment/amount.js +28 -0
  100. package/lib/payment/error.d.ts +13 -0
  101. package/lib/payment/error.js +52 -0
  102. package/lib/payment/footer.d.ts +4 -0
  103. package/lib/payment/footer.js +25 -0
  104. package/lib/payment/form/addon.d.ts +2 -0
  105. package/lib/payment/form/addon.js +37 -0
  106. package/lib/payment/form/address.d.ts +7 -0
  107. package/lib/payment/form/address.js +152 -0
  108. package/lib/payment/form/index.d.ts +9 -0
  109. package/lib/payment/form/index.js +464 -0
  110. package/lib/payment/form/phone.d.ts +4 -0
  111. package/lib/payment/form/phone.js +133 -0
  112. package/lib/payment/form/stripe.d.ts +13 -0
  113. package/lib/payment/form/stripe.js +213 -0
  114. package/lib/payment/header.d.ts +7 -0
  115. package/lib/payment/header.js +58 -0
  116. package/lib/payment/index.d.ts +28 -0
  117. package/lib/payment/index.js +382 -0
  118. package/lib/payment/product-card.d.ts +21 -0
  119. package/lib/payment/product-card.js +81 -0
  120. package/lib/payment/product-item.d.ts +19 -0
  121. package/lib/payment/product-item.js +160 -0
  122. package/lib/payment/product-skeleton.d.ts +4 -0
  123. package/lib/payment/product-skeleton.js +71 -0
  124. package/lib/payment/skeleton/overview.d.ts +2 -0
  125. package/lib/payment/skeleton/overview.js +48 -0
  126. package/lib/payment/skeleton/payment.d.ts +2 -0
  127. package/lib/payment/skeleton/payment.js +54 -0
  128. package/lib/payment/success.d.ts +8 -0
  129. package/lib/payment/success.js +215 -0
  130. package/lib/payment/summary.d.ts +12 -0
  131. package/lib/payment/summary.js +225 -0
  132. package/lib/theme.d.ts +1 -0
  133. package/lib/theme.js +19 -0
  134. package/lib/types/index.d.ts +19 -0
  135. package/lib/types/index.js +1 -0
  136. package/lib/types/shims.d.ts +18 -0
  137. package/lib/util.d.ts +52 -0
  138. package/lib/util.js +487 -0
  139. package/package.json +104 -0
  140. package/src/api.ts +24 -0
  141. package/src/checkout/index.tsx +74 -0
  142. package/src/components/input.tsx +58 -0
  143. package/src/components/livemode.tsx +23 -0
  144. package/src/components/pricing-table.tsx +207 -0
  145. package/src/components/status.tsx +19 -0
  146. package/src/components/switch.tsx +48 -0
  147. package/src/contexts/payment.tsx +74 -0
  148. package/src/dayjs.ts +17 -0
  149. package/src/index.ts +32 -0
  150. package/src/locales/en.tsx +218 -0
  151. package/src/locales/index.tsx +30 -0
  152. package/src/locales/zh.tsx +214 -0
  153. package/src/payment/amount.tsx +24 -0
  154. package/src/payment/error.tsx +29 -0
  155. package/src/payment/footer.tsx +12 -0
  156. package/src/payment/form/addon.tsx +24 -0
  157. package/src/payment/form/address.tsx +119 -0
  158. package/src/payment/form/index.tsx +401 -0
  159. package/src/payment/form/phone.tsx +103 -0
  160. package/src/payment/form/stripe.tsx +195 -0
  161. package/src/payment/header.tsx +40 -0
  162. package/src/payment/index.tsx +367 -0
  163. package/src/payment/product-card.tsx +55 -0
  164. package/src/payment/product-item.tsx +121 -0
  165. package/src/payment/product-skeleton.tsx +39 -0
  166. package/src/payment/skeleton/overview.tsx +21 -0
  167. package/src/payment/skeleton/payment.tsx +35 -0
  168. package/src/payment/success.tsx +186 -0
  169. package/src/payment/summary.tsx +198 -0
  170. package/src/theme.ts +18 -0
  171. package/src/types/index.ts +29 -0
  172. package/src/types/shims.d.ts +18 -0
  173. package/src/util.ts +543 -0
package/src/util.ts ADDED
@@ -0,0 +1,543 @@
1
+ /* eslint-disable no-nested-ternary */
2
+ /* eslint-disable @typescript-eslint/indent */
3
+ import type {
4
+ PriceCurrency,
5
+ PriceRecurring,
6
+ TCheckoutSessionExpanded,
7
+ TLineItemExpanded,
8
+ TPaymentCurrency,
9
+ TPaymentMethodExpanded,
10
+ TPrice,
11
+ TSubscriptionExpanded,
12
+ } from '@blocklet/payment-types';
13
+ import { BN, fromUnitToToken } from '@ocap/util';
14
+ import { defaultCountries } from 'react-international-phone';
15
+
16
+ import dayjs from './dayjs';
17
+ import { t } from './locales/index';
18
+
19
+ export const PAYMENT_KIT_DID = 'z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk';
20
+
21
+ export const getPrefix = () => {
22
+ const componentId = (window.blocklet.componentId || '').split('/').pop();
23
+ if (componentId === PAYMENT_KIT_DID) {
24
+ return window.blocklet.prefix;
25
+ }
26
+ const component = (window.blocklet.componentMountPoints || []).find((x: any) => x.did === PAYMENT_KIT_DID);
27
+ if (component) {
28
+ return component.mountPoint;
29
+ }
30
+
31
+ return window.blocklet.prefix;
32
+ };
33
+
34
+ export function formatToDate(date: Date | string | number, locale = 'en') {
35
+ if (!date) {
36
+ return '-';
37
+ }
38
+
39
+ return dayjs(date).locale(formatLocale(locale)).format('YYYY-MM-DD HH:mm:ss');
40
+ }
41
+
42
+ export function formatToDatetime(date: Date | string | number, locale = 'en') {
43
+ if (!date) {
44
+ return '-';
45
+ }
46
+
47
+ return dayjs(date).locale(formatLocale(locale)).format('lll');
48
+ }
49
+
50
+ export function formatTime(date: Date | string | number, format = 'YYYY-MM-DD HH:mm:ss', locale = 'en') {
51
+ if (!date) {
52
+ return '-';
53
+ }
54
+
55
+ return dayjs(date).locale(formatLocale(locale)).format(format);
56
+ }
57
+
58
+ export function formatDateTime(date: Date | string | number, locale = 'en') {
59
+ return dayjs(date).locale(formatLocale(locale)).format('YYYY-MM-DD HH:mm');
60
+ }
61
+
62
+ export const formatLocale = (locale = 'en') => {
63
+ if (locale === 'tw') {
64
+ return 'zh';
65
+ }
66
+
67
+ return locale;
68
+ };
69
+
70
+ export const formatPrettyMsLocale = (locale: string) => (locale === 'zh' ? 'zh_CN' : 'en_US');
71
+
72
+ export const formatError = (err: any) => {
73
+ const { details, errors, response } = err;
74
+
75
+ // graphql error
76
+ if (Array.isArray(errors)) {
77
+ return errors.map((x) => x.message).join('\n');
78
+ }
79
+
80
+ // joi validate error
81
+ if (Array.isArray(details)) {
82
+ const formatted = details.map((e) => {
83
+ const errorMessage = e.message.replace(/["]/g, "'");
84
+ const errorPath = e.path.join('.');
85
+ return `${errorPath}: ${errorMessage}`;
86
+ });
87
+
88
+ return `Validate failed: ${formatted.join(';')}`;
89
+ }
90
+
91
+ // axios error
92
+ if (response) {
93
+ return response.data.error || `${err.message}: ${JSON.stringify(response.data)}`;
94
+ }
95
+
96
+ return err.message;
97
+ };
98
+
99
+ export const formatPrice = (
100
+ price: TPrice,
101
+ currency: TPaymentCurrency,
102
+ unit_label?: string,
103
+ quantity: number = 1,
104
+ bn: boolean = true,
105
+ locale: string = 'en'
106
+ ) => {
107
+ const unit = getPriceUintAmountByCurrency(price, currency);
108
+ const amount = bn
109
+ ? fromUnitToToken(new BN(unit).mul(new BN(quantity)), currency.decimal).toString()
110
+ : +unit * quantity;
111
+ if (price?.type === 'recurring' && price.recurring) {
112
+ const recurring = formatRecurring(price.recurring, false, 'slash', locale);
113
+
114
+ if (unit_label) {
115
+ return `${amount} ${currency.symbol} / ${unit_label} ${recurring}`;
116
+ }
117
+ if (price.recurring.usage_type === 'metered') {
118
+ return `${amount} ${currency.symbol} / unit ${recurring}`;
119
+ }
120
+
121
+ return `${amount} ${currency.symbol} ${recurring}`;
122
+ }
123
+
124
+ return `${amount} ${currency.symbol}`;
125
+ };
126
+
127
+ export const formatPriceAmount = (
128
+ price: TPrice,
129
+ currency: TPaymentCurrency,
130
+ unit_label?: string,
131
+ quantity: number = 1,
132
+ bn: boolean = true
133
+ ) => {
134
+ const unit = getPriceUintAmountByCurrency(price, currency);
135
+ const amount = bn
136
+ ? fromUnitToToken(new BN(unit).mul(new BN(quantity)), currency.decimal).toString()
137
+ : +unit * quantity;
138
+ if (price?.type === 'recurring' && price.recurring) {
139
+ if (unit_label) {
140
+ return `${amount} ${currency.symbol} / ${unit_label}`;
141
+ }
142
+ if (price.recurring.usage_type === 'metered') {
143
+ return `${amount} ${currency.symbol} / unit`;
144
+ }
145
+
146
+ return `${amount} ${currency.symbol}`;
147
+ }
148
+
149
+ return `${amount} ${currency.symbol}`;
150
+ };
151
+
152
+ export function getStatementDescriptor(items: any[]) {
153
+ for (const item of items) {
154
+ if (item.price?.product?.statement_descriptor) {
155
+ return item.price?.product?.statement_descriptor;
156
+ }
157
+ }
158
+
159
+ return window.blocklet.appName;
160
+ }
161
+
162
+ export function formatRecurring(
163
+ recurring: PriceRecurring,
164
+ translate: boolean = true,
165
+ separator: string = 'per',
166
+ locale: string = 'en'
167
+ ) {
168
+ const intervals = {
169
+ hour: 'hourly',
170
+ day: 'daily',
171
+ week: 'weekly',
172
+ month: 'monthly',
173
+ year: 'yearly',
174
+ };
175
+
176
+ if (+recurring.interval_count === 1) {
177
+ const interval = t(`common.${recurring.interval}`, locale);
178
+ // @ts-ignore
179
+ return translate ? t(`common.${intervals[recurring.interval]}`, locale) : separator ? t(`common.${separator}`, locale, { interval }) : interval; // prettier-ignore
180
+ }
181
+
182
+ return t('common.recurring', locale, {
183
+ count: recurring.interval_count,
184
+ interval: t(`common.${recurring.interval}s`, locale),
185
+ });
186
+ }
187
+
188
+ export function getPriceUintAmountByCurrency(price: TPrice, currency: TPaymentCurrency) {
189
+ const options = getPriceCurrencyOptions(price);
190
+ const option = options.find((x) => x.currency_id === currency.id);
191
+ if (option) {
192
+ return option.unit_amount;
193
+ }
194
+
195
+ return price.unit_amount;
196
+ }
197
+
198
+ export function getPriceCurrencyOptions(price: TPrice): PriceCurrency[] {
199
+ if (Array.isArray(price.currency_options)) {
200
+ return price.currency_options;
201
+ }
202
+
203
+ return [{ currency_id: price.currency_id, unit_amount: price.unit_amount, tiers: null, custom_unit_amount: null }];
204
+ }
205
+
206
+ export function formatLineItemPricing(
207
+ item: TLineItemExpanded,
208
+ currency: TPaymentCurrency,
209
+ trial: number,
210
+ locale: string = 'en'
211
+ ): { primary: string; secondary?: string; quantity: string } {
212
+ const price = item.upsell_price || item.price;
213
+
214
+ let quantity = t('common.qty', locale, { count: item.quantity });
215
+ if (price.recurring?.usage_type === 'metered' || +item.quantity === 1) {
216
+ quantity = '';
217
+ }
218
+
219
+ const total = `${fromUnitToToken(
220
+ new BN(getPriceUintAmountByCurrency(price, currency)).mul(new BN(item.quantity)),
221
+ currency.decimal
222
+ )} ${currency.symbol}`;
223
+ const unit = `${fromUnitToToken(getPriceUintAmountByCurrency(price, currency))} ${currency.symbol}`;
224
+
225
+ const appendUnit = (v: string, alt: string) => {
226
+ if (price.product.unit_label) {
227
+ return `${v}/${price.product.unit_label}`;
228
+ }
229
+ if (price.recurring?.usage_type === 'metered' || item.quantity === 1) {
230
+ return alt;
231
+ }
232
+
233
+ return quantity ? t('common.each', locale, { unit }) : '';
234
+ };
235
+
236
+ if (price.type === 'recurring' && price.recurring) {
237
+ if (trial > 0) {
238
+ return {
239
+ primary: t('common.trial', locale, { count: trial }),
240
+ secondary: `${appendUnit(total, total)} ${formatRecurring(price.recurring, false, 'slash', locale)}`,
241
+ quantity,
242
+ };
243
+ }
244
+
245
+ return {
246
+ primary: total,
247
+ secondary: appendUnit(total, ''),
248
+ quantity,
249
+ };
250
+ }
251
+
252
+ return {
253
+ primary: total,
254
+ secondary: appendUnit(total, ''),
255
+ quantity,
256
+ };
257
+ }
258
+
259
+ export function getCheckoutAmount(
260
+ items: TLineItemExpanded[],
261
+ currency: TPaymentCurrency,
262
+ includeFreeTrial = false,
263
+ upsell = true
264
+ ) {
265
+ let renew = new BN(0);
266
+
267
+ const total = items
268
+ .reduce((acc, x) => {
269
+ const price = upsell ? x.upsell_price || x.price : x.price;
270
+ if (price.type === 'recurring') {
271
+ renew = renew.add(new BN(getPriceUintAmountByCurrency(price, currency)).mul(new BN(x.quantity)));
272
+
273
+ if (includeFreeTrial) {
274
+ return acc;
275
+ }
276
+ if (price.recurring?.usage_type === 'metered') {
277
+ return acc;
278
+ }
279
+ }
280
+ return acc.add(new BN(getPriceUintAmountByCurrency(price, currency)).mul(new BN(x.quantity)));
281
+ }, new BN(0))
282
+ .toString();
283
+
284
+ return { subtotal: total, total, renew: renew.toString(), discount: '0', shipping: '0', tax: '0' };
285
+ }
286
+
287
+ export function getRecurringPeriod(recurring: PriceRecurring) {
288
+ const { interval } = recurring;
289
+ const count = +recurring.interval_count || 1;
290
+ const dayInMs = 24 * 60 * 60 * 1000;
291
+
292
+ switch (interval) {
293
+ case 'hour':
294
+ return 60 * 60 * 1000;
295
+ case 'day':
296
+ return count * dayInMs;
297
+ case 'week':
298
+ return count * 7 * dayInMs;
299
+ case 'month':
300
+ return count * 30 * dayInMs;
301
+ case 'year':
302
+ return count * 365 * dayInMs;
303
+ default:
304
+ throw new Error(`Unsupported recurring interval: ${interval}`);
305
+ }
306
+ }
307
+
308
+ export function formatUpsellSaving(session: TCheckoutSessionExpanded, currency: TPaymentCurrency) {
309
+ const items = session.line_items as TLineItemExpanded[];
310
+ if (items[0]?.upsell_price_id) {
311
+ return '0';
312
+ }
313
+ if (!items[0]?.price.upsell?.upsells_to) {
314
+ return '0';
315
+ }
316
+
317
+ const from = getCheckoutAmount(items, currency, false, false);
318
+ const to = getCheckoutAmount(
319
+ items.map((x) => ({
320
+ ...x,
321
+ upsell_price_id: x.price.upsell?.upsells_to_id,
322
+ upsell_price: x.price.upsell?.upsells_to,
323
+ })) as TLineItemExpanded[],
324
+ currency,
325
+ false,
326
+ true
327
+ );
328
+
329
+ const fromRecurring = items[0].price?.recurring as PriceRecurring;
330
+ const toRecurring = items[0].price?.upsell?.upsells_to?.recurring as PriceRecurring;
331
+ if (!fromRecurring || !toRecurring) {
332
+ return '0';
333
+ }
334
+
335
+ const factor = Math.floor(getRecurringPeriod(toRecurring) / getRecurringPeriod(fromRecurring));
336
+
337
+ const before = new BN(from.total).mul(new BN(factor));
338
+ const after = new BN(to.total);
339
+
340
+ return Number(before.sub(after).mul(new BN(100)).div(before).toString()).toFixed(0);
341
+ }
342
+
343
+ export function formatCheckoutHeadlines(
344
+ session: TCheckoutSessionExpanded,
345
+ currency: TPaymentCurrency,
346
+ locale: string = 'en'
347
+ ): {
348
+ action: string;
349
+ amount: string;
350
+ then?: string;
351
+ secondary?: string;
352
+ } {
353
+ const items = session.line_items as TLineItemExpanded[];
354
+ const trial = session.subscription_data?.trial_period_days || 0;
355
+
356
+ const brand = getStatementDescriptor(items);
357
+ const { total } = getCheckoutAmount(items, currency, !!trial);
358
+ const amount = `${fromUnitToToken(total, currency.decimal)} ${currency.symbol}`;
359
+
360
+ // empty
361
+ if (items.length === 0) {
362
+ return {
363
+ action: t('payment.checkout.empty', locale),
364
+ amount: '0',
365
+ then: '',
366
+ };
367
+ }
368
+
369
+ const { name } = items[0]?.price.product || { name: '' };
370
+
371
+ // all one time
372
+ if (items.every((x) => x.price.type === 'one_time')) {
373
+ const action = t('payment.checkout.pay', locale, { payee: brand });
374
+ if (items.length > 1) {
375
+ return { action, amount };
376
+ }
377
+
378
+ return { action, amount, then: '' };
379
+ }
380
+
381
+ const item = items.find((x) => x.price.type === 'recurring');
382
+ const recurring = formatRecurring(
383
+ (item?.upsell_price || item?.price)?.recurring as PriceRecurring,
384
+ false,
385
+ 'per',
386
+ locale
387
+ );
388
+
389
+ // all recurring
390
+ if (items.every((x) => x.price.type === 'recurring')) {
391
+ const hasMetered = items.some((x) => x.price.type === 'recurring' && x.price.recurring?.usage_type === 'metered');
392
+ const subscription = [
393
+ hasMetered ? t('payment.checkout.least', locale) : '',
394
+ fromUnitToToken(
395
+ items.reduce((acc, x) => {
396
+ if (x.price.recurring?.usage_type === 'metered') {
397
+ return acc;
398
+ }
399
+ return acc.add(new BN(getPriceUintAmountByCurrency(x.price, currency)).mul(new BN(x.quantity)));
400
+ }, new BN(0)),
401
+ currency.decimal
402
+ ),
403
+ currency.symbol,
404
+ ]
405
+ .filter(Boolean)
406
+ .join(' ');
407
+ if (items.length > 1) {
408
+ if (trial > 0) {
409
+ return {
410
+ action: t('payment.checkout.try2', locale, { name, count: items.length - 1 }),
411
+ amount: t('payment.checkout.free', locale, { count: trial }),
412
+ then: t('payment.checkout.then', locale, { subscription, recurring }),
413
+ };
414
+ }
415
+
416
+ return {
417
+ action: t('payment.checkout.sub2', locale, { name, count: items.length - 1 }),
418
+ amount,
419
+ then: recurring,
420
+ };
421
+ }
422
+
423
+ if (trial > 0) {
424
+ return {
425
+ action: t('payment.checkout.try1', locale, { name }),
426
+ amount: t('payment.checkout.free', locale, { count: trial }),
427
+ then: t('payment.checkout.then', locale, { subscription, recurring }),
428
+ };
429
+ }
430
+
431
+ return {
432
+ action: t('payment.checkout.sub1', locale, { name }),
433
+ amount,
434
+ then: recurring,
435
+ };
436
+ }
437
+
438
+ // mixed
439
+ const subscription = fromUnitToToken(
440
+ items
441
+ .filter((x) => x.price.type === 'recurring')
442
+ .reduce((acc, x) => {
443
+ if (x.price.recurring?.usage_type === 'metered') {
444
+ return acc;
445
+ }
446
+ return acc.add(new BN(getPriceUintAmountByCurrency(x.price, currency)).mul(new BN(x.quantity)));
447
+ }, new BN(0)),
448
+ currency.decimal
449
+ );
450
+
451
+ return {
452
+ action: t('payment.checkout.pay', locale, { payee: brand }),
453
+ amount,
454
+ then: t('payment.checkout.then', locale, { subscription: `${subscription} ${currency.symbol}`, recurring }),
455
+ };
456
+ }
457
+
458
+ export function formatAmount(amount: string, decimals: number) {
459
+ return fromUnitToToken(amount, decimals);
460
+ }
461
+
462
+ export function findCurrency(methods: TPaymentMethodExpanded[], currencyId: string) {
463
+ for (const method of methods) {
464
+ for (const currency of method.payment_currencies) {
465
+ if (currency.id === currencyId) {
466
+ return currency;
467
+ }
468
+ }
469
+ }
470
+
471
+ return null;
472
+ }
473
+
474
+ export function isValidCountry(code: string) {
475
+ return defaultCountries.some((x) => x[1] === code);
476
+ }
477
+
478
+ export function stopEvent(e: React.SyntheticEvent<any>) {
479
+ try {
480
+ e.stopPropagation();
481
+ e.preventDefault();
482
+ // eslint-disable-next-line no-empty
483
+ } catch {
484
+ // Do nothing
485
+ }
486
+ }
487
+
488
+ export function sleep(ms: number) {
489
+ return new Promise((resolve) => {
490
+ setTimeout(resolve, ms);
491
+ });
492
+ }
493
+
494
+ export const getSubscriptionTimeSummary = (subscription: TSubscriptionExpanded) => {
495
+ const lines = [`Started on ${formatToDate(subscription.start_date * 1000)}`];
496
+ if (subscription.status === 'active' || subscription.status === 'trialing') {
497
+ if (subscription.cancel_at) {
498
+ lines.push(`will cancel on ${formatToDate(subscription.cancel_at * 1000)}`);
499
+ } else if (subscription.cancel_at_period_end) {
500
+ lines.push(`will cancel on ${formatToDate(subscription.current_period_end * 1000)}`);
501
+ } else {
502
+ lines.push(`will renew on ${formatToDate(subscription.current_period_end * 1000)}`);
503
+ }
504
+ } else if (subscription.status === 'past_due') {
505
+ lines.push(`will cancel on ${formatToDate(subscription.current_period_end * 1000)}`);
506
+ } else if (subscription.status === 'canceled') {
507
+ lines.push(`canceled on ${formatToDate(subscription.canceled_at * 1000)}`);
508
+ }
509
+
510
+ return lines.join(', ');
511
+ };
512
+
513
+ export const getSubscriptionAction = (subscription: TSubscriptionExpanded) => {
514
+ if (subscription.status !== 'canceled' && subscription.cancel_at_period_end) {
515
+ return { action: 'recover', variant: 'contained', color: 'primary' };
516
+ }
517
+
518
+ if (subscription.status === 'active' || subscription.status === 'trialing') {
519
+ if (subscription.cancel_at) {
520
+ return null;
521
+ }
522
+ if (subscription.cancel_at_period_end) {
523
+ return { action: 'recover', variant: 'contained', color: 'primary' };
524
+ }
525
+
526
+ return { action: 'cancel', variant: 'outlined', color: 'inherit' };
527
+ }
528
+
529
+ if (subscription.status === 'past_due') {
530
+ return { action: 'pastDue', variant: 'contained', color: 'primary' };
531
+ }
532
+
533
+ return null;
534
+ };
535
+
536
+ export const mergeExtraParams = (extra: Record<string, any> = {}) => {
537
+ const params = new URLSearchParams(window.location.search);
538
+ Object.keys(extra).forEach((key) => {
539
+ params.set(key, extra[key]);
540
+ });
541
+
542
+ return params.toString();
543
+ };