---
title: Currency Preflight Guide
audience: public
summary: Read the merchant.currency block from get-caller-identity before mounting the Clover payment iframe, and block a doomed checkout up front on a known cart-currency mismatch.
last_verified: 2026-07-10
---

# Currency preflight before mounting the Clover payment iframe

> ⚠️ **ADVISORY (2026-07-02, until further notice): parts of `merchant.currency` are less reliable than usual.**
> `merchant.currency.default` remains the value to use for preflight — keep using it as this guide
> describes (it is being re-grounded server-side to Clover's officially documented merchant-properties
> source). However, `multiCurrencyEnabled` and the non-default entries in `supported` derive from a
> Clover settings feed that Clover is not presently refreshing, so they may lag the merchant's actual
> configuration in either direction. Until this advisory is removed: prefer decisions based on
> `default`; treat a cart currency that is only allowed via `multiCurrencyEnabled`/`supported` extras
> as *uncertain* rather than guaranteed-chargeable, and keep the existing late-failure handling as the
> backstop. The endpoint contract and the patterns below remain correct.

## Charter / scope

`GET /api/v1/get-caller-identity` is a **general contextual/informational endpoint**. Its job is
to expose the **caller's context** — merchant / employee / app / integration — plus additive
capability blocks like `merchant.currency`. It is usable anywhere you need to know "who does this
token resolve to, and what can that merchant do." It is **not** a payment-verdict engine.

**This guide covers one consumer of that context: currency preflight.** Reading `merchant.currency`
to decide whether to mount the Clover iframe is a *consumer-side* decision that lives in your
integration — it is **not** the endpoint's contract or its reason for existing. The endpoint
returns **facts/context, never verdicts**; there is no `canMount` / preflight logic on the server.
The `canMountCloverIframe()` helper below is example client code this guide owns, not something the
endpoint provides. Other consumers read other blocks of the same context.

## The problem this solves

A Clover merchant can only settle a charge in a currency it actually supports.
If a cart is in the wrong currency, the charge fails — but that failure
surfaces *late*, inside the mounted Clover payment iframe, after the shopper has
already entered card details. That is the biggest pain point: mounting an iframe
that is already known to be unable to succeed.

`GET /api/v1/get-caller-identity` exposes the merchant's currency capabilities as
part of the caller context (see the Charter above). This guide uses that context:
read the currency block **before** mounting the iframe and block the doomed
checkout up front with a clear message.

This guide is self-contained on purpose. Everything an integrator needs is here:
the contract, the decision logic, and error handling.

You can confirm a given merchant's current currency (and re-pull it) at any time
from the WeeConnectPay dashboard, so no separate validation tooling is needed
here.

## The endpoint

```
GET {base}/api/v1/get-caller-identity
Authorization: Bearer <integration token>
Accept: application/json
```

- `{base}` is the environment you integrate against: `https://api.weeconnectpay.com`
  (production) or `https://apidev.weeconnectpay.com` (staging).
- The bearer token resolves to a single merchant context. The response describes
  that merchant — you do not pass a merchant id.

### Response shape

```jsonc
{
  "merchant": {
    "uuid": "YR03WQMR3BGY1",
    "name": "Example Merchant",
    "currency": {
      "default": "CAD",              // ISO-4217 primary processing currency
      "multiCurrencyEnabled": false, // can the merchant process more than its default
      "supported": ["CAD"]           // currencies the merchant can accept; always includes default
    }
  },
  "employee": { "uuid": "...", "name": "..." },
  "app": { "uuid": "...", "name": "..." },
  "public_access_key": { "...": "..." },
  "integration": { "uuid": "..." }    // present only when the token is integration-scoped
}
```

`currency` is the only new piece. It is additive and backward compatible: every
other field is unchanged.

## Which field to use, when

- **`default`** — the merchant's single primary settlement currency. For a
  single-currency merchant this is the only currency that will ever charge.
- **`supported`** — the full set the merchant can accept. **Always check the cart
  currency against `supported`, not `default`.** `supported` always contains
  `default`, so this works for both single- and multi-currency merchants.
- **`multiCurrencyEnabled`** — a convenience flag. When `false`, the merchant is
  single-currency and `supported` is exactly `[default]`. Use it only for
  messaging/UI (e.g. "this merchant accepts CAD only"); the gate itself is the
  `supported` membership check.

Rule of thumb: **gate on `supported`; explain with `default` / `multiCurrencyEnabled`.**

## The preflight decision

Run this before mounting the iframe:

```text
identity = GET /api/v1/get-caller-identity        // fail closed on any transport/HTTP error
currency = identity.merchant.currency

if currency field is ABSENT:        proceed (old API; Clover validates at charge)
else if currency is null:           proceed (UNKNOWN — not synced yet; Clover validates at charge)
else:
    cart = normalizeUpper(cartCurrency)            // ISO-4217, uppercase
    if currency.supported.includes(cart):  MOUNT the iframe
    else:                                  BLOCK — do not mount; show mismatch message
```

The two "proceed" branches deliberately do **not** block: a missing or unknown
currency is "we cannot preflight", not "this will fail". Blocking there would
break checkouts for not-yet-synced merchants. The block is reserved for a
*known* mismatch.

### Framework-agnostic snippet

This helper is **your integration's** consumer-side logic, not a capability of the
endpoint. It calls the general `get-caller-identity` context endpoint and interprets
the `merchant.currency` block locally to reach a mount/don't-mount decision:

```js
async function canMountCloverIframe({ baseUrl, token, cartCurrency }) {
  let identity;
  try {
    const res = await fetch(`${baseUrl}/api/v1/get-caller-identity`, {
      headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' },
      signal: AbortSignal.timeout(10000),
    });
    if (!res.ok) return { mount: false, reason: 'fail_closed', httpStatus: res.status };
    identity = await res.json();
  } catch (e) {
    return { mount: false, reason: 'fail_closed', error: String(e) }; // network/timeout
  }

  const currency = identity?.merchant?.currency;
  if (currency === undefined) return { mount: true, reason: 'no_contract' }; // old API
  if (currency === null) return { mount: true, reason: 'unknown' };          // not synced yet

  const cart = String(cartCurrency).toUpperCase();
  return currency.supported.map((c) => c.toUpperCase()).includes(cart)
    ? { mount: true, reason: 'supported', currency }
    : { mount: false, reason: 'mismatch', currency };                        // block + message
}
```

When `mount` is `false` with `reason: 'mismatch'`, show the shopper something
like: "This store charges in {default}. Your cart is in {cart}. Please switch
your currency to continue." Use `currency.default` (and `supported`) to fill it.

## Freshness: why `currency` can be null

`currency` is `null` until the merchant's settings have been synced from Clover
at least once. It becomes populated:

- automatically the next time the merchant logs into the WeeConnectPay dashboard
  (login performs a fresh Clover authorization and queues a settings sync), or
- when the merchant (or WeeConnectPay support) clicks "Refresh from Clover" in
  the dashboard.

After the first sync the value persists; logins re-sync at most every 6 hours.
In practice a merchant actively using the dashboard logged in within roughly the
last 30 minutes (the Clover access-token lifetime), so their currency is already
populated. The `null` case is mostly merchants who have not been active since
the feature shipped — treat it as "unknown" per the decision above.

## Worked examples

Single-currency (block a USD cart):

```jsonc
"currency": { "default": "CAD", "multiCurrencyEnabled": false, "supported": ["CAD"] }
// cart USD  -> not in supported -> BLOCK, do not mount
// cart CAD  -> in supported     -> MOUNT
```

Multi-currency:

```jsonc
"currency": { "default": "CAD", "multiCurrencyEnabled": true, "supported": ["CAD", "USD"] }
// cart USD  -> supported -> MOUNT
// cart EUR  -> not supported -> BLOCK
```

Never-synced:

```jsonc
"currency": null
// any cart -> UNKNOWN -> proceed (Clover validates at charge); optionally prompt dashboard login
```

## Error handling and edge cases

- **Network error / timeout / non-2xx**: fail closed — do not mount. A failure to
  resolve identity is not a license to proceed blind.
- **`merchant.currency` absent** (not null, the key is missing): you are calling
  an API build from before this feature. Treat as unknown and proceed; consider
  pointing the merchant at the updated environment.
- **Currency casing**: always uppercase both sides before comparing; treat values
  as ISO-4217 alpha codes.
- **Do not gate on `default` alone**: a multi-currency merchant's `default` is
  only one of several valid currencies. Always use `supported`.
- **Re-check per checkout**: cache the bearer/PAKMS per session if you like, but
  re-read `currency` each checkout in case the merchant reconfigured.

## Notes

- The contract is intentionally minimal: `default`, `multiCurrencyEnabled`,
  `supported`. Other Clover settings (symbol, source, read-only state, sync
  timestamp) are stored by WeeConnectPay but not exposed here — they are not
  actionable for a currency preflight.
