# Web SDK Reference

The Web SDK (`@omno-payment/checkout-js`) opens a cashier session in your page as a modal, inside a container, or as a mobile bottom sheet, and emits events for the payment lifecycle. It is framework-agnostic and ships TypeScript types. For how it fits with the backend session call, see [Integrating the Cashier](/guides/cashier/integrating-the-cashier).

- npm: [`@omno-payment/checkout-js`](https://www.npmjs.com/package/@omno-payment/checkout-js) — install the latest version; see the npm page for the current release.


## Installation

```bash
npm install @omno-payment/checkout-js
```

```bash
yarn add @omno-payment/checkout-js
```

```bash
pnpm add @omno-payment/checkout-js
```

## Quick start

```typescript
import CashierSDK, { CashierEmitEvent } from "@omno-payment/checkout-js";

const cashier = new CashierSDK({ baseUrl: "https://checkout.omno.com" });

// Handle payment success
cashier.on(CashierEmitEvent.PAYMENT_SUCCESS, (data) => {
  console.log("Payment success", data);
});

// Handle payment failure
cashier.on(CashierEmitEvent.PAYMENT_FAILED, (data) => {
  console.error("Payment failed", data);
});

// Open the cashier (sessionId and token from POST /public/cashier/session)
const sessionId = "25aee2fd-fd2a-4a1c-a127-df26206b2a30";
const token = "<checkout token from the create-session response>";
cashier.open({ sessionId, token });
```

> ℹ️ **Note:** Forward the `token` returned by [`POST /public/cashier/session`](/guides/cashier/integrating-the-cashier) alongside the `sessionId`. It authorizes opening the session and is **required** once checkout token verification is enabled for your account. See [The checkout token](/guides/cashier/integrating-the-cashier#the-checkout-token). Requires a Web SDK version that supports the `token` parameter.


> ℹ️ **Note:** Import `CashierEmitEvent` (and any other enums you use, such as `DeviceType` and `PaymentAction`) from the package. Referencing `CashierEmitEvent` without importing it throws a `ReferenceError`.


## Configuration

Pass a `CashierProperties` object to the constructor.

| Field | Type | Required | Description | Example |
|  --- | --- | --- | --- | --- |
| `baseUrl` | string | Yes | Checkout host. Always `https://checkout.omno.com`. | `https://checkout.omno.com` |
| `device` | `DeviceType` | No | `AUTO`, `DESKTOP` or `MOBILE`. Overrides auto-detection. | `DeviceType.AUTO` |
| `returnUrlAfterRedirection` | string | No | URL to return to after a redirect-based payment. | `https://merchant.example.com/return` |
| `styles` | `CashierStyles` | No | Modal and mobile styling (see [Styling](#styling)). |  |


```typescript
import {
  DeviceType,
  CashierEmitEvent,
  type ModalStyles,
  type MobileStyles,
  type CashierProperties
} from "@omno-payment/checkout-js";

const config: CashierProperties = {
  baseUrl: "https://checkout.omno.com",
  device: DeviceType.AUTO,
  returnUrlAfterRedirection: "https://merchant.example.com/return",
  styles: {
    modal: {
      backgroundColor: "rgba(0,0,0,0.4)",
      width: "900px",
      height: "800px",
      borderRadius: "12px",
      zIndex: 9
    } as ModalStyles,
    mobile: {
      backgroundColor: "rgba(0,0,0,0.4)",
      zIndex: 10
    } as MobileStyles
  }
};

const cashier = new CashierSDK(config);
```

## `open()` parameters

| Field | Type | Required | Description |
|  --- | --- | --- | --- |
| `sessionId` | string | Yes | Session ID from `POST /public/cashier/session`. |
| `token` | string | Yes¹ | Checkout token from the same create-session response. Authorizes opening the session; the SDK attaches it to the cashier URL. ¹Optional only while checkout token verification is disabled for your account — forward it now so you are ready when it is enabled. See [The checkout token](/guides/cashier/integrating-the-cashier#the-checkout-token). |
| `containerId` | string | No | ID of an element to mount the cashier into. Omit for a modal. |
| `paymentAction` | `PaymentAction` | No | Land directly on `DEPOSIT` or `WITHDRAW`. |


## Opening modes

```typescript
// Modal (default)
cashier.open({ sessionId, token });

// Inside a container element you provide
cashier.open({ sessionId, token, containerId: "cashier-slot" });

// Land directly on withdrawal or deposit
import { PaymentAction } from "@omno-payment/checkout-js";
cashier.open({ sessionId, token, paymentAction: PaymentAction.WITHDRAW });
cashier.open({ sessionId, token, paymentAction: PaymentAction.DEPOSIT });
```

## Methods

| Method | Description |
|  --- | --- |
| `open({ sessionId, token, containerId?, paymentAction? })` | Open the cashier (modal, container, or with a deposit/withdraw action). Forward the `token` from the create-session response. |
| `close()` | Close the cashier. |
| `reload()` | Reload the cashier. Reuses the `token` from the original `open()` — no need to pass it again. |
| `destroy()` | Destroy the instance and remove the iframe from the DOM. |
| `isOpen()` | Returns whether the cashier is currently open. |
| `getDeviceType()` | Returns the resolved device type. |
| `getSessionId()` | Returns the current session ID. |


## Events

Register handlers with `cashier.on(CashierEmitEvent.X, handler)`.

> ⚠️ **Warning:** SDK events are for UI updates only. Credit or debit balances only after a [webhook](/guides/getting-started/webhooks) or the [GET transaction endpoint](https://docs.omno.com/apis) confirms the final status. Browser events can be lost (closed tab) or spoofed.


### Payment lifecycle

| Event | Payload | Fires when |
|  --- | --- | --- |
| `PAYMENT_SUCCESS` | `PaymentEmitEventData` | A payment succeeds. |
| `PAYMENT_FAILED` | `PaymentEmitEventData` | A payment fails. |
| `PAYMENT_PENDING` | `PaymentEmitEventData` | A payment is pending. |
| `PAYMENT_CANCELED` | `PaymentEmitEventData` | The customer cancels the payment. |


`PaymentEmitEventData`:

| Field | Type | Notes |
|  --- | --- | --- |
| `transactionId` | string | Transaction identifier. |
| `status` | string | Payment status. |
| `currency` | string | Transaction currency. |
| `orderId` | string | Optional. Merchant order identifier. |
| `amount` | number | Optional. Amount. |
| `merchantTransactionId` | string | Optional. Your external reference. |
| `timestamp` | string | Optional. Event timestamp. |


### KYC

| Event | Payload | Fires when |
|  --- | --- | --- |
| `KYC_REQUIRED_FIELD_ERRORS` | `KYCRequiredFieldErrorsData[]` | Required KYC fields are missing or invalid. Each item: `{ fieldName, issue: 'MISSING' | 'INVALID', errorMessage }`. |
| `KYC_REQUIRED_LEVEL_ERRORS` | `KYCRequiredLevelErrorsData[]` | Required KYC level(s) are incompatible. Each item: `{ level, description }`. |


### Analytics

| Event | Payload | Fires when |
|  --- | --- | --- |
| `ANALYTICS_EVENT` | `PaymentEmitEventData` | Once per unique `transactionId`, deduplicated across retries, page navigation, multiple tabs and duplicate postMessages. |


### Iframe lifecycle

| Event | Payload | Fires when |
|  --- | --- | --- |
| `IFRAME_OPEN_REQUESTED` | `void` | An open is initiated. |
| `IFRAME_OPENED` | `{ sessionId }` | The iframe opened with a session. |
| `CASHIER_LOADED` | `void` | The cashier UI finished loading. |
| `IFRAME_CLOSE_REQUESTED` | `void` | A close is initiated. |
| `IFRAME_CLOSED` | `void` | The iframe closed. |
| `IFRAME_DESTROYED` | `void` | The iframe was removed from the DOM. |


### Other

| Event | Payload | Fires when |
|  --- | --- | --- |
| `LIVE_CHAT_CLICKED` | `void` | The Live Chat button is clicked inside the cashier. |
| `OVERLAY_CLICKED` | `void` | The customer clicks outside the cashier. |


## `ANALYTICS_EVENT` vs `PAYMENT_SUCCESS`

- Use **`PAYMENT_SUCCESS`** for immediate UI reactions (show a success screen, spinner, etc.).
- Use **`ANALYTICS_EVENT`** for analytics and conversion tracking. It fires exactly once per `transactionId`, so it will not double-count across retries, navigations or tabs.


```typescript
cashier.on(CashierEmitEvent.ANALYTICS_EVENT, (data) => {
  gtag("event", "purchase", {
    transaction_id: data.transactionId,
    currency: data.currency
  });
});
```

> ℹ️ **Note:** Neither event is authoritative for money movement. Confirm the final status via webhook or GET before crediting/debiting balances.


## Dynamic language

Set the cashier language at runtime by posting a `SET_LANGUAGE` message. The SDK forwards it to the cashier iframe — whether posted before or after `open()`.

```typescript
// Any BCP 47 language tag
window.postMessage({ type: "SET_LANGUAGE", data: { language: "tr" } }, "*");
window.postMessage({ type: "SET_LANGUAGE", data: { language: "en" } }, "*");
window.postMessage({ type: "SET_LANGUAGE", data: { language: "de" } }, "*");
```

- Posted **before** opening: stored and sent once the cashier loads.
- Posted **after** opening: forwarded immediately.


> **[TBD: exact list of supported language codes]** — the value must be supported by the cashier backend.


## Mobile behavior

The SDK auto-detects device type:

- **Desktop** — modal overlay with backdrop and close button.
- **Mobile** — bottom sheet optimized for touch.


Override detection with `device`:

```typescript
const cashier = new CashierSDK({
  baseUrl: "https://checkout.omno.com",
  device: DeviceType.MOBILE
});
```

### Mobile sheet height

By default the bottom sheet sizes to its content, capped at `90vh`. Set `styles.mobile.height`:

```typescript
const cashier = new CashierSDK({
  baseUrl: "https://checkout.omno.com",
  styles: {
    mobile: {
      height: "full" // full-screen sheet (100% of the viewport)
      // "auto" or omitted → default (content height, 90vh cap)
      // "200px" | "40%" | "70vh" → cap the sheet at that height
    }
  }
});
```

- Applies only on the mobile mount; ignored for desktop modal and container mounts.
- Accepted values: `'full'`, `'auto'`, or a CSS length/percentage (`px`, `%`, `vh`, `dvh`, `svh`, `rem`, `em`). Any other value falls back to the default.


## Styling

`styles.modal` (`ModalStyles`):

| Field | Type | Description |
|  --- | --- | --- |
| `backgroundColor` | string | Overlay background color. |
| `width` | string | Modal width. |
| `height` | string | Modal height. |
| `borderRadius` | string | Modal corner radius. |
| `zIndex` | number | Modal stacking order. |


`styles.mobile` (`MobileStyles`):

| Field | Type | Description |
|  --- | --- | --- |
| `backgroundColor` | string | Sheet/overlay background color. |
| `zIndex` | number | Stacking order. |
| `height` | `'full'` | `'auto'` | CSS length | Bottom-sheet height (see above). |


Default example configuration:

- modal: `rgba(0,0,0,0.4)`, `900px` × `800px`, radius `12px`, zIndex `9`
- mobile: `rgba(0,0,0,0.4)`, zIndex `10`


## Security

The SDK performs built-in origin validation and runs the cashier in an isolated, restricted iframe.

The checkout `token` is attached to the cashier URL only to open the session; the cashier removes it from the address bar immediately and keeps it server-side for the rest of the session. It is short-lived and bound to your merchant account, and OMNO verifies its signature (JWKS) on every request. Pass it through from your backend and never log it. If it expires, the customer sees a "session expired" screen — create a new session (and token) to continue. See [The checkout token](/guides/cashier/integrating-the-cashier#the-checkout-token).

## Failure states

- **Decline / PSP failure / timeout** — surfaced as `PAYMENT_FAILED` / `PAYMENT_PENDING` for UI, but confirm via webhook/GET (`DECLINED`, `FAILED`, `TIMEOUT`).
- **Customer abandons** — `PAYMENT_CANCELED` or `OVERLAY_CLICKED`, or the tab closes with no event. Reconcile via GET.
- **Pending resolving asynchronously** — `PAYMENT_PENDING`; the final status arrives via webhook.