@morefin/cashier-bootstrapper 0.1.6 → 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 +17 -13
- package/dist/example-usage.js +6 -7
- package/dist/index.d.ts +2 -1
- package/dist/index.js +37 -27
- package/dist/types.d.ts +3 -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
|
|
|
@@ -38,11 +38,11 @@ new CashierBootstrapper('#cashier-root', {
|
|
|
38
38
|
userId: 'user-789',
|
|
39
39
|
sessionId: 'session-abc',
|
|
40
40
|
predefinedAmounts: [100, 200, 300],
|
|
41
|
-
layout: 'default'
|
|
41
|
+
layout: 'default',
|
|
42
|
+
transactionType: ('deposit'|'withdrawal')
|
|
42
43
|
},
|
|
43
44
|
properties: {
|
|
44
|
-
|
|
45
|
-
cashierPath: '/cashier',
|
|
45
|
+
environment: CASHIER_ENVIRONMENT,
|
|
46
46
|
iframe: {
|
|
47
47
|
height: '720px',
|
|
48
48
|
minHeight: '640px',
|
|
@@ -61,7 +61,7 @@ import { CashierBootstrapper } from '@morefin/cashier-bootstrapper';
|
|
|
61
61
|
|
|
62
62
|
new CashierBootstrapper(null, {
|
|
63
63
|
properties: {
|
|
64
|
-
|
|
64
|
+
environment: CASHIER_ENVIRONMENT,
|
|
65
65
|
container: '#cashier-root'
|
|
66
66
|
}
|
|
67
67
|
});
|
|
@@ -73,7 +73,7 @@ new CashierBootstrapper(null, {
|
|
|
73
73
|
import { CashierBootstrapper } from '@morefin/cashier-bootstrapper';
|
|
74
74
|
|
|
75
75
|
new CashierBootstrapper('#cashier-root', {
|
|
76
|
-
properties: {
|
|
76
|
+
properties: { environment: CASHIER_ENVIRONMENT }
|
|
77
77
|
}, api => {
|
|
78
78
|
api.setCss(`
|
|
79
79
|
.payment-layout-root .payment-container {
|
|
@@ -98,7 +98,7 @@ Iframe-based embed that loads the cashier URL and exposes runtime controls once
|
|
|
98
98
|
|
|
99
99
|
**Parameters:**
|
|
100
100
|
- `container: string | HTMLElement | null | undefined` - Where the iframe is appended. If omitted, `config.properties.container` is used (falling back to `document.body`).
|
|
101
|
-
- `config?: CashierIframeConfig` - Request params and iframe properties. `properties.
|
|
101
|
+
- `config?: CashierIframeConfig` - Request params and iframe properties. `properties.environment` is required.
|
|
102
102
|
- `onReady?: (api: CashierIframeApi) => void` - Called when the iframe loads; exposes helpers:
|
|
103
103
|
- `api.setCss(css: string)` – inject CSS inside the cashier iframe
|
|
104
104
|
- `api.updateData(data: object)` – post updated `APP_DATA` to the cashier
|
|
@@ -114,9 +114,11 @@ interface CashierRequestParams {
|
|
|
114
114
|
sessionId?: string;
|
|
115
115
|
predefinedAmounts?: number[];
|
|
116
116
|
layout?: string;
|
|
117
|
-
|
|
117
|
+
transactionType?: string;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
+
type CashierEnvironment = 'production' | 'uat';
|
|
121
|
+
|
|
120
122
|
interface CashierIframeOptions {
|
|
121
123
|
width?: string;
|
|
122
124
|
height?: string;
|
|
@@ -128,9 +130,8 @@ interface CashierIframeOptions {
|
|
|
128
130
|
}
|
|
129
131
|
|
|
130
132
|
interface CashierIframeProperties {
|
|
131
|
-
|
|
133
|
+
environment: CashierEnvironment;
|
|
132
134
|
container?: string | HTMLElement;
|
|
133
|
-
cashierPath?: string;
|
|
134
135
|
iframe?: CashierIframeOptions;
|
|
135
136
|
}
|
|
136
137
|
|
|
@@ -142,6 +143,9 @@ interface CashierIframeConfig {
|
|
|
142
143
|
|
|
143
144
|
## Defaults
|
|
144
145
|
|
|
145
|
-
-
|
|
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`
|
|
146
150
|
- `iframe.width`/`iframe.height`: `100%`
|
|
147
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));
|
|
@@ -62,18 +68,15 @@ function buildQueryString(requestParams) {
|
|
|
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);
|
|
73
|
+
}
|
|
74
|
+
if (requestParams.transactionType) {
|
|
75
|
+
query.append('transactionType', requestParams.transactionType);
|
|
67
76
|
}
|
|
68
77
|
const qs = query.toString();
|
|
69
78
|
return qs ? `?${qs}` : '';
|
|
70
79
|
}
|
|
71
|
-
function normalizePath(path) {
|
|
72
|
-
if (!path) {
|
|
73
|
-
return '/cashier';
|
|
74
|
-
}
|
|
75
|
-
return path.startsWith('/') ? path : `/${path}`;
|
|
76
|
-
}
|
|
77
80
|
function resolveContainer(container) {
|
|
78
81
|
if (container instanceof HTMLElement) {
|
|
79
82
|
return container;
|
|
@@ -87,34 +90,44 @@ function resolveContainer(container) {
|
|
|
87
90
|
}
|
|
88
91
|
return document.body;
|
|
89
92
|
}
|
|
90
|
-
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) {
|
|
91
108
|
const trimmedHost = host.endsWith('/') ? host.slice(0, -1) : host;
|
|
92
|
-
const
|
|
93
|
-
|
|
109
|
+
const url = `${trimmedHost}${CASHIER_PATH}${buildQueryString(requestParams, fingerprint)}`;
|
|
110
|
+
parseAndVerifyCashierUrl(url);
|
|
111
|
+
return url;
|
|
94
112
|
}
|
|
95
113
|
function getOriginFromUrl(url) {
|
|
96
|
-
|
|
97
|
-
return new URL(url).origin;
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
return '*';
|
|
101
|
-
}
|
|
114
|
+
return parseAndVerifyCashierUrl(url).origin;
|
|
102
115
|
}
|
|
103
116
|
export class CashierBootstrapper {
|
|
104
117
|
constructor(container, config = {}, onReady) {
|
|
105
118
|
this.origin = '*';
|
|
106
119
|
this.ready = false;
|
|
107
120
|
this.onReady = onReady;
|
|
108
|
-
const
|
|
109
|
-
if (!
|
|
110
|
-
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');
|
|
111
124
|
}
|
|
125
|
+
this.host = resolveHost(environment);
|
|
112
126
|
this.fullConfig = {
|
|
113
127
|
requestParams: { ...config.requestParams },
|
|
114
128
|
properties: {
|
|
115
129
|
...config.properties,
|
|
116
|
-
|
|
117
|
-
cashierPath: normalizePath(config.properties?.cashierPath),
|
|
130
|
+
environment,
|
|
118
131
|
iframe: {
|
|
119
132
|
width: '100%',
|
|
120
133
|
height: '100%',
|
|
@@ -129,10 +142,7 @@ export class CashierBootstrapper {
|
|
|
129
142
|
}
|
|
130
143
|
async bootstrapIframe() {
|
|
131
144
|
const fp = await generateFingerprint();
|
|
132
|
-
|
|
133
|
-
this.fullConfig.requestParams.fingerprint = fp;
|
|
134
|
-
}
|
|
135
|
-
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);
|
|
136
146
|
this.origin = getOriginFromUrl(src);
|
|
137
147
|
if (this.iframe) {
|
|
138
148
|
this.iframe.src = src;
|
package/dist/types.d.ts
CHANGED
|
@@ -8,13 +8,14 @@ export interface CashierRequestParams {
|
|
|
8
8
|
sessionId?: string;
|
|
9
9
|
predefinedAmounts?: number[];
|
|
10
10
|
layout?: string;
|
|
11
|
-
|
|
11
|
+
transactionType?: string;
|
|
12
12
|
}
|
|
13
|
+
export type CashierEnvironment = 'production' | 'uat';
|
|
13
14
|
/**
|
|
14
15
|
* Bootstrap properties configuration
|
|
15
16
|
*/
|
|
16
17
|
export interface BootstrapProperties {
|
|
17
|
-
|
|
18
|
+
environment: CashierEnvironment;
|
|
18
19
|
/**
|
|
19
20
|
* Selector or element where HTML should be rendered
|
|
20
21
|
* Default: 'body' (replaces entire body)
|
|
@@ -69,10 +70,6 @@ export interface CashierIframeProperties extends BootstrapProperties {
|
|
|
69
70
|
* Optional DOM container (selector or element) where the iframe is appended.
|
|
70
71
|
*/
|
|
71
72
|
container?: string | HTMLElement;
|
|
72
|
-
/**
|
|
73
|
-
* Path to the cashier page. Default: '/cashier'.
|
|
74
|
-
*/
|
|
75
|
-
cashierPath?: string;
|
|
76
73
|
/**
|
|
77
74
|
* Iframe tuning options (dimensions, attributes).
|
|
78
75
|
*/
|