@juspay-tech/hyperswitch-control-center-embed-core 1.1.1 → 1.2.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 CHANGED
@@ -1,114 +1,166 @@
1
1
  # @juspay-tech/hyperswitch-control-center-embed-core
2
2
 
3
- Core JavaScript SDK for embedding Hyperswitch Control Center components into any web application.
3
+ [![npm version](https://img.shields.io/npm/v/@juspay-tech/hyperswitch-control-center-embed-core.svg)](https://www.npmjs.com/package/@juspay-tech/hyperswitch-control-center-embed-core)
4
4
 
5
- ## Overview
5
+ The core JavaScript SDK for Hyperswitch Control Center embedded components. It lets you add Control Center functionality, such as connector configuration, payments and refunds, to your own website.
6
6
 
7
- The `loadHyperswitch` function is the main entry point for initializing embedded Hyperswitch components.
8
- It:
9
-
10
- - **Loads UI via an iframe** from the Hyperswitch Control Center
11
- - **Requires a `fetchToken` function** that calls your backend to retrieve an embedded token
12
- - **Optionally accepts `initConfig`** to customize the appearance of the embedded UI
7
+ This package contains the initialization logic for embedded components along with the related TypeScript types. It has no framework dependency. If you use React, see [`@juspay-tech/hyperswitch-control-center-embed-react`](https://www.npmjs.com/package/@juspay-tech/hyperswitch-control-center-embed-react).
13
8
 
14
9
  ## Installation
15
10
 
16
- ```bash
11
+ ```sh
17
12
  npm install @juspay-tech/hyperswitch-control-center-embed-core
18
13
  ```
19
14
 
20
15
  or with `pnpm`:
21
16
 
22
- ```bash
17
+ ```sh
23
18
  pnpm add @juspay-tech/hyperswitch-control-center-embed-core
24
19
  ```
25
20
 
26
- ## Imports
21
+ ## Documentation
27
22
 
28
- ### ES Module (recommended)
23
+ - [Integration Guide](https://docs.hyperswitch.io/explore-hyperswitch/account-management/beta-embeddable-components/integration-guide)
24
+ - [API reference](#api-reference)
29
25
 
30
- ```javascript
31
- import { loadHyperswitch } from '@juspay-tech/hyperswitch-control-center-embed-core';
32
- ```
26
+ ## Usage
33
27
 
34
- ### CommonJS
28
+ Embedded components authenticate with a short-lived embedded token. Your server requests the token from Hyperswitch with your API key, and the SDK retrieves it from your server through a `fetchToken` function that you provide. Your API key is never exposed to the browser.
35
29
 
36
- ```javascript
37
- const { loadHyperswitch } = require('@juspay-tech/hyperswitch-control-center-embed-core');
38
- ```
30
+ ### Issue an embedded token on your server
39
31
 
40
- ## Basic usage
32
+ Add an endpoint to your server that calls the Hyperswitch embedded token API and returns the token.
41
33
 
42
- Initialize the SDK by providing a `fetchToken` function that calls your backend:
34
+ ```js
35
+ // GET /api/get-embedded-token
36
+ app.get('/api/get-embedded-token', async (req, res) => {
37
+ const response = await fetch(`${process.env.HYPERSWITCH_API_URL}/embedded/token`, {
38
+ headers: {
39
+ 'api-key': process.env.HYPERSWITCH_API_KEY,
40
+ 'x-profile-id': process.env.HYPERSWITCH_PROFILE_ID,
41
+ },
42
+ });
43
43
 
44
- ```javascript
45
- const hyperswitch = loadHyperswitch({
46
- fetchToken: async () => {
47
- // Call your backend endpoint to get the embedded token
48
- const response = await fetch('https://your-api.com/embedded/hyperswitch', {
49
- method: 'GET',
50
- headers: { 'Content-Type': 'application/json' }
51
- });
52
- const data = await response.json();
53
- return data.token;
44
+ if (!response.ok) {
45
+ return res.status(502).json({ error: 'Unable to issue an embedded token.' });
54
46
  }
47
+
48
+ const { token } = await response.json();
49
+ res.json({ token });
55
50
  });
56
51
  ```
57
52
 
58
- ## Optional customization
53
+ The token is scoped to the business profile passed in `x-profile-id`.
59
54
 
60
- You can customize the appearance by passing a theme configuration as `initConfig`:
55
+ ### `loadHyperswitch`
61
56
 
62
- ```javascript
63
- const initConfig = {
64
- primaryColor: '#0066ff',
65
- backgroundColor: '#ffffff',
66
- buttons: {
67
- primary: {
68
- backgroundColor: '#0066ff',
69
- textColor: '#ffffff',
70
- hoverBackgroundColor: '#0052cc'
71
- },
72
- secondary: {
73
- backgroundColor: '#f0f0f0',
74
- textColor: '#333333',
75
- hoverBackgroundColor: '#e0e0e0'
76
- }
77
- }
57
+ This synchronous function takes a function that retrieves the embedded token from your server, along with optional [appearance settings](#customizing-the-appearance). It returns a `Hyperswitch` instance.
58
+
59
+ ```js
60
+ import { loadHyperswitch } from '@juspay-tech/hyperswitch-control-center-embed-core';
61
+
62
+ const fetchToken = async () => {
63
+ // Fetch the embedded token by making an API call to your server
64
+ const response = await fetch('/api/get-embedded-token');
65
+ const { token } = await response.json();
66
+ return token;
78
67
  };
79
68
 
80
- const hyperswitch = loadHyperswitch({
81
- fetchToken: async () => { /* ... */ },
82
- initConfig
83
- });
69
+ const hyperswitch = loadHyperswitch({ fetchToken });
84
70
  ```
85
71
 
86
- All fields in `initConfig` are optional. Omitting them uses the default Control Center theme.
72
+ `fetchToken` is called when the instance is created, and again whenever the token expires.
87
73
 
88
- ## Creating a component
74
+ The package also supports CommonJS:
89
75
 
90
- Create an embeddable component instance:
76
+ ```js
77
+ const { loadHyperswitch } = require('@juspay-tech/hyperswitch-control-center-embed-core');
78
+ ```
91
79
 
92
- ```javascript
93
- const connectors = hyperswitch.create('connectors', {
94
- url: 'https://app.hyperswitch.io'
80
+ ### Creating and mounting a component
81
+
82
+ Create a component from the instance and mount it to an element on your page.
83
+
84
+ ```html
85
+ <div id="hyperswitch-root"></div>
86
+ ```
87
+
88
+ ```js
89
+ const payments = hyperswitch.create('payments', {
90
+ url: 'https://app.hyperswitch.io',
95
91
  });
92
+
93
+ payments.mount('#hyperswitch-root');
96
94
  ```
97
95
 
98
- Currently available component: `connectors`.
96
+ To remove a component, call `payments.destroy()`.
99
97
 
100
- ## Mounting
98
+ ### Supported components
101
99
 
102
- Mount the component into a container element in your HTML:
100
+ | Name | Description |
101
+ |------|-------------|
102
+ | `connectors` | Connector configuration |
103
+ | `payments` | Payments list and payment details |
104
+ | `refunds` | Refunds list and refund details |
103
105
 
104
- ```html
105
- <div id="hyperswitch-root"></div>
106
+ Multiple components can be created from a single instance.
107
+
108
+ ### Environments
109
+
110
+ The `url` option is the base URL of the Control Center that serves the components. The embedded token must be issued by the same environment.
111
+
112
+ | Environment | `url` | Token API base URL |
113
+ |-------------|-------|--------------------|
114
+ | Sandbox | `https://app.hyperswitch.io` | `https://app.hyperswitch.io/api` |
115
+ | Production | `https://live.hyperswitch.io` | `https://live.hyperswitch.io/api` |
116
+
117
+ ### Customizing the appearance
118
+
119
+ Pass a theme configuration as `initConfig` to match the components to your brand.
120
+
121
+ ```js
122
+ const hyperswitch = loadHyperswitch({
123
+ fetchToken,
124
+ initConfig: {
125
+ primaryColor: '#0066ff',
126
+ backgroundColor: '#ffffff',
127
+ buttons: {
128
+ primary: {
129
+ backgroundColor: '#0066ff',
130
+ textColor: '#ffffff',
131
+ hoverBackgroundColor: '#0052cc',
132
+ },
133
+ secondary: {
134
+ backgroundColor: '#f0f0f0',
135
+ textColor: '#333333',
136
+ hoverBackgroundColor: '#e0e0e0',
137
+ },
138
+ },
139
+ },
140
+ });
106
141
  ```
107
142
 
108
- ```javascript
109
- connectors.mount('#hyperswitch-root');
143
+ All fields in `initConfig` are optional. Omitted fields use the default Control Center theme. See [`MerchantTheme`](#merchanttheme) for the complete list.
144
+
145
+ ### Modals
146
+
147
+ Modals opened inside a component are displayed over the full host page. While a modal is open, the SDK stretches the component's iframe across the window and locks page scrolling, and restores both when the modal closes.
148
+
149
+ To keep modals inside the component, set `fullPageModals` to `false`:
150
+
151
+ ```js
152
+ const payments = hyperswitch.create('payments', {
153
+ url: 'https://app.hyperswitch.io',
154
+ fullPageModals: false,
155
+ });
110
156
  ```
111
157
 
158
+ Full-page modals rely on `position: fixed`. If an ancestor of the mount point has a CSS `transform`, `filter`, `perspective` or `contain`, or creates a stacking context below other page content, the modal can be clipped or covered. Set `fullPageModals` to `false` in that layout.
159
+
160
+ ### Content Security Policy
161
+
162
+ If you have deployed a [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP), include the Control Center origin in your `frame-src` directive.
163
+
112
164
  ## API reference
113
165
 
114
166
  ### `loadHyperswitch(options)`
@@ -117,8 +169,8 @@ Returns a `Hyperswitch` instance.
117
169
 
118
170
  | Parameter | Type | Required | Description |
119
171
  |-----------|------|----------|-------------|
120
- | `fetchToken` | `() => Promise<string \| undefined>` | Yes | Async function that fetches the embedded token from your backend |
121
- | `initConfig` | `MerchantTheme` | No | Theme customization object |
172
+ | `fetchToken` | `() => Promise<string \| undefined>` | Yes | Async function that fetches the embedded token from your server |
173
+ | `initConfig` | [`MerchantTheme`](#merchanttheme) | No | Theme customization object |
122
174
 
123
175
  ### `MerchantTheme`
124
176
 
@@ -135,23 +187,47 @@ Returns a `Hyperswitch` instance.
135
187
  | `buttons?.secondary?.textColor?` | `string` | Secondary button text color |
136
188
  | `buttons?.secondary?.hoverBackgroundColor?` | `string` | Secondary button hover color |
137
189
 
138
- ### `Hyperswitch.create(name, options)`
190
+ ### `hyperswitch.create(name, options)`
139
191
 
140
192
  Creates a component instance.
141
193
 
142
194
  | Parameter | Type | Description |
143
195
  |-----------|------|-------------|
144
- | `name` | `string` | Component name (e.g., `'connectors'`) |
145
- | `options.url` | `string` | Base URL for the embedded component |
196
+ | `name` | `'connectors' \| 'payments' \| 'refunds'` | The component to create |
197
+ | `options` | [`ElementOptions`](#elementoptions) | Component options |
146
198
 
147
- ### `.mount(selector)`
199
+ ### `ElementOptions`
148
200
 
149
- Mounts the component to a DOM element matching the given CSS selector.
201
+ | Property | Type | Description |
202
+ |----------|------|-------------|
203
+ | `url` | `string` | Base URL of the Control Center that serves the component |
204
+ | `width?` | `string` | Width of the component container. Defaults to `100%` |
205
+ | `height?` | `string` | Initial height of the component container. The component resizes to fit its content |
206
+ | `className?` | `string` | Additional class names for the component container |
207
+ | `style?` | `Partial<CSSStyleDeclaration>` | Inline styles for the component container |
208
+ | `fullPageModals?` | `boolean` | Display modals over the full host page. Defaults to `true` |
209
+ | `onResize?` | `(dimensions: { width: number; height: number }) => void` | Called when the component is resized |
150
210
 
151
- ## Documentation
211
+ ### `component.mount(target)`
152
212
 
153
- For more details and examples, see the [Integration Guide](https://docs.hyperswitch.io/explore-hyperswitch/account-management/beta-embeddable-components/integration-guide).
213
+ Mounts the component to a DOM element. `target` is a CSS selector or an `HTMLElement`. Returns the component.
214
+
215
+ ### `component.destroy()`
216
+
217
+ Removes the component from the page and cleans up its event listeners.
218
+
219
+ ## TypeScript support
220
+
221
+ This package includes TypeScript declarations. The following types are exported:
222
+
223
+ ```ts
224
+ import type {
225
+ HyperswitchInitOptions,
226
+ ElementOptions,
227
+ MerchantTheme,
228
+ } from '@juspay-tech/hyperswitch-control-center-embed-core';
229
+ ```
154
230
 
155
231
  ## License
156
232
 
157
- MIT
233
+ MIT
package/dist/element.d.ts CHANGED
@@ -7,14 +7,23 @@ declare abstract class HyperswitchElement {
7
7
  protected iframe: HTMLIFrameElement;
8
8
  private boundMessageHandler;
9
9
  readonly _internalId: string;
10
+ private isFullPage;
11
+ private previousBodyOverflow;
12
+ private showIframeTimer;
13
+ private iframeOrigin;
10
14
  constructor(instance: Hyperswitch, options?: ElementOptions);
11
15
  private setupElement;
12
16
  private setupIframe;
13
17
  private handleMessage;
18
+ private setFullPage;
19
+ private showIframe;
14
20
  mount(domNode: string | HTMLElement): HyperswitchElement;
15
21
  destroy(): void;
16
22
  protected abstract getElementType(): string;
17
23
  protected abstract getIframeSrc(): string;
18
24
  getIframe(): HTMLIFrameElement;
25
+ acceptMessage(event: MessageEvent): boolean;
26
+ postMessageToIframe(message: Record<string, unknown>): void;
27
+ isFullPageModalEnabled(): boolean;
19
28
  }
20
29
  export { HyperswitchElement };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,8 @@
1
1
  export { loadHyperswitch, Hyperswitch } from './hyperswitch';
2
2
  export { HyperswitchElement } from './element';
3
3
  export { ConnectorConfigurationComponent } from './connector-configuration';
4
+ export { PaymentsComponent } from './payments';
5
+ export { RefundsComponent } from './refunds';
4
6
  export type { HyperswitchInitOptions, ElementOptions } from './types';
5
7
  export type { MerchantTheme } from './init-config';
6
8
  export declare const VERSION = "1.0.0";
package/dist/index.esm.js CHANGED
@@ -1,6 +1,10 @@
1
1
  const isBrowser$1 = typeof window !== 'undefined';
2
2
  class HyperswitchElement {
3
3
  constructor(instance, options) {
4
+ this.isFullPage = false;
5
+ this.previousBodyOverflow = '';
6
+ this.showIframeTimer = null;
7
+ this.iframeOrigin = null;
4
8
  this.instance = instance;
5
9
  this.options = options || {};
6
10
  this._internalId = `hyper-el-${Math.random().toString(36).substring(7)}`;
@@ -40,17 +44,31 @@ class HyperswitchElement {
40
44
  this.element.appendChild(this.iframe);
41
45
  }
42
46
  handleMessage(event) {
43
- var _a;
47
+ var _a, _b, _c, _d;
44
48
  if (!isBrowser$1) {
45
49
  return;
46
50
  }
47
51
  if (this.options.onMessage) {
48
52
  this.options.onMessage(event.data);
49
53
  }
50
- if (((_a = event.data) === null || _a === void 0 ? void 0 : _a.type) === 'EMBEDDED_COMPONENT_RESIZE') {
54
+ if (!this.acceptMessage(event)) {
55
+ return;
56
+ }
57
+ if (this.isFullPageModalEnabled()) {
58
+ if (((_a = event.data) === null || _a === void 0 ? void 0 : _a.type) === 'EMBEDDED_MODAL_OPEN') {
59
+ this.setFullPage(true);
60
+ }
61
+ else if (((_b = event.data) === null || _b === void 0 ? void 0 : _b.type) === 'EMBEDDED_MODAL_CLOSE') {
62
+ this.setFullPage(false);
63
+ }
64
+ else if (((_c = event.data) === null || _c === void 0 ? void 0 : _c.type) === 'EMBEDDED_MODAL_VISIBLE') {
65
+ this.showIframe();
66
+ }
67
+ }
68
+ if (((_d = event.data) === null || _d === void 0 ? void 0 : _d.type) === 'EMBEDDED_COMPONENT_RESIZE') {
51
69
  const newHeight = event.data.height;
52
70
  const messageComponent = event.data.component || '';
53
- if (messageComponent === this.getElementType() && typeof newHeight === 'number' && newHeight > 0) {
71
+ if (!this.isFullPage && messageComponent === this.getElementType() && typeof newHeight === 'number' && newHeight > 0) {
54
72
  this.element.style.height = `${newHeight}px`;
55
73
  if (this.options.onResize) {
56
74
  this.options.onResize({
@@ -61,6 +79,49 @@ class HyperswitchElement {
61
79
  }
62
80
  }
63
81
  }
82
+ setFullPage(isFullPage) {
83
+ if (isFullPage !== this.isFullPage) {
84
+ this.isFullPage = isFullPage;
85
+ this.iframe.style.visibility = 'hidden';
86
+ if (isFullPage) {
87
+ this.previousBodyOverflow = document.body.style.overflow;
88
+ document.body.style.overflow = 'hidden';
89
+ Object.assign(this.iframe.style, {
90
+ position: 'fixed',
91
+ top: '0',
92
+ left: '0',
93
+ width: '100vw',
94
+ height: '100vh',
95
+ zIndex: '2147483647'
96
+ });
97
+ }
98
+ else {
99
+ document.body.style.overflow = this.previousBodyOverflow;
100
+ Object.assign(this.iframe.style, {
101
+ position: '',
102
+ top: '',
103
+ left: '',
104
+ width: '100%',
105
+ height: '100%',
106
+ zIndex: ''
107
+ });
108
+ }
109
+ if (this.showIframeTimer) {
110
+ clearTimeout(this.showIframeTimer);
111
+ }
112
+ this.showIframeTimer = setTimeout(() => this.showIframe(), 300);
113
+ }
114
+ this.postMessageToIframe({
115
+ type: isFullPage ? 'EMBEDDED_MODAL_OPENED' : 'EMBEDDED_MODAL_CLOSED'
116
+ });
117
+ }
118
+ showIframe() {
119
+ if (this.showIframeTimer) {
120
+ clearTimeout(this.showIframeTimer);
121
+ this.showIframeTimer = null;
122
+ }
123
+ this.iframe.style.visibility = '';
124
+ }
64
125
  mount(domNode) {
65
126
  if (!isBrowser$1) {
66
127
  throw new Error('HyperswitchElement.mount can only be called in a browser environment');
@@ -78,6 +139,14 @@ class HyperswitchElement {
78
139
  if (!isBrowser$1) {
79
140
  return;
80
141
  }
142
+ if (this.isFullPage) {
143
+ this.isFullPage = false;
144
+ document.body.style.overflow = this.previousBodyOverflow;
145
+ }
146
+ if (this.showIframeTimer) {
147
+ clearTimeout(this.showIframeTimer);
148
+ this.showIframeTimer = null;
149
+ }
81
150
  if (this.boundMessageHandler) {
82
151
  window.removeEventListener('message', this.boundMessageHandler);
83
152
  }
@@ -88,6 +157,26 @@ class HyperswitchElement {
88
157
  getIframe() {
89
158
  return this.iframe;
90
159
  }
160
+ acceptMessage(event) {
161
+ var _a;
162
+ if (event.source !== ((_a = this.iframe) === null || _a === void 0 ? void 0 : _a.contentWindow)) {
163
+ return false;
164
+ }
165
+ if (this.iframeOrigin === null && event.origin && event.origin !== 'null') {
166
+ this.iframeOrigin = event.origin;
167
+ }
168
+ return this.iframeOrigin === null || event.origin === this.iframeOrigin;
169
+ }
170
+ postMessageToIframe(message) {
171
+ var _a, _b;
172
+ if (this.iframeOrigin === null) {
173
+ return;
174
+ }
175
+ (_b = (_a = this.iframe) === null || _a === void 0 ? void 0 : _a.contentWindow) === null || _b === void 0 ? void 0 : _b.postMessage(message, this.iframeOrigin);
176
+ }
177
+ isFullPageModalEnabled() {
178
+ return this.options.fullPageModals !== false;
179
+ }
91
180
  }
92
181
 
93
182
  class ConnectorConfigurationComponent extends HyperswitchElement {
@@ -103,6 +192,32 @@ class ConnectorConfigurationComponent extends HyperswitchElement {
103
192
  }
104
193
  }
105
194
 
195
+ class PaymentsComponent extends HyperswitchElement {
196
+ constructor(instance, options) {
197
+ super(instance, options);
198
+ }
199
+ getElementType() {
200
+ return 'payments';
201
+ }
202
+ getIframeSrc() {
203
+ const baseUrl = this.options.url || 'http://localhost:9000';
204
+ return `${baseUrl}/embedded/payments`;
205
+ }
206
+ }
207
+
208
+ class RefundsComponent extends HyperswitchElement {
209
+ constructor(instance, options) {
210
+ super(instance, options);
211
+ }
212
+ getElementType() {
213
+ return 'refunds';
214
+ }
215
+ getIframeSrc() {
216
+ const baseUrl = this.options.url || 'http://localhost:9000';
217
+ return `${baseUrl}/embedded/refunds`;
218
+ }
219
+ }
220
+
106
221
  /**
107
222
  * Build the init_config object that Control Center expects
108
223
  * from the simple MerchantTheme contract.
@@ -188,17 +303,15 @@ class Hyperswitch {
188
303
  }
189
304
  window.addEventListener("message", async (event) => {
190
305
  var _a, _b, _c;
306
+ const sourceElement = Array.from(this.activeElements.values()).find((element) => element.acceptMessage(event));
307
+ if (!sourceElement) {
308
+ return;
309
+ }
191
310
  if (((_a = event.data) === null || _a === void 0 ? void 0 : _a.type) === "TOKEN_EXPIRED" && ((_b = event.data) === null || _b === void 0 ? void 0 : _b.value) === true) {
192
311
  await this.refetchAndBroadcastToken();
193
312
  }
194
313
  else if (((_c = event.data) === null || _c === void 0 ? void 0 : _c.type) === "EMBEDDED_IFRAME_READY") {
195
- // Find the element whose iframe matches the event source
196
- this.activeElements.forEach((element) => {
197
- const iframe = element.getIframe();
198
- if ((iframe === null || iframe === void 0 ? void 0 : iframe.contentWindow) === event.source) {
199
- this.onElementIframeLoaded(element);
200
- }
201
- });
314
+ this.onElementIframeLoaded(sourceElement);
202
315
  }
203
316
  });
204
317
  }
@@ -212,14 +325,11 @@ class Hyperswitch {
212
325
  if (newToken) {
213
326
  this.token = newToken;
214
327
  this.tokenError = null;
215
- this.activeElements.forEach((element, elementId) => {
216
- const iframe = element.getIframe();
217
- if (iframe === null || iframe === void 0 ? void 0 : iframe.contentWindow) {
218
- iframe.contentWindow.postMessage({
219
- type: 'AUTH_TOKEN',
220
- token: this.token
221
- }, '*');
222
- }
328
+ this.activeElements.forEach((element) => {
329
+ element.postMessageToIframe({
330
+ type: 'AUTH_TOKEN',
331
+ token: this.token
332
+ });
223
333
  });
224
334
  }
225
335
  else {
@@ -237,13 +347,10 @@ class Hyperswitch {
237
347
  }
238
348
  sendErrorToActiveElements(errorMessage) {
239
349
  this.activeElements.forEach((element) => {
240
- const iframe = element.getIframe();
241
- if (iframe === null || iframe === void 0 ? void 0 : iframe.contentWindow) {
242
- iframe.contentWindow.postMessage({
243
- type: 'AUTH_ERROR',
244
- error: errorMessage
245
- }, '*');
246
- }
350
+ element.postMessageToIframe({
351
+ type: 'AUTH_ERROR',
352
+ error: errorMessage
353
+ });
247
354
  });
248
355
  }
249
356
  create(type, options) {
@@ -252,6 +359,12 @@ class Hyperswitch {
252
359
  case 'connectors':
253
360
  element = new ConnectorConfigurationComponent(this, options);
254
361
  break;
362
+ case 'payments':
363
+ element = new PaymentsComponent(this, options);
364
+ break;
365
+ case 'refunds':
366
+ element = new RefundsComponent(this, options);
367
+ break;
255
368
  default:
256
369
  throw new Error(`Unknown element type: ${type}`);
257
370
  }
@@ -265,55 +378,47 @@ class Hyperswitch {
265
378
  return;
266
379
  }
267
380
  setTimeout(() => {
381
+ var _a;
268
382
  try {
269
- const contentWindow = iframe.contentWindow;
270
- if (!contentWindow) {
271
- return;
272
- }
273
383
  if (this.tokenError) {
274
- contentWindow.postMessage({
384
+ element.postMessageToIframe({
275
385
  type: 'AUTH_ERROR',
276
386
  error: this.tokenError
277
- }, '*');
387
+ });
278
388
  }
279
389
  else if (this.token) {
280
- contentWindow.postMessage({
390
+ element.postMessageToIframe({
281
391
  type: 'AUTH_TOKEN',
282
392
  token: this.token
283
- }, '*');
393
+ });
284
394
  }
285
395
  else {
286
- contentWindow.postMessage({
396
+ element.postMessageToIframe({
287
397
  type: 'AUTH_ERROR',
288
398
  error: 'No token available'
289
- }, '*');
290
- }
291
- // Send init_config (if available) as a separate message
292
- if (this.initConfig) {
293
- contentWindow.postMessage({
294
- type: 'INIT_CONFIG',
295
- init_config: this.initConfig
296
- }, '*');
399
+ });
297
400
  }
401
+ // Always send init_config. If merchant doesn't provide it, send an empty object.
402
+ element.postMessageToIframe({
403
+ type: 'INIT_CONFIG',
404
+ init_config: (_a = this.initConfig) !== null && _a !== void 0 ? _a : {},
405
+ sdk_capabilities: { full_page_modal: element.isFullPageModalEnabled() }
406
+ });
298
407
  }
299
408
  catch (error) {
300
409
  }
301
410
  }, 100);
302
411
  }).catch((error) => {
303
- const iframe = element.getIframe();
304
- const contentWindow = iframe === null || iframe === void 0 ? void 0 : iframe.contentWindow;
305
- if (contentWindow) {
306
- setTimeout(() => {
307
- try {
308
- contentWindow.postMessage({
309
- type: 'AUTH_ERROR',
310
- error: `Token initialization failed: ${error instanceof Error ? error.message : String(error)}`
311
- }, '*');
312
- }
313
- catch (_a) {
314
- }
315
- }, 100);
316
- }
412
+ setTimeout(() => {
413
+ try {
414
+ element.postMessageToIframe({
415
+ type: 'AUTH_ERROR',
416
+ error: `Token initialization failed: ${error instanceof Error ? error.message : String(error)}`
417
+ });
418
+ }
419
+ catch (_a) {
420
+ }
421
+ }, 100);
317
422
  });
318
423
  }
319
424
  }
@@ -323,5 +428,5 @@ function loadHyperswitch(options) {
323
428
 
324
429
  const VERSION = '1.0.0';
325
430
 
326
- export { ConnectorConfigurationComponent, Hyperswitch, HyperswitchElement, VERSION, loadHyperswitch };
431
+ export { ConnectorConfigurationComponent, Hyperswitch, HyperswitchElement, PaymentsComponent, RefundsComponent, VERSION, loadHyperswitch };
327
432
  //# sourceMappingURL=index.esm.js.map