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

# How Pesa works

> One trade from start to finish: lock, pay, prove, release.

Every trade has two sides. The **depositor** has USDC locked in escrow and receives fiat. The **taker** pays fiat and receives the USDC. In a buy, the liquidity provider (LP) is the depositor and the user is the taker. In a sell, the roles swap.

## A buy, step by step

<Steps>
  <Step title="The LP funds the escrow">
    The LP deposits USDC into the escrow contract. For each payment rail they set terms: currency, minimum rate, and a hash of the account that receives payments.
  </Step>

  <Step title="The user opens an order">
    The user picks an offer and signs `signal_intent`. The orchestrator checks the rate and limits, then locks that amount of the LP's USDC for this order. The order gets a pay-by time and a settle-by time.
  </Step>

  <Step title="The user pays">
    Only after the lock is confirmed does the app show the LP's payment details and a reference derived from the order ID. The user pays the LP in their payment app.
  </Step>

  <Step title="The phone proves the payment">
    The user signs in to their payment account inside the app. The phone captures the transfer record over TLS and proves, in zero knowledge, that it matches this order: amount, currency, payee, payer, reference and a final status. A verifier checks the proof and signs a claim.
  </Step>

  <Step title="The escrow releases">
    `fulfill_intent` checks the claim's signature, rebuilds the order context from on-chain data, and spends the payment's nullifier. The escrow then releases the USDC to the user.
  </Step>
</Steps>

```mermaid theme={null}
sequenceDiagram
    participant LP as LP (depositor)
    participant U as User (taker)
    participant O as Orchestrator
    participant E as Escrow
    participant Z as zkTLS verifier
    participant W as Payment provider
    LP->>E: deposit USDC, set rail terms
    U->>O: signal_intent
    O->>E: lock USDC for the order
    U->>W: pay the LP (fiat)
    U->>W: phone captures the transfer over TLS
    U->>Z: zero-knowledge proof
    Z-->>U: signed claim
    U->>O: fulfill_intent(claim)
    O->>E: release USDC to the user
```

## When something goes wrong

| Situation | What happens |
| - | - |
| The user never pays | After `settle_by`, anyone can call `expire_intent`. The USDC unlocks back to the LP. |
| The user paid, but the proof fails | The LP sees the money and calls `depositor_release`. The USDC goes to the user. |
| The user changes their mind before paying | The user calls `cancel_intent`. The USDC unlocks back to the LP. |
| The same payment is used twice | The claim's nullifier is already spent, so the second `fulfill_intent` fails. |

## Where the dollars come from

Users fund their Stellar wallet from any supported chain. A USDC deposit from Base, Arbitrum, Avalanche or another CCTP chain is burned there and minted as native USDC in the user's own Stellar wallet. USDT arrives through USDT0 and is converted to USDC on the Stellar DEX. See [Chain abstraction](/chain-abstraction/overview).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.