# WalletLimitsFees

Policy evaluation engine for transfer limits and fees in the MomentPay wallet platform.

## Responsibilities

- Evaluate whether a transfer amount is within configured transaction, daily, and monthly limits.
- Calculate applicable fees based on tier, currency, and transfer type.
- Capture policy decision evidence for audit traceability per ADR 0011.
- Store and version limit and fee policies.

## Architecture

`WalletLimitsFees` is a plain OTP application with no database dependency. Policies are held
in an ETS-backed GenServer (`PolicyStore`) for Phase 1-4 CI. The `PolicyEngine` is a pure
module (no side effects) — all storage and event emission happen in the command/query handlers.

## Commands

| Command | Function |
|---|---|
| `WalletLimitsFees.upsert_limit_policy/1` | Create or update a limit policy |
| `WalletLimitsFees.upsert_fee_policy/1` | Create or update a fee policy |

## Queries

| Query | Function |
|---|---|
| `WalletLimitsFees.evaluate_limits/4` | Check amount against configured limits |
| `WalletLimitsFees.calculate_fee/4` | Calculate applicable fee |

## Events

All events are broadcast to the `"wallet_limits_fees:events"` Phoenix.PubSub topic
as `{:domain_event, event_map}` tuples.

| Event | Description |
|---|---|
| `LimitPolicyUpdated.v1` | A limit policy was created or updated |
| `FeePolicyUpdated.v1` | A fee policy was created or updated |
| `PolicyDecisionCaptured.v1` | A limits or fee decision was evaluated (audit trail) |

## Policy Model

### Limit Policy

Keyed by `(tier, currency)`. Fields:
- `max_transaction_amount` — maximum single transfer amount (minor units)
- `daily_limit` — maximum daily cumulative spend (minor units)
- `monthly_limit` — maximum monthly cumulative spend (minor units)

### Fee Policy

Keyed by `(tier, currency, transfer_type)`. Fields:
- `flat_fee` — fixed fee component (minor units)
- `percentage_fee` — fractional fee e.g. `0.015` = 1.5%
- `min_fee` — minimum fee floor
- `max_fee` — maximum fee ceiling (0 = no ceiling)

Fee formula: `fee = max(flat_fee + trunc(amount * percentage_fee), min_fee)`; if `max_fee > 0`, `fee = min(fee, max_fee)`.

## Boundary Constraints

- No direct transfer state management (that is `wallet_transfers`).
- No ledger writes (that is `wallet_ledger`).
- No auth logic (that is `wallet_auth`).
- This app is a pure policy evaluator; business decisions are delegated from `wallet_journey`.

## Usage Example

```elixir
# Upsert policies
WalletLimitsFees.upsert_limit_policy(%{
  tier: :standard, currency: "USD",
  max_transaction_amount: 500_000, daily_limit: 1_000_000, monthly_limit: 5_000_000
})

WalletLimitsFees.upsert_fee_policy(%{
  tier: :standard, currency: "USD", transfer_type: :internal,
  flat_fee: 50, percentage_fee: 0.005, min_fee: 50, max_fee: 500
})

# Evaluate a transfer
{:ok, :within_limits} = WalletLimitsFees.evaluate_limits(:standard, "USD", 10_000, %{daily_used: 200_000})
{:ok, fee} = WalletLimitsFees.calculate_fee(:standard, "USD", :internal, 10_000)
```
