# Virtual IBAN Policy
**Document:** P11-SB-C01-POLICY
**Status:** Approved
**Date:** 2026-03-27

---

## 1. Purpose

This document defines the policy for Virtual IBAN assignment, lifecycle management, validation rules, and compliance requirements within the wallet platform.

---

## 2. IBAN Structure and Validation

### 2.1 Format

Virtual IBANs follow the ISO 13616 standard:
- 2-letter country code (e.g., `AE`, `GB`, `DE`)
- 2-digit check digits (Mod-97 verified)
- BBAN (Basic Bank Account Number) — country-specific alphanumeric string

### 2.2 Check Digit Validation (ISO 7064 Mod-97)

All IBANs **MUST** pass the ISO 7064 Mod-97 algorithm:
1. Move the first 4 characters to the end of the string.
2. Replace each letter A–Z with its numeric value: A=10, B=11, ..., Z=35.
3. Interpret the resulting string as a large integer.
4. Valid if `result mod 97 == 1`.

**Enforcement:** The `WalletAccounts.VirtualIban.valid_iban?/1` function enforces this at the domain level. Storage is rejected for any IBAN that fails validation.

### 2.3 Uniqueness

Each IBAN string is **globally unique** within the platform:
- Enforced by a `UNIQUE` constraint on `virtual_ibans.iban` in the database.
- Enforced by the `iban_idx` ETS index in `VirtualIbanStore`.
- Duplicate IBAN store attempts return `{:error, :duplicate_iban}`.

---

## 3. Lifecycle

```
:available  ──assign──>  :assigned  ──release──>  :released
    │                        │                        │
    └────deactivate──>  :deactivated  <──deactivate───┘
                             ↑
                   (terminal — no recovery)
```

### 3.1 States

| State | Description |
|---|---|
| `:available` | Provisioned but not yet assigned to any account. |
| `:assigned` | Actively assigned to a wallet account for inbound routing. |
| `:released` | Previously assigned; returned to the pool for reassignment. |
| `:deactivated` | Permanently retired. No further assignment is permitted. |

### 3.2 Transition Rules

| From | To | Actor | Rule |
|---|---|---|---|
| `:available` | `:assigned` | `AssignVirtualIban` command | Account must exist and be active. |
| `:assigned` | `:released` | `ReleaseVirtualIban` command | Account closure or explicit operator release. |
| `:released` | `:assigned` | `AssignVirtualIban` command | Reassignment is permitted. |
| any | `:deactivated` | `DeactivateVirtualIban` command | Operator action only. Terminal. |

---

## 4. Compliance and Data Governance

### 4.1 PII Classification

Virtual IBAN records are classified as **`:financial`** PII under the platform data classification scheme (ADR 0011).

This means:
- Access is limited to `wallet_accounts` (read/write) and `wallet_compliance` (read/audit).
- No cross-boundary export without compliance approval.

### 4.2 Retention

| Trigger | Retention Period |
|---|---|
| Deactivation date | **7 years** |

Retention labels are stored on each record:
- `pii_retention_years: 7`
- `pii_classification: :financial`

### 4.3 Audit Trail

All lifecycle transitions emit:
- An `AuditEvent` via `WalletObservability.AuditEvent`.
- A `DomainEvent` on `WalletWeb.PubSub`.

These are queryable via the `wallet_compliance` audit log.

---

## 5. Access Control Policy

| Role | Permitted Actions |
|---|---|
| `wallet_accounts` app | Create, assign, release, deactivate, read all fields |
| `wallet_compliance` | Read all fields for audit; cannot mutate |
| `wallet_integrations` | Read `iban` string for inbound routing |
| Other apps | No access |

---

## 6. Implementation References

- Domain struct: `apps/wallet_accounts/lib/wallet_accounts/virtual_iban.ex`
- ETS store: `apps/wallet_accounts/lib/wallet_accounts/virtual_iban_store.ex`
- DB schema: `apps/wallet_database/lib/wallet_database/schemas/accounts/virtual_iban.ex`
- Migration: `apps/wallet_database/priv/repo/migrations/20260327130000_create_virtual_ibans.exs`
- Tests: `apps/wallet_accounts/test/wallet_accounts/virtual_iban_test.exs` (40 tests, 0 failures)
