npm package: @henrylabs/js
Description: The Henry client SDK lets you embed checkout and card collection elements directly into your website.
Latest version: 1.0.5

# @henrylabs/js

The Henry client SDK lets you embed checkout and card collection elements
directly into your website.

[![npm version](https://img.shields.io/npm/v/@henrylabs/js.svg?style=flat-square)](https://www.npmjs.com/package/@henrylabs/js)

## Installation

Use `npm` to install the Henry.js module:

```sh
npm install @henrylabs/js
```

Or load it directly from the CDN by adding a script tag to your page:

```html
<script src="https://cdn.jsdelivr.net/npm/@henrylabs/js"></script>
```

When loaded via CDN, the SDK is available as `window.Henry`.

## Usage

Import the SDK as an ES module:

```js
import Henry from "@henrylabs/js";
```

---

## Checkout Element

The Checkout Element renders an embedded checkout widget for one or more product
links. To use it, you'll need a **Widget ID** from the
[Henry Developer Dashboard](https://app.henrylabs.ai/dashboard/settings/app/developer).

```js
const el = Henry.createCheckoutElement("#checkout-container", {
  widgetId: "widget_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
  items: [
    {
      link: "https://www.nike.com/t/some-product/ABC-123",
      quantity: 2,
      variant: {
        size: 10,
        color: "white",
      },
    },
  ],
});
```

Add a container element in your HTML:

```html
<div id="checkout-container"></div>
```

### Options

**`widgetId`** `string` - Required. Your Widget ID from the Henry dashboard.

**`items`** `CartItem[]` - Required. One or more products to include in the
checkout. Each item requires a `link` (product URL) and optionally accepts
`quantity`, `variant` (e.g. `{ size, color }`), and `metadata`.

**`settings`** `CheckoutOptions` - Optional settings for the checkout, including
custom fees, purchase options, and event triggers.

**`display.size`** `"full" | "comfortable" | "compact" | "bubble" | "direct"` - Controls
the widget's display size.

**`display.theme`** `"light" | "dark" | "system"` - Sets the color theme.

### Full Example

```js
const el = Henry.createCheckoutElement("#checkout-id-1", {
  widgetId: "widget_UtjTglNV1jg1TvlooBgRH6XdFdPURIYTbLM",
  items: [
    {
      link: "https://www.nike.com/t/al8-womens-shoes-r93WJ3g5/FJ3794-109990",
      // Set quantity for this item (default is 1)
      quantity: 2,
      // Set recommended variant info for this item
      variant: {
        size: 10,
        color: "white",
      },
      // Set the recommended shipping option for this item
      shippingOption: {
        value: "express",
      },
    },
    {
      link: "https://usa.lastresortab.com/products/vm001-black?variant=46245656821911",
      shippingOption: {
        value: "express",
      },
      // Set whatever custom metadata you want
      metadata: {
        creatorSource: "@HenryHenderson",
      },
    },
  ],
  settings: {
    // Set custom fees for this checkout
    commissionFeeFixed: {
      value: 1.99,
      currency: "USD",
    },
    commissionFeePercent: 0.05,
    // Prevent buyers from removing items at checkout
    options: {
      allowPartialPurchase: false,
    },
    // Trigger a pre-defined webhook when the order is complete (configure in the dashboard)
    events: [
      {
        type: "order.purchase.complete",
        data: [
          {
            type: "send_webhook",
            webhookUUID: "123e4567-e89b-12d3-a456-426614174000",
          },
        ],
      },
    ],
  },
  display: {
    size: "comfortable",
    theme: "system",
  },
});
```

### Events

Use `.on()` to listen for checkout events:

```js
el.on("checkout-complete", ({ status, order }) => {
  console.log("Checkout finished with status:", status);
});

el.on("checkout-closed", ({ source }) => {
  console.log("Checkout closed by:", source); // "user" | "system"
});
```

Use `.off()` to remove a listener, and `.destroy()` to unmount the widget and
clean up all listeners:

```js
el.off("checkout-complete", handler);
```

```js
el.destroy();
```

---

## Card Element

The Card Element renders a secure card input iframe for collecting payment card
details. Card data is collected entirely within the iframe - sensitive
information is never exposed to your page.

```js
const cardEl = Henry.createCardElement("#card-container");
```

You can optionally pass styles to customize the card input fields:

```js
const cardEl = Henry.createCardElement("#card-container", {
  styles: {
    cardNumber: {
      base: {
        color: "#333",
        fontSize: "16px",
      },
      "::placeholder": {
        color: "#aaa",
      },
    },
  },
});
```

### Methods

**`validate()`** - Returns a `Promise<boolean>` indicating whether the current
card input is valid.

```js
const isValid = await cardEl.validate();
```

**`submit()`** - Tokenizes the card and returns a `Promise<{ cardToken: string
}>`. Use the returned token to process the payment server-side.

```js
const { cardToken } = await cardEl.submit();
```

**`destroy()`** - Removes the iframe and cleans up event listeners.

```js
cardEl.destroy();
```

---

## TypeScript support

This package includes TypeScript declarations. Key types exported from
`@henrylabs/js`:

```ts
import type { CartItem, CheckoutOptions } from "@henrylabs/js";
```

---

## React

When using Henry.js in React, use a `ref` guard to prevent the element from
being created more than once in Strict Mode:

```tsx
import { useEffect, useRef } from "react";
import Henry from "@henrylabs/js";

function Checkout() {
  const initialized = useRef(false);

  useEffect(() => {
    if (initialized.current) return;
    initialized.current = true;

    const el = Henry.createCheckoutElement("#checkout-container", {
      widgetId: "widget_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
      items: [{ link: "https://example.com/products/item", quantity: 1 }],
      display: { size: "compact", theme: "dark" },
    });

    el.on("checkout-complete", ({ status, order }) => {
      console.log("Done:", status, order);
    });

    return () => el.destroy();
  }, []);

  return <div id="checkout-container" />;
}
```