@monei-js/components 1.7.10 → 2.1.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/README.md +100 -9
- package/dist/chunk.js +13 -0
- package/dist/index.cjs +1643 -0
- package/dist/index.d.cts +528 -0
- package/dist/index.d.ts +526 -14
- package/dist/index.js +1616 -7
- package/dist/monei.umd.production.min.js +64 -0
- package/dist/monei.umd.production.min.js.map +1 -0
- package/package.json +42 -65
- package/src/bridge/types.ts +106 -0
- package/dist/api.d.ts +0 -13
- package/dist/bizum/container.d.ts +0 -4
- package/dist/bizum/index.d.ts +0 -5
- package/dist/bizum/modal.d.ts +0 -3
- package/dist/bizum/types.d.ts +0 -30
- package/dist/cardInput/index.d.ts +0 -8
- package/dist/cardInput/types.d.ts +0 -51
- package/dist/click2pay/container.d.ts +0 -4
- package/dist/click2pay/index.d.ts +0 -5
- package/dist/click2pay/modal.d.ts +0 -3
- package/dist/click2pay/types.d.ts +0 -23
- package/dist/cofidis/container.d.ts +0 -4
- package/dist/cofidis/index.d.ts +0 -6
- package/dist/cofidis/modal.d.ts +0 -3
- package/dist/cofidis/types.d.ts +0 -45
- package/dist/cofidis/widget.d.ts +0 -3
- package/dist/cofidisLoan/container.d.ts +0 -4
- package/dist/cofidisLoan/index.d.ts +0 -7
- package/dist/cofidisLoan/modal.d.ts +0 -4
- package/dist/cofidisLoan/types.d.ts +0 -45
- package/dist/cofidisLoan/widget.d.ts +0 -4
- package/dist/components.cjs.development.js +0 -18997
- package/dist/components.cjs.development.js.map +0 -1
- package/dist/components.cjs.production.min.js +0 -2
- package/dist/components.cjs.production.min.js.map +0 -1
- package/dist/components.esm.js +0 -18979
- package/dist/components.esm.js.map +0 -1
- package/dist/config.d.ts +0 -28
- package/dist/container.d.ts +0 -1
- package/dist/enums.d.ts +0 -4
- package/dist/googlePay/index.d.ts +0 -4
- package/dist/googlePay/types.d.ts +0 -21
- package/dist/paymentModal/container.d.ts +0 -4
- package/dist/paymentModal/index.d.ts +0 -6
- package/dist/paymentModal/types.d.ts +0 -28
- package/dist/paymentRequest/index.d.ts +0 -4
- package/dist/paymentRequest/types.d.ts +0 -22
- package/dist/paypal/index.d.ts +0 -4
- package/dist/paypal/types.d.ts +0 -27
- package/dist/types.d.ts +0 -194
- package/dist/utils.d.ts +0 -11
- package/types/belter.d.ts +0 -1
- package/types/component.d.ts +0 -196
- package/types/css.d.ts +0 -9
- package/types/global.d.ts +0 -3
- package/types/post-robot.d.ts +0 -1
package/dist/index.js
CHANGED
|
@@ -1,8 +1,1617 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { t as __exportAll } from "./chunk.js";
|
|
2
|
+
import { Thumbmark } from "@thumbmarkjs/thumbmarkjs";
|
|
3
|
+
import { WindowMessenger, connect } from "penpal";
|
|
4
|
+
//#region ../shared/lib/utils.ts
|
|
5
|
+
/** Parse a CSS px value — accepts "10px", "10", or 10. Returns number or defaultValue. */
|
|
6
|
+
const parsePx = (value, defaultValue) => {
|
|
7
|
+
if (value == null) return defaultValue;
|
|
8
|
+
if (typeof value === "number") return value;
|
|
9
|
+
const parsed = parseInt(value);
|
|
10
|
+
return isNaN(parsed) ? defaultValue : parsed;
|
|
11
|
+
};
|
|
12
|
+
/** Convert a value to CSS px string — 10 → "10px", "10px" → "10px", undefined → defaultValue */
|
|
13
|
+
const toCssPx = (value, defaultValue) => {
|
|
14
|
+
if (value == null) return defaultValue;
|
|
15
|
+
return typeof value === "number" ? `${value}px` : value || defaultValue;
|
|
16
|
+
};
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/utils.ts
|
|
19
|
+
var utils_exports = /* @__PURE__ */ __exportAll({
|
|
20
|
+
fixLocalstorage: () => fixLocalstorage,
|
|
21
|
+
getClientEnv: () => getClientEnv,
|
|
22
|
+
getClientFingerprint: () => getClientFingerprint,
|
|
23
|
+
isMethodSupported: () => isMethodSupported,
|
|
24
|
+
loadScript: () => loadScript,
|
|
25
|
+
parsePx: () => parsePx,
|
|
26
|
+
stringify: () => stringify,
|
|
27
|
+
toCssPx: () => toCssPx,
|
|
28
|
+
transformStyle: () => transformStyle,
|
|
29
|
+
validateComponentProps: () => validateComponentProps,
|
|
30
|
+
withError: () => withError
|
|
31
|
+
});
|
|
32
|
+
const transformStyle = (style) => {
|
|
33
|
+
return Object.keys(style).reduce((result, prop) => {
|
|
34
|
+
if (typeof style[prop] === "string") result[prop] = style[prop];
|
|
35
|
+
else result[prop] = `${style[prop]}px`;
|
|
36
|
+
return result;
|
|
37
|
+
}, {});
|
|
38
|
+
};
|
|
39
|
+
const loadScript = (src, nonce, dataset, forceReload) => {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
const existingScript = Array.from(document.scripts).find((s) => s.src === src);
|
|
42
|
+
if (existingScript) if (forceReload) existingScript.remove();
|
|
43
|
+
else {
|
|
44
|
+
resolve(existingScript);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
const script = document.createElement("script");
|
|
48
|
+
script.src = src;
|
|
49
|
+
script.onload = () => resolve(script);
|
|
50
|
+
script.onerror = (error) => reject(error);
|
|
51
|
+
if (nonce) script.setAttribute("nonce", nonce);
|
|
52
|
+
if (dataset) Object.keys(dataset).forEach((key) => {
|
|
53
|
+
if (dataset[key]) script.dataset[key] = dataset[key];
|
|
54
|
+
});
|
|
55
|
+
document.head.appendChild(script);
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
const stringify = (obj) => {
|
|
59
|
+
return Object.keys(obj).reduce((result, key) => {
|
|
60
|
+
if (obj[key] !== void 0) result.push(`${encodeURIComponent(key)}=${encodeURIComponent(obj[key])}`);
|
|
61
|
+
return result;
|
|
62
|
+
}, []).join("&");
|
|
63
|
+
};
|
|
64
|
+
const fixLocalstorage = () => {
|
|
65
|
+
let localStorage;
|
|
66
|
+
try {
|
|
67
|
+
({localStorage} = window);
|
|
68
|
+
} catch (_error) {
|
|
69
|
+
const localStorageMock = (() => {
|
|
70
|
+
let store = /* @__PURE__ */ new Map();
|
|
71
|
+
return {
|
|
72
|
+
getItem(key) {
|
|
73
|
+
return store.get(key);
|
|
74
|
+
},
|
|
75
|
+
setItem: function(key, value) {
|
|
76
|
+
store.set(key, value);
|
|
77
|
+
},
|
|
78
|
+
clear: function() {
|
|
79
|
+
store = /* @__PURE__ */ new Map();
|
|
80
|
+
},
|
|
81
|
+
removeItem: function(key) {
|
|
82
|
+
store.delete(key);
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
})();
|
|
86
|
+
Object.defineProperty(window, "localStorage", { value: localStorageMock });
|
|
87
|
+
localStorage = localStorageMock;
|
|
88
|
+
}
|
|
89
|
+
return localStorage;
|
|
90
|
+
};
|
|
91
|
+
const getLanguage = () => {
|
|
92
|
+
let lang;
|
|
93
|
+
const localStorage = fixLocalstorage();
|
|
94
|
+
try {
|
|
95
|
+
lang = localStorage.getItem("i18nextLng");
|
|
96
|
+
} catch (_error) {}
|
|
97
|
+
if (lang) return lang.split("-")[0];
|
|
98
|
+
return "";
|
|
99
|
+
};
|
|
100
|
+
const safe = (fn) => {
|
|
101
|
+
try {
|
|
102
|
+
return String(fn() ?? "");
|
|
103
|
+
} catch {
|
|
104
|
+
return "";
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const getClientEnv = () => {
|
|
108
|
+
const env = [
|
|
109
|
+
window.screen.colorDepth,
|
|
110
|
+
window.screen.height,
|
|
111
|
+
window.screen.width,
|
|
112
|
+
(/* @__PURE__ */ new Date()).getTimezoneOffset(),
|
|
113
|
+
getLanguage(),
|
|
114
|
+
safe(() => Intl.DateTimeFormat().resolvedOptions().timeZone),
|
|
115
|
+
safe(() => window.devicePixelRatio),
|
|
116
|
+
safe(() => navigator.hardwareConcurrency),
|
|
117
|
+
safe(() => navigator.deviceMemory),
|
|
118
|
+
safe(() => navigator.maxTouchPoints),
|
|
119
|
+
safe(() => navigator.platform),
|
|
120
|
+
safe(() => navigator.languages?.join(","))
|
|
121
|
+
].join("|");
|
|
122
|
+
return window.btoa(env);
|
|
123
|
+
};
|
|
124
|
+
const FINGERPRINT_SEED_KEY = "_monei_fp";
|
|
125
|
+
const randomHex = (length) => {
|
|
126
|
+
if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
|
|
127
|
+
const bytes = new Uint8Array(length);
|
|
128
|
+
crypto.getRandomValues(bytes);
|
|
129
|
+
return Array.from(bytes, (b) => (b % 16).toString(16)).join("");
|
|
130
|
+
}
|
|
131
|
+
return Array.from({ length }, () => Math.floor(Math.random() * 16).toString(16)).join("");
|
|
132
|
+
};
|
|
133
|
+
let cachedSeed = null;
|
|
134
|
+
const getPersistentSeed = async () => {
|
|
135
|
+
if (cachedSeed) return { seed: cachedSeed };
|
|
136
|
+
try {
|
|
137
|
+
const localStorage = fixLocalstorage();
|
|
138
|
+
cachedSeed = localStorage.getItem(FINGERPRINT_SEED_KEY);
|
|
139
|
+
if (!cachedSeed) {
|
|
140
|
+
cachedSeed = randomHex(32);
|
|
141
|
+
try {
|
|
142
|
+
localStorage.setItem(FINGERPRINT_SEED_KEY, cachedSeed);
|
|
143
|
+
} catch {}
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
cachedSeed = randomHex(32);
|
|
147
|
+
}
|
|
148
|
+
return { seed: cachedSeed };
|
|
149
|
+
};
|
|
150
|
+
const thumbmark = new Thumbmark({ logging: false });
|
|
151
|
+
thumbmark.includeComponent("persistent_seed", getPersistentSeed);
|
|
152
|
+
const getClientFingerprint = async () => {
|
|
153
|
+
const [, result] = await withError(thumbmark.get());
|
|
154
|
+
return result?.thumbmark ?? "";
|
|
155
|
+
};
|
|
156
|
+
const withError = async (p) => {
|
|
157
|
+
try {
|
|
158
|
+
return [void 0, await p];
|
|
159
|
+
} catch (err) {
|
|
160
|
+
return [err, void 0];
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const isMethodSupported = (config, paymentMethod) => {
|
|
164
|
+
return config.paymentMethods && config.paymentMethods.includes(paymentMethod);
|
|
165
|
+
};
|
|
166
|
+
const validateComponentProps = ({ props }) => {
|
|
167
|
+
if (props.paymentId) return;
|
|
168
|
+
if (props.accountId && props.amount && props.currency) return;
|
|
169
|
+
throw new Error("You need to provide paymentId or accountId amount and currency");
|
|
170
|
+
};
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/config.ts
|
|
173
|
+
const stage = import.meta.env?.STAGE || "prod";
|
|
174
|
+
const domain = import.meta.env?.DOMAIN || "http://localhost:3001";
|
|
175
|
+
const version = import.meta.env?.VERSION || "v1";
|
|
176
|
+
const globalConfig = {
|
|
177
|
+
domain,
|
|
178
|
+
version,
|
|
179
|
+
stage,
|
|
180
|
+
isProduction: stage === "prod",
|
|
181
|
+
paymentPageUrl: `${domain}/${version}/payment-page/`,
|
|
182
|
+
cardInputUrl: `${domain}/${version}/inner-card-input/`,
|
|
183
|
+
paymentModalUrl: `${domain}/${version}/inner-payment-modal/`,
|
|
184
|
+
paypalUrl: `${domain}/${version}/inner-paypal/`,
|
|
185
|
+
bizumUrl: `${domain}/${version}/inner-bizum/`,
|
|
186
|
+
bizumButtonUrl: `${domain}/${version}/inner-bizum-button/`,
|
|
187
|
+
paymentRequestUrl: `${domain}/${version}/inner-payment-request/`
|
|
188
|
+
};
|
|
189
|
+
const stageConfig = {
|
|
190
|
+
local: {
|
|
191
|
+
domain: "http://card-input.localhost:1355",
|
|
192
|
+
paymentPageUrl: `http://payment-page.localhost:1355/`,
|
|
193
|
+
cardInputUrl: `http://card-input.localhost:1355/`,
|
|
194
|
+
paymentModalUrl: `http://payment-modal.localhost:1355/`,
|
|
195
|
+
paypalUrl: `http://paypal.localhost:1355/`,
|
|
196
|
+
bizumUrl: `http://bizum.localhost:1355/`,
|
|
197
|
+
bizumButtonUrl: `http://bizum-button.localhost:1355/`,
|
|
198
|
+
paymentRequestUrl: `http://payment-request.localhost:1355/`,
|
|
199
|
+
apiUrl: "https://api.monei-dev.com/v1",
|
|
200
|
+
secureDomain: "https://secure.monei-dev.com",
|
|
201
|
+
rootDomain: "monei-dev.com"
|
|
202
|
+
},
|
|
203
|
+
"local-prod": {
|
|
204
|
+
domain: "http://card-input.localhost:1355",
|
|
205
|
+
paymentPageUrl: `http://payment-page.localhost:1355/`,
|
|
206
|
+
cardInputUrl: `http://card-input.localhost:1355/`,
|
|
207
|
+
paymentModalUrl: `http://payment-modal.localhost:1355/`,
|
|
208
|
+
paypalUrl: `http://paypal.localhost:1355/`,
|
|
209
|
+
bizumUrl: `http://bizum.localhost:1355/`,
|
|
210
|
+
bizumButtonUrl: `http://bizum-button.localhost:1355/`,
|
|
211
|
+
paymentRequestUrl: `http://payment-request.localhost:1355/`,
|
|
212
|
+
apiUrl: "https://api.monei.com/v1",
|
|
213
|
+
secureDomain: "https://secure.monei.com",
|
|
214
|
+
rootDomain: "monei.com"
|
|
215
|
+
},
|
|
216
|
+
tunnel: {
|
|
217
|
+
domain: "https://card-input.monei-dev-tunnel.com",
|
|
218
|
+
paymentPageUrl: `https://payment-page.monei-dev-tunnel.com/`,
|
|
219
|
+
cardInputUrl: `https://card-input.monei-dev-tunnel.com/`,
|
|
220
|
+
paymentModalUrl: `https://payment-modal.monei-dev-tunnel.com/`,
|
|
221
|
+
paypalUrl: `https://paypal.monei-dev-tunnel.com/`,
|
|
222
|
+
bizumUrl: `https://bizum.monei-dev-tunnel.com/`,
|
|
223
|
+
bizumButtonUrl: `https://bizum-button.monei-dev-tunnel.com/`,
|
|
224
|
+
paymentRequestUrl: `https://payment-request.monei-dev-tunnel.com/`,
|
|
225
|
+
apiUrl: "https://api.monei.com/v1",
|
|
226
|
+
secureDomain: "https://secure.monei.com",
|
|
227
|
+
rootDomain: "monei.com"
|
|
228
|
+
},
|
|
229
|
+
dev: {
|
|
230
|
+
apiUrl: "https://api.monei-dev.com/v1",
|
|
231
|
+
secureDomain: "https://secure.monei-dev.com",
|
|
232
|
+
rootDomain: "monei-dev.com"
|
|
233
|
+
},
|
|
234
|
+
prod: {
|
|
235
|
+
apiUrl: "https://api.monei.com/v1",
|
|
236
|
+
secureDomain: "https://secure.monei.com",
|
|
237
|
+
rootDomain: "monei.com"
|
|
238
|
+
}
|
|
239
|
+
};
|
|
240
|
+
const config = {
|
|
241
|
+
...globalConfig,
|
|
242
|
+
...stageConfig[stage]
|
|
243
|
+
};
|
|
244
|
+
//#endregion
|
|
245
|
+
//#region src/api.ts
|
|
246
|
+
var api_exports = /* @__PURE__ */ __exportAll({
|
|
247
|
+
confirmPayment: () => confirmPayment$1,
|
|
248
|
+
createApplePaySession: () => createApplePaySession,
|
|
249
|
+
createPayment: () => createPayment,
|
|
250
|
+
createToken: () => createToken$1,
|
|
251
|
+
getPayment: () => getPayment,
|
|
252
|
+
getPaymentMethods: () => getPaymentMethods,
|
|
253
|
+
sendPaymentReceipt: () => sendPaymentReceipt,
|
|
254
|
+
validateBizumPhone: () => validateBizumPhone
|
|
255
|
+
});
|
|
256
|
+
const request = async (url, { method = "GET", headers, body } = {}) => {
|
|
257
|
+
if (method === "POST") headers = {
|
|
258
|
+
"Content-Type": "application/json",
|
|
259
|
+
...headers
|
|
260
|
+
};
|
|
261
|
+
const res = await fetch(`${config.apiUrl}${url}`, {
|
|
262
|
+
method,
|
|
263
|
+
headers,
|
|
264
|
+
body: body ? JSON.stringify(body) : void 0
|
|
265
|
+
});
|
|
266
|
+
const [, result] = await withError(res.json());
|
|
267
|
+
if (!res.ok) throw result || new Error(res.statusText);
|
|
268
|
+
return result;
|
|
269
|
+
};
|
|
270
|
+
const pmCache = {};
|
|
271
|
+
const getPaymentMethods = async ({ accountId, paymentId }) => {
|
|
272
|
+
let params;
|
|
273
|
+
if (paymentId) params = { paymentId };
|
|
274
|
+
else if (accountId) params = { accountId };
|
|
275
|
+
else throw new Error("You need to provide paymentId or accountId");
|
|
276
|
+
const query = stringify(params);
|
|
277
|
+
if (query in pmCache) return pmCache[query];
|
|
278
|
+
const promise = request(`/payment-methods?${query}`);
|
|
279
|
+
pmCache[query] = promise;
|
|
280
|
+
promise.catch(() => delete pmCache[query]);
|
|
281
|
+
return promise;
|
|
282
|
+
};
|
|
283
|
+
const createToken$1 = async ({ paymentMethod, paymentId, sessionId, amount, currency, accountId, transactionType }) => {
|
|
284
|
+
let body;
|
|
285
|
+
if (paymentId) body = {
|
|
286
|
+
paymentId,
|
|
287
|
+
paymentMethod
|
|
288
|
+
};
|
|
289
|
+
else if (accountId) body = {
|
|
290
|
+
accountId,
|
|
291
|
+
paymentMethod,
|
|
292
|
+
sessionId,
|
|
293
|
+
amount,
|
|
294
|
+
currency,
|
|
295
|
+
transactionType
|
|
296
|
+
};
|
|
297
|
+
else throw new Error("You need to provide paymentId or accountId");
|
|
298
|
+
const res = await request("/tokens", {
|
|
299
|
+
method: "POST",
|
|
300
|
+
headers: {
|
|
301
|
+
"X-Client-Env": getClientEnv(),
|
|
302
|
+
"X-Client-Fingerprint": await getClientFingerprint()
|
|
303
|
+
},
|
|
304
|
+
body
|
|
305
|
+
});
|
|
306
|
+
if (res.paymentToken && !res.token) res.token = res.paymentToken;
|
|
307
|
+
return res;
|
|
308
|
+
};
|
|
309
|
+
const getPayment = async (paymentId) => {
|
|
310
|
+
return request(`/payments/${paymentId}/client-get`);
|
|
311
|
+
};
|
|
312
|
+
const confirmPayment$1 = async ({ paymentId, ...body }) => {
|
|
313
|
+
return request(`/payments/${paymentId}/client-confirm`, {
|
|
314
|
+
method: "POST",
|
|
315
|
+
headers: {
|
|
316
|
+
"X-Client-Env": getClientEnv(),
|
|
317
|
+
"X-Client-Fingerprint": await getClientFingerprint()
|
|
318
|
+
},
|
|
319
|
+
body
|
|
320
|
+
});
|
|
321
|
+
};
|
|
322
|
+
const createPayment = async ({ signature, ...body }) => {
|
|
323
|
+
return request("/client-payments", {
|
|
324
|
+
method: "POST",
|
|
325
|
+
headers: {
|
|
326
|
+
"X-Client-Env": getClientEnv(),
|
|
327
|
+
"X-Client-Fingerprint": await getClientFingerprint(),
|
|
328
|
+
"Client-Signature": signature
|
|
329
|
+
},
|
|
330
|
+
body
|
|
331
|
+
});
|
|
332
|
+
};
|
|
333
|
+
const sendPaymentReceipt = async ({ paymentId, customerEmail, signature, language }) => {
|
|
334
|
+
return request(`/payments/${paymentId}/client-receipt`, {
|
|
335
|
+
method: "POST",
|
|
336
|
+
body: {
|
|
337
|
+
customerEmail,
|
|
338
|
+
signature,
|
|
339
|
+
language
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
};
|
|
343
|
+
const createApplePaySession = async (body) => {
|
|
344
|
+
return request(`/apple-pay/sessions`, {
|
|
345
|
+
method: "POST",
|
|
346
|
+
body
|
|
347
|
+
});
|
|
348
|
+
};
|
|
349
|
+
const validateBizumPhone = async (req) => {
|
|
350
|
+
return request(`/bizum/validate-phone`, {
|
|
351
|
+
method: "POST",
|
|
352
|
+
body: req
|
|
353
|
+
});
|
|
354
|
+
};
|
|
355
|
+
//#endregion
|
|
356
|
+
//#region src/bridge/event-emitter.ts
|
|
357
|
+
var EventEmitter = class {
|
|
358
|
+
constructor() {
|
|
359
|
+
this.listeners = /* @__PURE__ */ new Map();
|
|
360
|
+
this.destroyed = false;
|
|
361
|
+
}
|
|
362
|
+
on(eventName, handler) {
|
|
363
|
+
if (this.destroyed) return { cancel: () => {} };
|
|
364
|
+
let handlers = this.listeners.get(eventName);
|
|
365
|
+
if (!handlers) {
|
|
366
|
+
handlers = /* @__PURE__ */ new Set();
|
|
367
|
+
this.listeners.set(eventName, handlers);
|
|
368
|
+
}
|
|
369
|
+
handlers.add(handler);
|
|
370
|
+
return { cancel: () => {
|
|
371
|
+
handlers.delete(handler);
|
|
372
|
+
} };
|
|
373
|
+
}
|
|
374
|
+
once(eventName, handler) {
|
|
375
|
+
const wrapper = (...args) => {
|
|
376
|
+
this.off(eventName, wrapper);
|
|
377
|
+
handler(...args);
|
|
378
|
+
};
|
|
379
|
+
return this.on(eventName, wrapper);
|
|
380
|
+
}
|
|
381
|
+
trigger(eventName, ...args) {
|
|
382
|
+
if (this.destroyed) return;
|
|
383
|
+
const handlers = this.listeners.get(eventName);
|
|
384
|
+
if (!handlers) return;
|
|
385
|
+
for (const handler of handlers) try {
|
|
386
|
+
handler(...args);
|
|
387
|
+
} catch (e) {
|
|
388
|
+
console.error(e);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
off(eventName, handler) {
|
|
392
|
+
const handlers = this.listeners.get(eventName);
|
|
393
|
+
if (handlers) handlers.delete(handler);
|
|
394
|
+
}
|
|
395
|
+
destroy() {
|
|
396
|
+
this.destroyed = true;
|
|
397
|
+
this.listeners.clear();
|
|
398
|
+
}
|
|
399
|
+
};
|
|
400
|
+
//#endregion
|
|
401
|
+
//#region src/bridge/drivers/react.ts
|
|
402
|
+
/**
|
|
403
|
+
* Creates a React driver for a bridge component.
|
|
404
|
+
* Returns a forwardRef component that:
|
|
405
|
+
* - On mount: creates bridge instance, renders into container div
|
|
406
|
+
* - On update: calls updateProps with new props
|
|
407
|
+
* - On unmount: calls destroy
|
|
408
|
+
* - Exposes bridge instance via ref (needed for CardInput's createToken)
|
|
409
|
+
*/
|
|
410
|
+
function createReactDriver(ComponentClass) {
|
|
411
|
+
return ({ React }) => {
|
|
412
|
+
return React.forwardRef(function BridgeReactWrapper(props, ref) {
|
|
413
|
+
const containerRef = React.useRef(null);
|
|
414
|
+
const instanceRef = React.useRef(null);
|
|
415
|
+
const isMounted = React.useRef(false);
|
|
416
|
+
const prevPropsRef = React.useRef({});
|
|
417
|
+
React.useLayoutEffect(() => {
|
|
418
|
+
const el = containerRef.current;
|
|
419
|
+
if (!el) return;
|
|
420
|
+
let cancelled = false;
|
|
421
|
+
const instance = new ComponentClass({ ...props });
|
|
422
|
+
instanceRef.current = instance;
|
|
423
|
+
instance.render(el).then(() => {
|
|
424
|
+
if (cancelled) return;
|
|
425
|
+
setRef(ref, instance);
|
|
426
|
+
isMounted.current = true;
|
|
427
|
+
}).catch((err) => {
|
|
428
|
+
if (!cancelled) console.error(err);
|
|
429
|
+
});
|
|
430
|
+
return () => {
|
|
431
|
+
cancelled = true;
|
|
432
|
+
instance.destroy();
|
|
433
|
+
instanceRef.current = null;
|
|
434
|
+
isMounted.current = false;
|
|
435
|
+
setRef(ref, null);
|
|
436
|
+
};
|
|
437
|
+
}, []);
|
|
438
|
+
React.useEffect(() => {
|
|
439
|
+
if (!isMounted.current || !instanceRef.current) return;
|
|
440
|
+
if (shallowEqual$1(prevPropsRef.current, props)) return;
|
|
441
|
+
prevPropsRef.current = { ...props };
|
|
442
|
+
instanceRef.current.updateProps({ ...props }).catch(noop);
|
|
443
|
+
});
|
|
444
|
+
return React.createElement("div", { ref: containerRef });
|
|
445
|
+
});
|
|
446
|
+
};
|
|
8
447
|
}
|
|
448
|
+
/** Set a React ref (callback or object ref) */
|
|
449
|
+
function setRef(ref, value) {
|
|
450
|
+
if (typeof ref === "function") ref(value);
|
|
451
|
+
else if (ref) ref.current = value;
|
|
452
|
+
}
|
|
453
|
+
function shallowEqual$1(a, b) {
|
|
454
|
+
const keysA = Object.keys(a);
|
|
455
|
+
const keysB = Object.keys(b);
|
|
456
|
+
if (keysA.length !== keysB.length) return false;
|
|
457
|
+
for (const key of keysA) if (a[key] !== b[key]) return false;
|
|
458
|
+
return true;
|
|
459
|
+
}
|
|
460
|
+
function noop() {}
|
|
461
|
+
//#endregion
|
|
462
|
+
//#region src/bridge/drivers/vue.ts
|
|
463
|
+
/**
|
|
464
|
+
* Creates a Vue 3 driver for a bridge component.
|
|
465
|
+
* Returns a Vue component options object that:
|
|
466
|
+
* - On mounted: creates bridge instance, renders into template div
|
|
467
|
+
* - On $attrs change: calls updateProps
|
|
468
|
+
* - On unmounted: calls destroy
|
|
469
|
+
* - Exposes bridge instance via template ref
|
|
470
|
+
*/
|
|
471
|
+
function createVue3Driver(ComponentClass) {
|
|
472
|
+
return () => ({
|
|
473
|
+
template: "<div></div>",
|
|
474
|
+
inheritAttrs: false,
|
|
475
|
+
mounted() {
|
|
476
|
+
const el = this.$el;
|
|
477
|
+
this.__bridgeInstance = new ComponentClass({ ...this.$attrs });
|
|
478
|
+
this.__bridgeInstance.render(el).catch(console.error);
|
|
479
|
+
},
|
|
480
|
+
watch: { $attrs: {
|
|
481
|
+
handler() {
|
|
482
|
+
if (this.__bridgeInstance) this.__bridgeInstance.updateProps({ ...this.$attrs }).catch(() => {});
|
|
483
|
+
},
|
|
484
|
+
deep: true
|
|
485
|
+
} },
|
|
486
|
+
unmounted() {
|
|
487
|
+
if (this.__bridgeInstance) {
|
|
488
|
+
this.__bridgeInstance.destroy();
|
|
489
|
+
this.__bridgeInstance = null;
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
}
|
|
494
|
+
//#endregion
|
|
495
|
+
//#region src/bridge/drivers/angular.ts
|
|
496
|
+
/**
|
|
497
|
+
* Creates an Angular driver for a bridge component.
|
|
498
|
+
* Returns an Angular module that declares a component which:
|
|
499
|
+
* - On ngOnInit: creates bridge instance, renders into host element
|
|
500
|
+
* - On ngDoCheck: calls updateProps when props change
|
|
501
|
+
* - On ngOnDestroy: calls destroy
|
|
502
|
+
*/
|
|
503
|
+
function createAngularDriver(ComponentClass) {
|
|
504
|
+
return ({ Component: AngularComponent, NgModule, ElementRef, NgZone, Inject }) => {
|
|
505
|
+
class ComponentInstance {
|
|
506
|
+
constructor(elementRef, zone) {
|
|
507
|
+
this.props = {};
|
|
508
|
+
this.__bridgeInstance = null;
|
|
509
|
+
this._prevProps = {};
|
|
510
|
+
this.elementRef = elementRef;
|
|
511
|
+
this.zone = zone;
|
|
512
|
+
}
|
|
513
|
+
getProps() {
|
|
514
|
+
const wrapped = { ...this.props };
|
|
515
|
+
const { zone } = this;
|
|
516
|
+
for (const [key, value] of Object.entries(wrapped)) if (typeof value === "function") wrapped[key] = function angularZoneWrapped(...args) {
|
|
517
|
+
return zone.run(() => value.apply(this, args));
|
|
518
|
+
};
|
|
519
|
+
return wrapped;
|
|
520
|
+
}
|
|
521
|
+
ngOnInit() {
|
|
522
|
+
const el = this.elementRef.nativeElement;
|
|
523
|
+
this.__bridgeInstance = new ComponentClass(this.getProps());
|
|
524
|
+
this.__bridgeInstance.render(el).catch(console.error);
|
|
525
|
+
}
|
|
526
|
+
ngDoCheck() {
|
|
527
|
+
if (this.__bridgeInstance && !shallowEqual(this._prevProps, this.props)) {
|
|
528
|
+
this._prevProps = { ...this.props };
|
|
529
|
+
this.__bridgeInstance.updateProps(this.getProps()).catch(() => {});
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
ngOnDestroy() {
|
|
533
|
+
if (this.__bridgeInstance) {
|
|
534
|
+
this.__bridgeInstance.destroy();
|
|
535
|
+
this.__bridgeInstance = null;
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
ComponentInstance.parameters = [[new Inject(ElementRef)], [new Inject(NgZone)]];
|
|
540
|
+
ComponentInstance.annotations = [new AngularComponent({
|
|
541
|
+
selector: ComponentClass.prototype.__options?.tag || "bridge-component",
|
|
542
|
+
template: "<div></div>",
|
|
543
|
+
inputs: ["props"]
|
|
544
|
+
})];
|
|
545
|
+
class ModuleInstance {}
|
|
546
|
+
ModuleInstance.annotations = [new NgModule({
|
|
547
|
+
declarations: [ComponentInstance],
|
|
548
|
+
exports: [ComponentInstance]
|
|
549
|
+
})];
|
|
550
|
+
return ModuleInstance;
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
function shallowEqual(a, b) {
|
|
554
|
+
const keysA = Object.keys(a);
|
|
555
|
+
const keysB = Object.keys(b);
|
|
556
|
+
if (keysA.length !== keysB.length) return false;
|
|
557
|
+
for (const key of keysA) if (a[key] !== b[key]) return false;
|
|
558
|
+
return true;
|
|
559
|
+
}
|
|
560
|
+
//#endregion
|
|
561
|
+
//#region src/bridge/drivers/index.ts
|
|
562
|
+
const driverFactories = {
|
|
563
|
+
react: createReactDriver,
|
|
564
|
+
vue3: createVue3Driver,
|
|
565
|
+
angular: createAngularDriver
|
|
566
|
+
};
|
|
567
|
+
/**
|
|
568
|
+
* Resolve a framework driver for a bridge component constructor.
|
|
569
|
+
* Called by both the static and instance .driver() methods.
|
|
570
|
+
*/
|
|
571
|
+
function resolveDriver(name, ComponentClass, deps) {
|
|
572
|
+
const factory = driverFactories[name];
|
|
573
|
+
if (!factory) throw new Error(`Unknown driver: ${name}. Available: ${Object.keys(driverFactories).join(", ")}`);
|
|
574
|
+
return factory(ComponentClass)(deps);
|
|
575
|
+
}
|
|
576
|
+
//#endregion
|
|
577
|
+
//#region ../shared/lib/bridge-constants.ts
|
|
578
|
+
/** Protocol identifier for sync bootstrap messages between parent and child bridges */
|
|
579
|
+
const BRIDGE_INIT_MSG = "__monei_bridge_init__";
|
|
580
|
+
//#endregion
|
|
581
|
+
//#region src/bridge/constants.ts
|
|
582
|
+
/** Bridge lifecycle events (replaces zoid.EVENT) */
|
|
583
|
+
const EVENT = {
|
|
584
|
+
RENDERED: "RENDERED",
|
|
585
|
+
RESIZE: "RESIZE",
|
|
586
|
+
DESTROY: "DESTROY",
|
|
587
|
+
BEFORE_CLOSE: "BEFORE_CLOSE",
|
|
588
|
+
DISPLAY: "DISPLAY"
|
|
589
|
+
};
|
|
590
|
+
/**
|
|
591
|
+
* Lifecycle prop names filtered from dispatch.
|
|
592
|
+
* These are wired to bridge events, NOT forwarded to child.
|
|
593
|
+
*
|
|
594
|
+
* NOTE: onError, onFocus, onLoad are NOT here — they are user-facing
|
|
595
|
+
* props that merchants pass and children call.
|
|
596
|
+
*/
|
|
597
|
+
const LIFECYCLE_PROPS = new Set([
|
|
598
|
+
"onClose",
|
|
599
|
+
"onDestroy",
|
|
600
|
+
"onRendered",
|
|
601
|
+
"onRender",
|
|
602
|
+
"onDisplay",
|
|
603
|
+
"onResize",
|
|
604
|
+
"onProps"
|
|
605
|
+
]);
|
|
606
|
+
//#endregion
|
|
607
|
+
//#region src/bridge/props.ts
|
|
608
|
+
/**
|
|
609
|
+
* DOM utility: remove element from DOM.
|
|
610
|
+
* Replaces belter.destroyElement().
|
|
611
|
+
*/
|
|
612
|
+
function destroyElement(el) {
|
|
613
|
+
if (el) el.remove();
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* CSS utility: convert number to CSS pixel string, pass strings through.
|
|
617
|
+
* Replaces belter.toCSS().
|
|
618
|
+
*/
|
|
619
|
+
function toCSS(value) {
|
|
620
|
+
return typeof value === "number" ? `${value}px` : value;
|
|
621
|
+
}
|
|
622
|
+
//#endregion
|
|
623
|
+
//#region src/bridge/render.ts
|
|
624
|
+
/** Per-component iframe sandbox attributes — least-privilege per component */
|
|
625
|
+
const SANDBOX = {
|
|
626
|
+
"monei-card-input": "allow-scripts allow-forms allow-same-origin",
|
|
627
|
+
"monei-bizum": "allow-scripts allow-forms allow-same-origin allow-popups allow-popups-to-escape-sandbox",
|
|
628
|
+
"monei-bizum-button": "allow-scripts allow-forms allow-same-origin allow-popups allow-popups-to-escape-sandbox",
|
|
629
|
+
"monei-paypal": "allow-scripts allow-forms allow-same-origin allow-popups allow-popups-to-escape-sandbox",
|
|
630
|
+
"monei-payment-request": "allow-scripts allow-forms allow-same-origin allow-popups",
|
|
631
|
+
"monei-payment-modal": "allow-scripts allow-forms allow-same-origin allow-popups allow-popups-to-escape-sandbox",
|
|
632
|
+
"monei-google-pay": "allow-scripts allow-forms allow-same-origin allow-popups"
|
|
633
|
+
};
|
|
634
|
+
/**
|
|
635
|
+
* Resolve a container argument to an HTMLElement.
|
|
636
|
+
*/
|
|
637
|
+
function resolveContainer(container) {
|
|
638
|
+
if (!container) throw new Error("render() requires a container element or selector");
|
|
639
|
+
if (typeof container === "string") {
|
|
640
|
+
const el = document.querySelector(container);
|
|
641
|
+
if (!el) throw new Error(`Container not found: ${container}`);
|
|
642
|
+
return el;
|
|
643
|
+
}
|
|
644
|
+
return container;
|
|
645
|
+
}
|
|
646
|
+
/**
|
|
647
|
+
* Generate a unique ID for the container element.
|
|
648
|
+
*/
|
|
649
|
+
function generateUid(tag) {
|
|
650
|
+
return `${tag}-${Math.random().toString(36).slice(2, 9)}`;
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Wire lifecycle props to bridge events.
|
|
654
|
+
* Called before containerTemplate in the render flow.
|
|
655
|
+
*/
|
|
656
|
+
function wireLifecycleProps(props, event) {
|
|
657
|
+
const mappings = [
|
|
658
|
+
["onClose", EVENT.DESTROY],
|
|
659
|
+
["onDestroy", EVENT.DESTROY],
|
|
660
|
+
["onRendered", EVENT.RENDERED],
|
|
661
|
+
["onResize", EVENT.RESIZE]
|
|
662
|
+
];
|
|
663
|
+
for (const [propKey, eventName] of mappings) event.on(eventName, (...args) => {
|
|
664
|
+
const fn = props[propKey];
|
|
665
|
+
if (typeof fn === "function") fn(...args);
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Execute computed prop factories and classify remaining user props into
|
|
670
|
+
* dispatch map (functions) and data payload (everything else).
|
|
671
|
+
* Called AFTER containerTemplate so computed factories see state mutations.
|
|
672
|
+
*/
|
|
673
|
+
function buildDataPayload(context) {
|
|
674
|
+
const { props, options, state, dispatchMap } = context;
|
|
675
|
+
const dataProps = {};
|
|
676
|
+
const computedPropKeys = new Set(options.computedProps ? Object.keys(options.computedProps) : []);
|
|
677
|
+
if (options.computedProps) for (const [key, config] of Object.entries(options.computedProps)) {
|
|
678
|
+
const result = config.factory({
|
|
679
|
+
props,
|
|
680
|
+
state
|
|
681
|
+
});
|
|
682
|
+
if (config.type === "function") dispatchMap.set(key, result);
|
|
683
|
+
else dataProps[key] = result;
|
|
684
|
+
}
|
|
685
|
+
for (const [key, value] of Object.entries(props)) {
|
|
686
|
+
if (LIFECYCLE_PROPS.has(key) || computedPropKeys.has(key)) continue;
|
|
687
|
+
if (typeof value === "function") dispatchMap.set(key, value);
|
|
688
|
+
else dataProps[key] = value;
|
|
689
|
+
}
|
|
690
|
+
const builtins = new Set([
|
|
691
|
+
"close",
|
|
692
|
+
"resize",
|
|
693
|
+
"show",
|
|
694
|
+
"hide",
|
|
695
|
+
"getParentDomain",
|
|
696
|
+
"focus"
|
|
697
|
+
]);
|
|
698
|
+
return {
|
|
699
|
+
dataProps,
|
|
700
|
+
functionNames: [...dispatchMap.keys()].filter((k) => !builtins.has(k))
|
|
701
|
+
};
|
|
702
|
+
}
|
|
703
|
+
/**
|
|
704
|
+
* Implements .render(), .updateProps(), .destroy(), .close() on a bridge component instance.
|
|
705
|
+
* Called once from createComponent to wire up the actual rendering logic.
|
|
706
|
+
*/
|
|
707
|
+
function attachRenderMethods(instance, options) {
|
|
708
|
+
const renderState = {
|
|
709
|
+
dispatchMap: /* @__PURE__ */ new Map(),
|
|
710
|
+
connection: null,
|
|
711
|
+
remote: null,
|
|
712
|
+
containerEl: null,
|
|
713
|
+
frame: null,
|
|
714
|
+
destroyed: false,
|
|
715
|
+
ownsContainer: false,
|
|
716
|
+
pendingPropsUpdate: null,
|
|
717
|
+
pendingRemovals: /* @__PURE__ */ new Set()
|
|
718
|
+
};
|
|
719
|
+
let renderedTriggered = false;
|
|
720
|
+
let lifecycleWired = false;
|
|
721
|
+
renderState.dispatchMap.set("resize", (dimensions) => {
|
|
722
|
+
const { autoResize } = options;
|
|
723
|
+
const applied = {};
|
|
724
|
+
if (dimensions.width !== void 0 && (!autoResize || autoResize.width === true)) applied.width = dimensions.width;
|
|
725
|
+
if (dimensions.height !== void 0 && (!autoResize || autoResize.height === true)) applied.height = dimensions.height;
|
|
726
|
+
if (renderState.frame) {
|
|
727
|
+
if (applied.width !== void 0) renderState.frame.style.width = toCSS(applied.width);
|
|
728
|
+
if (applied.height !== void 0) renderState.frame.style.height = toCSS(applied.height);
|
|
729
|
+
}
|
|
730
|
+
if (!renderedTriggered) {
|
|
731
|
+
renderedTriggered = true;
|
|
732
|
+
instance.event.trigger(EVENT.RENDERED);
|
|
733
|
+
}
|
|
734
|
+
instance.event.trigger(EVENT.RESIZE, applied);
|
|
735
|
+
});
|
|
736
|
+
renderState.dispatchMap.set("close", () => instance.close());
|
|
737
|
+
renderState.dispatchMap.set("show", () => {
|
|
738
|
+
if (renderState.frame) renderState.frame.style.display = "";
|
|
739
|
+
});
|
|
740
|
+
renderState.dispatchMap.set("hide", () => {
|
|
741
|
+
if (renderState.frame) renderState.frame.style.display = "none";
|
|
742
|
+
});
|
|
743
|
+
renderState.dispatchMap.set("getParentDomain", () => window.location.origin);
|
|
744
|
+
renderState.dispatchMap.set("focus", () => {
|
|
745
|
+
if (renderState.frame) renderState.frame.focus();
|
|
746
|
+
});
|
|
747
|
+
/**
|
|
748
|
+
* Render the component into the DOM and establish Penpal connection.
|
|
749
|
+
*
|
|
750
|
+
* Phase ordering:
|
|
751
|
+
* 1. wireLifecycleProps — bind onClose/onRendered/onResize to bridge events
|
|
752
|
+
* 2. prerenderTemplate — create loading placeholder (optional)
|
|
753
|
+
* 3. containerTemplate — build wrapper DOM, may mutate `state` (e.g., state.__container for PayPal)
|
|
754
|
+
* 4. buildDataPayload — run computed prop factories (read state mutations from step 3),
|
|
755
|
+
* classify user props into dispatch map (functions) and data payload (serializable values)
|
|
756
|
+
* 5. appendToDOM — container or bare iframe inserted into target element
|
|
757
|
+
* 6. triggerDISPLAY — notify container template that DOM is ready
|
|
758
|
+
* 7. sendBootstrap — postMessage initial props to iframe before script loads
|
|
759
|
+
* 8. createConnection — establish Penpal async RPC channel, deliver props via updateProps
|
|
760
|
+
*/
|
|
761
|
+
instance.render = async (container) => {
|
|
762
|
+
if (renderState.destroyed) throw new Error("Component is destroyed");
|
|
763
|
+
if (renderState.frame) throw new Error("Component already rendered");
|
|
764
|
+
const targetEl = resolveContainer(container);
|
|
765
|
+
const uid = generateUid(options.tag);
|
|
766
|
+
const doc = targetEl.ownerDocument;
|
|
767
|
+
const frame = doc.createElement("iframe");
|
|
768
|
+
frame.src = options.url;
|
|
769
|
+
frame.setAttribute("title", options.tag.replace(/-/g, "_"));
|
|
770
|
+
frame.setAttribute("allowpaymentrequest", "true");
|
|
771
|
+
frame.setAttribute("allow", "payment");
|
|
772
|
+
frame.style.border = "none";
|
|
773
|
+
const sandbox = SANDBOX[options.tag] ?? "allow-scripts allow-forms allow-same-origin";
|
|
774
|
+
frame.setAttribute("sandbox", sandbox);
|
|
775
|
+
if (!lifecycleWired) {
|
|
776
|
+
wireLifecycleProps(instance.props, instance.event);
|
|
777
|
+
lifecycleWired = true;
|
|
778
|
+
}
|
|
779
|
+
const templateSubs = [];
|
|
780
|
+
const trackedEvent = {
|
|
781
|
+
on(eventName, handler) {
|
|
782
|
+
const sub = instance.event.on(eventName, handler);
|
|
783
|
+
templateSubs.push(sub);
|
|
784
|
+
return sub;
|
|
785
|
+
},
|
|
786
|
+
once(eventName, handler) {
|
|
787
|
+
const sub = instance.event.once(eventName, handler);
|
|
788
|
+
templateSubs.push(sub);
|
|
789
|
+
return sub;
|
|
790
|
+
},
|
|
791
|
+
trigger(eventName, ...args) {
|
|
792
|
+
instance.event.trigger(eventName, ...args);
|
|
793
|
+
},
|
|
794
|
+
off(eventName, handler) {
|
|
795
|
+
instance.event.off(eventName, handler);
|
|
796
|
+
},
|
|
797
|
+
destroy() {
|
|
798
|
+
instance.event.destroy();
|
|
799
|
+
}
|
|
800
|
+
};
|
|
801
|
+
let placeholder;
|
|
802
|
+
let containerNode;
|
|
803
|
+
let dataProps = {};
|
|
804
|
+
let functionNames = [];
|
|
805
|
+
try {
|
|
806
|
+
if (options.prerenderTemplate) placeholder = options.prerenderTemplate({
|
|
807
|
+
uid,
|
|
808
|
+
frame,
|
|
809
|
+
placeholder: void 0,
|
|
810
|
+
doc,
|
|
811
|
+
props: instance.props,
|
|
812
|
+
event: trackedEvent,
|
|
813
|
+
dimensions: options.dimensions ?? {
|
|
814
|
+
width: "100%",
|
|
815
|
+
height: "auto"
|
|
816
|
+
},
|
|
817
|
+
state: instance.state,
|
|
818
|
+
container: targetEl
|
|
819
|
+
});
|
|
820
|
+
if (options.containerTemplate) containerNode = options.containerTemplate({
|
|
821
|
+
uid,
|
|
822
|
+
frame,
|
|
823
|
+
placeholder,
|
|
824
|
+
doc,
|
|
825
|
+
props: instance.props,
|
|
826
|
+
event: trackedEvent,
|
|
827
|
+
dimensions: options.dimensions ?? {
|
|
828
|
+
width: "100%",
|
|
829
|
+
height: "auto"
|
|
830
|
+
},
|
|
831
|
+
state: instance.state,
|
|
832
|
+
container: targetEl
|
|
833
|
+
});
|
|
834
|
+
({dataProps, functionNames} = buildDataPayload({
|
|
835
|
+
props: instance.props,
|
|
836
|
+
options,
|
|
837
|
+
state: instance.state,
|
|
838
|
+
dispatchMap: renderState.dispatchMap
|
|
839
|
+
}));
|
|
840
|
+
} catch (err) {
|
|
841
|
+
for (const sub of templateSubs) sub.cancel();
|
|
842
|
+
const builtins = new Set([
|
|
843
|
+
"close",
|
|
844
|
+
"resize",
|
|
845
|
+
"show",
|
|
846
|
+
"hide",
|
|
847
|
+
"getParentDomain",
|
|
848
|
+
"focus"
|
|
849
|
+
]);
|
|
850
|
+
for (const key of renderState.dispatchMap.keys()) if (!builtins.has(key)) renderState.dispatchMap.delete(key);
|
|
851
|
+
throw err;
|
|
852
|
+
}
|
|
853
|
+
renderState.frame = frame;
|
|
854
|
+
const sendInitialProps = () => {
|
|
855
|
+
if (frame.contentWindow) frame.contentWindow.postMessage({
|
|
856
|
+
type: BRIDGE_INIT_MSG,
|
|
857
|
+
props: dataProps,
|
|
858
|
+
functionNames
|
|
859
|
+
}, new URL(options.url).origin);
|
|
860
|
+
};
|
|
861
|
+
try {
|
|
862
|
+
if (containerNode) {
|
|
863
|
+
targetEl.appendChild(containerNode);
|
|
864
|
+
renderState.containerEl = containerNode;
|
|
865
|
+
renderState.ownsContainer = true;
|
|
866
|
+
} else {
|
|
867
|
+
targetEl.appendChild(frame);
|
|
868
|
+
renderState.containerEl = targetEl;
|
|
869
|
+
}
|
|
870
|
+
instance.event.trigger(EVENT.DISPLAY);
|
|
871
|
+
frame.addEventListener("load", sendInitialProps);
|
|
872
|
+
if (frame.contentWindow) sendInitialProps();
|
|
873
|
+
if (!frame.contentWindow) throw new Error("iframe contentWindow is not available — frame may not be in the DOM");
|
|
874
|
+
const connection = connect({
|
|
875
|
+
messenger: new WindowMessenger({
|
|
876
|
+
remoteWindow: frame.contentWindow,
|
|
877
|
+
allowedOrigins: [new URL(options.url).origin]
|
|
878
|
+
}),
|
|
879
|
+
methods: { callParent: (name, args) => {
|
|
880
|
+
const fn = renderState.dispatchMap.get(name);
|
|
881
|
+
if (!fn) throw new Error(`Unknown parent method: ${name}`);
|
|
882
|
+
return fn(...args);
|
|
883
|
+
} }
|
|
884
|
+
});
|
|
885
|
+
renderState.connection = connection;
|
|
886
|
+
let remote;
|
|
887
|
+
try {
|
|
888
|
+
remote = await connection.promise;
|
|
889
|
+
} catch (err) {
|
|
890
|
+
if (renderState.destroyed) return;
|
|
891
|
+
throw err;
|
|
892
|
+
}
|
|
893
|
+
if (renderState.destroyed) return;
|
|
894
|
+
renderState.remote = remote;
|
|
895
|
+
instance.state.__callChild = remote.callChild;
|
|
896
|
+
await remote.updateProps(dataProps, functionNames);
|
|
897
|
+
if (renderState.destroyed) return;
|
|
898
|
+
if (renderState.pendingPropsUpdate) {
|
|
899
|
+
const pending = renderState.pendingPropsUpdate;
|
|
900
|
+
renderState.pendingPropsUpdate = null;
|
|
901
|
+
const retainedFns = new Set(pending.functionNames ?? []);
|
|
902
|
+
const flushRemovals = /* @__PURE__ */ new Map();
|
|
903
|
+
for (const key of Object.keys(pending.dataProps)) if (renderState.dispatchMap.has(key) && !retainedFns.has(key)) {
|
|
904
|
+
flushRemovals.set(key, renderState.dispatchMap.get(key));
|
|
905
|
+
renderState.pendingRemovals.add(key);
|
|
906
|
+
}
|
|
907
|
+
await renderState.remote.updateProps(pending.dataProps, pending.functionNames);
|
|
908
|
+
if (renderState.destroyed) return;
|
|
909
|
+
for (const [key, originalFn] of flushRemovals) {
|
|
910
|
+
renderState.pendingRemovals.delete(key);
|
|
911
|
+
if (renderState.dispatchMap.get(key) === originalFn) renderState.dispatchMap.delete(key);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
if (!renderedTriggered) {
|
|
915
|
+
renderedTriggered = true;
|
|
916
|
+
instance.event.trigger(EVENT.RENDERED);
|
|
917
|
+
}
|
|
918
|
+
frame.removeEventListener("load", sendInitialProps);
|
|
919
|
+
} catch (err) {
|
|
920
|
+
frame.removeEventListener("load", sendInitialProps);
|
|
921
|
+
for (const sub of templateSubs) sub.cancel();
|
|
922
|
+
delete instance.state.__callChild;
|
|
923
|
+
if (renderState.connection) {
|
|
924
|
+
renderState.connection.destroy();
|
|
925
|
+
renderState.connection = null;
|
|
926
|
+
}
|
|
927
|
+
renderState.remote = null;
|
|
928
|
+
renderState.frame = null;
|
|
929
|
+
if (renderState.ownsContainer && renderState.containerEl) renderState.containerEl.remove();
|
|
930
|
+
else if (frame.parentNode) frame.remove();
|
|
931
|
+
renderState.containerEl = null;
|
|
932
|
+
renderState.ownsContainer = false;
|
|
933
|
+
renderState.pendingPropsUpdate = null;
|
|
934
|
+
renderState.pendingRemovals.clear();
|
|
935
|
+
const builtins = new Set([
|
|
936
|
+
"close",
|
|
937
|
+
"resize",
|
|
938
|
+
"show",
|
|
939
|
+
"hide",
|
|
940
|
+
"getParentDomain",
|
|
941
|
+
"focus"
|
|
942
|
+
]);
|
|
943
|
+
for (const key of renderState.dispatchMap.keys()) if (!builtins.has(key)) renderState.dispatchMap.delete(key);
|
|
944
|
+
renderedTriggered = false;
|
|
945
|
+
throw err;
|
|
946
|
+
}
|
|
947
|
+
};
|
|
948
|
+
instance.updateProps = async (newProps) => {
|
|
949
|
+
if (renderState.destroyed) throw new Error("Component is destroyed");
|
|
950
|
+
const normalized = normalizeAliases(newProps, options.aliases);
|
|
951
|
+
Object.assign(instance.props, normalized);
|
|
952
|
+
if (!renderState.frame) return;
|
|
953
|
+
const newDataProps = {};
|
|
954
|
+
const newFunctionNames = [];
|
|
955
|
+
const computedPropKeys = new Set(options.computedProps ? Object.keys(options.computedProps) : []);
|
|
956
|
+
const deferredRemovals = /* @__PURE__ */ new Map();
|
|
957
|
+
for (const [key, value] of Object.entries(normalized)) {
|
|
958
|
+
if (LIFECYCLE_PROPS.has(key) || computedPropKeys.has(key)) continue;
|
|
959
|
+
if (typeof value === "function") {
|
|
960
|
+
const hasStaleBufferedData = renderState.pendingPropsUpdate != null && key in renderState.pendingPropsUpdate.dataProps;
|
|
961
|
+
const hasPendingRemoval = renderState.pendingRemovals.has(key);
|
|
962
|
+
if (!renderState.dispatchMap.has(key) || hasStaleBufferedData || hasPendingRemoval) newFunctionNames.push(key);
|
|
963
|
+
renderState.dispatchMap.set(key, value);
|
|
964
|
+
} else {
|
|
965
|
+
if (renderState.dispatchMap.has(key)) {
|
|
966
|
+
if (renderState.remote) {
|
|
967
|
+
deferredRemovals.set(key, renderState.dispatchMap.get(key));
|
|
968
|
+
renderState.pendingRemovals.add(key);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
newDataProps[key] = value;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
const resolvedFunctionNames = newFunctionNames.length > 0 ? newFunctionNames : void 0;
|
|
975
|
+
if (renderState.remote) {
|
|
976
|
+
await renderState.remote.updateProps(newDataProps, resolvedFunctionNames);
|
|
977
|
+
for (const [key, originalFn] of deferredRemovals) {
|
|
978
|
+
renderState.pendingRemovals.delete(key);
|
|
979
|
+
if (renderState.dispatchMap.get(key) === originalFn) renderState.dispatchMap.delete(key);
|
|
980
|
+
}
|
|
981
|
+
} else {
|
|
982
|
+
if (!renderState.pendingPropsUpdate) renderState.pendingPropsUpdate = {
|
|
983
|
+
dataProps: {},
|
|
984
|
+
functionNames: void 0
|
|
985
|
+
};
|
|
986
|
+
Object.assign(renderState.pendingPropsUpdate.dataProps, newDataProps);
|
|
987
|
+
if (renderState.pendingPropsUpdate.functionNames) {
|
|
988
|
+
const dataKeys = Object.keys(newDataProps);
|
|
989
|
+
if (dataKeys.length > 0) {
|
|
990
|
+
const dataKeySet = new Set(dataKeys);
|
|
991
|
+
renderState.pendingPropsUpdate.functionNames = renderState.pendingPropsUpdate.functionNames.filter((n) => !dataKeySet.has(n));
|
|
992
|
+
if (renderState.pendingPropsUpdate.functionNames.length === 0) renderState.pendingPropsUpdate.functionNames = void 0;
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
if (resolvedFunctionNames) {
|
|
996
|
+
for (const name of resolvedFunctionNames) delete renderState.pendingPropsUpdate.dataProps[name];
|
|
997
|
+
renderState.pendingPropsUpdate.functionNames = [...renderState.pendingPropsUpdate.functionNames ?? [], ...resolvedFunctionNames];
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
instance.destroy = async () => {
|
|
1002
|
+
if (renderState.destroyed) return;
|
|
1003
|
+
renderState.destroyed = true;
|
|
1004
|
+
instance.event.trigger(EVENT.DESTROY);
|
|
1005
|
+
if (renderState.connection) {
|
|
1006
|
+
renderState.connection.destroy();
|
|
1007
|
+
renderState.connection = null;
|
|
1008
|
+
}
|
|
1009
|
+
renderState.remote = null;
|
|
1010
|
+
if (renderState.frame) {
|
|
1011
|
+
renderState.frame.remove();
|
|
1012
|
+
renderState.frame = null;
|
|
1013
|
+
}
|
|
1014
|
+
if (renderState.ownsContainer && renderState.containerEl) renderState.containerEl.remove();
|
|
1015
|
+
instance.event.destroy();
|
|
1016
|
+
renderState.dispatchMap.clear();
|
|
1017
|
+
};
|
|
1018
|
+
instance.close = async () => {
|
|
1019
|
+
await instance.destroy();
|
|
1020
|
+
};
|
|
1021
|
+
/**
|
|
1022
|
+
* Expose renderState for tests
|
|
1023
|
+
*/
|
|
1024
|
+
instance.__renderState = renderState;
|
|
1025
|
+
}
|
|
1026
|
+
//#endregion
|
|
1027
|
+
//#region src/bridge/create-component.ts
|
|
1028
|
+
const driverPackageMap = {
|
|
1029
|
+
react: "@monei-js/react-components",
|
|
1030
|
+
vue3: "@monei-js/vue-components",
|
|
1031
|
+
angular: "@monei-js/angular-components"
|
|
1032
|
+
};
|
|
1033
|
+
function warnDriverDeprecation(name) {
|
|
1034
|
+
const pkg = driverPackageMap[name];
|
|
1035
|
+
const replacement = pkg ? `Use ${pkg} instead.` : "";
|
|
1036
|
+
console.warn(`[MONEI] .driver('${name}') is deprecated. ${replacement}`.trim());
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* Normalize aliased prop names to canonical names.
|
|
1040
|
+
* Aliases define alternative INPUT names: { style: 'innerStyle' } means
|
|
1041
|
+
* merchants can pass either `style` or `innerStyle`, but internally
|
|
1042
|
+
* the canonical name `style` is always used (matching zoid semantics).
|
|
1043
|
+
*/
|
|
1044
|
+
function normalizeAliases(props, aliases) {
|
|
1045
|
+
if (!aliases) return props;
|
|
1046
|
+
const reverseMap = /* @__PURE__ */ new Map();
|
|
1047
|
+
for (const [canonical, alias] of Object.entries(aliases)) reverseMap.set(alias, canonical);
|
|
1048
|
+
const result = {};
|
|
1049
|
+
for (const [key, value] of Object.entries(props)) {
|
|
1050
|
+
const canonicalKey = reverseMap.get(key) ?? key;
|
|
1051
|
+
result[canonicalKey] = value;
|
|
1052
|
+
}
|
|
1053
|
+
return result;
|
|
1054
|
+
}
|
|
1055
|
+
/**
|
|
1056
|
+
* Creates a bridge component constructor (replaces zoid.create).
|
|
1057
|
+
*
|
|
1058
|
+
* Returns a class that can be instantiated with `new Component(props)`
|
|
1059
|
+
* and has a static `.driver()` method for framework wrappers.
|
|
1060
|
+
*/
|
|
1061
|
+
function createComponent(options) {
|
|
1062
|
+
class Component {
|
|
1063
|
+
constructor(props) {
|
|
1064
|
+
this.state = {};
|
|
1065
|
+
this.event = new EventEmitter();
|
|
1066
|
+
const normalizedProps = normalizeAliases(props, options.aliases);
|
|
1067
|
+
if (options.defaultProps) this.props = {
|
|
1068
|
+
...options.defaultProps({ event: this.event }),
|
|
1069
|
+
...normalizedProps
|
|
1070
|
+
};
|
|
1071
|
+
else this.props = { ...normalizedProps };
|
|
1072
|
+
if (options.validate) options.validate({ props: this.props });
|
|
1073
|
+
attachRenderMethods(this, options);
|
|
1074
|
+
if (options.submit) this.submit = options.submit(this);
|
|
1075
|
+
}
|
|
1076
|
+
async render(_container) {
|
|
1077
|
+
throw new Error("render() not attached");
|
|
1078
|
+
}
|
|
1079
|
+
async updateProps(_props) {
|
|
1080
|
+
throw new Error("updateProps() not attached — call render() first");
|
|
1081
|
+
}
|
|
1082
|
+
async close() {
|
|
1083
|
+
throw new Error("close() not attached");
|
|
1084
|
+
}
|
|
1085
|
+
async destroy() {
|
|
1086
|
+
throw new Error("destroy() not attached");
|
|
1087
|
+
}
|
|
1088
|
+
driver(name, deps) {
|
|
1089
|
+
warnDriverDeprecation(name);
|
|
1090
|
+
return resolveDriver(name, Component, deps);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
function ComponentWrapper(props) {
|
|
1094
|
+
if (!(this instanceof ComponentWrapper)) return new ComponentWrapper(props);
|
|
1095
|
+
return new Component(props);
|
|
1096
|
+
}
|
|
1097
|
+
ComponentWrapper.prototype = Component.prototype;
|
|
1098
|
+
ComponentWrapper.driver = function(name, deps) {
|
|
1099
|
+
warnDriverDeprecation(name);
|
|
1100
|
+
return resolveDriver(name, ComponentWrapper, deps);
|
|
1101
|
+
};
|
|
1102
|
+
Component.prototype.__options = options;
|
|
1103
|
+
return ComponentWrapper;
|
|
1104
|
+
}
|
|
1105
|
+
//#endregion
|
|
1106
|
+
//#region src/enums.ts
|
|
1107
|
+
let CLASS = /* @__PURE__ */ function(CLASS) {
|
|
1108
|
+
CLASS["VISIBLE"] = "is-visible";
|
|
1109
|
+
CLASS["INVISIBLE"] = "is-invisible";
|
|
1110
|
+
return CLASS;
|
|
1111
|
+
}({});
|
|
1112
|
+
//#endregion
|
|
1113
|
+
//#region src/container.ts
|
|
1114
|
+
function createContainerTemplate(config = {}) {
|
|
1115
|
+
const { background = "#fff", overflow, borderRadius, fadeOnRender = true, allowTransparency = false, modal = false } = config;
|
|
1116
|
+
return function containerTemplateFn({ uid, frame, placeholder, doc, props, event, dimensions: { width, height } }) {
|
|
1117
|
+
if (!frame) return;
|
|
1118
|
+
if (allowTransparency) {
|
|
1119
|
+
frame.setAttribute("allowtransparency", "true");
|
|
1120
|
+
frame.style.background = "transparent";
|
|
1121
|
+
}
|
|
1122
|
+
const div = doc.createElement("div");
|
|
1123
|
+
div.setAttribute("id", uid);
|
|
1124
|
+
const style = doc.createElement("style");
|
|
1125
|
+
if (props?.cspNonce) style.setAttribute("nonce", props.cspNonce);
|
|
1126
|
+
if (modal) {
|
|
1127
|
+
const backdrop = doc.createElement("div");
|
|
1128
|
+
backdrop.classList.add("backdrop");
|
|
1129
|
+
style.appendChild(doc.createTextNode(`
|
|
1130
|
+
#${uid} {
|
|
1131
|
+
position: fixed;
|
|
1132
|
+
inset: 0;
|
|
1133
|
+
z-index: 99999;
|
|
1134
|
+
}
|
|
1135
|
+
#${uid} > .backdrop {
|
|
1136
|
+
position: fixed;
|
|
1137
|
+
inset: 0;
|
|
1138
|
+
background-color: rgba(40, 40, 40, 0.7);
|
|
1139
|
+
opacity: 0;
|
|
1140
|
+
transition: opacity 0.35s ease-in-out;
|
|
1141
|
+
will-change: opacity;
|
|
1142
|
+
}
|
|
1143
|
+
#${uid} > .backdrop.${CLASS.VISIBLE} {
|
|
1144
|
+
opacity: 1;
|
|
1145
|
+
}
|
|
1146
|
+
#${uid} > iframe {
|
|
1147
|
+
display: block;
|
|
1148
|
+
position: absolute;
|
|
1149
|
+
width: 100%;
|
|
1150
|
+
height: 100%;
|
|
1151
|
+
top: 0;
|
|
1152
|
+
left: 0;
|
|
1153
|
+
transition: opacity .2s ease-in-out;
|
|
1154
|
+
}
|
|
1155
|
+
#${uid} > iframe.${CLASS.INVISIBLE} {
|
|
1156
|
+
opacity: 0;
|
|
1157
|
+
}
|
|
1158
|
+
#${uid} > iframe.${CLASS.VISIBLE} {
|
|
1159
|
+
opacity: 1;
|
|
1160
|
+
}
|
|
1161
|
+
`));
|
|
1162
|
+
div.appendChild(backdrop);
|
|
1163
|
+
div.appendChild(frame);
|
|
1164
|
+
if (placeholder) div.appendChild(placeholder);
|
|
1165
|
+
div.appendChild(style);
|
|
1166
|
+
event.on(EVENT.DISPLAY, () => {
|
|
1167
|
+
backdrop.offsetHeight;
|
|
1168
|
+
requestAnimationFrame(() => backdrop.classList.add(CLASS.VISIBLE));
|
|
1169
|
+
});
|
|
1170
|
+
event.on(EVENT.BEFORE_CLOSE, () => {
|
|
1171
|
+
backdrop.classList.remove(CLASS.VISIBLE);
|
|
1172
|
+
});
|
|
1173
|
+
frame.classList.add(CLASS.INVISIBLE);
|
|
1174
|
+
if (fadeOnRender && placeholder) placeholder.classList.add(CLASS.VISIBLE);
|
|
1175
|
+
event.on(EVENT.RENDERED, () => {
|
|
1176
|
+
if (fadeOnRender) {
|
|
1177
|
+
if (placeholder) {
|
|
1178
|
+
placeholder.classList.remove(CLASS.VISIBLE);
|
|
1179
|
+
placeholder.classList.add(CLASS.INVISIBLE);
|
|
1180
|
+
}
|
|
1181
|
+
frame.classList.remove(CLASS.INVISIBLE);
|
|
1182
|
+
frame.classList.add(CLASS.VISIBLE);
|
|
1183
|
+
setTimeout(() => destroyElement(placeholder), 1);
|
|
1184
|
+
} else {
|
|
1185
|
+
if (placeholder) destroyElement(placeholder);
|
|
1186
|
+
frame.style.transition = "none";
|
|
1187
|
+
frame.classList.remove(CLASS.INVISIBLE);
|
|
1188
|
+
frame.classList.add(CLASS.VISIBLE);
|
|
1189
|
+
}
|
|
1190
|
+
});
|
|
1191
|
+
} else {
|
|
1192
|
+
const innerStyle = props?.style || props?.innerStyle;
|
|
1193
|
+
const initialHeight = height === "0" ? "0" : toCSS(innerStyle?.base?.height || innerStyle?.height || (height === "auto" ? 45 : height));
|
|
1194
|
+
const containerStyles = [
|
|
1195
|
+
"display: block",
|
|
1196
|
+
"position: relative",
|
|
1197
|
+
`width: ${width}`,
|
|
1198
|
+
`height: ${allowTransparency ? height : initialHeight}`,
|
|
1199
|
+
`background: ${background}`
|
|
1200
|
+
];
|
|
1201
|
+
if (overflow) containerStyles.push(`overflow: ${overflow}`);
|
|
1202
|
+
if (borderRadius) containerStyles.push(`border-radius: ${borderRadius}`);
|
|
1203
|
+
style.appendChild(doc.createTextNode(`
|
|
1204
|
+
#${uid} {
|
|
1205
|
+
${containerStyles.join(";\n ")};
|
|
1206
|
+
}
|
|
1207
|
+
#${uid} > iframe {
|
|
1208
|
+
display: block;
|
|
1209
|
+
position: absolute;
|
|
1210
|
+
width: 100%;
|
|
1211
|
+
height: 100%;
|
|
1212
|
+
top: 0;
|
|
1213
|
+
left: 0;
|
|
1214
|
+
transition: opacity .2s ease-in-out;
|
|
1215
|
+
}
|
|
1216
|
+
#${uid} > iframe.${CLASS.INVISIBLE} {
|
|
1217
|
+
opacity: 0;
|
|
1218
|
+
}
|
|
1219
|
+
#${uid} > iframe.${CLASS.VISIBLE} {
|
|
1220
|
+
opacity: 1;
|
|
1221
|
+
}
|
|
1222
|
+
`));
|
|
1223
|
+
div.appendChild(frame);
|
|
1224
|
+
if (placeholder) div.appendChild(placeholder);
|
|
1225
|
+
div.appendChild(style);
|
|
1226
|
+
event.on(EVENT.RESIZE, ({ width: newWidth, height: newHeight }) => {
|
|
1227
|
+
if (typeof newWidth === "number") div.style.width = toCSS(newWidth);
|
|
1228
|
+
if (typeof newHeight === "number") div.style.height = toCSS(newHeight);
|
|
1229
|
+
});
|
|
1230
|
+
if (placeholder) {
|
|
1231
|
+
frame.classList.add(CLASS.INVISIBLE);
|
|
1232
|
+
if (fadeOnRender) placeholder.classList.add(CLASS.VISIBLE);
|
|
1233
|
+
event.on(EVENT.RENDERED, () => {
|
|
1234
|
+
if (fadeOnRender) {
|
|
1235
|
+
placeholder.classList.remove(CLASS.VISIBLE);
|
|
1236
|
+
placeholder.classList.add(CLASS.INVISIBLE);
|
|
1237
|
+
frame.classList.remove(CLASS.INVISIBLE);
|
|
1238
|
+
frame.classList.add(CLASS.VISIBLE);
|
|
1239
|
+
setTimeout(() => destroyElement(placeholder), 1);
|
|
1240
|
+
} else {
|
|
1241
|
+
destroyElement(placeholder);
|
|
1242
|
+
frame.style.transition = "none";
|
|
1243
|
+
frame.classList.remove(CLASS.INVISIBLE);
|
|
1244
|
+
frame.classList.add(CLASS.VISIBLE);
|
|
1245
|
+
}
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
return div;
|
|
1250
|
+
};
|
|
1251
|
+
}
|
|
1252
|
+
const containerTemplate = createContainerTemplate({
|
|
1253
|
+
overflow: "hidden",
|
|
1254
|
+
borderRadius: "var(--monei-radius, 4px)"
|
|
1255
|
+
});
|
|
1256
|
+
const modalContainerTemplate = createContainerTemplate({ modal: true });
|
|
1257
|
+
//#endregion
|
|
1258
|
+
//#region src/card-input/index.ts
|
|
1259
|
+
const getCreateTokenFunction = (ref) => {
|
|
1260
|
+
if (ref.state?.__createToken) return ref.state.__createToken;
|
|
1261
|
+
throw new Error("Index is not registered");
|
|
1262
|
+
};
|
|
1263
|
+
const createToken = async (component, options) => {
|
|
1264
|
+
console.warn("[MONEI] Standalone createToken() is deprecated. Use cardInput.submit() instead.");
|
|
1265
|
+
const { data } = await getCreateTokenFunction(component)(options);
|
|
1266
|
+
return data;
|
|
1267
|
+
};
|
|
1268
|
+
const CardInput = createComponent({
|
|
1269
|
+
tag: "monei-card-input",
|
|
1270
|
+
url: config.cardInputUrl,
|
|
1271
|
+
dimensions: {
|
|
1272
|
+
width: "100%",
|
|
1273
|
+
height: "auto"
|
|
1274
|
+
},
|
|
1275
|
+
autoResize: {
|
|
1276
|
+
width: false,
|
|
1277
|
+
height: true
|
|
1278
|
+
},
|
|
1279
|
+
aliases: {
|
|
1280
|
+
sessionId: "orderId",
|
|
1281
|
+
style: "innerStyle"
|
|
1282
|
+
},
|
|
1283
|
+
computedProps: { __getConfig: {
|
|
1284
|
+
factory: ({ props }) => getPaymentMethods.bind(null, props),
|
|
1285
|
+
type: "function"
|
|
1286
|
+
} },
|
|
1287
|
+
validate: validateComponentProps,
|
|
1288
|
+
submit: (instance) => async (opts) => {
|
|
1289
|
+
const createTokenFn = instance.state?.__createToken;
|
|
1290
|
+
if (!createTokenFn) throw new Error("CardInput is not rendered or connection is pending — call render() first");
|
|
1291
|
+
const { data } = await createTokenFn(opts);
|
|
1292
|
+
return data;
|
|
1293
|
+
},
|
|
1294
|
+
containerTemplate: (params) => {
|
|
1295
|
+
params.state.__createToken = async (data = {}) => {
|
|
1296
|
+
const callChild = params.state.__callChild;
|
|
1297
|
+
if (!callChild) throw new Error("CardInput instance is not registered");
|
|
1298
|
+
return { data: await callChild("createToken", [data]) };
|
|
1299
|
+
};
|
|
1300
|
+
return containerTemplate(params);
|
|
1301
|
+
},
|
|
1302
|
+
prerenderTemplate: ({ doc, props }) => {
|
|
1303
|
+
const html = doc.createElement("html");
|
|
1304
|
+
const body = doc.createElement("body");
|
|
1305
|
+
body.style.margin = "0";
|
|
1306
|
+
body.style.background = "#fff";
|
|
1307
|
+
const div = doc.createElement("div");
|
|
1308
|
+
div.style.height = "45px";
|
|
1309
|
+
div.style.boxSizing = "border-box";
|
|
1310
|
+
const style = props.style || props.innerStyle;
|
|
1311
|
+
if (style?.base) Object.assign(div.style, transformStyle(style.base));
|
|
1312
|
+
if (style?.loading) Object.assign(div.style, transformStyle(style.loading));
|
|
1313
|
+
body.appendChild(div);
|
|
1314
|
+
html.appendChild(body);
|
|
1315
|
+
return html;
|
|
1316
|
+
}
|
|
1317
|
+
});
|
|
1318
|
+
const PaymentModal = createComponent({
|
|
1319
|
+
tag: "monei-payment-modal",
|
|
1320
|
+
url: config.paymentModalUrl,
|
|
1321
|
+
dimensions: {
|
|
1322
|
+
width: "100%",
|
|
1323
|
+
height: "100%"
|
|
1324
|
+
},
|
|
1325
|
+
autoResize: {
|
|
1326
|
+
width: false,
|
|
1327
|
+
height: false
|
|
1328
|
+
},
|
|
1329
|
+
aliases: { style: "innerStyle" },
|
|
1330
|
+
containerTemplate: createContainerTemplate({
|
|
1331
|
+
background: "transparent",
|
|
1332
|
+
fadeOnRender: false,
|
|
1333
|
+
allowTransparency: true
|
|
1334
|
+
})
|
|
1335
|
+
});
|
|
1336
|
+
const confirmPayment = (params) => {
|
|
1337
|
+
const rootNode = document.createElement("div");
|
|
1338
|
+
const bodyOverflow = document.body.style.overflow;
|
|
1339
|
+
Object.assign(document.body.style, { overflow: "hidden" });
|
|
1340
|
+
Object.assign(rootNode.style, {
|
|
1341
|
+
position: "fixed",
|
|
1342
|
+
overflow: "hidden",
|
|
1343
|
+
display: "block",
|
|
1344
|
+
top: "0",
|
|
1345
|
+
bottom: "0",
|
|
1346
|
+
left: "0",
|
|
1347
|
+
right: "0",
|
|
1348
|
+
width: "100%",
|
|
1349
|
+
height: "100%",
|
|
1350
|
+
zIndex: "99999"
|
|
1351
|
+
});
|
|
1352
|
+
document.body.appendChild(rootNode);
|
|
1353
|
+
let modal = null;
|
|
1354
|
+
const cleanup = () => {
|
|
1355
|
+
modal?.destroy();
|
|
1356
|
+
modal = null;
|
|
1357
|
+
Object.assign(document.body.style, { overflow: bodyOverflow });
|
|
1358
|
+
if (rootNode.parentNode) rootNode.parentNode.removeChild(rootNode);
|
|
1359
|
+
};
|
|
1360
|
+
return new Promise((resolve, reject) => {
|
|
1361
|
+
modal = new PaymentModal({
|
|
1362
|
+
...params,
|
|
1363
|
+
isOpen: true,
|
|
1364
|
+
onResult: (result) => {
|
|
1365
|
+
resolve(result);
|
|
1366
|
+
cleanup();
|
|
1367
|
+
},
|
|
1368
|
+
onError: (error) => {
|
|
1369
|
+
reject(error);
|
|
1370
|
+
cleanup();
|
|
1371
|
+
}
|
|
1372
|
+
});
|
|
1373
|
+
modal.render(rootNode).catch((err) => {
|
|
1374
|
+
cleanup();
|
|
1375
|
+
reject(err);
|
|
1376
|
+
});
|
|
1377
|
+
});
|
|
1378
|
+
};
|
|
1379
|
+
//#endregion
|
|
1380
|
+
//#region src/paypal/index.ts
|
|
1381
|
+
const namespace = "monei-paypal";
|
|
1382
|
+
const onLoadPaypal = async (props, state, src) => {
|
|
1383
|
+
state.__scriptId = (await loadScript(src, props.cspNonce, {
|
|
1384
|
+
namespace,
|
|
1385
|
+
cspNonce: props.cspNonce
|
|
1386
|
+
}, true)).dataset.uidAuto;
|
|
1387
|
+
const defaultStyle = {
|
|
1388
|
+
layout: "horizontal",
|
|
1389
|
+
tagline: false,
|
|
1390
|
+
height: 45
|
|
1391
|
+
};
|
|
1392
|
+
const radius = parsePx(props.style?.borderRadius ?? props.borderRadius);
|
|
1393
|
+
if (radius) defaultStyle.borderRadius = radius;
|
|
1394
|
+
const style = {
|
|
1395
|
+
...defaultStyle,
|
|
1396
|
+
...props.style
|
|
1397
|
+
};
|
|
1398
|
+
style.height = parsePx(style.height, 45);
|
|
1399
|
+
props.onLoad?.(true);
|
|
1400
|
+
let token;
|
|
1401
|
+
await window[namespace].Buttons({
|
|
1402
|
+
createOrder: async () => {
|
|
1403
|
+
try {
|
|
1404
|
+
const result = await createToken$1({
|
|
1405
|
+
paymentId: props.paymentId,
|
|
1406
|
+
accountId: props.accountId,
|
|
1407
|
+
sessionId: props.sessionId,
|
|
1408
|
+
amount: props.amount,
|
|
1409
|
+
currency: props.currency,
|
|
1410
|
+
transactionType: props.transactionType,
|
|
1411
|
+
paymentMethod: { paypal: {} }
|
|
1412
|
+
});
|
|
1413
|
+
({token} = result);
|
|
1414
|
+
return result.paypal?.orderId;
|
|
1415
|
+
} catch (error) {
|
|
1416
|
+
props.onError?.(error);
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1419
|
+
},
|
|
1420
|
+
onApprove: function(data) {
|
|
1421
|
+
if (data.orderID) props.onSubmit({ token });
|
|
1422
|
+
else props.onError?.(data);
|
|
1423
|
+
},
|
|
1424
|
+
onClick: function(_data, actions) {
|
|
1425
|
+
let shouldOpen = true;
|
|
1426
|
+
if (props.onBeforeOpen) shouldOpen = props.onBeforeOpen();
|
|
1427
|
+
if (!shouldOpen) return actions.reject();
|
|
1428
|
+
},
|
|
1429
|
+
style
|
|
1430
|
+
}).render(state.__container);
|
|
1431
|
+
};
|
|
1432
|
+
const PayPal = createComponent({
|
|
1433
|
+
tag: namespace,
|
|
1434
|
+
url: config.paypalUrl,
|
|
1435
|
+
dimensions: {
|
|
1436
|
+
width: "100%",
|
|
1437
|
+
height: "0"
|
|
1438
|
+
},
|
|
1439
|
+
autoResize: {
|
|
1440
|
+
width: false,
|
|
1441
|
+
height: false
|
|
1442
|
+
},
|
|
1443
|
+
aliases: { style: "innerStyle" },
|
|
1444
|
+
computedProps: {
|
|
1445
|
+
__getConfig: {
|
|
1446
|
+
factory: ({ props }) => getPaymentMethods.bind(null, props),
|
|
1447
|
+
type: "function"
|
|
1448
|
+
},
|
|
1449
|
+
__onLoadPayPal: {
|
|
1450
|
+
factory: ({ props, state }) => onLoadPaypal.bind(null, props, state),
|
|
1451
|
+
type: "function"
|
|
1452
|
+
}
|
|
1453
|
+
},
|
|
1454
|
+
containerTemplate: (params) => {
|
|
1455
|
+
params.state.__container = params.container;
|
|
1456
|
+
if (!params.props.accountId && !params.props.paymentId) throw new Error("You need to provide paymentId or accountId");
|
|
1457
|
+
const container = containerTemplate(params);
|
|
1458
|
+
params.event.on(EVENT.DESTROY, () => {
|
|
1459
|
+
window[namespace]?.Buttons?.instances?.forEach((instance) => {
|
|
1460
|
+
instance.close();
|
|
1461
|
+
});
|
|
1462
|
+
const scriptId = params.state.__scriptId;
|
|
1463
|
+
if (scriptId) {
|
|
1464
|
+
const script = document.querySelector(`script[data-uid-auto="${scriptId}"]`);
|
|
1465
|
+
if (script) script.remove();
|
|
1466
|
+
}
|
|
1467
|
+
});
|
|
1468
|
+
return container;
|
|
1469
|
+
}
|
|
1470
|
+
});
|
|
1471
|
+
const BizumModal = createComponent({
|
|
1472
|
+
tag: "monei-bizum",
|
|
1473
|
+
url: config.bizumUrl,
|
|
1474
|
+
autoResize: {
|
|
1475
|
+
width: false,
|
|
1476
|
+
height: false
|
|
1477
|
+
},
|
|
1478
|
+
containerTemplate: (params) => {
|
|
1479
|
+
if (!params.frame) return;
|
|
1480
|
+
if (params.props.isHidden) {
|
|
1481
|
+
const div = params.doc.createElement("div");
|
|
1482
|
+
div.setAttribute("id", params.uid);
|
|
1483
|
+
div.style.display = "none";
|
|
1484
|
+
div.appendChild(params.frame);
|
|
1485
|
+
if (params.placeholder) div.appendChild(params.placeholder);
|
|
1486
|
+
return div;
|
|
1487
|
+
}
|
|
1488
|
+
return modalContainerTemplate(params);
|
|
1489
|
+
},
|
|
1490
|
+
defaultProps: ({ event }) => ({ onBeforeClose: () => {
|
|
1491
|
+
event.trigger(EVENT.BEFORE_CLOSE);
|
|
1492
|
+
} })
|
|
1493
|
+
});
|
|
1494
|
+
const Bizum = createComponent({
|
|
1495
|
+
tag: "monei-bizum-button",
|
|
1496
|
+
url: config.bizumButtonUrl,
|
|
1497
|
+
dimensions: {
|
|
1498
|
+
width: "100%",
|
|
1499
|
+
height: "auto"
|
|
1500
|
+
},
|
|
1501
|
+
autoResize: {
|
|
1502
|
+
width: false,
|
|
1503
|
+
height: true
|
|
1504
|
+
},
|
|
1505
|
+
aliases: { style: "innerStyle" },
|
|
1506
|
+
computedProps: {
|
|
1507
|
+
__getConfig: {
|
|
1508
|
+
factory: ({ props }) => getPaymentMethods.bind(null, props),
|
|
1509
|
+
type: "function"
|
|
1510
|
+
},
|
|
1511
|
+
__onClick: {
|
|
1512
|
+
factory: ({ props, state }) => {
|
|
1513
|
+
const rootNode = document.createElement("div");
|
|
1514
|
+
return ({ amount, currency }) => {
|
|
1515
|
+
if (state.isOpen) return;
|
|
1516
|
+
let shouldOpen = true;
|
|
1517
|
+
if (props.onBeforeOpen) shouldOpen = props.onBeforeOpen();
|
|
1518
|
+
if (!shouldOpen) return;
|
|
1519
|
+
state.isOpen = true;
|
|
1520
|
+
if (!rootNode.parentNode) document.body.appendChild(rootNode);
|
|
1521
|
+
const bizumModal = new BizumModal({
|
|
1522
|
+
amount,
|
|
1523
|
+
currency,
|
|
1524
|
+
...props,
|
|
1525
|
+
onClose: () => {
|
|
1526
|
+
state.isOpen = false;
|
|
1527
|
+
window.onpopstate = null;
|
|
1528
|
+
document.body.style.overflow = "";
|
|
1529
|
+
},
|
|
1530
|
+
isOpen: true
|
|
1531
|
+
});
|
|
1532
|
+
window.history.pushState(null, "", window.location.href);
|
|
1533
|
+
window.onpopstate = () => bizumModal.updateProps({ isOpen: false });
|
|
1534
|
+
Object.assign(document.body.style, { overflow: "hidden" });
|
|
1535
|
+
return bizumModal.render(rootNode);
|
|
1536
|
+
};
|
|
1537
|
+
},
|
|
1538
|
+
type: "function"
|
|
1539
|
+
}
|
|
1540
|
+
},
|
|
1541
|
+
validate: validateComponentProps,
|
|
1542
|
+
containerTemplate
|
|
1543
|
+
});
|
|
1544
|
+
const GooglePay = createComponent({
|
|
1545
|
+
tag: "monei-google-pay",
|
|
1546
|
+
url: config.paymentRequestUrl,
|
|
1547
|
+
dimensions: {
|
|
1548
|
+
width: "100%",
|
|
1549
|
+
height: "0"
|
|
1550
|
+
},
|
|
1551
|
+
autoResize: {
|
|
1552
|
+
width: false,
|
|
1553
|
+
height: true
|
|
1554
|
+
},
|
|
1555
|
+
aliases: { style: "innerStyle" },
|
|
1556
|
+
computedProps: { __getConfig: {
|
|
1557
|
+
factory: ({ props }) => getPaymentMethods.bind(null, props),
|
|
1558
|
+
type: "function"
|
|
1559
|
+
} },
|
|
1560
|
+
validate: validateComponentProps,
|
|
1561
|
+
containerTemplate
|
|
1562
|
+
});
|
|
1563
|
+
const PaymentRequest = createComponent({
|
|
1564
|
+
tag: "monei-payment-request",
|
|
1565
|
+
url: config.paymentRequestUrl,
|
|
1566
|
+
dimensions: {
|
|
1567
|
+
width: "100%",
|
|
1568
|
+
height: "auto"
|
|
1569
|
+
},
|
|
1570
|
+
autoResize: {
|
|
1571
|
+
width: false,
|
|
1572
|
+
height: true
|
|
1573
|
+
},
|
|
1574
|
+
aliases: { style: "innerStyle" },
|
|
1575
|
+
computedProps: { __getConfig: {
|
|
1576
|
+
factory: ({ props }) => getPaymentMethods.bind(null, props),
|
|
1577
|
+
type: "function"
|
|
1578
|
+
} },
|
|
1579
|
+
validate: validateComponentProps,
|
|
1580
|
+
containerTemplate
|
|
1581
|
+
});
|
|
1582
|
+
//#endregion
|
|
1583
|
+
//#region src/types.ts
|
|
1584
|
+
let NextActionType = /* @__PURE__ */ function(NextActionType) {
|
|
1585
|
+
NextActionType["CONFIRM"] = "CONFIRM";
|
|
1586
|
+
NextActionType["FRICTIONLESS_CHALLENGE"] = "FRICTIONLESS_CHALLENGE";
|
|
1587
|
+
NextActionType["BIZUM_CHALLENGE"] = "BIZUM_CHALLENGE";
|
|
1588
|
+
NextActionType["CHALLENGE"] = "CHALLENGE";
|
|
1589
|
+
NextActionType["COMPLETE"] = "COMPLETE";
|
|
1590
|
+
return NextActionType;
|
|
1591
|
+
}({});
|
|
1592
|
+
let CardBrand = /* @__PURE__ */ function(CardBrand) {
|
|
1593
|
+
CardBrand["Visa"] = "visa";
|
|
1594
|
+
CardBrand["Mastercard"] = "mastercard";
|
|
1595
|
+
CardBrand["Amex"] = "amex";
|
|
1596
|
+
CardBrand["Discover"] = "discover";
|
|
1597
|
+
CardBrand["Diners"] = "diners";
|
|
1598
|
+
CardBrand["JCB"] = "jcb";
|
|
1599
|
+
CardBrand["Maestro"] = "maestro";
|
|
1600
|
+
CardBrand["UnionPay"] = "unionpay";
|
|
1601
|
+
CardBrand["Elo"] = "elo";
|
|
1602
|
+
CardBrand["Hipercard"] = "hipercard";
|
|
1603
|
+
return CardBrand;
|
|
1604
|
+
}({});
|
|
1605
|
+
let TransactionType = /* @__PURE__ */ function(TransactionType) {
|
|
1606
|
+
TransactionType["AUTH"] = "AUTH";
|
|
1607
|
+
TransactionType["SALE"] = "SALE";
|
|
1608
|
+
TransactionType["PAYOUT"] = "PAYOUT";
|
|
1609
|
+
TransactionType["VERIF"] = "VERIF";
|
|
1610
|
+
return TransactionType;
|
|
1611
|
+
}({});
|
|
1612
|
+
//#endregion
|
|
1613
|
+
//#region src/index.ts
|
|
1614
|
+
const api = api_exports;
|
|
1615
|
+
const utils = utils_exports;
|
|
1616
|
+
//#endregion
|
|
1617
|
+
export { Bizum, BizumModal, CardBrand, CardInput, GooglePay, NextActionType, PayPal, PaymentModal, PaymentRequest, TransactionType, api, config, confirmPayment, createToken, utils };
|