# Referral System

## Purpose

Phase 12 turns the first-touch attribution captured during Telegram registration into a server-authoritative referral lifecycle. A relationship begins as `pending`, becomes `active` only after verified activity and risk checks, and may create configured rewards through the immutable double-entry ledger.

The browser cannot create an attribution, select a referrer, activate a relationship, submit a reward amount, or post a ledger transaction.

## Lifecycle

```mermaid
stateDiagram-v2
  [*] --> pending: Valid new-user attribution
  pending --> active: Threshold and checks pass
  pending --> held: Risk or account restriction
  held --> pending: Admin approves evidence
  pending --> rejected: Admin rejects
  held --> rejected: Admin rejects
```

`active`, `rejected`, and `reversed` are terminal for the Phase 12 review API. A future policy reversal must append a linked ledger reversal and a new history event; it must never edit the original reward entries.

## Atomic activation flow

1. The authenticated user successfully claims a task under the Phase 11 rules.
2. The task service inserts the unique completion inside its existing MySQL transaction.
3. The referral service locks the referred user's relationship.
4. It locks and normalizes the current activation/reward settings and settlement currency.
5. It counts authoritative `task_completions`; no browser counter is accepted.
6. It checks both account states, account/referral bans, minimum account age, inverse referral loops, shared device evidence, and existing risk state.
7. An unsafe relationship moves to `held` with append-only evidence and receives no referral reward.
8. An eligible relationship receives one unique activation row linked to the source task completion.
9. Optional referrer/referee rewards debit the system referral pool and credit each user's available account with equal integer minor units.
10. Activation, history, reward rows, ledger transactions, ledger entries, wallet projections, and task completion commit or roll back together.

Activation and reward event keys are deterministic per referral and beneficiary. Database uniqueness and row locking prevent a racing task claim or Admin approval from paying twice.

## Business settings

| Setting                       | Type and enforced range                          | Default |
| ----------------------------- | ------------------------------------------------ | ------: |
| `activation_task_threshold`   | Integer, 1–1,000 verified completions            |       1 |
| `minimum_account_age_seconds` | Integer, 0–31,536,000                            |  86,400 |
| `referrer_reward_minor`       | Integer minor units, 0–1,000,000,000,000         |       0 |
| `referee_reward_minor`        | Integer minor units, 0–1,000,000,000,000         |       0 |
| `required_for_withdrawal`     | Boolean; consumed by the future withdrawal phase |   false |

Zero reward is valid and creates no financial record. All amount responses are decimal strings. A settings update requires the current version of each changed field, a reason, authenticated Admin CSRF, Admin IP policy, sensitive rate limiting, and `referrals.settings.update`.

Account-age rules are re-evaluated on a later verified task completion or an approved Admin review. Scheduled age-only reconciliation can use the same activation service when the scheduler phase is implemented.

## Public API

All routes require a verified Telegram user session and the `referrals` feature flag.

| Method and route                   | Behavior                                                                 |
| ---------------------------------- | ------------------------------------------------------------------------ |
| `GET /api/v1/referrals`            | Paginated referred-user list; optional public status filter              |
| `GET /api/v1/referrals/summary`    | Counts, posted reward total, currency metadata, and current public rules |
| `GET /api/v1/referrals/share-link` | Opaque `https://t.me/<bot>?start=ref_<code>` link                        |

The list returns a limited referee identity (`first_name`, optional `username`), public relationship ID, status, timestamps, and task-threshold progress. It does not return Telegram IDs, internal IDs, device hashes, ban records, ledger metadata, or risk evidence. Internal `held` is presented publicly as `under_review`.

## Admin API

| Method and route                          | Permission                  | Behavior                                              |
| ----------------------------------------- | --------------------------- | ----------------------------------------------------- |
| `GET /api/v1/admin/referrals`             | `referrals.view`            | Paginated status/risk/search queue                    |
| `GET /api/v1/admin/referrals/settings`    | `referrals.view`            | Values and optimistic versions                        |
| `PATCH /api/v1/admin/referrals/settings`  | `referrals.settings.update` | Versioned threshold/reward update                     |
| `GET /api/v1/admin/referrals/:id`         | `referrals.view`            | Relationship, progress, signals, rewards, and history |
| `POST /api/v1/admin/referrals/:id/review` | `referrals.review`          | Approve, hold, or reject with reason and version      |

An Admin can review state, not rewrite the referrer, referee, referral code, attribution source, or attribution time. Approving a held item re-runs every current automated check before activation and reward.

## Deployment checklist

1. Run `npm run migrate:dry-run` and confirm migration `017` is pending.
2. Back up and identify the intended Oracle MySQL database before `npm run migrate`.
3. Confirm the `referral_rewards` system account exists for the active settlement currency.
4. Review default zero-reward settings and change them only through the versioned Admin endpoint.
5. Run `npm run verify:database-schema` and `npm run check`.
6. Test one new-user referral with non-production accounts and zero reward before enabling monetary referral rewards.
7. Monitor held relationships, duplicate-key conflicts, ledger reconciliation, and Admin audit records.

No live Telegram account, database, Redis deployment, user balance, or external payment service is accessed by the automated tests.
