# WalletTransfers

OTP application for Phase 4 of the MercuryPay digital wallet platform.
Responsible for the full lifecycle of money transfers between wallet accounts.

## Responsibilities

- Transfer lifecycle management: initiate, reserve, complete, fail, cancel.
- Per-transfer locking to prevent concurrent state transitions (ADR 0004).
- Idempotency support via `idempotency_key` on all initiation calls.
- Domain event emission on every state transition.
- Audit telemetry emission for compliance traceability (ADR 0007).

## State Machine

```
:initiated --[reserve/2]--> :reserved --[complete/1]--> :completed (terminal)
                                       --[fail/2]-----> :failed    (terminal)
:initiated --[cancel/2]--> :canceled                               (terminal)
```

Terminal states (`:completed`, `:failed`, `:canceled`) reject all further transitions.

## Public API (`WalletTransfers`)

### Commands

| Function | Description |
|---|---|
| `initiate/6` | `initiate(user_id, from_account_id, to_account_id, amount, currency, opts)` |
| `reserve/2`  | `reserve(transfer_id, opts \\ [])` |
| `complete/2` | `complete(transfer_id, opts \\ [])` |
| `fail/3`     | `fail(transfer_id, reason, opts \\ [])` |
| `cancel/3`   | `cancel(transfer_id, reason, opts \\ [])` |

### Queries

| Function | Description |
|---|---|
| `get_transfer/1`       | `get_transfer(transfer_id)` → `{:ok, transfer}` or `{:error, :not_found}` |
| `list_user_transfers/1` | `list_user_transfers(user_id)` → `[transfer]` |

### `initiate/6` Options

| Option | Type | Default | Description |
|---|---|---|---|
| `idempotency_key` | `string` | auto-generated | Safe-retry key |
| `type`            | `:internal \| :p2p \| :external` | `:internal` | Transfer type |
| `reference`       | `string` | auto-generated | Unique business reference |
| `correlation_id`  | `string` | auto-generated | Distributed tracing ID |
| `fee_amount`      | `integer` | `0` | Fee in minor units |
| `metadata`        | `map` | `%{}` | Arbitrary metadata |

## Events

All events are broadcast on the `"wallet_transfers:events"` PubSub topic as
`{:domain_event, event_map}` tuples. Each event implements `@behaviour WalletEvents.DomainEvent`.

| Event Module | Event Name | Trigger |
|---|---|---|
| `WalletTransfers.Events.TransferInitiated` | `TransferInitiated.v1` | `initiate/6` |
| `WalletTransfers.Events.TransferReserved`  | `TransferReserved.v1`  | `reserve/2` |
| `WalletTransfers.Events.TransferCompleted` | `TransferCompleted.v1` | `complete/2` |
| `WalletTransfers.Events.TransferFailed`    | `TransferFailed.v1`    | `fail/3` |
| `WalletTransfers.Events.TransferCanceled`  | `TransferCanceled.v1`  | `cancel/3` |

## Storage

- `WalletTransfers.TransferStore` — ETS-backed GenServer; primary store keyed by `transfer_id`.
  - `:wallet_transfers_store`    — `{transfer_id, transfer}` set.
  - `:wallet_transfers_refs`     — `{reference, transfer_id}` set for uniqueness enforcement.
  - `:wallet_transfers_user_idx` — `{user_id, transfer_id}` bag for user listing.
- `WalletTransfers.LockStore` — ETS-backed GenServer for optimistic per-transfer locking.
  - `:wallet_transfers_locks` — `{transfer_id, owner_pid, acquired_at}` set.
  - Stale locks (dead owner PID) are automatically reclaimed on next acquire attempt.

## Migration

`priv/repo/migrations/20260311000004_create_transfers.exs` — Ecto migration stub for
Mysql persistence (used when the app graduates from in-memory to database-backed storage).

## Boundary Constraints

- **No direct ledger writes.** Ledger debits/credits are the responsibility of `wallet_ledger`.
  Orchestration between transfers and ledger should happen at the service/controller layer.
- **No auth logic.** Authentication and authorization are the responsibility of `wallet_auth`.
- **No circular dependencies** on `wallet_ledger` or `wallet_accounts`.
- All state transitions must go through command handlers with lock acquisition/release.

## Architecture Notes

This app follows the same patterns as `wallet_accounts` and `wallet_ledger`:

- **ETS-backed GenServer stores** — in-memory persistence for Phase 4 CI without a database.
- **Pure state machine modules** — `WalletTransfers.Transfer` contains only pure functions.
- **Command handlers** — `execute/N` functions that acquire locks, mutate state, persist, emit events.
- **Events with `@behaviour WalletEvents.DomainEvent`** — versioned, past-tense event names.
- **Public facade module** — `defdelegate` entries mapping to command/query handlers.
- **Application supervisor** — starts `TransferStore` and `LockStore` under `one_for_one`.
