@morefin/cashier-bootstrapper 0.1.7 → 0.1.8
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 +15 -13
- package/dist/example-usage.js +6 -7
- package/dist/index.d.ts +2 -1
- package/dist/index.js +35 -28
- package/dist/types.d.ts +2 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,10 +10,10 @@ npm install @morefin/cashier-bootstrapper
|
|
|
10
10
|
|
|
11
11
|
## Usage
|
|
12
12
|
|
|
13
|
-
Throughout the examples below, set
|
|
13
|
+
Throughout the examples below, set an environment once and reuse it:
|
|
14
14
|
|
|
15
15
|
```typescript
|
|
16
|
-
const
|
|
16
|
+
const CASHIER_ENVIRONMENT = 'uat' as const; // 'production' | 'uat'
|
|
17
17
|
```
|
|
18
18
|
|
|
19
19
|
### Basic Embed
|
|
@@ -22,7 +22,7 @@ const CASHIER_HOST = 'http://uat-api.morefin.com'; // change per environment
|
|
|
22
22
|
import { CashierBootstrapper } from '@morefin/cashier-bootstrapper';
|
|
23
23
|
|
|
24
24
|
new CashierBootstrapper('#cashier-root', {
|
|
25
|
-
properties: {
|
|
25
|
+
properties: { environment: CASHIER_ENVIRONMENT }
|
|
26
26
|
});
|
|
27
27
|
```
|
|
28
28
|
|
|
@@ -39,11 +39,10 @@ new CashierBootstrapper('#cashier-root', {
|
|
|
39
39
|
sessionId: 'session-abc',
|
|
40
40
|
predefinedAmounts: [100, 200, 300],
|
|
41
41
|
layout: 'default',
|
|
42
|
-
transactionType: ('
|
|
42
|
+
transactionType: ('deposit'|'withdrawal')
|
|
43
43
|
},
|
|
44
44
|
properties: {
|
|
45
|
-
|
|
46
|
-
cashierPath: '/cashier',
|
|
45
|
+
environment: CASHIER_ENVIRONMENT,
|
|
47
46
|
iframe: {
|
|
48
47
|
height: '720px',
|
|
49
48
|
minHeight: '640px',
|
|
@@ -62,7 +61,7 @@ import { CashierBootstrapper } from '@morefin/cashier-bootstrapper';
|
|
|
62
61
|
|
|
63
62
|
new CashierBootstrapper(null, {
|
|
64
63
|
properties: {
|
|
65
|
-
|
|
64
|
+
environment: CASHIER_ENVIRONMENT,
|
|
66
65
|
container: '#cashier-root'
|
|
67
66
|
}
|
|
68
67
|
});
|
|
@@ -74,7 +73,7 @@ new CashierBootstrapper(null, {
|
|
|
74
73
|
import { CashierBootstrapper } from '@morefin/cashier-bootstrapper';
|
|
75
74
|
|
|
76
75
|
new CashierBootstrapper('#cashier-root', {
|
|
77
|
-
properties: {
|
|
76
|
+
properties: { environment: CASHIER_ENVIRONMENT }
|
|
78
77
|
}, api => {
|
|
79
78
|
api.setCss(`
|
|
80
79
|
.payment-layout-root .payment-container {
|
|
@@ -99,7 +98,7 @@ Iframe-based embed that loads the cashier URL and exposes runtime controls once
|
|
|
99
98
|
|
|
100
99
|
**Parameters:**
|
|
101
100
|
- `container: string | HTMLElement | null | undefined` - Where the iframe is appended. If omitted, `config.properties.container` is used (falling back to `document.body`).
|
|
102
|
-
- `config?: CashierIframeConfig` - Request params and iframe properties. `properties.
|
|
101
|
+
- `config?: CashierIframeConfig` - Request params and iframe properties. `properties.environment` is required.
|
|
103
102
|
- `onReady?: (api: CashierIframeApi) => void` - Called when the iframe loads; exposes helpers:
|
|
104
103
|
- `api.setCss(css: string)` – inject CSS inside the cashier iframe
|
|
105
104
|
- `api.updateData(data: object)` – post updated `APP_DATA` to the cashier
|
|
@@ -115,10 +114,11 @@ interface CashierRequestParams {
|
|
|
115
114
|
sessionId?: string;
|
|
116
115
|
predefinedAmounts?: number[];
|
|
117
116
|
layout?: string;
|
|
118
|
-
fingerprint?: string;
|
|
119
117
|
transactionType?: string;
|
|
120
118
|
}
|
|
121
119
|
|
|
120
|
+
type CashierEnvironment = 'production' | 'uat';
|
|
121
|
+
|
|
122
122
|
interface CashierIframeOptions {
|
|
123
123
|
width?: string;
|
|
124
124
|
height?: string;
|
|
@@ -130,9 +130,8 @@ interface CashierIframeOptions {
|
|
|
130
130
|
}
|
|
131
131
|
|
|
132
132
|
interface CashierIframeProperties {
|
|
133
|
-
|
|
133
|
+
environment: CashierEnvironment;
|
|
134
134
|
container?: string | HTMLElement;
|
|
135
|
-
cashierPath?: string;
|
|
136
135
|
iframe?: CashierIframeOptions;
|
|
137
136
|
}
|
|
138
137
|
|
|
@@ -144,6 +143,9 @@ interface CashierIframeConfig {
|
|
|
144
143
|
|
|
145
144
|
## Defaults
|
|
146
145
|
|
|
147
|
-
-
|
|
146
|
+
- Cashier path is fixed to `/cashier`
|
|
147
|
+
- `environment: 'production'` resolves to `https://api.morefin.com`
|
|
148
|
+
- `environment: 'uat'` resolves to `https://uat-api.morefin.com`
|
|
149
|
+
- Iframe URL origin is validated to allow only `api.morefin.com` and `uat-api.morefin.com`
|
|
148
150
|
- `iframe.width`/`iframe.height`: `100%`
|
|
149
151
|
- `iframe.allow`: `geolocation *;camera *;payment *;clipboard-read *;clipboard-write *;autoplay *;microphone *;fullscreen *;accelerometer *;magnetometer *;gyroscope *;picture-in-picture *;otp-credentials *;`
|
package/dist/example-usage.js
CHANGED
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
* This file is not included in the npm package, it's just for reference.
|
|
4
4
|
*/
|
|
5
5
|
import { CashierBootstrapper } from './index';
|
|
6
|
-
const
|
|
6
|
+
const CASHIER_ENVIRONMENT = 'uat';
|
|
7
7
|
// Example 1: Minimal configuration with selector
|
|
8
8
|
export function example1() {
|
|
9
9
|
new CashierBootstrapper('#cashier-root', {
|
|
10
|
-
properties: {
|
|
10
|
+
properties: { environment: CASHIER_ENVIRONMENT }
|
|
11
11
|
});
|
|
12
12
|
}
|
|
13
13
|
// Example 2: Custom request params + iframe options
|
|
@@ -22,8 +22,7 @@ export function example2(container) {
|
|
|
22
22
|
layout: 'default'
|
|
23
23
|
},
|
|
24
24
|
properties: {
|
|
25
|
-
|
|
26
|
-
cashierPath: '/cashier',
|
|
25
|
+
environment: CASHIER_ENVIRONMENT,
|
|
27
26
|
iframe: {
|
|
28
27
|
height: '720px',
|
|
29
28
|
minHeight: '640px',
|
|
@@ -37,7 +36,7 @@ export function example2(container) {
|
|
|
37
36
|
// Example 3: Provide container via config instead of constructor
|
|
38
37
|
export function example3() {
|
|
39
38
|
new CashierBootstrapper(null, {
|
|
40
|
-
properties: {
|
|
39
|
+
properties: { environment: CASHIER_ENVIRONMENT, container: '#cashier-root' }
|
|
41
40
|
});
|
|
42
41
|
}
|
|
43
42
|
// Example 4: Update CSS at runtime after iframe is ready
|
|
@@ -51,7 +50,7 @@ export function example4(container) {
|
|
|
51
50
|
layout: 'default'
|
|
52
51
|
},
|
|
53
52
|
properties: {
|
|
54
|
-
|
|
53
|
+
environment: CASHIER_ENVIRONMENT
|
|
55
54
|
}
|
|
56
55
|
}, api => {
|
|
57
56
|
api.setCss(`
|
|
@@ -64,7 +63,7 @@ export function example4(container) {
|
|
|
64
63
|
// Example 5: Updating data via the postMessage API
|
|
65
64
|
export function example5(container) {
|
|
66
65
|
new CashierBootstrapper(container, {
|
|
67
|
-
properties: {
|
|
66
|
+
properties: { environment: CASHIER_ENVIRONMENT }
|
|
68
67
|
}, api => {
|
|
69
68
|
api.updateData({
|
|
70
69
|
userId: 'updated-user'
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CashierIframeApi, CashierIframeConfig } from './types';
|
|
2
|
-
export type { CashierRequestParams, CashierIframeApi, CashierIframeConfig, CashierIframeProperties } from './types';
|
|
2
|
+
export type { CashierEnvironment, CashierRequestParams, CashierIframeApi, CashierIframeConfig, CashierIframeProperties } from './types';
|
|
3
3
|
type FingerprintJSGlobal = {
|
|
4
4
|
load: () => Promise<{
|
|
5
5
|
get: () => Promise<{
|
|
@@ -17,6 +17,7 @@ export declare class CashierBootstrapper {
|
|
|
17
17
|
private origin;
|
|
18
18
|
private ready;
|
|
19
19
|
private readonly fullConfig;
|
|
20
|
+
private readonly host;
|
|
20
21
|
private readonly onReady?;
|
|
21
22
|
constructor(container: string | HTMLElement | null | undefined, config?: CashierIframeConfig, onReady?: (api: CashierIframeApi) => void);
|
|
22
23
|
private bootstrapIframe;
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
const DEFAULT_IFRAME_ALLOW = 'geolocation *;camera *;payment *;clipboard-read *;clipboard-write *;autoplay *;microphone *;fullscreen *;accelerometer *;magnetometer *;gyroscope *;picture-in-picture *;otp-credentials *;';
|
|
2
|
+
const CASHIER_PATH = '/cashier';
|
|
2
3
|
const FINGERPRINT_CDN = 'https://cdn.jsdelivr.net/npm/@fingerprintjs/fingerprintjs@4/dist/fp.min.js';
|
|
4
|
+
const CASHIER_HOSTS = {
|
|
5
|
+
production: 'https://api.morefin.com',
|
|
6
|
+
uat: 'https://uat-api.morefin.com'
|
|
7
|
+
};
|
|
8
|
+
const ALLOWED_CASHIER_DOMAINS = new Set(['api.morefin.com', 'uat-api.morefin.com']);
|
|
3
9
|
let fpLoaderPromise;
|
|
4
10
|
function loadFingerprintLibrary() {
|
|
5
11
|
if (typeof window === 'undefined') {
|
|
@@ -40,7 +46,7 @@ async function generateFingerprint() {
|
|
|
40
46
|
return undefined;
|
|
41
47
|
}
|
|
42
48
|
}
|
|
43
|
-
function buildQueryString(requestParams) {
|
|
49
|
+
function buildQueryString(requestParams, fingerprint) {
|
|
44
50
|
const query = new URLSearchParams();
|
|
45
51
|
if (requestParams.merchantId != null) {
|
|
46
52
|
query.append('merchantId', String(requestParams.merchantId));
|
|
@@ -56,14 +62,14 @@ function buildQueryString(requestParams) {
|
|
|
56
62
|
}
|
|
57
63
|
if (Array.isArray(requestParams.predefinedAmounts)) {
|
|
58
64
|
requestParams.predefinedAmounts.forEach(amount => {
|
|
59
|
-
query.append('
|
|
65
|
+
query.append('predefined_amounts', String(amount));
|
|
60
66
|
});
|
|
61
67
|
}
|
|
62
68
|
if (requestParams.layout != null && requestParams.layout !== '' && requestParams.layout !== 'undefined') {
|
|
63
69
|
query.append('layout', requestParams.layout);
|
|
64
70
|
}
|
|
65
|
-
if (
|
|
66
|
-
query.append('fingerprint',
|
|
71
|
+
if (fingerprint) {
|
|
72
|
+
query.append('fingerprint', fingerprint);
|
|
67
73
|
}
|
|
68
74
|
if (requestParams.transactionType) {
|
|
69
75
|
query.append('transactionType', requestParams.transactionType);
|
|
@@ -71,12 +77,6 @@ function buildQueryString(requestParams) {
|
|
|
71
77
|
const qs = query.toString();
|
|
72
78
|
return qs ? `?${qs}` : '';
|
|
73
79
|
}
|
|
74
|
-
function normalizePath(path) {
|
|
75
|
-
if (!path) {
|
|
76
|
-
return '/cashier';
|
|
77
|
-
}
|
|
78
|
-
return path.startsWith('/') ? path : `/${path}`;
|
|
79
|
-
}
|
|
80
80
|
function resolveContainer(container) {
|
|
81
81
|
if (container instanceof HTMLElement) {
|
|
82
82
|
return container;
|
|
@@ -90,34 +90,44 @@ function resolveContainer(container) {
|
|
|
90
90
|
}
|
|
91
91
|
return document.body;
|
|
92
92
|
}
|
|
93
|
-
function
|
|
93
|
+
function resolveHost(environment) {
|
|
94
|
+
const host = CASHIER_HOSTS[environment];
|
|
95
|
+
if (!host) {
|
|
96
|
+
throw new Error(`Unsupported cashier environment "${environment}"`);
|
|
97
|
+
}
|
|
98
|
+
return host;
|
|
99
|
+
}
|
|
100
|
+
function parseAndVerifyCashierUrl(url) {
|
|
101
|
+
const parsedUrl = new URL(url);
|
|
102
|
+
if (!ALLOWED_CASHIER_DOMAINS.has(parsedUrl.hostname)) {
|
|
103
|
+
throw new Error(`Cashier domain "${parsedUrl.hostname}" is not allowed`);
|
|
104
|
+
}
|
|
105
|
+
return parsedUrl;
|
|
106
|
+
}
|
|
107
|
+
function buildIframeUrl(host, requestParams, fingerprint) {
|
|
94
108
|
const trimmedHost = host.endsWith('/') ? host.slice(0, -1) : host;
|
|
95
|
-
const
|
|
96
|
-
|
|
109
|
+
const url = `${trimmedHost}${CASHIER_PATH}${buildQueryString(requestParams, fingerprint)}`;
|
|
110
|
+
parseAndVerifyCashierUrl(url);
|
|
111
|
+
return url;
|
|
97
112
|
}
|
|
98
113
|
function getOriginFromUrl(url) {
|
|
99
|
-
|
|
100
|
-
return new URL(url).origin;
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
return '*';
|
|
104
|
-
}
|
|
114
|
+
return parseAndVerifyCashierUrl(url).origin;
|
|
105
115
|
}
|
|
106
116
|
export class CashierBootstrapper {
|
|
107
117
|
constructor(container, config = {}, onReady) {
|
|
108
118
|
this.origin = '*';
|
|
109
119
|
this.ready = false;
|
|
110
120
|
this.onReady = onReady;
|
|
111
|
-
const
|
|
112
|
-
if (!
|
|
113
|
-
throw new Error('Cashier
|
|
121
|
+
const environment = config.properties?.environment;
|
|
122
|
+
if (!environment) {
|
|
123
|
+
throw new Error('Cashier environment is required to bootstrap the iframe');
|
|
114
124
|
}
|
|
125
|
+
this.host = resolveHost(environment);
|
|
115
126
|
this.fullConfig = {
|
|
116
127
|
requestParams: { ...config.requestParams },
|
|
117
128
|
properties: {
|
|
118
129
|
...config.properties,
|
|
119
|
-
|
|
120
|
-
cashierPath: normalizePath(config.properties?.cashierPath),
|
|
130
|
+
environment,
|
|
121
131
|
iframe: {
|
|
122
132
|
width: '100%',
|
|
123
133
|
height: '100%',
|
|
@@ -132,10 +142,7 @@ export class CashierBootstrapper {
|
|
|
132
142
|
}
|
|
133
143
|
async bootstrapIframe() {
|
|
134
144
|
const fp = await generateFingerprint();
|
|
135
|
-
|
|
136
|
-
this.fullConfig.requestParams.fingerprint = fp;
|
|
137
|
-
}
|
|
138
|
-
const src = buildIframeUrl(this.fullConfig.properties.host, this.fullConfig.properties.cashierPath ?? '/cashier', this.fullConfig.requestParams);
|
|
145
|
+
const src = buildIframeUrl(this.host, this.fullConfig.requestParams, fp);
|
|
139
146
|
this.origin = getOriginFromUrl(src);
|
|
140
147
|
if (this.iframe) {
|
|
141
148
|
this.iframe.src = src;
|
package/dist/types.d.ts
CHANGED
|
@@ -8,14 +8,14 @@ export interface CashierRequestParams {
|
|
|
8
8
|
sessionId?: string;
|
|
9
9
|
predefinedAmounts?: number[];
|
|
10
10
|
layout?: string;
|
|
11
|
-
fingerprint?: string;
|
|
12
11
|
transactionType?: string;
|
|
13
12
|
}
|
|
13
|
+
export type CashierEnvironment = 'production' | 'uat';
|
|
14
14
|
/**
|
|
15
15
|
* Bootstrap properties configuration
|
|
16
16
|
*/
|
|
17
17
|
export interface BootstrapProperties {
|
|
18
|
-
|
|
18
|
+
environment: CashierEnvironment;
|
|
19
19
|
/**
|
|
20
20
|
* Selector or element where HTML should be rendered
|
|
21
21
|
* Default: 'body' (replaces entire body)
|
|
@@ -70,10 +70,6 @@ export interface CashierIframeProperties extends BootstrapProperties {
|
|
|
70
70
|
* Optional DOM container (selector or element) where the iframe is appended.
|
|
71
71
|
*/
|
|
72
72
|
container?: string | HTMLElement;
|
|
73
|
-
/**
|
|
74
|
-
* Path to the cashier page. Default: '/cashier'.
|
|
75
|
-
*/
|
|
76
|
-
cashierPath?: string;
|
|
77
73
|
/**
|
|
78
74
|
* Iframe tuning options (dimensions, attributes).
|
|
79
75
|
*/
|