> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# eSIM reseller billing model: wallet charges and refunds on travel eSIMs

> Why the travel-eSIM reseller lane bills differently from IoT connectivity SIM plans — external inventory purchased against your prepaid wallet, the purchase/refund ledger rows it produces, how the internal charge and refund doors work, and what to expect when the external service is unreachable.

# eSIM reseller billing model

The travel-eSIM reseller lane sells **consumer travel eSIM inventory** —
prepaid data plans an end user installs by scanning a QR code — out of a
standalone eSIM service that Orbit runs as an external partner system. Every
purchase in that lane debits your org's prepaid wallet, and every reversal
credits it back along the same wallet rails. That is a deliberately different
billing driver from the one the [Connectivity SIM model](/concepts/connectivity-sim-model)
documents for IoT/M2M data-only lines, and this page gives you the mental
model the connectivity page does not: where the money moves, what the ledger
rows look like, and what disappears (and what must not disappear) when the
external service has a bad day.

For the mechanics of the ledger itself — append-only rows, micro-cent
accounting, paused-send flags — start at [Wallets, credits, and
charges](/concepts/wallets-credits-and-charges). This page is the reseller
lane's overlay on that model.

## Why this lane is its own billing model

Connectivity SIMs and travel eSIMs are different inventory with different
billing drivers:

|                    | IoT connectivity SIM                    | Travel eSIM (reseller lane)                                                 |
| ------------------ | --------------------------------------- | --------------------------------------------------------------------------- |
| **Inventory**      | Data-only line on an ICCID you manage   | Consumer plan bought from the external eSIM catalog                         |
| **Billed on**      | Plan enrollment + metered session bytes | Order total, charged once per purchase                                      |
| **Charge source**  | Orbit's own usage rating                | The standalone external eSIM service, calling Orbit's internal billing door |
| **Refund path**    | Adjustments to usage records            | Automatic compensating wallet credit by the service                         |
| **Where it lands** | Usage metering pipeline                 | One purchase/refund pair of wallet ledger rows                              |

The lane is architected so that **the external service never holds the
wallet**. Orbit's spendable balance lives behind one atomic wallet
implementation, and a service that moved money by writing the database
directly would leave the customer's balance untouched while corrupting the
ledger. So the eSIM service can't touch the wallet at all: it buys and
refunds through two internal service-to-service endpoints on the Orbit API,
and the Orbit wallet implementation does the move.

This is the same split the public SDK sees: you buy travel eSIMs from the
reseller surface; the charges arrive in your wallet from the internal door.
Neither you nor the service can spend wallet balance without one of those two
doors.

## The wallet ledger rows a purchase produces

A travel-eSIM purchase lands as a single **debit** row on the org ledger,
visible on `GET /api/v1/billing/transactions` and in the Billing dashboard
alongside your SMS / voice / AI debits:

* The **reference** names the order — `charge:esim_purchase:<order-id>` for a
  first purchase, `charge:esim_topup:<order-id>` for a plan top-up — so
  finance can split first-purchase revenue from top-up revenue and a single
  order always points at a single row.
* The **amount** is the tenant-facing price of the order, with sub-cent
  prices preserved precisely in Orbit's micro-cent accounting (the ledger
  tracks millionths of a cent; see [Wallets, credits, and
  charges](/concepts/wallets-credits-and-charges#units-dollars-cents-and-micro-cents)).
  Fractional-cent prices — a per-MB rate, a promotional rounding — are
  billed at their true value and the sub-cent remainder banks rather than
  rounding up on every purchase.

When a purchase reverses — the end user's plan failed to provision, the
order was cancelled — the external service files a refund through the same
internal door and the reversal lands as a **credit** row that references the
original order:

* The credit's **amount equals exactly what left the wallet** for that order
  — Orbit re-derives it from its own ledger instead of trusting the caller,
  and the value matches even when sub-cent arithmetic split the original
  charge between the whole-cent balance and the carry remainder.
* A **repeated refund request for the same order is a short-circuited
  replay**: it reports the original refund amount and touches no money. The
  reversal lands exactly once, and the ledger shows it landed.

Reconciliation stays one-order-one-pair: every purchase order ends in
either one debit row (sold) or a debit + credit pair (reversed), and the
org balance
never moves without a row, so the [Ledger
reconciliation](/billing/cdr-export-billing-reconciliation) story — replay
the ledger and the balance reconciles — holds for this lane too.

## The two internal doors, at a tenant level

The reseller lane's two doors are internal endpoints — they authenticate with
a signed service-to-service token, not your developer API key, so you call
them only through the eSIM reseller surface, not directly. Their shape is
worth understanding because it defines what your ledger can contain:

* **Charge.** The service sends the order id it is selling, the order kind
  (first purchase or top-up), and the price. The wallet's idempotency key is
  derived from `(kind, order-id)` — deterministically, on the server — so a
  retried request for one order charges once. A per-call amount ceiling
  refuses a price that exceeds Orbit's configured maximum before any wallet
  call happens, and every accepted charge appends an audit entry naming the
  order. A charge below one cent is refused at the door: Orbit will only
  accept a charge it can later refund, and a sub-cent-moved-nothing debit
  would leave no ledger row for a refund to compensate.
* **Refund.** The service sends the same `(kind, order-id)` pair plus a
  free-text reason (for example, that provisioning failed). Orbit looks the
  charge up in its own ledger, credits exactly that amount back, and
  one-shot semantics on two independent layers — the durable ledger row and
  the wallet claim — make a retried refund report its own earlier result.
  A refund is refused rather than credited blindly when no charge row exists
  for the order — inventing money on an unproven charge is the one failure
  the lane never commits.

You never see a refund request with an amount of its choosing to credit —
the door carries no amount by design, because Orbit re-reads the real debit
every time.

## Failure-mode expectations

The lane runs **debit then provision then compensate** — a purchase debits
the wallet first, the external service provisions the SIM, and a non-success
refunds. Orbit has no reservation primitive on this path, so the honest
recovery when an order fails mid-flight is the compensating refund, and both
sides treat it as the only reversal.

* **Inventory vs money can only diverge for an operation, never for a
  direction.** If the external service times out or is unreachable after the
  wallet moved, the refund door closes the money gap: the order compensates
  and the credit lands. The refund refusing "no charge on record" is what
  stops the worse divergence — a credit for an order the wallet never moved.
* **A drained wallet refuses the purchase, not the refund.** Wallet
  insufficiency (`INSUFFICIENT_BALANCE`, HTTP 402) is rejected before any
  debit, so a purchase either debits or doesn't happen — it never half-opens.
* **A retried identical request collapses.** The deterministic idempotency
  key makes re-sending the same charge or refund safe; a `DEDUCT_IN_FLIGHT`
  (409) on a retry means the identical charge is still in flight, so the
  service retries it with a short backoff instead of starting over.
* **Every move is auditable.** Both doors append a per-call audit entry
  naming the order id and kind, so a purchase, a refund, or a refused credit
  always leaves a trail to reconcile against — you can audit the lane end to
  end from the ledger.

## What this is not

* **Not your public API.** The charge/refund pair is internal — the signed
  token scopes it to the external service, so your developer key cannot
  reach it and your ledger rows arrive through the reseller surface, not by
  calling these endpoints yourself.
* **Not IoT plan billing.** Travel-eSIM orders own this lane; a SIM
  provisioning pipeline that bills usage on a managed ICCID belongs to the
  [Connectivity SIM model](/concepts/connectivity-sim-model).
* **Not a runbook.** The reseller surface's buy/refund UI and this lane's
  service internals live elsewhere; this page is the concept those assume.

## Cross-links

* [Wallets, credits, and charges](/concepts/wallets-credits-and-charges) —
  the ledger model both lanes' rows live on (units, idempotency,
  degradation).
* [Connectivity SIM model](/concepts/connectivity-sim-model) — the
  plan+metered-data billing driver this lane deliberately differs from.
* [Provisioning and test mode](/concepts/provisioning-and-test-mode) — how
  Orbit separates test traffic from production traffic.
* [Ledger reconciliation](/billing/cdr-export-billing-reconciliation) — how
  finance replays this lane's purchase/refund pairs.
