@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.
- package/LICENSE +13 -0
- package/README.md +29 -0
- package/babel.config.es.js +8 -0
- package/build.config.ts +29 -0
- package/es/api.d.ts +2 -0
- package/es/api.js +18 -0
- package/es/checkout/index.d.ts +15 -0
- package/es/checkout/index.js +61 -0
- package/es/components/input.d.ts +23 -0
- package/es/components/input.js +44 -0
- package/es/components/livemode.d.ts +2 -0
- package/es/components/livemode.js +24 -0
- package/es/components/pricing-table.d.ts +18 -0
- package/es/components/pricing-table.js +175 -0
- package/es/components/status.d.ts +3 -0
- package/es/components/status.js +20 -0
- package/es/components/switch.d.ts +6 -0
- package/es/components/switch.js +42 -0
- package/es/contexts/payment.d.ts +29 -0
- package/es/contexts/payment.js +45 -0
- package/es/dayjs.d.ts +2 -0
- package/es/dayjs.js +14 -0
- package/es/index.d.ts +16 -0
- package/es/index.js +29 -0
- package/es/locales/en.d.ts +2 -0
- package/es/locales/en.js +213 -0
- package/es/locales/index.d.ts +10 -0
- package/es/locales/index.js +20 -0
- package/es/locales/zh.d.ts +2 -0
- package/es/locales/zh.js +213 -0
- package/es/payment/amount.d.ts +12 -0
- package/es/payment/amount.js +22 -0
- package/es/payment/error.d.ts +13 -0
- package/es/payment/error.js +12 -0
- package/es/payment/footer.d.ts +4 -0
- package/es/payment/footer.js +9 -0
- package/es/payment/form/addon.d.ts +2 -0
- package/es/payment/form/addon.js +14 -0
- package/es/payment/form/address.d.ts +7 -0
- package/es/payment/form/address.js +119 -0
- package/es/payment/form/index.d.ts +9 -0
- package/es/payment/form/index.js +337 -0
- package/es/payment/form/phone.d.ts +4 -0
- package/es/payment/form/phone.js +97 -0
- package/es/payment/form/stripe.d.ts +13 -0
- package/es/payment/form/stripe.js +158 -0
- package/es/payment/header.d.ts +7 -0
- package/es/payment/header.js +29 -0
- package/es/payment/index.d.ts +28 -0
- package/es/payment/index.js +327 -0
- package/es/payment/product-card.d.ts +21 -0
- package/es/payment/product-card.js +34 -0
- package/es/payment/product-item.d.ts +19 -0
- package/es/payment/product-item.js +107 -0
- package/es/payment/product-skeleton.d.ts +4 -0
- package/es/payment/product-skeleton.js +34 -0
- package/es/payment/skeleton/overview.d.ts +2 -0
- package/es/payment/skeleton/overview.js +13 -0
- package/es/payment/skeleton/payment.d.ts +2 -0
- package/es/payment/skeleton/payment.js +19 -0
- package/es/payment/success.d.ts +8 -0
- package/es/payment/success.js +164 -0
- package/es/payment/summary.d.ts +12 -0
- package/es/payment/summary.js +178 -0
- package/es/theme.d.ts +1 -0
- package/es/theme.js +17 -0
- package/es/types/index.d.ts +19 -0
- package/es/types/index.js +0 -0
- package/es/types/shims.d.ts +18 -0
- package/es/util.d.ts +52 -0
- package/es/util.js +390 -0
- package/lib/api.d.ts +2 -0
- package/lib/api.js +26 -0
- package/lib/checkout/index.d.ts +15 -0
- package/lib/checkout/index.js +83 -0
- package/lib/components/input.d.ts +23 -0
- package/lib/components/input.js +72 -0
- package/lib/components/livemode.d.ts +2 -0
- package/lib/components/livemode.js +29 -0
- package/lib/components/pricing-table.d.ts +18 -0
- package/lib/components/pricing-table.js +232 -0
- package/lib/components/status.d.ts +3 -0
- package/lib/components/status.js +23 -0
- package/lib/components/switch.d.ts +6 -0
- package/lib/components/switch.js +51 -0
- package/lib/contexts/payment.d.ts +29 -0
- package/lib/contexts/payment.js +73 -0
- package/lib/dayjs.d.ts +2 -0
- package/lib/dayjs.js +21 -0
- package/lib/index.d.ts +16 -0
- package/lib/index.js +143 -0
- package/lib/locales/en.d.ts +2 -0
- package/lib/locales/en.js +220 -0
- package/lib/locales/index.d.ts +10 -0
- package/lib/locales/index.js +33 -0
- package/lib/locales/zh.d.ts +2 -0
- package/lib/locales/zh.js +220 -0
- package/lib/payment/amount.d.ts +12 -0
- package/lib/payment/amount.js +28 -0
- package/lib/payment/error.d.ts +13 -0
- package/lib/payment/error.js +52 -0
- package/lib/payment/footer.d.ts +4 -0
- package/lib/payment/footer.js +25 -0
- package/lib/payment/form/addon.d.ts +2 -0
- package/lib/payment/form/addon.js +37 -0
- package/lib/payment/form/address.d.ts +7 -0
- package/lib/payment/form/address.js +152 -0
- package/lib/payment/form/index.d.ts +9 -0
- package/lib/payment/form/index.js +464 -0
- package/lib/payment/form/phone.d.ts +4 -0
- package/lib/payment/form/phone.js +133 -0
- package/lib/payment/form/stripe.d.ts +13 -0
- package/lib/payment/form/stripe.js +213 -0
- package/lib/payment/header.d.ts +7 -0
- package/lib/payment/header.js +58 -0
- package/lib/payment/index.d.ts +28 -0
- package/lib/payment/index.js +382 -0
- package/lib/payment/product-card.d.ts +21 -0
- package/lib/payment/product-card.js +81 -0
- package/lib/payment/product-item.d.ts +19 -0
- package/lib/payment/product-item.js +160 -0
- package/lib/payment/product-skeleton.d.ts +4 -0
- package/lib/payment/product-skeleton.js +71 -0
- package/lib/payment/skeleton/overview.d.ts +2 -0
- package/lib/payment/skeleton/overview.js +48 -0
- package/lib/payment/skeleton/payment.d.ts +2 -0
- package/lib/payment/skeleton/payment.js +54 -0
- package/lib/payment/success.d.ts +8 -0
- package/lib/payment/success.js +215 -0
- package/lib/payment/summary.d.ts +12 -0
- package/lib/payment/summary.js +225 -0
- package/lib/theme.d.ts +1 -0
- package/lib/theme.js +19 -0
- package/lib/types/index.d.ts +19 -0
- package/lib/types/index.js +1 -0
- package/lib/types/shims.d.ts +18 -0
- package/lib/util.d.ts +52 -0
- package/lib/util.js +487 -0
- package/package.json +104 -0
- package/src/api.ts +24 -0
- package/src/checkout/index.tsx +74 -0
- package/src/components/input.tsx +58 -0
- package/src/components/livemode.tsx +23 -0
- package/src/components/pricing-table.tsx +207 -0
- package/src/components/status.tsx +19 -0
- package/src/components/switch.tsx +48 -0
- package/src/contexts/payment.tsx +74 -0
- package/src/dayjs.ts +17 -0
- package/src/index.ts +32 -0
- package/src/locales/en.tsx +218 -0
- package/src/locales/index.tsx +30 -0
- package/src/locales/zh.tsx +214 -0
- package/src/payment/amount.tsx +24 -0
- package/src/payment/error.tsx +29 -0
- package/src/payment/footer.tsx +12 -0
- package/src/payment/form/addon.tsx +24 -0
- package/src/payment/form/address.tsx +119 -0
- package/src/payment/form/index.tsx +401 -0
- package/src/payment/form/phone.tsx +103 -0
- package/src/payment/form/stripe.tsx +195 -0
- package/src/payment/header.tsx +40 -0
- package/src/payment/index.tsx +367 -0
- package/src/payment/product-card.tsx +55 -0
- package/src/payment/product-item.tsx +121 -0
- package/src/payment/product-skeleton.tsx +39 -0
- package/src/payment/skeleton/overview.tsx +21 -0
- package/src/payment/skeleton/payment.tsx +35 -0
- package/src/payment/success.tsx +186 -0
- package/src/payment/summary.tsx +198 -0
- package/src/theme.ts +18 -0
- package/src/types/index.ts +29 -0
- package/src/types/shims.d.ts +18 -0
- package/src/util.ts +543 -0
package/es/util.js
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { BN, fromUnitToToken } from "@ocap/util";
|
|
2
|
+
import { defaultCountries } from "react-international-phone";
|
|
3
|
+
import dayjs from "./dayjs.js";
|
|
4
|
+
import { t } from "./locales/index.js";
|
|
5
|
+
export const PAYMENT_KIT_DID = "z2qaCNvKMv5GjouKdcDWexv6WqtHbpNPQDnAk";
|
|
6
|
+
export const getPrefix = () => {
|
|
7
|
+
const componentId = (window.blocklet.componentId || "").split("/").pop();
|
|
8
|
+
if (componentId === PAYMENT_KIT_DID) {
|
|
9
|
+
return window.blocklet.prefix;
|
|
10
|
+
}
|
|
11
|
+
const component = (window.blocklet.componentMountPoints || []).find((x) => x.did === PAYMENT_KIT_DID);
|
|
12
|
+
if (component) {
|
|
13
|
+
return component.mountPoint;
|
|
14
|
+
}
|
|
15
|
+
return window.blocklet.prefix;
|
|
16
|
+
};
|
|
17
|
+
export function formatToDate(date, locale = "en") {
|
|
18
|
+
if (!date) {
|
|
19
|
+
return "-";
|
|
20
|
+
}
|
|
21
|
+
return dayjs(date).locale(formatLocale(locale)).format("YYYY-MM-DD HH:mm:ss");
|
|
22
|
+
}
|
|
23
|
+
export function formatToDatetime(date, locale = "en") {
|
|
24
|
+
if (!date) {
|
|
25
|
+
return "-";
|
|
26
|
+
}
|
|
27
|
+
return dayjs(date).locale(formatLocale(locale)).format("lll");
|
|
28
|
+
}
|
|
29
|
+
export function formatTime(date, format = "YYYY-MM-DD HH:mm:ss", locale = "en") {
|
|
30
|
+
if (!date) {
|
|
31
|
+
return "-";
|
|
32
|
+
}
|
|
33
|
+
return dayjs(date).locale(formatLocale(locale)).format(format);
|
|
34
|
+
}
|
|
35
|
+
export function formatDateTime(date, locale = "en") {
|
|
36
|
+
return dayjs(date).locale(formatLocale(locale)).format("YYYY-MM-DD HH:mm");
|
|
37
|
+
}
|
|
38
|
+
export const formatLocale = (locale = "en") => {
|
|
39
|
+
if (locale === "tw") {
|
|
40
|
+
return "zh";
|
|
41
|
+
}
|
|
42
|
+
return locale;
|
|
43
|
+
};
|
|
44
|
+
export const formatPrettyMsLocale = (locale) => locale === "zh" ? "zh_CN" : "en_US";
|
|
45
|
+
export const formatError = (err) => {
|
|
46
|
+
const { details, errors, response } = err;
|
|
47
|
+
if (Array.isArray(errors)) {
|
|
48
|
+
return errors.map((x) => x.message).join("\n");
|
|
49
|
+
}
|
|
50
|
+
if (Array.isArray(details)) {
|
|
51
|
+
const formatted = details.map((e) => {
|
|
52
|
+
const errorMessage = e.message.replace(/["]/g, "'");
|
|
53
|
+
const errorPath = e.path.join(".");
|
|
54
|
+
return `${errorPath}: ${errorMessage}`;
|
|
55
|
+
});
|
|
56
|
+
return `Validate failed: ${formatted.join(";")}`;
|
|
57
|
+
}
|
|
58
|
+
if (response) {
|
|
59
|
+
return response.data.error || `${err.message}: ${JSON.stringify(response.data)}`;
|
|
60
|
+
}
|
|
61
|
+
return err.message;
|
|
62
|
+
};
|
|
63
|
+
export const formatPrice = (price, currency, unit_label, quantity = 1, bn = true, locale = "en") => {
|
|
64
|
+
const unit = getPriceUintAmountByCurrency(price, currency);
|
|
65
|
+
const amount = bn ? fromUnitToToken(new BN(unit).mul(new BN(quantity)), currency.decimal).toString() : +unit * quantity;
|
|
66
|
+
if (price?.type === "recurring" && price.recurring) {
|
|
67
|
+
const recurring = formatRecurring(price.recurring, false, "slash", locale);
|
|
68
|
+
if (unit_label) {
|
|
69
|
+
return `${amount} ${currency.symbol} / ${unit_label} ${recurring}`;
|
|
70
|
+
}
|
|
71
|
+
if (price.recurring.usage_type === "metered") {
|
|
72
|
+
return `${amount} ${currency.symbol} / unit ${recurring}`;
|
|
73
|
+
}
|
|
74
|
+
return `${amount} ${currency.symbol} ${recurring}`;
|
|
75
|
+
}
|
|
76
|
+
return `${amount} ${currency.symbol}`;
|
|
77
|
+
};
|
|
78
|
+
export const formatPriceAmount = (price, currency, unit_label, quantity = 1, bn = true) => {
|
|
79
|
+
const unit = getPriceUintAmountByCurrency(price, currency);
|
|
80
|
+
const amount = bn ? fromUnitToToken(new BN(unit).mul(new BN(quantity)), currency.decimal).toString() : +unit * quantity;
|
|
81
|
+
if (price?.type === "recurring" && price.recurring) {
|
|
82
|
+
if (unit_label) {
|
|
83
|
+
return `${amount} ${currency.symbol} / ${unit_label}`;
|
|
84
|
+
}
|
|
85
|
+
if (price.recurring.usage_type === "metered") {
|
|
86
|
+
return `${amount} ${currency.symbol} / unit`;
|
|
87
|
+
}
|
|
88
|
+
return `${amount} ${currency.symbol}`;
|
|
89
|
+
}
|
|
90
|
+
return `${amount} ${currency.symbol}`;
|
|
91
|
+
};
|
|
92
|
+
export function getStatementDescriptor(items) {
|
|
93
|
+
for (const item of items) {
|
|
94
|
+
if (item.price?.product?.statement_descriptor) {
|
|
95
|
+
return item.price?.product?.statement_descriptor;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return window.blocklet.appName;
|
|
99
|
+
}
|
|
100
|
+
export function formatRecurring(recurring, translate = true, separator = "per", locale = "en") {
|
|
101
|
+
const intervals = {
|
|
102
|
+
hour: "hourly",
|
|
103
|
+
day: "daily",
|
|
104
|
+
week: "weekly",
|
|
105
|
+
month: "monthly",
|
|
106
|
+
year: "yearly"
|
|
107
|
+
};
|
|
108
|
+
if (+recurring.interval_count === 1) {
|
|
109
|
+
const interval = t(`common.${recurring.interval}`, locale);
|
|
110
|
+
return translate ? t(`common.${intervals[recurring.interval]}`, locale) : separator ? t(`common.${separator}`, locale, { interval }) : interval;
|
|
111
|
+
}
|
|
112
|
+
return t("common.recurring", locale, {
|
|
113
|
+
count: recurring.interval_count,
|
|
114
|
+
interval: t(`common.${recurring.interval}s`, locale)
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
export function getPriceUintAmountByCurrency(price, currency) {
|
|
118
|
+
const options = getPriceCurrencyOptions(price);
|
|
119
|
+
const option = options.find((x) => x.currency_id === currency.id);
|
|
120
|
+
if (option) {
|
|
121
|
+
return option.unit_amount;
|
|
122
|
+
}
|
|
123
|
+
return price.unit_amount;
|
|
124
|
+
}
|
|
125
|
+
export function getPriceCurrencyOptions(price) {
|
|
126
|
+
if (Array.isArray(price.currency_options)) {
|
|
127
|
+
return price.currency_options;
|
|
128
|
+
}
|
|
129
|
+
return [{ currency_id: price.currency_id, unit_amount: price.unit_amount, tiers: null, custom_unit_amount: null }];
|
|
130
|
+
}
|
|
131
|
+
export function formatLineItemPricing(item, currency, trial, locale = "en") {
|
|
132
|
+
const price = item.upsell_price || item.price;
|
|
133
|
+
let quantity = t("common.qty", locale, { count: item.quantity });
|
|
134
|
+
if (price.recurring?.usage_type === "metered" || +item.quantity === 1) {
|
|
135
|
+
quantity = "";
|
|
136
|
+
}
|
|
137
|
+
const total = `${fromUnitToToken(
|
|
138
|
+
new BN(getPriceUintAmountByCurrency(price, currency)).mul(new BN(item.quantity)),
|
|
139
|
+
currency.decimal
|
|
140
|
+
)} ${currency.symbol}`;
|
|
141
|
+
const unit = `${fromUnitToToken(getPriceUintAmountByCurrency(price, currency))} ${currency.symbol}`;
|
|
142
|
+
const appendUnit = (v, alt) => {
|
|
143
|
+
if (price.product.unit_label) {
|
|
144
|
+
return `${v}/${price.product.unit_label}`;
|
|
145
|
+
}
|
|
146
|
+
if (price.recurring?.usage_type === "metered" || item.quantity === 1) {
|
|
147
|
+
return alt;
|
|
148
|
+
}
|
|
149
|
+
return quantity ? t("common.each", locale, { unit }) : "";
|
|
150
|
+
};
|
|
151
|
+
if (price.type === "recurring" && price.recurring) {
|
|
152
|
+
if (trial > 0) {
|
|
153
|
+
return {
|
|
154
|
+
primary: t("common.trial", locale, { count: trial }),
|
|
155
|
+
secondary: `${appendUnit(total, total)} ${formatRecurring(price.recurring, false, "slash", locale)}`,
|
|
156
|
+
quantity
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
return {
|
|
160
|
+
primary: total,
|
|
161
|
+
secondary: appendUnit(total, ""),
|
|
162
|
+
quantity
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
primary: total,
|
|
167
|
+
secondary: appendUnit(total, ""),
|
|
168
|
+
quantity
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
export function getCheckoutAmount(items, currency, includeFreeTrial = false, upsell = true) {
|
|
172
|
+
let renew = new BN(0);
|
|
173
|
+
const total = items.reduce((acc, x) => {
|
|
174
|
+
const price = upsell ? x.upsell_price || x.price : x.price;
|
|
175
|
+
if (price.type === "recurring") {
|
|
176
|
+
renew = renew.add(new BN(getPriceUintAmountByCurrency(price, currency)).mul(new BN(x.quantity)));
|
|
177
|
+
if (includeFreeTrial) {
|
|
178
|
+
return acc;
|
|
179
|
+
}
|
|
180
|
+
if (price.recurring?.usage_type === "metered") {
|
|
181
|
+
return acc;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return acc.add(new BN(getPriceUintAmountByCurrency(price, currency)).mul(new BN(x.quantity)));
|
|
185
|
+
}, new BN(0)).toString();
|
|
186
|
+
return { subtotal: total, total, renew: renew.toString(), discount: "0", shipping: "0", tax: "0" };
|
|
187
|
+
}
|
|
188
|
+
export function getRecurringPeriod(recurring) {
|
|
189
|
+
const { interval } = recurring;
|
|
190
|
+
const count = +recurring.interval_count || 1;
|
|
191
|
+
const dayInMs = 24 * 60 * 60 * 1e3;
|
|
192
|
+
switch (interval) {
|
|
193
|
+
case "hour":
|
|
194
|
+
return 60 * 60 * 1e3;
|
|
195
|
+
case "day":
|
|
196
|
+
return count * dayInMs;
|
|
197
|
+
case "week":
|
|
198
|
+
return count * 7 * dayInMs;
|
|
199
|
+
case "month":
|
|
200
|
+
return count * 30 * dayInMs;
|
|
201
|
+
case "year":
|
|
202
|
+
return count * 365 * dayInMs;
|
|
203
|
+
default:
|
|
204
|
+
throw new Error(`Unsupported recurring interval: ${interval}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
export function formatUpsellSaving(session, currency) {
|
|
208
|
+
const items = session.line_items;
|
|
209
|
+
if (items[0]?.upsell_price_id) {
|
|
210
|
+
return "0";
|
|
211
|
+
}
|
|
212
|
+
if (!items[0]?.price.upsell?.upsells_to) {
|
|
213
|
+
return "0";
|
|
214
|
+
}
|
|
215
|
+
const from = getCheckoutAmount(items, currency, false, false);
|
|
216
|
+
const to = getCheckoutAmount(
|
|
217
|
+
items.map((x) => ({
|
|
218
|
+
...x,
|
|
219
|
+
upsell_price_id: x.price.upsell?.upsells_to_id,
|
|
220
|
+
upsell_price: x.price.upsell?.upsells_to
|
|
221
|
+
})),
|
|
222
|
+
currency,
|
|
223
|
+
false,
|
|
224
|
+
true
|
|
225
|
+
);
|
|
226
|
+
const fromRecurring = items[0].price?.recurring;
|
|
227
|
+
const toRecurring = items[0].price?.upsell?.upsells_to?.recurring;
|
|
228
|
+
if (!fromRecurring || !toRecurring) {
|
|
229
|
+
return "0";
|
|
230
|
+
}
|
|
231
|
+
const factor = Math.floor(getRecurringPeriod(toRecurring) / getRecurringPeriod(fromRecurring));
|
|
232
|
+
const before = new BN(from.total).mul(new BN(factor));
|
|
233
|
+
const after = new BN(to.total);
|
|
234
|
+
return Number(before.sub(after).mul(new BN(100)).div(before).toString()).toFixed(0);
|
|
235
|
+
}
|
|
236
|
+
export function formatCheckoutHeadlines(session, currency, locale = "en") {
|
|
237
|
+
const items = session.line_items;
|
|
238
|
+
const trial = session.subscription_data?.trial_period_days || 0;
|
|
239
|
+
const brand = getStatementDescriptor(items);
|
|
240
|
+
const { total } = getCheckoutAmount(items, currency, !!trial);
|
|
241
|
+
const amount = `${fromUnitToToken(total, currency.decimal)} ${currency.symbol}`;
|
|
242
|
+
if (items.length === 0) {
|
|
243
|
+
return {
|
|
244
|
+
action: t("payment.checkout.empty", locale),
|
|
245
|
+
amount: "0",
|
|
246
|
+
then: ""
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
const { name } = items[0]?.price.product || { name: "" };
|
|
250
|
+
if (items.every((x) => x.price.type === "one_time")) {
|
|
251
|
+
const action = t("payment.checkout.pay", locale, { payee: brand });
|
|
252
|
+
if (items.length > 1) {
|
|
253
|
+
return { action, amount };
|
|
254
|
+
}
|
|
255
|
+
return { action, amount, then: "" };
|
|
256
|
+
}
|
|
257
|
+
const item = items.find((x) => x.price.type === "recurring");
|
|
258
|
+
const recurring = formatRecurring(
|
|
259
|
+
(item?.upsell_price || item?.price)?.recurring,
|
|
260
|
+
false,
|
|
261
|
+
"per",
|
|
262
|
+
locale
|
|
263
|
+
);
|
|
264
|
+
if (items.every((x) => x.price.type === "recurring")) {
|
|
265
|
+
const hasMetered = items.some((x) => x.price.type === "recurring" && x.price.recurring?.usage_type === "metered");
|
|
266
|
+
const subscription2 = [
|
|
267
|
+
hasMetered ? t("payment.checkout.least", locale) : "",
|
|
268
|
+
fromUnitToToken(
|
|
269
|
+
items.reduce((acc, x) => {
|
|
270
|
+
if (x.price.recurring?.usage_type === "metered") {
|
|
271
|
+
return acc;
|
|
272
|
+
}
|
|
273
|
+
return acc.add(new BN(getPriceUintAmountByCurrency(x.price, currency)).mul(new BN(x.quantity)));
|
|
274
|
+
}, new BN(0)),
|
|
275
|
+
currency.decimal
|
|
276
|
+
),
|
|
277
|
+
currency.symbol
|
|
278
|
+
].filter(Boolean).join(" ");
|
|
279
|
+
if (items.length > 1) {
|
|
280
|
+
if (trial > 0) {
|
|
281
|
+
return {
|
|
282
|
+
action: t("payment.checkout.try2", locale, { name, count: items.length - 1 }),
|
|
283
|
+
amount: t("payment.checkout.free", locale, { count: trial }),
|
|
284
|
+
then: t("payment.checkout.then", locale, { subscription: subscription2, recurring })
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
return {
|
|
288
|
+
action: t("payment.checkout.sub2", locale, { name, count: items.length - 1 }),
|
|
289
|
+
amount,
|
|
290
|
+
then: recurring
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
if (trial > 0) {
|
|
294
|
+
return {
|
|
295
|
+
action: t("payment.checkout.try1", locale, { name }),
|
|
296
|
+
amount: t("payment.checkout.free", locale, { count: trial }),
|
|
297
|
+
then: t("payment.checkout.then", locale, { subscription: subscription2, recurring })
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return {
|
|
301
|
+
action: t("payment.checkout.sub1", locale, { name }),
|
|
302
|
+
amount,
|
|
303
|
+
then: recurring
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
const subscription = fromUnitToToken(
|
|
307
|
+
items.filter((x) => x.price.type === "recurring").reduce((acc, x) => {
|
|
308
|
+
if (x.price.recurring?.usage_type === "metered") {
|
|
309
|
+
return acc;
|
|
310
|
+
}
|
|
311
|
+
return acc.add(new BN(getPriceUintAmountByCurrency(x.price, currency)).mul(new BN(x.quantity)));
|
|
312
|
+
}, new BN(0)),
|
|
313
|
+
currency.decimal
|
|
314
|
+
);
|
|
315
|
+
return {
|
|
316
|
+
action: t("payment.checkout.pay", locale, { payee: brand }),
|
|
317
|
+
amount,
|
|
318
|
+
then: t("payment.checkout.then", locale, { subscription: `${subscription} ${currency.symbol}`, recurring })
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
export function formatAmount(amount, decimals) {
|
|
322
|
+
return fromUnitToToken(amount, decimals);
|
|
323
|
+
}
|
|
324
|
+
export function findCurrency(methods, currencyId) {
|
|
325
|
+
for (const method of methods) {
|
|
326
|
+
for (const currency of method.payment_currencies) {
|
|
327
|
+
if (currency.id === currencyId) {
|
|
328
|
+
return currency;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
export function isValidCountry(code) {
|
|
335
|
+
return defaultCountries.some((x) => x[1] === code);
|
|
336
|
+
}
|
|
337
|
+
export function stopEvent(e) {
|
|
338
|
+
try {
|
|
339
|
+
e.stopPropagation();
|
|
340
|
+
e.preventDefault();
|
|
341
|
+
} catch {
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
export function sleep(ms) {
|
|
345
|
+
return new Promise((resolve) => {
|
|
346
|
+
setTimeout(resolve, ms);
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
export const getSubscriptionTimeSummary = (subscription) => {
|
|
350
|
+
const lines = [`Started on ${formatToDate(subscription.start_date * 1e3)}`];
|
|
351
|
+
if (subscription.status === "active" || subscription.status === "trialing") {
|
|
352
|
+
if (subscription.cancel_at) {
|
|
353
|
+
lines.push(`will cancel on ${formatToDate(subscription.cancel_at * 1e3)}`);
|
|
354
|
+
} else if (subscription.cancel_at_period_end) {
|
|
355
|
+
lines.push(`will cancel on ${formatToDate(subscription.current_period_end * 1e3)}`);
|
|
356
|
+
} else {
|
|
357
|
+
lines.push(`will renew on ${formatToDate(subscription.current_period_end * 1e3)}`);
|
|
358
|
+
}
|
|
359
|
+
} else if (subscription.status === "past_due") {
|
|
360
|
+
lines.push(`will cancel on ${formatToDate(subscription.current_period_end * 1e3)}`);
|
|
361
|
+
} else if (subscription.status === "canceled") {
|
|
362
|
+
lines.push(`canceled on ${formatToDate(subscription.canceled_at * 1e3)}`);
|
|
363
|
+
}
|
|
364
|
+
return lines.join(", ");
|
|
365
|
+
};
|
|
366
|
+
export const getSubscriptionAction = (subscription) => {
|
|
367
|
+
if (subscription.status !== "canceled" && subscription.cancel_at_period_end) {
|
|
368
|
+
return { action: "recover", variant: "contained", color: "primary" };
|
|
369
|
+
}
|
|
370
|
+
if (subscription.status === "active" || subscription.status === "trialing") {
|
|
371
|
+
if (subscription.cancel_at) {
|
|
372
|
+
return null;
|
|
373
|
+
}
|
|
374
|
+
if (subscription.cancel_at_period_end) {
|
|
375
|
+
return { action: "recover", variant: "contained", color: "primary" };
|
|
376
|
+
}
|
|
377
|
+
return { action: "cancel", variant: "outlined", color: "inherit" };
|
|
378
|
+
}
|
|
379
|
+
if (subscription.status === "past_due") {
|
|
380
|
+
return { action: "pastDue", variant: "contained", color: "primary" };
|
|
381
|
+
}
|
|
382
|
+
return null;
|
|
383
|
+
};
|
|
384
|
+
export const mergeExtraParams = (extra = {}) => {
|
|
385
|
+
const params = new URLSearchParams(window.location.search);
|
|
386
|
+
Object.keys(extra).forEach((key) => {
|
|
387
|
+
params.set(key, extra[key]);
|
|
388
|
+
});
|
|
389
|
+
return params.toString();
|
|
390
|
+
};
|
package/lib/api.d.ts
ADDED
package/lib/api.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
|
|
7
|
+
var _context = require("@arcblock/ux/lib/Locale/context");
|
|
8
|
+
var _axios = _interopRequireDefault(require("axios"));
|
|
9
|
+
var _isNull = _interopRequireDefault(require("lodash/isNull"));
|
|
10
|
+
var _util = require("./util");
|
|
11
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
12
|
+
const api = _axios.default.create();
|
|
13
|
+
api.interceptors.request.use(config => {
|
|
14
|
+
const prefix = (0, _util.getPrefix)();
|
|
15
|
+
config.baseURL = prefix || "";
|
|
16
|
+
config.timeout = 8e3;
|
|
17
|
+
const livemode = localStorage.getItem("livemode");
|
|
18
|
+
const locale = (0, _context.getLocale)(window.blocklet.languages);
|
|
19
|
+
config.params = {
|
|
20
|
+
...(config.params || {}),
|
|
21
|
+
livemode: (0, _isNull.default)(livemode) ? true : JSON.parse(livemode),
|
|
22
|
+
locale
|
|
23
|
+
};
|
|
24
|
+
return config;
|
|
25
|
+
}, err => Promise.reject(err));
|
|
26
|
+
module.exports = api;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { CheckoutProps } from '../types';
|
|
3
|
+
declare function Checkout({ id, onPaid, onError, mode, extraParams }: CheckoutProps): import("react").JSX.Element;
|
|
4
|
+
declare namespace Checkout {
|
|
5
|
+
var defaultProps: {
|
|
6
|
+
onPaid: any;
|
|
7
|
+
onError: {
|
|
8
|
+
(...data: any[]): void;
|
|
9
|
+
(message?: any, ...optionalParams: any[]): void;
|
|
10
|
+
};
|
|
11
|
+
mode: string;
|
|
12
|
+
extraParams: {};
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export default Checkout;
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
module.exports = Checkout;
|
|
7
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
8
|
+
var _ahooks = require("ahooks");
|
|
9
|
+
var _noop = _interopRequireDefault(require("lodash/noop"));
|
|
10
|
+
var _react = require("react");
|
|
11
|
+
var _ufo = require("ufo");
|
|
12
|
+
var _api = _interopRequireDefault(require("../api"));
|
|
13
|
+
var _payment = _interopRequireDefault(require("../payment"));
|
|
14
|
+
var _util = require("../util");
|
|
15
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
|
+
const startFromPaymentLink = async (id, params) => {
|
|
17
|
+
const {
|
|
18
|
+
data
|
|
19
|
+
} = await _api.default.post(`/api/checkout-sessions/start/${id}?${(0, _util.mergeExtraParams)(params)}`);
|
|
20
|
+
return data;
|
|
21
|
+
};
|
|
22
|
+
const fetchCheckoutSession = async id => {
|
|
23
|
+
const {
|
|
24
|
+
data
|
|
25
|
+
} = await _api.default.get(`/api/checkout-sessions/retrieve/${id}`);
|
|
26
|
+
return data;
|
|
27
|
+
};
|
|
28
|
+
function Checkout({
|
|
29
|
+
id,
|
|
30
|
+
onPaid,
|
|
31
|
+
onError,
|
|
32
|
+
mode,
|
|
33
|
+
extraParams
|
|
34
|
+
}) {
|
|
35
|
+
if (!id.startsWith("plink_") && !id.startsWith("cs_")) {
|
|
36
|
+
throw new Error("Either a checkout session or a payment link id is required.");
|
|
37
|
+
}
|
|
38
|
+
const type = id.startsWith("plink_") ? "paymentLink" : "checkoutSession";
|
|
39
|
+
const [state, setState] = (0, _ahooks.useSetState)({
|
|
40
|
+
completed: false,
|
|
41
|
+
appError: null
|
|
42
|
+
});
|
|
43
|
+
const {
|
|
44
|
+
error: apiError,
|
|
45
|
+
data
|
|
46
|
+
} = (0, _ahooks.useRequest)(() => type === "paymentLink" ? startFromPaymentLink(id, extraParams) : fetchCheckoutSession(id));
|
|
47
|
+
(0, _react.useEffect)(() => {
|
|
48
|
+
if (type === "paymentLink" && mode === "standalone" && data) {
|
|
49
|
+
window.location.replace((0, _ufo.joinURL)((0, _util.getPrefix)(), `/checkout/pay/${data.checkoutSession.id}`));
|
|
50
|
+
}
|
|
51
|
+
}, [type, mode, data]);
|
|
52
|
+
const handlePaid = () => {
|
|
53
|
+
setState({
|
|
54
|
+
completed: true
|
|
55
|
+
});
|
|
56
|
+
onPaid?.(data);
|
|
57
|
+
};
|
|
58
|
+
const handleError = err => {
|
|
59
|
+
console.error(err);
|
|
60
|
+
setState({
|
|
61
|
+
appError: err
|
|
62
|
+
});
|
|
63
|
+
onError?.(err);
|
|
64
|
+
};
|
|
65
|
+
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_payment.default, {
|
|
66
|
+
checkoutSession: data?.checkoutSession,
|
|
67
|
+
paymentMethods: data?.paymentMethods,
|
|
68
|
+
paymentIntent: data?.paymentIntent,
|
|
69
|
+
paymentLink: data?.paymentLink,
|
|
70
|
+
customer: data?.customer,
|
|
71
|
+
completed: state.completed,
|
|
72
|
+
error: apiError || state.appError,
|
|
73
|
+
onPaid: handlePaid,
|
|
74
|
+
onError: handleError,
|
|
75
|
+
mode
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
Checkout.defaultProps = {
|
|
79
|
+
onPaid: _noop.default,
|
|
80
|
+
onError: console.error,
|
|
81
|
+
mode: "inline",
|
|
82
|
+
extraParams: {}
|
|
83
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { TextFieldProps } from '@mui/material';
|
|
3
|
+
import { RegisterOptions } from 'react-hook-form';
|
|
4
|
+
type InputProps = TextFieldProps & {
|
|
5
|
+
name: string;
|
|
6
|
+
label?: string;
|
|
7
|
+
placeholder?: string;
|
|
8
|
+
errorPosition?: 'right' | 'bottom';
|
|
9
|
+
rules?: RegisterOptions;
|
|
10
|
+
};
|
|
11
|
+
export declare function FormInputError({ error }: {
|
|
12
|
+
error: string;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
14
|
+
declare function FormInput({ name, label, placeholder, rules, errorPosition, ...rest }: InputProps): import("react").JSX.Element;
|
|
15
|
+
declare namespace FormInput {
|
|
16
|
+
var defaultProps: {
|
|
17
|
+
label: string;
|
|
18
|
+
placeholder: string;
|
|
19
|
+
errorPosition: string;
|
|
20
|
+
rules: {};
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export default FormInput;
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.FormInputError = FormInputError;
|
|
7
|
+
module.exports = FormInput;
|
|
8
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
9
|
+
var _material = require("@mui/material");
|
|
10
|
+
var _get = _interopRequireDefault(require("lodash/get"));
|
|
11
|
+
var _reactHookForm = require("react-hook-form");
|
|
12
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
|
+
FormInput.defaultProps = {
|
|
14
|
+
label: "",
|
|
15
|
+
placeholder: "",
|
|
16
|
+
errorPosition: "bottom",
|
|
17
|
+
rules: {}
|
|
18
|
+
};
|
|
19
|
+
function FormInputError({
|
|
20
|
+
error
|
|
21
|
+
}) {
|
|
22
|
+
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.InputAdornment, {
|
|
23
|
+
position: "end",
|
|
24
|
+
children: /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Typography, {
|
|
25
|
+
component: "span",
|
|
26
|
+
color: "error",
|
|
27
|
+
children: error
|
|
28
|
+
})
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
function FormInput({
|
|
32
|
+
name,
|
|
33
|
+
label,
|
|
34
|
+
placeholder,
|
|
35
|
+
rules,
|
|
36
|
+
errorPosition,
|
|
37
|
+
...rest
|
|
38
|
+
}) {
|
|
39
|
+
const {
|
|
40
|
+
control,
|
|
41
|
+
formState
|
|
42
|
+
} = (0, _reactHookForm.useFormContext)();
|
|
43
|
+
const error = (0, _get.default)(formState.errors, name)?.message;
|
|
44
|
+
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_reactHookForm.Controller, {
|
|
45
|
+
name,
|
|
46
|
+
control,
|
|
47
|
+
rules,
|
|
48
|
+
render: ({
|
|
49
|
+
field
|
|
50
|
+
}) => /* @__PURE__ */(0, _jsxRuntime.jsxs)(_material.Box, {
|
|
51
|
+
sx: {
|
|
52
|
+
width: "100%"
|
|
53
|
+
},
|
|
54
|
+
children: [!!label && /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.FormLabel, {
|
|
55
|
+
children: label
|
|
56
|
+
}), /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.TextField, {
|
|
57
|
+
fullWidth: true,
|
|
58
|
+
error: !!(0, _get.default)(formState.errors, name),
|
|
59
|
+
helperText: errorPosition === "bottom" && error ? error : "",
|
|
60
|
+
placeholder,
|
|
61
|
+
size: "small",
|
|
62
|
+
...field,
|
|
63
|
+
...rest,
|
|
64
|
+
InputProps: Object.assign(rest.InputProps || {}, errorPosition === "right" && error ? {
|
|
65
|
+
endAdornment: /* @__PURE__ */(0, _jsxRuntime.jsx)(FormInputError, {
|
|
66
|
+
error
|
|
67
|
+
})
|
|
68
|
+
} : {})
|
|
69
|
+
})]
|
|
70
|
+
})
|
|
71
|
+
});
|
|
72
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
module.exports = Livemode;
|
|
7
|
+
var _jsxRuntime = require("react/jsx-runtime");
|
|
8
|
+
var _context = require("@arcblock/ux/lib/Locale/context");
|
|
9
|
+
var _material = require("@mui/material");
|
|
10
|
+
function Livemode() {
|
|
11
|
+
const {
|
|
12
|
+
t
|
|
13
|
+
} = (0, _context.useLocaleContext)();
|
|
14
|
+
return /* @__PURE__ */(0, _jsxRuntime.jsx)(_material.Chip, {
|
|
15
|
+
label: t("common.livemode"),
|
|
16
|
+
size: "small",
|
|
17
|
+
sx: {
|
|
18
|
+
ml: 2,
|
|
19
|
+
height: 18,
|
|
20
|
+
lineHeight: 1,
|
|
21
|
+
textTransform: "uppercase",
|
|
22
|
+
fontSize: "0.8rem",
|
|
23
|
+
fontWeight: "bold",
|
|
24
|
+
borderRadius: "4px",
|
|
25
|
+
backgroundColor: "#ffde92",
|
|
26
|
+
color: "#bb5504"
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import type { TPricingTableExpanded } from '@blocklet/payment-types';
|
|
3
|
+
type Props = {
|
|
4
|
+
table: TPricingTableExpanded;
|
|
5
|
+
onSelect: (priceId: string) => void;
|
|
6
|
+
alignItems?: 'center' | 'left';
|
|
7
|
+
mode?: 'checkout' | 'select';
|
|
8
|
+
interval?: string;
|
|
9
|
+
};
|
|
10
|
+
declare function PricingTable({ table, alignItems, interval, mode, onSelect }: Props): import("react").JSX.Element;
|
|
11
|
+
declare namespace PricingTable {
|
|
12
|
+
var defaultProps: {
|
|
13
|
+
alignItems: string;
|
|
14
|
+
mode: string;
|
|
15
|
+
interval: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export default PricingTable;
|