---
title: "Currency and pricing"
description: "Resolve event currency, present host prices safely, and keep buyer checkout aligned with the intended server authority."
---

Currency, buyer presentation, and SeatLayer's hold record are related but
separate decisions. Define the authority your checkout uses before adding a
custom price book.

## Currency precedence

| Source | Purpose |
|---|---|
| Event or organization currency | Normal order currency; travels with event data |
| SDK `currency` | Formatting fallback when event data has no currency |
| `USD` | Built-in fallback |

One hold or booking uses one ISO 4217 currency. SeatLayer does not convert
amounts between currencies, and a locale change affects formatting rather than
the amount or currency.

```js title="buyer/currency.js"
const picker = new seatlayer.SeatPicker({
  container: "#picker",
  event: "ev_9f3a",
  publicKey: "pk_test_…",
  currency: "EUR", // event/org data wins when present
});
```

For a public Platform event, use a publishable key that matches the event mode
(`pk_test_…` for test or `pk_live_…` for live) and register the page's exact
Embed origin. The SDK obtains Public-only access directly and keeps the grant in
memory, so your server does not mint a buyer token when the chart loads. For a
login, presale, partner, or channel audience, replace `publicKey` with an async
`buyerAccessTokenProvider` backed by your authenticated server; an explicit
provider or token takes precedence.

## Default: charge the inspected hold

For the simplest and safest checkout, inspect the hold from your backend and
calculate the payment from its `unitPrice`, `currency`, and `quantity`.

```ts title="server/order-total.ts"
const hold = await inspectHold(eventKey, holdId);

const total = hold.items.reduce(
  (sum, item) => sum + item.unitPrice * item.quantity,
  0,
);

assertOneCurrency(hold.items);
await createPayment({ amount: total, currency: hold.items[0].currency });
```

This makes the event's locked price snapshot, SeatLayer reports, and your charged amount
agree.

## Host price books

`SeatPicker.pricing` can make your application price-authoritative for the
buyer presentation and SDK checkout handoff:

```js title="buyer/member-pricing.js"
const picker = new seatlayer.SeatPicker({
  container: "#picker",
  event: "ev_9f3a",
  publicKey: "pk_test_…",
  pricing: {
    prices: {
      vip: 150,
      standard: {
        base: 60,
        tiers: {
          early: 45,
          door: 75,
        },
      },
    },
    formatter: (amount, currency) => `${amount} ${currency}`,
  },
  onCheckout: (_hold, _seats, handoff) => {
    startCheckout({
      holdId: handoff.holdId,
      displayedTotal: handoff.total,
    });
  },
});
```

`prices` accepts a flat category amount or a base amount plus tier overrides.
`formatter` changes rendered money text. The picker applies the override to
displayed totals and the `CheckoutHandoff` it gives your callback.

<Aside type="caution" title="The hold retains the event price">
  A host override does not rewrite the `unitPrice` stored in SeatLayer's hold.
  A fresh server inspection still returns the event-authoritative snapshot.
  Treat the browser handoff as context, never as trusted payment input.
</Aside>

## Choose one server policy

<Tabs>
  <TabItem label="Chart-priced checkout">
    Ignore browser-posted totals. Inspect the hold and charge its recorded line
    items. This keeps SeatLayer's amount reporting aligned with payment.
  </TabItem>
  <TabItem label="Host-priced checkout">
    Resolve the same category and tier ids against your own server-side price
    book. Validate eligibility, currency, discounts, and tax there. Record both
    your charged amount and SeatLayer's inspected amount because SeatLayer
    reports will continue to reflect the event's locked snapshot.
  </TabItem>
</Tabs>

Never trust a custom amount posted from the browser—even if it matches the
`CheckoutHandoff`. Send the opaque `holdId` plus your own authenticated cart or
price-context identifier. Recompute the intended amount on the server.

## Prevent mismatches

- Keep category keys and tier ids stable across the chart and host price book.
- Version promotional and membership rules so checkout can reproduce them.
- Reject mixed currencies before creating a payment.
- Store the hold id, booking reference, price-book version, and charged amount.
- Decide how refunds and reports reconcile when host and chart prices differ.
- Test resumed and extended holds; a client refresh must not change eligibility.
- Display taxes and fees separately unless they are intentionally part of unit
  price.

## Money units

SeatLayer chart and hold examples use major units, such as `45` for 45.00.
Payment providers often require minor units. Convert once on your server using a
currency-aware library; do not assume every currency has two decimal places.

Continue to [holds and checkout](/buyer-sdk/holds-and-checkout),
[ticket tiers](/buyer-sdk/ticket-tiers), or the
[complete checkout example](/examples/complete-checkout).