@dintero/checkout-web-sdk 0.5.10 → 0.6.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 +4 -2
- package/coverage/Chrome Headless 117.0.5938.92 (Linux x86_64)/html/checkout.ts.html +91 -4
- package/coverage/Chrome Headless 117.0.5938.92 (Linux x86_64)/html/createIframeAsync.ts.html +15 -15
- package/coverage/Chrome Headless 117.0.5938.92 (Linux x86_64)/html/index.html +88 -43
- package/coverage/Chrome Headless 117.0.5938.92 (Linux x86_64)/html/index.ts.html +1085 -95
- package/coverage/Chrome Headless 117.0.5938.92 (Linux x86_64)/html/popOut.ts.html +377 -0
- package/coverage/Chrome Headless 117.0.5938.92 (Linux x86_64)/html/popOutBackdrop.ts.html +1046 -0
- package/coverage/Chrome Headless 117.0.5938.92 (Linux x86_64)/html/popOutButton.ts.html +374 -0
- package/coverage/Chrome Headless 117.0.5938.92 (Linux x86_64)/html/subscribe.ts.html +218 -29
- package/coverage/Chrome Headless 117.0.5938.92 (Linux x86_64)/html/url.ts.html +121 -22
- package/dist/declarations/src/checkout.d.ts +29 -1
- package/dist/declarations/src/index.d.ts +17 -1
- package/dist/declarations/src/subscribe.d.ts +62 -0
- package/dist/dintero-checkout-web-sdk.cjs.dev.js +917 -53
- package/dist/dintero-checkout-web-sdk.cjs.prod.js +917 -53
- package/dist/dintero-checkout-web-sdk.esm.js +917 -53
- package/dist/dintero-checkout-web-sdk.umd.min.js +1 -1
- package/dist/dintero-checkout-web-sdk.umd.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ require('native-promise-only');
|
|
|
6
6
|
|
|
7
7
|
var pkg = {
|
|
8
8
|
name: "@dintero/checkout-web-sdk",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.6.0",
|
|
10
10
|
description: "Dintero Checkout SDK for web frontends",
|
|
11
11
|
main: "dist/dintero-checkout-web-sdk.cjs.js",
|
|
12
12
|
module: "dist/dintero-checkout-web-sdk.esm.js",
|
|
@@ -74,6 +74,8 @@ let InternalCheckoutEvents = /*#__PURE__*/function (InternalCheckoutEvents) {
|
|
|
74
74
|
InternalCheckoutEvents["HeightChanged"] = "HeightChanged";
|
|
75
75
|
InternalCheckoutEvents["LanguageChanged"] = "LanguageChanged";
|
|
76
76
|
InternalCheckoutEvents["ScrollToTop"] = "ScrollToTop";
|
|
77
|
+
InternalCheckoutEvents["ShowPopOutButton"] = "ShowPopOutButton";
|
|
78
|
+
InternalCheckoutEvents["HidePopOutButton"] = "HidePopOutButton";
|
|
77
79
|
return InternalCheckoutEvents;
|
|
78
80
|
}({});
|
|
79
81
|
|
|
@@ -95,19 +97,55 @@ const getSessionUrl = options => {
|
|
|
95
97
|
endpoint,
|
|
96
98
|
language,
|
|
97
99
|
ui,
|
|
98
|
-
shouldCallValidateSession
|
|
100
|
+
shouldCallValidateSession,
|
|
101
|
+
popOut
|
|
99
102
|
} = options;
|
|
100
103
|
if (!endpoint) {
|
|
101
104
|
throw new Error("Invalid endpoint");
|
|
102
105
|
}
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
106
|
+
const params = new URLSearchParams();
|
|
107
|
+
params.append('sdk', pkg.version);
|
|
108
|
+
if (ui) {
|
|
109
|
+
params.append('ui', ui);
|
|
110
|
+
}
|
|
111
|
+
if (language) {
|
|
112
|
+
params.append('language', language);
|
|
113
|
+
}
|
|
114
|
+
if (shouldCallValidateSession) {
|
|
115
|
+
params.append('client_side_validation', 'true');
|
|
116
|
+
}
|
|
117
|
+
if (popOut) {
|
|
118
|
+
params.append('role', 'pop_out_launcher');
|
|
119
|
+
}
|
|
120
|
+
if (endpoint === "https://checkout.dintero.com") {
|
|
121
|
+
// Default endpoint will redirect via the view endpoint
|
|
122
|
+
return `${endpoint}/v1/view/${sid}?${params.toString()}`;
|
|
123
|
+
}
|
|
124
|
+
// When a custom endpoint is set skip the view redirect endpoint since
|
|
125
|
+
// custom endpoints like localhost and PR builds does not support the
|
|
126
|
+
// serverside view flow.
|
|
127
|
+
params.append('sid', sid);
|
|
128
|
+
return `${endpoint}/?${params.toString()}`;
|
|
129
|
+
};
|
|
130
|
+
const padTralingSlash = endpoint => endpoint.endsWith('/') ? endpoint : `${endpoint}/`;
|
|
131
|
+
const getPopOutUrl = ({
|
|
132
|
+
sid,
|
|
133
|
+
endpoint,
|
|
134
|
+
language,
|
|
135
|
+
shouldCallValidateSession
|
|
136
|
+
}) => {
|
|
137
|
+
if (shouldCallValidateSession) {
|
|
138
|
+
return `${padTralingSlash(endpoint)}?loader=true`;
|
|
139
|
+
}
|
|
140
|
+
const params = new URLSearchParams();
|
|
141
|
+
params.append('ui', 'fullscreen');
|
|
142
|
+
params.append('role', 'pop_out_payment');
|
|
143
|
+
params.append('sid', sid);
|
|
144
|
+
params.append('sdk', pkg.version);
|
|
145
|
+
if (language) {
|
|
146
|
+
params.append('language', language);
|
|
147
|
+
}
|
|
148
|
+
return `${endpoint}/?${params.toString()}`;
|
|
111
149
|
};
|
|
112
150
|
|
|
113
151
|
/**
|
|
@@ -172,9 +210,9 @@ const createIframeAsync = (container, endpoint, url) => {
|
|
|
172
210
|
/**
|
|
173
211
|
* Post a message acknowledgement to the checkout iframe.
|
|
174
212
|
*/
|
|
175
|
-
const postAck = (
|
|
176
|
-
if (event.data.mid &&
|
|
177
|
-
|
|
213
|
+
const postAck = (source, event) => {
|
|
214
|
+
if (event.data.mid && source) {
|
|
215
|
+
source.postMessage({
|
|
178
216
|
ack: event.data.mid
|
|
179
217
|
}, event.origin || "*");
|
|
180
218
|
}
|
|
@@ -218,7 +256,7 @@ const postSessionRefresh = (iframe, sid) => {
|
|
|
218
256
|
};
|
|
219
257
|
|
|
220
258
|
/**
|
|
221
|
-
* Post
|
|
259
|
+
* Post SetActivePaymentProductType-event to the checkout iframe.
|
|
222
260
|
*/
|
|
223
261
|
const postActivePaymentProductType = (iframe, sid, paymentProductType) => {
|
|
224
262
|
if (iframe.contentWindow) {
|
|
@@ -230,6 +268,67 @@ const postActivePaymentProductType = (iframe, sid, paymentProductType) => {
|
|
|
230
268
|
}
|
|
231
269
|
};
|
|
232
270
|
|
|
271
|
+
/**
|
|
272
|
+
* Post ClosePopOut-event to the checkout iframe.
|
|
273
|
+
*/
|
|
274
|
+
const postValidatePopOutEvent = (iframe, sid) => {
|
|
275
|
+
if (iframe.contentWindow) {
|
|
276
|
+
iframe.contentWindow.postMessage({
|
|
277
|
+
type: "ValidatingPopOut",
|
|
278
|
+
sid
|
|
279
|
+
}, "*");
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Post OpenPopOutFailed-event to the checkout iframe.
|
|
285
|
+
*/
|
|
286
|
+
const postOpenPopOutFailedEvent = (iframe, sid) => {
|
|
287
|
+
if (iframe.contentWindow) {
|
|
288
|
+
iframe.contentWindow.postMessage({
|
|
289
|
+
type: "OpenPopOutFailed",
|
|
290
|
+
sid
|
|
291
|
+
}, "*");
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Post OpenedPopOut-event to the checkout iframe.
|
|
297
|
+
*/
|
|
298
|
+
const postOpenPopOutEvent = (iframe, sid) => {
|
|
299
|
+
if (iframe.contentWindow) {
|
|
300
|
+
iframe.contentWindow.postMessage({
|
|
301
|
+
type: "OpenedPopOut",
|
|
302
|
+
sid
|
|
303
|
+
}, "*");
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Post ClosePopOut-event to the checkout iframe.
|
|
309
|
+
*/
|
|
310
|
+
const postClosePopOutEvent = (iframe, sid) => {
|
|
311
|
+
if (iframe.contentWindow) {
|
|
312
|
+
iframe.contentWindow.postMessage({
|
|
313
|
+
type: "ClosedPopOut",
|
|
314
|
+
sid
|
|
315
|
+
}, "*");
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Post SetLanguage-event to the checkout iframe.
|
|
321
|
+
*/
|
|
322
|
+
const postSetLanguage = (iframe, sid, language) => {
|
|
323
|
+
if (iframe.contentWindow) {
|
|
324
|
+
iframe.contentWindow.postMessage({
|
|
325
|
+
type: "SetLanguage",
|
|
326
|
+
sid,
|
|
327
|
+
language
|
|
328
|
+
}, "*");
|
|
329
|
+
}
|
|
330
|
+
};
|
|
331
|
+
|
|
233
332
|
/**
|
|
234
333
|
* Subscribe to events from an iframe given a handler and a set
|
|
235
334
|
* of event types.
|
|
@@ -245,13 +344,14 @@ const subscribe = options => {
|
|
|
245
344
|
|
|
246
345
|
// Wrap event handler in a function that checks for correct origin and
|
|
247
346
|
// filters on event type(s) in the event data.
|
|
347
|
+
const endpointUrl = new URL(endpoint);
|
|
248
348
|
const wrappedHandler = event => {
|
|
249
|
-
const correctOrigin = event.origin ===
|
|
349
|
+
const correctOrigin = event.origin === endpointUrl.origin;
|
|
250
350
|
const correctWindow = event.source === checkout.iframe.contentWindow;
|
|
251
351
|
const correctSid = event.data && event.data.sid === sid;
|
|
252
352
|
const correctMessageType = eventTypes.indexOf(event.data && event.data.type) !== -1;
|
|
253
353
|
if (correctOrigin && correctWindow && correctSid && correctMessageType) {
|
|
254
|
-
postAck(checkout.iframe, event);
|
|
354
|
+
postAck(checkout.iframe.contentWindow, event);
|
|
255
355
|
handler(event.data, checkout);
|
|
256
356
|
}
|
|
257
357
|
};
|
|
@@ -270,10 +370,490 @@ const subscribe = options => {
|
|
|
270
370
|
};
|
|
271
371
|
};
|
|
272
372
|
|
|
373
|
+
const getBackdropZIndex = () => {
|
|
374
|
+
// Iterate all DOM items to get current highest element.
|
|
375
|
+
const elements = document.getElementsByTagName('*');
|
|
376
|
+
const highest = Array.from(elements).reduce((acc, element) => {
|
|
377
|
+
try {
|
|
378
|
+
const zIndexStr = document.defaultView.getComputedStyle(element, null).getPropertyValue("z-index");
|
|
379
|
+
const zIndex = parseInt(zIndexStr || '0');
|
|
380
|
+
if (!isNaN(zIndex) && zIndex > acc) {
|
|
381
|
+
return zIndex;
|
|
382
|
+
}
|
|
383
|
+
} catch (e) {
|
|
384
|
+
// Ignore errors when getting z-index
|
|
385
|
+
console.error(e);
|
|
386
|
+
}
|
|
387
|
+
return acc;
|
|
388
|
+
}, 0);
|
|
389
|
+
if (highest < 9999) {
|
|
390
|
+
return '9999';
|
|
391
|
+
}
|
|
392
|
+
return (highest + 1).toString();
|
|
393
|
+
};
|
|
394
|
+
const STYLE_ID = 'dintero-checkout-sdk-style';
|
|
395
|
+
const BACKDROP_ID = 'dintero-checkout-sdk-backdrop';
|
|
396
|
+
const BACKDROP_DESCRIPTION = 'dintero-checkout-sdk-backdrop-description';
|
|
397
|
+
const FOCUS_CHECKOUT_BUTTON_ID = 'dintero-checkout-sdk-backdrop-focus';
|
|
398
|
+
const CLOSE_BACKDROP_BUTTON_ID = 'dintero-checkout-sdk-backdrop-close';
|
|
399
|
+
const wrapPreventDefault = fn => {
|
|
400
|
+
// Creates a wrapped function that will invoke preventDefault() to stop
|
|
401
|
+
// the event from bubbling up the DOM tree.
|
|
402
|
+
return e => {
|
|
403
|
+
e.preventDefault();
|
|
404
|
+
e.stopPropagation();
|
|
405
|
+
fn();
|
|
406
|
+
return false;
|
|
407
|
+
};
|
|
408
|
+
};
|
|
409
|
+
const appendBackdropStyles = () => {
|
|
410
|
+
// Check if exists before appending to DOM
|
|
411
|
+
if (document.getElementById(STYLE_ID)) {
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
// Add style to DOM
|
|
415
|
+
const style = document.createElement('style');
|
|
416
|
+
style.setAttribute('id', STYLE_ID);
|
|
417
|
+
style.innerHTML = `
|
|
418
|
+
@keyframes ${BACKDROP_ID}-fade-in {
|
|
419
|
+
from {opacity: 0;}
|
|
420
|
+
to {opacity: 1;}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
#${BACKDROP_ID} {
|
|
424
|
+
position: fixed;
|
|
425
|
+
top: 0;
|
|
426
|
+
bottom: 0;
|
|
427
|
+
left: 0;
|
|
428
|
+
right: 0;
|
|
429
|
+
height: 100vh;
|
|
430
|
+
width: 100vw;
|
|
431
|
+
background-color: rgba(0,0,0,0.9);
|
|
432
|
+
background: radial-gradient(rgba(0,0,0,0.9) 0%, rgba(0,0,0,0.8) 100%);
|
|
433
|
+
cursor: pointer;
|
|
434
|
+
animation: 20ms ease-out ${BACKDROP_ID}-fade-in;
|
|
435
|
+
display: flex;
|
|
436
|
+
flex-direction: column;
|
|
437
|
+
justify-content: center;
|
|
438
|
+
align-items: center;
|
|
439
|
+
gap: 20px;
|
|
440
|
+
color: #ffffff;
|
|
441
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
|
|
442
|
+
font-size: 18px;
|
|
443
|
+
font-weight: 400;
|
|
444
|
+
line-height: normal;
|
|
445
|
+
text-rendering: geometricPrecision;
|
|
446
|
+
margin: 0;
|
|
447
|
+
padding: 0;
|
|
448
|
+
border: 0;
|
|
449
|
+
vertical-align: baseline;
|
|
450
|
+
line-height: normal;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
#${BACKDROP_ID} p {
|
|
454
|
+
padding: 0;
|
|
455
|
+
margin: 0;
|
|
456
|
+
border: 0;
|
|
457
|
+
user-select: none;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
#${FOCUS_CHECKOUT_BUTTON_ID} {
|
|
461
|
+
background-color: #efefef !important;
|
|
462
|
+
color: #000000 !important;
|
|
463
|
+
font-size: 16px !important;
|
|
464
|
+
font-weight: 600 !important;
|
|
465
|
+
border-radius: 200px !important;
|
|
466
|
+
margin: 0 !important;
|
|
467
|
+
line-height: normal !important;
|
|
468
|
+
border: none !important;
|
|
469
|
+
padding: 10px 20px !important;
|
|
470
|
+
user-select: none !important;
|
|
471
|
+
cursor: pointer !important;
|
|
472
|
+
}
|
|
473
|
+
#${FOCUS_CHECKOUT_BUTTON_ID}:hover,
|
|
474
|
+
#${FOCUS_CHECKOUT_BUTTON_ID}:focus {
|
|
475
|
+
outline: none !important;
|
|
476
|
+
background-color: #ffffff !important;
|
|
477
|
+
border: none !important;
|
|
478
|
+
color: #000000 !important;
|
|
479
|
+
padding: 10px 20px !important;
|
|
480
|
+
margin: 0 !important;
|
|
481
|
+
}
|
|
482
|
+
#${FOCUS_CHECKOUT_BUTTON_ID}:focus{
|
|
483
|
+
outline-offset: 2px;
|
|
484
|
+
outline: 1px #ffffff solid !important;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
#${CLOSE_BACKDROP_BUTTON_ID} {
|
|
488
|
+
background: transparent !important;
|
|
489
|
+
padding: 0 !important;
|
|
490
|
+
margin: 0 !important;
|
|
491
|
+
border: none !important;
|
|
492
|
+
border-radius: 4px !important;
|
|
493
|
+
height: 24px !important;
|
|
494
|
+
width: 24px !important;
|
|
495
|
+
color: #efefef !important;
|
|
496
|
+
position: absolute !important;
|
|
497
|
+
top: 16px !important;
|
|
498
|
+
right: 24px !important;
|
|
499
|
+
transition: all 200ms ease-out !important;
|
|
500
|
+
cursor: pointer !important;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
#${CLOSE_BACKDROP_BUTTON_ID}:hover,
|
|
504
|
+
#${CLOSE_BACKDROP_BUTTON_ID}:focus {
|
|
505
|
+
outline: none !important;
|
|
506
|
+
color: #ffffff !important;
|
|
507
|
+
border: none !important;
|
|
508
|
+
background: transparent !important;
|
|
509
|
+
padding: 0 !important;
|
|
510
|
+
margin: 0 !important;
|
|
511
|
+
position: absolute;
|
|
512
|
+
top: 16px;
|
|
513
|
+
right: 24px;
|
|
514
|
+
}
|
|
515
|
+
#${CLOSE_BACKDROP_BUTTON_ID}:focus{
|
|
516
|
+
outline: 1px #ffffff solid !important;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
#${BACKDROP_ID}:before,
|
|
520
|
+
#${BACKDROP_ID}:after,
|
|
521
|
+
#${BACKDROP_ID} > *:before,
|
|
522
|
+
#${BACKDROP_ID} > *:after {
|
|
523
|
+
content: '';
|
|
524
|
+
content: none;
|
|
525
|
+
}
|
|
526
|
+
`;
|
|
527
|
+
document.head.appendChild(style);
|
|
528
|
+
};
|
|
529
|
+
const createBackdropDOM = () => {
|
|
530
|
+
// Dark translucent backdrop element
|
|
531
|
+
const backdrop = document.createElement('div');
|
|
532
|
+
backdrop.setAttribute("id", BACKDROP_ID);
|
|
533
|
+
backdrop.setAttribute("role", "dialog");
|
|
534
|
+
backdrop.style.zIndex = getBackdropZIndex();
|
|
535
|
+
return backdrop;
|
|
536
|
+
};
|
|
537
|
+
const createCloseButtonDOM = label => {
|
|
538
|
+
// Close button for the top right corner
|
|
539
|
+
const button = document.createElement('button');
|
|
540
|
+
button.setAttribute("id", CLOSE_BACKDROP_BUTTON_ID);
|
|
541
|
+
button.setAttribute("type", "button");
|
|
542
|
+
button.setAttribute("aria-label", label);
|
|
543
|
+
button.innerHTML = `
|
|
544
|
+
<svg
|
|
545
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
546
|
+
width="24"
|
|
547
|
+
height="24"
|
|
548
|
+
viewBox="0 0 24 24"
|
|
549
|
+
fill="none"
|
|
550
|
+
stroke="currentColor"
|
|
551
|
+
stroke-width="2"
|
|
552
|
+
stroke-linecap="round"
|
|
553
|
+
stroke-linejoin="round"
|
|
554
|
+
alt="close icon"
|
|
555
|
+
>
|
|
556
|
+
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
557
|
+
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
558
|
+
</svg>`;
|
|
559
|
+
return button;
|
|
560
|
+
};
|
|
561
|
+
const createDinteroLogoDOM = () => {
|
|
562
|
+
// Close button for the top right corner
|
|
563
|
+
const div = document.createElement('div');
|
|
564
|
+
div.innerHTML = `
|
|
565
|
+
<svg width="120px" height="22px" viewBox="0 0 630 111" version="1.1" >
|
|
566
|
+
<g id="Page-1" stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
|
|
567
|
+
<g id="Dintero" fill="#ffffff" fillRule="nonzero">
|
|
568
|
+
<path d="M376.23,60.48 L376.23,73.54 L454.13,73.54 C456.31,41.55 435.85,23.71 410.61,23.71 C385.37,23.71 367.09,41.77 367.09,66.79 C367.09,92.03 386.02,110.31 411.91,110.31 C433.02,110.31 448.9,97.25 453.25,82.24 L436.5,82.24 C432.37,89.42 423.88,95.51 411.91,95.51 C395.16,95.51 382.75,83.11 382.75,66.79 C382.75,50.69 394.72,38.5 410.6,38.5 C426.48,38.5 438.45,50.68 438.45,66.79 L444.54,60.48 L376.23,60.48 Z M154.29,17.83 L171.7,17.83 L171.7,0.42 L154.29,0.42 L154.29,17.83 Z M120.34,108.13 L191.27,108.13 L191.27,93.77 L120.34,93.77 L120.34,108.13 Z M156.46,40.24 L156.46,108.13 L171.69,108.13 L171.69,45.47 C171.69,32.85 165.82,25.89 151.89,25.89 L120.34,25.89 L120.34,40.25 L156.46,40.25 L156.46,40.24 Z M499.17,25.88 L464.36,25.88 L464.36,40.24 L483.94,40.24 L484.16,108.13 L499.39,108.13 L499.17,62.44 C499.17,48.51 508.53,40.25 521.58,40.25 L535.29,40.25 L535.29,25.89 L524.41,25.89 C509.18,25.89 501.78,31.33 497.65,41.56 L495.47,47 L499.17,47.65 L499.17,25.88 Z M288.76,25.88 L310.52,25.88 L310.52,6.3 L325.75,6.3 L325.75,25.88 L359.69,25.88 L359.69,40.24 L325.75,40.24 L325.75,93.77 L359.69,93.77 L359.69,108.13 L332.49,108.13 C318.56,108.13 310.51,98.99 310.51,86.37 L310.51,40.24 L288.75,40.24 L288.75,25.88 L288.76,25.88 Z M464.35,108.13 L535.28,108.13 L535.28,93.77 L464.35,93.77 L464.35,108.13 Z M108.6,54.17 C108.6,23.06 85.54,0.43 53.77,0.43 L0.9,0.43 L0.9,108.14 L53.77,108.14 C85.53,108.13 108.6,85.5 108.6,54.17 M248.07,23.71 C234.58,23.71 223.92,31.98 220,41.55 L220,25.88 L204.77,25.88 L204.77,108.13 L220,108.13 L220,66.35 C220,53.08 224.79,38.93 243.72,38.93 C259.39,38.93 267.44,48.07 267.44,67.43 L267.44,108.12 L282.67,108.12 L282.67,64.6 C282.67,35.02 265.91,23.71 248.07,23.71 M586.2,110.31 C611.22,110.31 629.72,92.03 629.72,67.01 C629.72,41.99 611.23,23.71 586.2,23.71 C560.96,23.71 542.68,41.99 542.68,67.01 C542.68,92.03 560.96,110.31 586.2,110.31 M586.2,95.51 C570.32,95.51 558.35,83.33 558.35,67.01 C558.35,50.69 570.32,38.51 586.2,38.51 C602.08,38.51 614.05,50.69 614.05,67.01 C614.05,83.33 602.08,95.51 586.2,95.51 M16.99,92.9 L16.99,15.66 L51.8,15.66 C75.3,15.66 92.05,31.98 92.05,54.61 C92.05,76.8 75.3,92.91 51.8,92.91 L16.99,92.91 L16.99,92.9 Z" id="Shape"></path>
|
|
569
|
+
</g>
|
|
570
|
+
</g>
|
|
571
|
+
</svg>`;
|
|
572
|
+
return div;
|
|
573
|
+
};
|
|
574
|
+
const createLabelDOM = text => {
|
|
575
|
+
// Text about the pop out
|
|
576
|
+
const p = document.createElement('p');
|
|
577
|
+
p.setAttribute('id', BACKDROP_DESCRIPTION);
|
|
578
|
+
p.innerText = text;
|
|
579
|
+
return p;
|
|
580
|
+
};
|
|
581
|
+
const createFocusButtonDOM = text => {
|
|
582
|
+
// Mock button to give the user a call to action element to click, even
|
|
583
|
+
// though the entire backdrop (except the close button) returns focus to the
|
|
584
|
+
// checkout.
|
|
585
|
+
const button = document.createElement('button');
|
|
586
|
+
button.setAttribute("id", FOCUS_CHECKOUT_BUTTON_ID);
|
|
587
|
+
button.setAttribute("type", "button");
|
|
588
|
+
button.innerText = text;
|
|
589
|
+
return button;
|
|
590
|
+
};
|
|
591
|
+
const focusTrap = e => {
|
|
592
|
+
// Prevent the user focusing outside of the backdrop while it is visible
|
|
593
|
+
const focusButton = document.getElementById(FOCUS_CHECKOUT_BUTTON_ID);
|
|
594
|
+
const closeButton = document.getElementById(CLOSE_BACKDROP_BUTTON_ID);
|
|
595
|
+
if (e.key === 'Tab' || e.code === "Tab") {
|
|
596
|
+
if (document.activeElement === focusButton) {
|
|
597
|
+
closeButton.focus();
|
|
598
|
+
e.preventDefault();
|
|
599
|
+
} else {
|
|
600
|
+
// Tab
|
|
601
|
+
focusButton.focus();
|
|
602
|
+
e.preventDefault();
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
const createBackdropView = options => {
|
|
607
|
+
// Add styles needed for the backdrop;
|
|
608
|
+
appendBackdropStyles();
|
|
609
|
+
// Create DOM nodes
|
|
610
|
+
const backdrop = createBackdropDOM();
|
|
611
|
+
const closeButton = createCloseButtonDOM(options.event.closeLabel);
|
|
612
|
+
const dinteroLogo = createDinteroLogoDOM();
|
|
613
|
+
const label = createLabelDOM(options.event.descriptionLabel);
|
|
614
|
+
const focusButton = createFocusButtonDOM(options.event.focusLabel);
|
|
615
|
+
|
|
616
|
+
// Add click handlers
|
|
617
|
+
backdrop.onclick = wrapPreventDefault(options.focus);
|
|
618
|
+
focusButton.onclick = wrapPreventDefault(options.focus);
|
|
619
|
+
closeButton.onclick = wrapPreventDefault(options.close);
|
|
620
|
+
|
|
621
|
+
// Add focus trap when backdrop is visible
|
|
622
|
+
document.addEventListener('keydown', focusTrap);
|
|
623
|
+
|
|
624
|
+
// Append to document
|
|
625
|
+
backdrop.appendChild(closeButton);
|
|
626
|
+
backdrop.appendChild(dinteroLogo);
|
|
627
|
+
backdrop.appendChild(label);
|
|
628
|
+
backdrop.appendChild(focusButton);
|
|
629
|
+
document.body.appendChild(backdrop);
|
|
630
|
+
backdrop.focus();
|
|
631
|
+
return backdrop;
|
|
632
|
+
};
|
|
633
|
+
const setBackdropLabels = event => {
|
|
634
|
+
const focusButton = document.getElementById(FOCUS_CHECKOUT_BUTTON_ID);
|
|
635
|
+
if (focusButton) {
|
|
636
|
+
focusButton.innerText = event.focusLabel;
|
|
637
|
+
}
|
|
638
|
+
const description = document.getElementById(BACKDROP_DESCRIPTION);
|
|
639
|
+
if (description) {
|
|
640
|
+
description.innerText = event.descriptionLabel;
|
|
641
|
+
}
|
|
642
|
+
const closeButton = document.getElementById(CLOSE_BACKDROP_BUTTON_ID);
|
|
643
|
+
if (closeButton) {
|
|
644
|
+
closeButton.setAttribute('aria-label', event.descriptionLabel);
|
|
645
|
+
}
|
|
646
|
+
};
|
|
647
|
+
const createBackdrop = options => {
|
|
648
|
+
try {
|
|
649
|
+
// Check if backdrop already exists
|
|
650
|
+
const backdrop = document.getElementById(BACKDROP_ID);
|
|
651
|
+
if (backdrop) {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
return createBackdropView(options);
|
|
655
|
+
} catch (e) {
|
|
656
|
+
// Ignore errors when creating backdrop. If it fails we should not
|
|
657
|
+
// block the payment.
|
|
658
|
+
console.error(e);
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
const removeBackdrop = () => {
|
|
662
|
+
try {
|
|
663
|
+
const backdrop = document.getElementById(BACKDROP_ID);
|
|
664
|
+
if (backdrop) {
|
|
665
|
+
document.body.removeChild(backdrop);
|
|
666
|
+
}
|
|
667
|
+
document.removeEventListener('keydown', focusTrap);
|
|
668
|
+
} catch (e) {
|
|
669
|
+
// Ignore errors when closing backdrop. If it fails we should not stop
|
|
670
|
+
// the rest of the application from working.
|
|
671
|
+
console.error(e);
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
const OPEN_POP_OUT_BUTTON_ID = "dintero-checkout-sdk-launch-pop-out";
|
|
676
|
+
const configureButton = (button, {
|
|
677
|
+
label,
|
|
678
|
+
disabled,
|
|
679
|
+
top,
|
|
680
|
+
left,
|
|
681
|
+
right,
|
|
682
|
+
styles,
|
|
683
|
+
onClick
|
|
684
|
+
}) => {
|
|
685
|
+
button.setAttribute('id', OPEN_POP_OUT_BUTTON_ID);
|
|
686
|
+
button.setAttribute('type', 'button');
|
|
687
|
+
|
|
688
|
+
// Is clickable
|
|
689
|
+
if (disabled === 'true') {
|
|
690
|
+
button.setAttribute('disabled', disabled);
|
|
691
|
+
} else {
|
|
692
|
+
button.removeAttribute('disabled');
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
// Click handler
|
|
696
|
+
button.onclick = event => {
|
|
697
|
+
// Do not submit any form on the page using the SDK
|
|
698
|
+
event.preventDefault();
|
|
699
|
+
event.stopPropagation();
|
|
700
|
+
|
|
701
|
+
// Update look
|
|
702
|
+
button.style.boxShadow = 'inset 0 0 10px rgba(34, 84, 65, 0.9)';
|
|
703
|
+
|
|
704
|
+
// Invoke handler
|
|
705
|
+
onClick();
|
|
706
|
+
|
|
707
|
+
// Reset look
|
|
708
|
+
window.setTimeout(() => {
|
|
709
|
+
button.style.boxShadow = 'none';
|
|
710
|
+
}, 200);
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
// Label
|
|
714
|
+
button.innerText = label;
|
|
715
|
+
|
|
716
|
+
// Position
|
|
717
|
+
button.style.position = 'absolute';
|
|
718
|
+
button.style.top = top + 'px';
|
|
719
|
+
button.style.left = left + 'px';
|
|
720
|
+
button.style.right = right + 'px';
|
|
721
|
+
|
|
722
|
+
// Appearance from checkout
|
|
723
|
+
for (const [key, value] of Object.entries(styles)) {
|
|
724
|
+
button.style[key] = value;
|
|
725
|
+
}
|
|
726
|
+
};
|
|
727
|
+
const addPopOutButton = options => {
|
|
728
|
+
// Will add or update existing button
|
|
729
|
+
const {
|
|
730
|
+
container
|
|
731
|
+
} = options;
|
|
732
|
+
const exists = document.getElementById(OPEN_POP_OUT_BUTTON_ID);
|
|
733
|
+
const button = exists || document.createElement('button');
|
|
734
|
+
configureButton(button, options);
|
|
735
|
+
if (!exists) {
|
|
736
|
+
container.appendChild(button);
|
|
737
|
+
}
|
|
738
|
+
};
|
|
739
|
+
const setPopOutButtonDisabled = disabled => {
|
|
740
|
+
try {
|
|
741
|
+
const button = document.getElementById(OPEN_POP_OUT_BUTTON_ID);
|
|
742
|
+
if (button) {
|
|
743
|
+
if (disabled) {
|
|
744
|
+
button.setAttribute('disabled', disabled.toString());
|
|
745
|
+
} else {
|
|
746
|
+
button.removeAttribute('disabled');
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
} catch (e) {
|
|
750
|
+
// Ignore error and continue
|
|
751
|
+
console.error(e);
|
|
752
|
+
}
|
|
753
|
+
};
|
|
754
|
+
const removePopOutButton = () => {
|
|
755
|
+
try {
|
|
756
|
+
const button = document.getElementById(OPEN_POP_OUT_BUTTON_ID);
|
|
757
|
+
if (button) {
|
|
758
|
+
button.remove();
|
|
759
|
+
}
|
|
760
|
+
} catch (e) {
|
|
761
|
+
// Ignore error and continue
|
|
762
|
+
console.error(e);
|
|
763
|
+
}
|
|
764
|
+
};
|
|
765
|
+
|
|
766
|
+
const WIDTH = Math.min(480, window.screen.width);
|
|
767
|
+
const HEIGHT = Math.min(840, window.screen.height);
|
|
768
|
+
let popOutWindow;
|
|
769
|
+
const createPopOutWindow = (url, width, height) => {
|
|
770
|
+
return new Promise(resolve => {
|
|
771
|
+
try {
|
|
772
|
+
// Opens a centered pop up window
|
|
773
|
+
const left = window.screenX + (window.outerWidth - width) / 2;
|
|
774
|
+
const top = window.screenY + (window.outerHeight - height) / 2;
|
|
775
|
+
const features = `width=${width},height=${height},left=${left},top=${top},location=no,menubar=no,toolbar=no,status=no`;
|
|
776
|
+
const popOut = window.open(url, 'dintero-checkout', features);
|
|
777
|
+
if (!popOut) {
|
|
778
|
+
console.log('createPopOutWindow no popOut');
|
|
779
|
+
resolve(undefined);
|
|
780
|
+
return;
|
|
781
|
+
}
|
|
782
|
+
const timeout = window.setTimeout(() => {
|
|
783
|
+
console.log('createPopOutWindow timeout');
|
|
784
|
+
resolve(undefined);
|
|
785
|
+
}, 3000);
|
|
786
|
+
popOut.addEventListener('load', event => {
|
|
787
|
+
console.log('createPopOutWindow loaded', {
|
|
788
|
+
popOut,
|
|
789
|
+
event
|
|
790
|
+
});
|
|
791
|
+
clearTimeout(timeout);
|
|
792
|
+
resolve(popOut);
|
|
793
|
+
});
|
|
794
|
+
} catch (err) {
|
|
795
|
+
resolve(undefined);
|
|
796
|
+
}
|
|
797
|
+
});
|
|
798
|
+
};
|
|
799
|
+
const openPopOut = async options => {
|
|
800
|
+
let unsubscribe;
|
|
801
|
+
let intervalId = -1;
|
|
802
|
+
if (popOutWindow && !popOutWindow.closed) {
|
|
803
|
+
// Skip if already open.
|
|
804
|
+
return;
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// Open popup window
|
|
808
|
+
const url = getPopOutUrl(options);
|
|
809
|
+
popOutWindow = await createPopOutWindow(url, WIDTH, HEIGHT);
|
|
810
|
+
const focusPopOut = () => {
|
|
811
|
+
if (popOutWindow) {
|
|
812
|
+
popOutWindow.focus();
|
|
813
|
+
}
|
|
814
|
+
};
|
|
815
|
+
const cleanUpClosed = () => {
|
|
816
|
+
window.clearInterval(intervalId);
|
|
817
|
+
intervalId = -1;
|
|
818
|
+
window.removeEventListener('beforeunload', closePopOut);
|
|
819
|
+
popOutWindow = undefined;
|
|
820
|
+
options.onClose();
|
|
821
|
+
if (unsubscribe) {
|
|
822
|
+
unsubscribe();
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
const closePopOut = () => {
|
|
826
|
+
if (popOutWindow) {
|
|
827
|
+
popOutWindow.close();
|
|
828
|
+
}
|
|
829
|
+
cleanUpClosed();
|
|
830
|
+
};
|
|
831
|
+
const checkIfPopupClosed = () => {
|
|
832
|
+
if (popOutWindow && popOutWindow.closed) {
|
|
833
|
+
cleanUpClosed();
|
|
834
|
+
}
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
// Close pop out if current window is closed
|
|
838
|
+
window.addEventListener('beforeunload', closePopOut);
|
|
839
|
+
|
|
840
|
+
// Check if checkout is still open
|
|
841
|
+
intervalId = window.setInterval(checkIfPopupClosed, 200);
|
|
842
|
+
|
|
843
|
+
// Set up pub/sub of messages from pop out to SDK
|
|
844
|
+
unsubscribe = options.onOpen(popOutWindow);
|
|
845
|
+
return {
|
|
846
|
+
close: closePopOut,
|
|
847
|
+
focus: focusPopOut,
|
|
848
|
+
popOutWindow
|
|
849
|
+
};
|
|
850
|
+
};
|
|
851
|
+
|
|
273
852
|
/**
|
|
274
853
|
* An event handler that navigates to the href in the event.
|
|
275
854
|
*/
|
|
276
|
-
const followHref = event => {
|
|
855
|
+
const followHref = (event, checkout) => {
|
|
856
|
+
cleanUpPopOut(checkout);
|
|
277
857
|
if (event.href) {
|
|
278
858
|
windowLocationAssign(event.href);
|
|
279
859
|
}
|
|
@@ -299,7 +879,7 @@ const scrollToIframeTop = (event, checkout) => {
|
|
|
299
879
|
behavior: 'smooth'
|
|
300
880
|
});
|
|
301
881
|
} catch (e) {
|
|
302
|
-
// Ignore
|
|
882
|
+
// Ignore error silently bug log it to the console.
|
|
303
883
|
console.error(e);
|
|
304
884
|
}
|
|
305
885
|
};
|
|
@@ -312,30 +892,247 @@ const setLanguage = (event, checkout) => {
|
|
|
312
892
|
checkout.language = event.language;
|
|
313
893
|
}
|
|
314
894
|
};
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
895
|
+
|
|
896
|
+
/**
|
|
897
|
+
* Wrap function with try catch so an error in a single function won't short circuit other code in the current context.
|
|
898
|
+
*/
|
|
899
|
+
const safelyInvoke = fn => {
|
|
900
|
+
try {
|
|
901
|
+
fn();
|
|
902
|
+
} catch (e) {
|
|
903
|
+
console.error(e);
|
|
904
|
+
}
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Handle messages sendt to the SDK from the pop out.
|
|
909
|
+
*/
|
|
910
|
+
const createPopOutMessageHandler = (source, checkout) => {
|
|
911
|
+
// Change language in embed if changed in pop out
|
|
912
|
+
const popOutChangedLanguageHandler = {
|
|
913
|
+
internalPopOutHandler: true,
|
|
914
|
+
eventTypes: [InternalCheckoutEvents.LanguageChanged],
|
|
915
|
+
handler: (eventData, checkout) => {
|
|
916
|
+
// Tell the embedded checkout to change language.
|
|
917
|
+
postSetLanguage(checkout.iframe, checkout.options.sid, eventData.language);
|
|
918
|
+
}
|
|
919
|
+
};
|
|
920
|
+
|
|
921
|
+
// Close pop out, and remove SDK rendered button when payment is completed.
|
|
922
|
+
const paymentCompletedEvents = [CheckoutEvents.SessionCancel, CheckoutEvents.SessionPaymentOnHold, CheckoutEvents.SessionPaymentAuthorized, CheckoutEvents.SessionPaymentError];
|
|
923
|
+
const popOutCompletedHandler = {
|
|
924
|
+
internalPopOutHandler: true,
|
|
925
|
+
eventTypes: paymentCompletedEvents,
|
|
926
|
+
handler: (eventData, checkout) => {
|
|
927
|
+
if (eventData.href) {
|
|
928
|
+
// Remove open pop out button rendered by SDK
|
|
929
|
+
removePopOutButton();
|
|
930
|
+
|
|
931
|
+
// Close checkout
|
|
932
|
+
try {
|
|
933
|
+
source.close();
|
|
934
|
+
} catch (e) {
|
|
935
|
+
console.error(e);
|
|
936
|
+
}
|
|
937
|
+
} else {
|
|
938
|
+
console.error('Payment Complete event missing href property');
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
};
|
|
942
|
+
|
|
943
|
+
// Listens to messages from pop out window and routes the events to dedicated handlers
|
|
944
|
+
const messageRouter = event => {
|
|
945
|
+
// Check that we should handle the message
|
|
946
|
+
if (event.source === source && event.data.context === 'popOut' && event.data.sid === checkout.options.sid) {
|
|
947
|
+
// Check if handler matches incoming event and trigger the handler if so.
|
|
948
|
+
[
|
|
949
|
+
// SDK events for managing the pop out flow.
|
|
950
|
+
popOutChangedLanguageHandler, popOutCompletedHandler,
|
|
951
|
+
// Events configured when the checkout was embedded.
|
|
952
|
+
...checkout.handlers].forEach(handlerObject => {
|
|
953
|
+
if (handlerObject.eventTypes.includes(event.data.type) && handlerObject.handler) {
|
|
954
|
+
// Invoking the handler function if the event type matches the handler.
|
|
955
|
+
safelyInvoke(() => {
|
|
956
|
+
handlerObject.handler(event.data, checkout);
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
}
|
|
961
|
+
};
|
|
962
|
+
// Add messageRouter event listener to the Pop Out
|
|
963
|
+
window.addEventListener('message', messageRouter);
|
|
964
|
+
|
|
965
|
+
// Return unsubscribe function
|
|
966
|
+
return () => {
|
|
967
|
+
window.removeEventListener('message', messageRouter);
|
|
968
|
+
};
|
|
969
|
+
};
|
|
970
|
+
|
|
971
|
+
/**
|
|
972
|
+
* Configures and shows the pop out with the payment options.
|
|
973
|
+
*/
|
|
974
|
+
const showPopOut = async (event, checkout) => {
|
|
975
|
+
const {
|
|
976
|
+
close,
|
|
977
|
+
focus,
|
|
978
|
+
popOutWindow
|
|
979
|
+
} = await openPopOut({
|
|
980
|
+
sid: checkout.options.sid,
|
|
981
|
+
endpoint: checkout.options.endpoint,
|
|
982
|
+
shouldCallValidateSession: Boolean(checkout.options.onValidateSession),
|
|
983
|
+
language: event.language,
|
|
984
|
+
onOpen: popOutWindow => createPopOutMessageHandler(popOutWindow, checkout),
|
|
985
|
+
onClose: () => {
|
|
986
|
+
removeBackdrop();
|
|
987
|
+
postClosePopOutEvent(checkout.iframe, checkout.options.sid);
|
|
988
|
+
setPopOutButtonDisabled(false);
|
|
989
|
+
checkout.popOutWindow = undefined;
|
|
990
|
+
}
|
|
991
|
+
});
|
|
992
|
+
if (popOutWindow) {
|
|
993
|
+
postOpenPopOutEvent(checkout.iframe, checkout.options.sid);
|
|
994
|
+
// Add pop out window to checkout instance
|
|
995
|
+
checkout.popOutWindow = popOutWindow;
|
|
996
|
+
createBackdrop({
|
|
997
|
+
focus,
|
|
998
|
+
close,
|
|
999
|
+
event
|
|
1000
|
+
});
|
|
1001
|
+
return true;
|
|
1002
|
+
} else {
|
|
1003
|
+
postOpenPopOutFailedEvent(checkout.iframe, checkout.options.sid);
|
|
1004
|
+
return false;
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* Create callback function for the client side validation flow. It allows the
|
|
1010
|
+
* host application to validate the content of the payment session before the
|
|
1011
|
+
* pop out is opened.
|
|
1012
|
+
*/
|
|
1013
|
+
const createPopOutValidationCallback = (event, checkout) => {
|
|
1014
|
+
return result => {
|
|
1015
|
+
// Tell the embedded iframe about the validation result so it can show an error message if
|
|
1016
|
+
// the validation failed.
|
|
1017
|
+
postValidationResult(checkout.iframe, checkout.options.sid, result);
|
|
1018
|
+
if (result.success && checkout.popOutWindow) {
|
|
1019
|
+
// Redirect user to session in pop out window
|
|
1020
|
+
checkout.popOutWindow.location.href = getPopOutUrl({
|
|
1021
|
+
sid: checkout.options.sid,
|
|
1022
|
+
endpoint: checkout.options.endpoint,
|
|
1023
|
+
shouldCallValidateSession: false,
|
|
1024
|
+
language: event.language
|
|
1025
|
+
});
|
|
1026
|
+
} else {
|
|
1027
|
+
// Close pop out
|
|
1028
|
+
if (checkout.popOutWindow) {
|
|
1029
|
+
checkout.popOutWindow.close();
|
|
1030
|
+
}
|
|
1031
|
+
// Log validation error to console log.
|
|
1032
|
+
console.error(result.clientValidationError);
|
|
321
1033
|
}
|
|
322
|
-
pairs.push(["language", checkout.language]);
|
|
323
|
-
pairs.push(["sdk", pkg.version]);
|
|
324
|
-
const urlQuery = pairs.filter(([key, value]) => value).map(([key, value]) => `${key}=${value}`).join("&");
|
|
325
|
-
checkout.iframe.setAttribute("src", `${endpoint}/embedResult/?${urlQuery}`);
|
|
326
|
-
handler(event, checkout);
|
|
327
1034
|
};
|
|
328
1035
|
};
|
|
329
1036
|
|
|
1037
|
+
/**
|
|
1038
|
+
* Handle click event on the SDK rendered pop out button
|
|
1039
|
+
*/
|
|
1040
|
+
const handlePopOutButtonClick = async (event, checkout) => {
|
|
1041
|
+
// Disable button while pop out is open
|
|
1042
|
+
const opened = await showPopOut(event, checkout);
|
|
1043
|
+
if (opened && checkout.options.onValidateSession) {
|
|
1044
|
+
// Let the host application validate the payment session before opening checkout.
|
|
1045
|
+
|
|
1046
|
+
// Tell the embedded iframe that we are validating the session
|
|
1047
|
+
postValidatePopOutEvent(checkout.iframe, checkout.options.sid);
|
|
1048
|
+
|
|
1049
|
+
// Create callback function added to the SDK event and onValidateSession attributes
|
|
1050
|
+
const callback = createPopOutValidationCallback(event, checkout);
|
|
1051
|
+
|
|
1052
|
+
// Invoke onValidateSession function defined in checkout options
|
|
1053
|
+
try {
|
|
1054
|
+
checkout.options.onValidateSession({
|
|
1055
|
+
type: CheckoutEvents.ValidateSession,
|
|
1056
|
+
session: checkout.session,
|
|
1057
|
+
callback
|
|
1058
|
+
}, checkout, callback);
|
|
1059
|
+
} catch (e) {
|
|
1060
|
+
console.error(e);
|
|
1061
|
+
postValidationResult(checkout.iframe, checkout.options.sid, {
|
|
1062
|
+
success: false,
|
|
1063
|
+
clientValidationError: 'Validation runtime error'
|
|
1064
|
+
});
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
};
|
|
1068
|
+
|
|
1069
|
+
/**
|
|
1070
|
+
* Type guard for ShowPopOutButton
|
|
1071
|
+
*/
|
|
1072
|
+
const isShowPopOutButton = event => {
|
|
1073
|
+
return event && event.type === InternalCheckoutEvents.ShowPopOutButton;
|
|
1074
|
+
};
|
|
1075
|
+
|
|
1076
|
+
/**
|
|
1077
|
+
* Display the SDK rendered pop out button on top of the embedded iframe
|
|
1078
|
+
*/
|
|
1079
|
+
const handleShowButton = (event, checkout) => {
|
|
1080
|
+
if (isShowPopOutButton(event)) {
|
|
1081
|
+
addPopOutButton({
|
|
1082
|
+
container: checkout.options.innerContainer,
|
|
1083
|
+
label: event.openLabel,
|
|
1084
|
+
top: event.top,
|
|
1085
|
+
left: event.left,
|
|
1086
|
+
right: event.right,
|
|
1087
|
+
styles: event.styles,
|
|
1088
|
+
disabled: event.disabled,
|
|
1089
|
+
onClick: () => handlePopOutButtonClick(event, checkout)
|
|
1090
|
+
});
|
|
1091
|
+
setBackdropLabels(event);
|
|
1092
|
+
}
|
|
1093
|
+
};
|
|
1094
|
+
|
|
1095
|
+
/**
|
|
1096
|
+
* Remove the pop out button above the embedded iframe
|
|
1097
|
+
*/
|
|
1098
|
+
const handleRemoveButton = (event, checkout) => {
|
|
1099
|
+
if (event.type === InternalCheckoutEvents.HidePopOutButton) {
|
|
1100
|
+
removePopOutButton();
|
|
1101
|
+
}
|
|
1102
|
+
};
|
|
1103
|
+
const cleanUpPopOut = checkout => {
|
|
1104
|
+
// Ensures that the pop out is no longer open when the payment is completed or the checkout is destroyed.
|
|
1105
|
+
removePopOutButton();
|
|
1106
|
+
removeBackdrop();
|
|
1107
|
+
if (checkout.popOutWindow) {
|
|
1108
|
+
try {
|
|
1109
|
+
checkout.popOutWindow.close();
|
|
1110
|
+
// Pop out message handlers will be removed when the pop out window is closed
|
|
1111
|
+
// via the interval created by openPopOut.
|
|
1112
|
+
} catch (e) {
|
|
1113
|
+
console.error(e);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
|
|
330
1118
|
/**
|
|
331
1119
|
* Show a dintero payment session in an embedded iframe.
|
|
332
1120
|
*/
|
|
333
1121
|
const embed = async options => {
|
|
1122
|
+
// Create inner container to offset any styling on the container.
|
|
1123
|
+
const innerContainer = document.createElement('div');
|
|
1124
|
+
innerContainer.style.position = 'relative';
|
|
1125
|
+
innerContainer.style['box-sizing'] = 'border-box';
|
|
1126
|
+
const internalOptions = {
|
|
1127
|
+
endpoint: "https://checkout.dintero.com",
|
|
1128
|
+
innerContainer: innerContainer,
|
|
1129
|
+
...options
|
|
1130
|
+
};
|
|
334
1131
|
const {
|
|
335
1132
|
container,
|
|
336
1133
|
sid,
|
|
337
1134
|
language,
|
|
338
|
-
endpoint
|
|
1135
|
+
endpoint,
|
|
339
1136
|
onSession,
|
|
340
1137
|
onSessionCancel,
|
|
341
1138
|
onPayment,
|
|
@@ -345,33 +1142,45 @@ const embed = async options => {
|
|
|
345
1142
|
onSessionLocked,
|
|
346
1143
|
onSessionLockFailed,
|
|
347
1144
|
onActivePaymentType,
|
|
348
|
-
onValidateSession
|
|
349
|
-
|
|
1145
|
+
onValidateSession,
|
|
1146
|
+
popOut
|
|
1147
|
+
} = internalOptions;
|
|
350
1148
|
let checkout;
|
|
351
1149
|
const subscriptions = [];
|
|
1150
|
+
let has_delivered_final_event = false;
|
|
352
1151
|
|
|
353
1152
|
// Create iframe
|
|
1153
|
+
container.appendChild(innerContainer);
|
|
354
1154
|
const {
|
|
355
1155
|
iframe,
|
|
356
1156
|
initiate
|
|
357
|
-
} = createIframeAsync(
|
|
1157
|
+
} = createIframeAsync(innerContainer, endpoint, getSessionUrl({
|
|
358
1158
|
sid,
|
|
359
1159
|
endpoint,
|
|
360
1160
|
language,
|
|
361
1161
|
ui: "inline",
|
|
362
|
-
shouldCallValidateSession: onValidateSession !== undefined
|
|
1162
|
+
shouldCallValidateSession: onValidateSession !== undefined,
|
|
1163
|
+
popOut
|
|
363
1164
|
}));
|
|
364
1165
|
|
|
365
1166
|
/**
|
|
366
|
-
* Function that removes the iframe and all event listeners.
|
|
1167
|
+
* Function that removes the iframe, pop out and all event listeners.
|
|
367
1168
|
*/
|
|
368
1169
|
const destroy = () => {
|
|
1170
|
+
cleanUpPopOut(checkout);
|
|
369
1171
|
if (iframe) {
|
|
1172
|
+
if (internalOptions.popOut) {
|
|
1173
|
+
// Try to remove backdrop if it exists
|
|
1174
|
+
removeBackdrop();
|
|
1175
|
+
}
|
|
370
1176
|
subscriptions.forEach(sub => sub.unsubscribe());
|
|
371
1177
|
if (iframe.parentElement) {
|
|
372
|
-
|
|
1178
|
+
innerContainer.removeChild(iframe);
|
|
373
1179
|
}
|
|
374
1180
|
}
|
|
1181
|
+
if (innerContainer.parentElement) {
|
|
1182
|
+
container.removeChild(innerContainer);
|
|
1183
|
+
}
|
|
375
1184
|
};
|
|
376
1185
|
|
|
377
1186
|
/**
|
|
@@ -392,7 +1201,8 @@ const embed = async options => {
|
|
|
392
1201
|
resolve(sessionEvent);
|
|
393
1202
|
},
|
|
394
1203
|
eventTypes: [resolveEvent],
|
|
395
|
-
checkout
|
|
1204
|
+
checkout,
|
|
1205
|
+
source: checkout.iframe.contentWindow
|
|
396
1206
|
}));
|
|
397
1207
|
eventSubscriptions.push(subscribe({
|
|
398
1208
|
sid,
|
|
@@ -402,7 +1212,8 @@ const embed = async options => {
|
|
|
402
1212
|
reject(`Received unexpected event: ${rejectEvent}`);
|
|
403
1213
|
},
|
|
404
1214
|
eventTypes: [rejectEvent],
|
|
405
|
-
checkout
|
|
1215
|
+
checkout,
|
|
1216
|
+
source: checkout.iframe.contentWindow
|
|
406
1217
|
}));
|
|
407
1218
|
action();
|
|
408
1219
|
});
|
|
@@ -423,9 +1234,44 @@ const embed = async options => {
|
|
|
423
1234
|
const submitValidationResult = result => {
|
|
424
1235
|
postValidationResult(iframe, sid, result);
|
|
425
1236
|
};
|
|
1237
|
+
|
|
1238
|
+
/**
|
|
1239
|
+
* Internal result event message handler wrapper, to replace the content of the iframe with a success/or
|
|
1240
|
+
* error message. Only used when the embed function in the SDK has a dedicated handler for onPayment, onError etc.
|
|
1241
|
+
* If no custom handler is set the followHref handler is used instead.
|
|
1242
|
+
*/
|
|
1243
|
+
const handleWithResult = (sid, endpoint, handler) => {
|
|
1244
|
+
return (event, checkout) => {
|
|
1245
|
+
if (!has_delivered_final_event) {
|
|
1246
|
+
has_delivered_final_event = true;
|
|
1247
|
+
cleanUpPopOut(checkout);
|
|
1248
|
+
const eventKeys = ["sid", "merchant_reference", "transaction_id", "error"];
|
|
1249
|
+
const pairs = eventKeys.map(key => [key, event[key]]);
|
|
1250
|
+
if (event.type === CheckoutEvents.SessionCancel && !event.error) {
|
|
1251
|
+
pairs.push(["error", "cancelled"]);
|
|
1252
|
+
}
|
|
1253
|
+
pairs.push(["language", checkout.language]);
|
|
1254
|
+
pairs.push(["sdk", pkg.version]);
|
|
1255
|
+
const urlQuery = pairs.filter(([key, value]) => value).map(([key, value]) => `${key}=${value}`).join("&");
|
|
1256
|
+
checkout.iframe.setAttribute("src", `${endpoint}/embedResult/?${urlQuery}`);
|
|
1257
|
+
handler(event, checkout);
|
|
1258
|
+
}
|
|
1259
|
+
};
|
|
1260
|
+
};
|
|
426
1261
|
const wrappedOnValidateSession = (event, checkout) => {
|
|
427
1262
|
if (onValidateSession) {
|
|
428
|
-
|
|
1263
|
+
try {
|
|
1264
|
+
onValidateSession({
|
|
1265
|
+
...event,
|
|
1266
|
+
callback: submitValidationResult
|
|
1267
|
+
}, checkout, submitValidationResult);
|
|
1268
|
+
} catch (e) {
|
|
1269
|
+
console.error(e);
|
|
1270
|
+
submitValidationResult({
|
|
1271
|
+
success: false,
|
|
1272
|
+
clientValidationError: "Validation runtime error"
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
429
1275
|
}
|
|
430
1276
|
};
|
|
431
1277
|
const wrappedOnSessionLocked = (event, checkout) => {
|
|
@@ -433,20 +1279,16 @@ const embed = async options => {
|
|
|
433
1279
|
onSessionLocked(event, checkout, refreshSession);
|
|
434
1280
|
}
|
|
435
1281
|
};
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
lockSession,
|
|
443
|
-
refreshSession,
|
|
444
|
-
setActivePaymentProductType,
|
|
445
|
-
submitValidationResult
|
|
1282
|
+
const wrappedOnLoadedOrUpdated = (event, checkout) => {
|
|
1283
|
+
// Update the checkout instance to include the session object
|
|
1284
|
+
checkout.session = event.session;
|
|
1285
|
+
if (onSession) {
|
|
1286
|
+
onSession(event, checkout);
|
|
1287
|
+
}
|
|
446
1288
|
};
|
|
447
1289
|
|
|
448
1290
|
// Add event handlers (or in some cases add a fallback href handler).
|
|
449
|
-
[{
|
|
1291
|
+
const handlers = [{
|
|
450
1292
|
handler: setLanguage,
|
|
451
1293
|
eventTypes: [InternalCheckoutEvents.LanguageChanged]
|
|
452
1294
|
}, {
|
|
@@ -456,7 +1298,7 @@ const embed = async options => {
|
|
|
456
1298
|
handler: scrollToIframeTop,
|
|
457
1299
|
eventTypes: [InternalCheckoutEvents.ScrollToTop]
|
|
458
1300
|
}, {
|
|
459
|
-
handler:
|
|
1301
|
+
handler: wrappedOnLoadedOrUpdated,
|
|
460
1302
|
eventTypes: [CheckoutEvents.SessionLoaded, CheckoutEvents.SessionUpdated]
|
|
461
1303
|
}, {
|
|
462
1304
|
eventTypes: [CheckoutEvents.SessionPaymentOnHold],
|
|
@@ -485,7 +1327,28 @@ const embed = async options => {
|
|
|
485
1327
|
}, {
|
|
486
1328
|
handler: wrappedOnValidateSession,
|
|
487
1329
|
eventTypes: [CheckoutEvents.ValidateSession]
|
|
488
|
-
}
|
|
1330
|
+
}, {
|
|
1331
|
+
handler: handleShowButton,
|
|
1332
|
+
eventTypes: [InternalCheckoutEvents.ShowPopOutButton]
|
|
1333
|
+
}, {
|
|
1334
|
+
handler: handleRemoveButton,
|
|
1335
|
+
eventTypes: [InternalCheckoutEvents.HidePopOutButton]
|
|
1336
|
+
}];
|
|
1337
|
+
// Create checkout object that wraps the destroy function.
|
|
1338
|
+
checkout = {
|
|
1339
|
+
destroy,
|
|
1340
|
+
iframe,
|
|
1341
|
+
language,
|
|
1342
|
+
lockSession,
|
|
1343
|
+
refreshSession,
|
|
1344
|
+
setActivePaymentProductType,
|
|
1345
|
+
submitValidationResult,
|
|
1346
|
+
options: internalOptions,
|
|
1347
|
+
handlers,
|
|
1348
|
+
session: undefined,
|
|
1349
|
+
popOutWindow: undefined
|
|
1350
|
+
};
|
|
1351
|
+
handlers.forEach(({
|
|
489
1352
|
handler,
|
|
490
1353
|
eventTypes
|
|
491
1354
|
}) => {
|
|
@@ -495,7 +1358,8 @@ const embed = async options => {
|
|
|
495
1358
|
endpoint,
|
|
496
1359
|
handler,
|
|
497
1360
|
eventTypes,
|
|
498
|
-
checkout
|
|
1361
|
+
checkout,
|
|
1362
|
+
source: checkout.iframe.contentWindow
|
|
499
1363
|
}));
|
|
500
1364
|
}
|
|
501
1365
|
});
|