@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 CHANGED
@@ -10,10 +10,10 @@ npm install @morefin/cashier-bootstrapper
10
10
 
11
11
  ## Usage
12
12
 
13
- Throughout the examples below, set a host variable once and reuse it:
13
+ Throughout the examples below, set an environment once and reuse it:
14
14
 
15
15
  ```typescript
16
- const CASHIER_HOST = 'http://uat-api.morefin.com'; // change per environment
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: { host: CASHIER_HOST }
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
- host: CASHIER_HOST,
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
- host: CASHIER_HOST,
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: { host: CASHIER_HOST }
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.host` is required.
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
- fingerprint?: string;
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
- host: string;
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
- - `cashierPath`: `/cashier`
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 *;`
@@ -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 CASHIER_HOST = 'http://localhost:7082'; // change per environment
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: { host: CASHIER_HOST }
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
- host: CASHIER_HOST,
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: { host: CASHIER_HOST, container: '#cashier-root' }
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
- host: CASHIER_HOST
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: { host: CASHIER_HOST }
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 (requestParams.fingerprint) {
66
- query.append('fingerprint', requestParams.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 buildIframeUrl(host, cashierPath, requestParams) {
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 path = normalizePath(cashierPath);
93
- return `${trimmedHost}${path}${buildQueryString(requestParams)}`;
109
+ const url = `${trimmedHost}${CASHIER_PATH}${buildQueryString(requestParams, fingerprint)}`;
110
+ parseAndVerifyCashierUrl(url);
111
+ return url;
94
112
  }
95
113
  function getOriginFromUrl(url) {
96
- try {
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 host = config.properties?.host;
109
- if (!host) {
110
- throw new Error('Cashier host is required to bootstrap the iframe');
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
- host,
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
- if (fp) {
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
- fingerprint?: string;
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
- host: string;
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
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@morefin/cashier-bootstrapper",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Bootstrap service for initializing cashier payment page data from API",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",