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

# Purchase flow — SDK bridge

> The end-to-end purchase flow and why it must stay SDK-mediated.

Purchases are the highest-stakes integration surface. They are **SDK-mediated
by design**, and this guide explains the full flow plus the invariants.

## Why SDK-mediated?

The SDK core is the *only* place that knows the purchase endpoint paths
(`/api/v1/security/pin-session`, `.../{id}/verify`, `/api/v1/security/events`).
Your game provides business data only. This keeps every client in the
ecosystem on the same, enforced, secure path — including the environment
pre-flight that a hand-rolled client would skip.

## The flow

```text theme={"dark"}
your game                        PurchaseClient                backend
     │ mint(itemId, amount, purchaseId, platform, details)
     ├─► check_environment_integrity()
     │      └─ risks? ─────────────► Blocked + ENV_COMPROMISED
     │                                                        (no request)
     │
     ├─► POST /api/v1/security/pin-session
     │      ◄─ { sessionId, sessionToken, expiresInMs, envLevel }
     │
     player enters PIN
     │
     ├─► verify_pin(sessionId, sessionToken, pin, VerifyRequest)
     │      └─ POST /api/v1/security/pin-session/{id}/verify
     │           ◄─ Valid | WrongPin{locked,attempts} | Failed{code,message}
     │
     └──► report_security_event(...)   // best-effort audit (202)
```

## The pre-flight gate

* Risks `DEVICE_ROOTED` / `DEBUGGER_ATTACHED` → level `COMPROMISED`
  → **blocked**.
* Any other risk (screen capture, suspicious accessibility) → `WARN`
  → **blocked**.
* Clean host → `SAFE` → a one-time PIN session is minted.

When blocked, an `ENV_COMPROMISED` security event is reported
best-effort, and **no purchase request is sent**.

## Verification invariants

<Warning>
  Verified sessions are consumed on success and failures are rate-limited.
  `verify_pin` is single-shot — never retry it.
</Warning>

* `amount` travels as a **decimal string** on the wire.
* Always send a fresh **`idempotencyKey`** per authorized withdrawal.
* `destinationAddress` is the single authorization target — validate it
  carefully before asking the player to approve.
* `x-secure-session` carries the minted `sessionToken`; the bearer access
  token authorizes the SDK side of the call.

## Wallet custody boundary

The SDK **never sees, holds, or signs wallet private keys**. The player
authorizes a withdrawal by PIN; MusterBox's custody layer (multi-party
computation) executes it. Never pass a wallet key or mnemonic to the SDK, and
never log custody material.

## Rules of the order

1. Mint a PIN session only from a **clean** environment.
2. Show the PIN prompt; never log the PIN.
3. Verify once; handle `WrongPin` (retry with feedback) vs `Failed`
   (session/transport) differently.
4. On `Valid`, proceed with fulfillment; on `locked`, stop.

Next: [Environment integrity & anti-tamper](/guides/environment-integrity-and-anti-tamper).
