@morefin/cashier-bootstrapper 0.3.4 → 0.3.6
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 +9 -2
- package/dist/example-usage.js +2 -0
- package/dist/index.d.ts +8 -1
- package/dist/index.js +92 -2
- package/dist/types.d.ts +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,11 +37,12 @@ new CashierBootstrapper('#cashier-root', {
|
|
|
37
37
|
terminalId: 'terminal-456',
|
|
38
38
|
userId: 'user-789',
|
|
39
39
|
sessionId: 'session-abc',
|
|
40
|
+
locale: 'sv_SE',
|
|
41
|
+
channel: 'ios',
|
|
40
42
|
predefinedAmounts: [100, 200, 300],
|
|
41
43
|
layout: 'default',
|
|
42
44
|
transactionType: ('deposit'|'withdrawal'),
|
|
43
45
|
attributes: {
|
|
44
|
-
channel: 'web',
|
|
45
46
|
campaign: 'spring-launch',
|
|
46
47
|
isVip: true
|
|
47
48
|
}
|
|
@@ -62,7 +63,9 @@ new CashierBootstrapper('#cashier-root', {
|
|
|
62
63
|
});
|
|
63
64
|
```
|
|
64
65
|
|
|
65
|
-
`requestParams.
|
|
66
|
+
`requestParams.channel` selects the cashier payment method order channel. Supported values are `windows`, `mac`, `ios`, `android`, and `other`; when omitted, the bootstrapper derives it from the user device.
|
|
67
|
+
|
|
68
|
+
`requestParams.attributes` sends custom cashier data as the `attributes` query parameter. Use it for values the cashier should receive as part of the request payload. It does not control payment method ordering.
|
|
66
69
|
|
|
67
70
|
`properties.iframe.attributes` applies plain HTML attributes directly to the rendered `<iframe>`. Use it for DOM concerns such as `data-*`, `loading`, or other iframe element attributes.
|
|
68
71
|
|
|
@@ -150,12 +153,16 @@ interface CashierRequestParams {
|
|
|
150
153
|
terminalId?: string;
|
|
151
154
|
userId?: string;
|
|
152
155
|
sessionId?: string;
|
|
156
|
+
locale?: string;
|
|
157
|
+
channel?: CashierRuntimeChannel | string;
|
|
153
158
|
predefinedAmounts?: number[];
|
|
154
159
|
layout?: string;
|
|
155
160
|
transactionType?: string;
|
|
156
161
|
attributes?: Record<string, unknown>;
|
|
157
162
|
}
|
|
158
163
|
|
|
164
|
+
type CashierRuntimeChannel = 'windows' | 'mac' | 'ios' | 'android' | 'other';
|
|
165
|
+
|
|
159
166
|
type CashierEnvironment = 'production' | 'uat';
|
|
160
167
|
|
|
161
168
|
interface CashierIframeOptions {
|
package/dist/example-usage.js
CHANGED
|
@@ -18,6 +18,7 @@ export function example2(container) {
|
|
|
18
18
|
terminalId: 'terminal-456',
|
|
19
19
|
userId: 'user-789',
|
|
20
20
|
sessionId: 'session-abc',
|
|
21
|
+
locale: 'sv_SE',
|
|
21
22
|
predefinedAmounts: [100, 200, 300],
|
|
22
23
|
layout: 'default'
|
|
23
24
|
},
|
|
@@ -46,6 +47,7 @@ export function example4(container) {
|
|
|
46
47
|
merchantId: 'merchant-123',
|
|
47
48
|
terminalId: 'terminal-456',
|
|
48
49
|
userId: 'user-789',
|
|
50
|
+
locale: 'sv_SE',
|
|
49
51
|
predefinedAmounts: [100, 200, 300],
|
|
50
52
|
layout: 'default'
|
|
51
53
|
},
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CashierIframeApi, CashierIframeConfig } from './types';
|
|
2
|
-
export type { CashierCallbacks, CashierEnvironment, CashierRequestParams, CashierIframeApi, CashierIframeConfig, CashierIframeProperties, CashierResultCallbackPayload, CashierRedirectPayload, CashierValidationFailedPayload } from './types';
|
|
2
|
+
export type { CashierCallbacks, CashierEnvironment, CashierRequestParams, CashierRuntimeChannel, CashierIframeApi, CashierIframeConfig, CashierIframeProperties, CashierResultCallbackPayload, CashierRedirectPayload, CashierValidationFailedPayload } from './types';
|
|
3
3
|
type FingerprintJSGlobal = {
|
|
4
4
|
load: () => Promise<{
|
|
5
5
|
get: () => Promise<{
|
|
@@ -18,6 +18,9 @@ export declare class CashierBootstrapper {
|
|
|
18
18
|
private redirectOverlayPanel?;
|
|
19
19
|
private redirectOverlayIframe?;
|
|
20
20
|
private overlayPositionSyncHandler?;
|
|
21
|
+
private managedRedirectTab?;
|
|
22
|
+
private managedRedirectTabTransactionId?;
|
|
23
|
+
private managedRedirectTabCheckIntervalId?;
|
|
21
24
|
private origin;
|
|
22
25
|
private ready;
|
|
23
26
|
private readonly fullConfig;
|
|
@@ -41,6 +44,10 @@ export declare class CashierBootstrapper {
|
|
|
41
44
|
private normalizeRedirectTarget;
|
|
42
45
|
private normalizeRedirectMethod;
|
|
43
46
|
private normalizeRedirectParameters;
|
|
47
|
+
private openManagedRedirectTab;
|
|
48
|
+
private openManagedRedirectTabWithPost;
|
|
49
|
+
private trackManagedRedirectTab;
|
|
50
|
+
private clearManagedRedirectTabWatcher;
|
|
44
51
|
private openPopupWithPost;
|
|
45
52
|
private writePostFormToWindow;
|
|
46
53
|
private submitPostForm;
|
package/dist/index.js
CHANGED
|
@@ -2,10 +2,12 @@ const DEFAULT_IFRAME_ALLOW = 'geolocation *;camera *;payment *;clipboard-read *;
|
|
|
2
2
|
const CASHIER_PATH = '/cashier';
|
|
3
3
|
const FINGERPRINT_CDN = 'https://cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@4/dist/fp.min.js';
|
|
4
4
|
const CASHIER_REDIRECT_EVENT = 'CASHIER_REDIRECT';
|
|
5
|
+
const CASHIER_REDIRECT_TAB_CLOSED_EVENT = 'CASHIER_REDIRECT_TAB_CLOSED';
|
|
5
6
|
const TOP_URL_REPLACE_EVENT = 'TOP_URL_REPLACE';
|
|
6
7
|
const CASHIER_IFRAME_OVERLAY_CLOSE_EVENT = 'CASHIER_IFRAME_OVERLAY_CLOSE';
|
|
7
8
|
const CASHIER_RESULT_EVENT = 'CASHIER_RESULT';
|
|
8
9
|
const CASHIER_VALIDATION_FAILED_EVENT = 'CASHIER_VALIDATION_FAILED';
|
|
10
|
+
const CASHIER_RUNTIME_CHANNELS = ['windows', 'mac', 'ios', 'android', 'other'];
|
|
9
11
|
const CASHIER_ENVIRONMENT_CONFIG = {
|
|
10
12
|
production: {
|
|
11
13
|
host: 'https://api.morefin.com',
|
|
@@ -64,6 +66,37 @@ async function generateFingerprint() {
|
|
|
64
66
|
return undefined;
|
|
65
67
|
}
|
|
66
68
|
}
|
|
69
|
+
function normalizeRuntimeChannel(channel) {
|
|
70
|
+
if (channel == null) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
const normalized = channel.trim().toLowerCase();
|
|
74
|
+
return CASHIER_RUNTIME_CHANNELS.includes(normalized)
|
|
75
|
+
? normalized
|
|
76
|
+
: undefined;
|
|
77
|
+
}
|
|
78
|
+
function detectRuntimeChannel() {
|
|
79
|
+
if (typeof navigator === 'undefined') {
|
|
80
|
+
return undefined;
|
|
81
|
+
}
|
|
82
|
+
const userAgent = `${navigator.userAgent ?? ''} ${navigator.appVersion ?? ''}`.toLowerCase();
|
|
83
|
+
if (userAgent.includes('android')) {
|
|
84
|
+
return 'android';
|
|
85
|
+
}
|
|
86
|
+
if (userAgent.includes('iphone') || userAgent.includes('ipad') || userAgent.includes('ipod')) {
|
|
87
|
+
return 'ios';
|
|
88
|
+
}
|
|
89
|
+
if (userAgent.includes('windows')) {
|
|
90
|
+
return 'windows';
|
|
91
|
+
}
|
|
92
|
+
if (userAgent.includes('mac')) {
|
|
93
|
+
return 'mac';
|
|
94
|
+
}
|
|
95
|
+
return 'other';
|
|
96
|
+
}
|
|
97
|
+
function resolveRuntimeChannel(channel) {
|
|
98
|
+
return channel == null ? detectRuntimeChannel() : normalizeRuntimeChannel(channel);
|
|
99
|
+
}
|
|
67
100
|
function buildQueryString(requestParams, fingerprint) {
|
|
68
101
|
const query = new URLSearchParams();
|
|
69
102
|
if (requestParams.merchantId != null) {
|
|
@@ -78,6 +111,13 @@ function buildQueryString(requestParams, fingerprint) {
|
|
|
78
111
|
if (requestParams.sessionId != null) {
|
|
79
112
|
query.append('sessionId', String(requestParams.sessionId));
|
|
80
113
|
}
|
|
114
|
+
if (requestParams.locale != null && requestParams.locale !== '' && requestParams.locale !== 'undefined') {
|
|
115
|
+
query.append('locale', String(requestParams.locale));
|
|
116
|
+
}
|
|
117
|
+
const channel = resolveRuntimeChannel(requestParams.channel);
|
|
118
|
+
if (channel) {
|
|
119
|
+
query.append('channel', channel);
|
|
120
|
+
}
|
|
81
121
|
if (Array.isArray(requestParams.predefinedAmounts)) {
|
|
82
122
|
requestParams.predefinedAmounts.forEach(amount => {
|
|
83
123
|
query.append('predefinedAmounts', String(amount));
|
|
@@ -296,6 +336,7 @@ export class CashierBootstrapper {
|
|
|
296
336
|
const redirectTarget = this.normalizeRedirectTarget(redirectPayload.target);
|
|
297
337
|
const redirectMethod = this.normalizeRedirectMethod(redirectPayload.method);
|
|
298
338
|
const redirectParameters = this.normalizeRedirectParameters(redirectPayload.parameters);
|
|
339
|
+
const transactionId = this.asString(redirectPayload.transactionId);
|
|
299
340
|
if (typeof redirectUrl !== 'string' || redirectUrl.trim() === '') {
|
|
300
341
|
return;
|
|
301
342
|
}
|
|
@@ -315,12 +356,12 @@ export class CashierBootstrapper {
|
|
|
315
356
|
case 'tab':
|
|
316
357
|
console.log('[CashierBootstrapper] Handling redirect target "tab".', { redirectUrl, redirectMethod, redirectParameters });
|
|
317
358
|
if (redirectMethod === 'POST') {
|
|
318
|
-
if (!this.
|
|
359
|
+
if (!this.openManagedRedirectTabWithPost(redirectUrl, redirectParameters, transactionId)) {
|
|
319
360
|
this.submitPostForm(redirectUrl, redirectParameters, '_blank');
|
|
320
361
|
}
|
|
321
362
|
}
|
|
322
363
|
else {
|
|
323
|
-
|
|
364
|
+
this.openManagedRedirectTab(redirectUrl, transactionId);
|
|
324
365
|
}
|
|
325
366
|
break;
|
|
326
367
|
case 'window':
|
|
@@ -453,6 +494,55 @@ export class CashierBootstrapper {
|
|
|
453
494
|
return acc;
|
|
454
495
|
}, {});
|
|
455
496
|
}
|
|
497
|
+
openManagedRedirectTab(url, transactionId) {
|
|
498
|
+
if (typeof window === 'undefined') {
|
|
499
|
+
return false;
|
|
500
|
+
}
|
|
501
|
+
const popup = window.open(url, '_blank');
|
|
502
|
+
if (!popup) {
|
|
503
|
+
return false;
|
|
504
|
+
}
|
|
505
|
+
this.trackManagedRedirectTab(popup, transactionId);
|
|
506
|
+
return true;
|
|
507
|
+
}
|
|
508
|
+
openManagedRedirectTabWithPost(url, parameters, transactionId) {
|
|
509
|
+
if (typeof window === 'undefined') {
|
|
510
|
+
return false;
|
|
511
|
+
}
|
|
512
|
+
const popup = window.open('', '_blank');
|
|
513
|
+
if (!popup) {
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
if (!this.writePostFormToWindow(popup, url, parameters)) {
|
|
517
|
+
return false;
|
|
518
|
+
}
|
|
519
|
+
this.trackManagedRedirectTab(popup, transactionId);
|
|
520
|
+
return true;
|
|
521
|
+
}
|
|
522
|
+
trackManagedRedirectTab(tab, transactionId) {
|
|
523
|
+
this.clearManagedRedirectTabWatcher();
|
|
524
|
+
this.managedRedirectTab = tab;
|
|
525
|
+
this.managedRedirectTabTransactionId = transactionId;
|
|
526
|
+
this.managedRedirectTabCheckIntervalId = setInterval(() => {
|
|
527
|
+
if (!this.managedRedirectTab?.closed) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
const closedTransactionId = this.managedRedirectTabTransactionId;
|
|
531
|
+
this.managedRedirectTab = null;
|
|
532
|
+
this.managedRedirectTabTransactionId = undefined;
|
|
533
|
+
this.clearManagedRedirectTabWatcher();
|
|
534
|
+
this.postMessage(CASHIER_REDIRECT_TAB_CLOSED_EVENT, {
|
|
535
|
+
transactionId: closedTransactionId
|
|
536
|
+
});
|
|
537
|
+
}, 500);
|
|
538
|
+
}
|
|
539
|
+
clearManagedRedirectTabWatcher() {
|
|
540
|
+
if (this.managedRedirectTabCheckIntervalId === undefined) {
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
clearInterval(this.managedRedirectTabCheckIntervalId);
|
|
544
|
+
this.managedRedirectTabCheckIntervalId = undefined;
|
|
545
|
+
}
|
|
456
546
|
openPopupWithPost(url, parameters) {
|
|
457
547
|
if (typeof window === 'undefined') {
|
|
458
548
|
return false;
|
package/dist/types.d.ts
CHANGED
|
@@ -6,11 +6,14 @@ export interface CashierRequestParams {
|
|
|
6
6
|
terminalId?: string;
|
|
7
7
|
userId?: string;
|
|
8
8
|
sessionId?: string;
|
|
9
|
+
locale?: string;
|
|
10
|
+
channel?: CashierRuntimeChannel | string;
|
|
9
11
|
predefinedAmounts?: number[];
|
|
10
12
|
layout?: string;
|
|
11
13
|
transactionType?: string;
|
|
12
14
|
attributes?: Record<string, unknown>;
|
|
13
15
|
}
|
|
16
|
+
export type CashierRuntimeChannel = 'windows' | 'mac' | 'ios' | 'android' | 'other';
|
|
14
17
|
export type CashierEnvironment = 'production' | 'uat' | 'local' | 'local4200';
|
|
15
18
|
/**
|
|
16
19
|
* Bootstrap properties configuration
|