# @shoppexio/storefront

Official JavaScript storefront SDK for Shoppex.

Use this package when you want to embed Shoppex products, cart behavior, and hosted checkout into a custom frontend.

## Good fit

Use this package for:

- product loading
- cart state
- checkout redirects
- storefront helpers

## Install

```bash
npm install @shoppexio/storefront
```

## Quick start

```ts
import shoppex from '@shoppexio/storefront';

shoppex.init('my-store');

const products = await shoppex.getProducts();
shoppex.addToCart(products.data?.[0]?.uniqid ?? '', 'default', 1, {
  custom_fields: {
    customer_note: 'Gift order',
  },
});
await shoppex.checkout();
```

Cart items use a stable `line_id`. Use this identifier for cart updates and
removals because one product can have multiple cart configurations.

```ts
const [line] = shoppex.getCart();

if (line) {
  shoppex.updateCartItem(line.line_id, { quantity: 2 });
  shoppex.removeFromCart(line.line_id);
}
```

## Buyer currency

`shoppex.init('store-slug', { currency: 'EUR' })` prices every catalog read
(`getStorefront`, `getStore`, `getProducts`, `getProduct`) in that currency and
uses it for cart quotes and checkout. The currency must be enabled in the
shop's settings: a read for a currency the shop does not sell in fails with
`code: 'errors.storefront.currency_unavailable'` and `errorParams.available`
listing the enabled ones. Nothing falls back to the shop default silently.
`data.shop.currency` names the currency a response is priced in;
`default_currency` and `available_currencies` describe the shop's setup.

## Multi-currency checkout

When buyers can select a storefront currency, quote the cart and create the
invoice with the same currency:

```ts
const selectedCurrency = 'EUR';
await shoppex.quoteCart(undefined, selectedCurrency);

const checkoutUrl = await shoppex.buildCheckoutUrl({
  currency: selectedCurrency,
});
```

After a currency change, quote the cart again before checkout. Quote tokens are
currency-bound; Shoppex rejects a mismatch as
`errors.checkout.quote_token_stale` instead of creating an invoice for a total
the buyer did not approve.

## Custom customer account

Build your own OTP login, order history, and download UI without adding a
merchant API key or custom backend. Headless customer accounts are available on
every Shoppex plan; only the separate Headless Checkout SDK requires Business:

```ts
import { createCustomerClient } from '@shoppexio/storefront/customer';

const customer = createCustomerClient({
  shop: 'my-store',
  publishableKey: 'pk_live_your_key',
});

await customer.requestOtp('buyer@example.com');
await customer.verifyOtp('buyer@example.com', '123456', { rememberMe: true });

const orders = await customer.orders();
const firstOrder = orders.invoices[0];

if (firstOrder) {
  const order = await customer.order(firstOrder.uniqid);

  for (const item of order.lineItems) {
    const instructions = item.deliveryText ?? item.product?.serviceText;
    console.log(item.deliveryStatus, instructions, item.deliverySummary);
  }
}
```

The publishable key and current browser origin must be configured under
**Settings -> Developer -> Headless**. `rememberMe` is opt-in and keeps
the buyer signed in for up to 30 days.

The order detail includes delivery status, service instructions, codes, serials,
notes, links, and file indexes. Delivery content appears only after Shoppex marks
the item as delivered. Sanitize rich-text delivery instructions before you render
them as HTML.

## Coupon validation

Use the options form when validating a coupon for a selected product variant:

```ts
const result = await shoppex.validateCoupon('SAVE10', {
  productId: 'prod_abc123',
  variantId: 'variant_lifetime',
});
```

Calling `validateCoupon(code)` without options validates against the current SDK cart. Affiliate/referral codes are separate and should use `validateAffiliateCode` or `applyAffiliateCode`. If `program_enabled` is false in an affiliate validation response, the shop-level affiliate program is disabled even if individual links exist.

## Live online users

The SDK can power a custom live online visitor counter. It provides the data; your storefront renders the badge.

```ts
import shoppex from '@shoppexio/storefront';

shoppex.init('my-store');

async function refreshLiveUsers() {
  await shoppex.touchStorefrontPresence();

  const result = await shoppex.getStorefrontOnlineUsers();
  if (result.success && result.data) {
    console.log(`${result.data.count} online`);
  }
}

await refreshLiveUsers();
setInterval(refreshLiveUsers, 30_000);
```

## Not this package

If you want the authenticated Developer API wrapper for backend integrations, use `@shoppexio/sdk`. Do not ship `@shoppexio/sdk` in a browser bundle; use this Storefront SDK for headless storefronts.

## Docs

- Storefront docs: [docs.shoppex.io/sdk/introduction](https://docs.shoppex.io/sdk/introduction)
- Installation: [docs.shoppex.io/sdk/installation](https://docs.shoppex.io/sdk/installation)
- Headless customer accounts: [docs.shoppex.io/developers/headless/customer-accounts](https://docs.shoppex.io/developers/headless/customer-accounts)
