# Refute Core — API Foundation through Batch 3A

## Batch 3A cryptocurrency routing

Refute Core separates canonical accounting assets from blockchain routes. USDT remains one
ledger asset while `USDT/TRC20` and `USDT/ERC20` are independently audited deposit and
withdrawal configurations. Install with `refute:crypto-config-install` and verify with
`refute:crypto-config-audit`. Newly seeded routes are disabled until provider, contract, fee,
limit, confirmation and precision settings have been reviewed.

This application is the migration source for **Refute Core**. Batches 0B–0C introduce the Core module boundary and permanent versioned API namespaces without removing or renaming the existing LocalCoin-compatible routes.

## Versioned Core endpoints

Service discovery remains available at:

- `GET /api/v1/core/health`
- `GET /api/v1/core/version`
- `GET /api/v1/core/modules`

## Permanent API v1 namespaces

Refute Invest, Refute Forex and future Refute clients should integrate through these namespaces:

- `/api/v1/auth`
- `/api/v1/profile`
- `/api/v1/kyc`
- `/api/v1/wallets`
- `/api/v1/crypto`
- `/api/v1/p2p`
- `/api/v1/transfers`

Batch 0C deliberately delegates existing capabilities to the current LocalCoin API controllers. This preserves behavior while giving clients a stable URL contract that later batches can move behind new Core services without changing client URLs.

## Authentication lifecycle

The v1 surface includes registration/login/password recovery, profile completion, account verification and logout. Protected product namespaces continue to use Sanctum plus the existing account-status and profile-completion middleware.

## Financial compatibility rule

Batch 0C does **not** introduce a second implementation of wallet, crypto or P2P financial behavior. The v1 routes call the existing controllers until their corresponding Core services are introduced.

The `/api/v1/transfers` namespace is the exception: only `GET /api/v1/transfers/capabilities` exists. It explicitly reports that value transfers are not yet enabled. Invest/Forex value movement must not be implemented until the global identity, service-authentication and double-entry ledger layers are available.

## Module boundaries

Refute Core currently declares these module boundaries:

1. Identity
2. Wallet
3. Ledger
4. Crypto
5. P2P
6. Escrow
7. Transfers

`App\Core\RefuteCore::module($key)` can now resolve an individual module descriptor/service from the registry.

## Configuration

`config/refute.php` remains the source of Core service metadata and module feature flags.

Default settlement asset: `USDT`.

## Legacy compatibility

Existing `/api/*`, `/user/*`, `/admin/*` and public web routes remain available. `systemDetails()` remains intentionally unchanged because the source application's legacy vendor/update infrastructure may still rely on the original product identifier.

## Still intentionally deferred

Batch 0C does **not**:

- create ledger tables;
- migrate wallet balances;
- introduce cross-product user IDs;
- move funds between Invest/Forex and Core;
- change P2P escrow accounting;
- change deposit/withdrawal settlement;
- remove any legacy LocalCoin API route.


## Batch 1A — Canonical account model

Refute Core is the canonical identity owner. Every Core user has a stable
`refute_user_id` UUID. Refute Invest and Refute Forex keep their own local
`users.id` values and are associated to the canonical account through
`refute_product_links`.

The normalized identity API intentionally hides the legacy meaning of fields
such as `ev`, `sv`, `kv`, `ts`, and `profile_complete` behind stable status
labels. Product backends should consume this contract rather than depending
on LocalCoin-era database column conventions.

In Batch 1A UUID resolution is self-only for Sanctum user tokens. Trusted
backend-to-backend resolution is deferred to Batch 1C, where scoped service
authentication is introduced.

## Batch 2G — Durable financial-operation idempotency

Batch 2G adds a domain-level idempotency layer above journal- and hold-specific
idempotency keys. `FinancialIdempotencyService` runs the callback and persists
its completed result inside the same `LedgerAtomicOperationService` transaction.
A thrown exception rolls back both the financial/domain mutation and the
idempotency claim; a final business rejection may be returned as
`FinancialIdempotencyOutcome::rejected(...)` so exact retries replay that result.

The raw external idempotency key is never stored. Core stores a SHA-256 key hash,
a request fingerprint, a bounded canonical result payload, and a result hash.
`crypto.deposit.callback` records are non-expiring by default; other records use
the configured retention period unless the caller sets `retention_days => 0`.

## Batch 2H — LocalCoin wallet opening-balance migration

Batch 2H keeps `wallets.balance` unchanged and imports a reviewed, cryptographically
hashed snapshot into the shadow ledger. Positive balances post `Debit System Migration`
and `Credit User Available`; zero balances are recorded as skipped without a journal.
Apply and rollback are gated by `REFUTE_WALLET_MIGRATION_APPLY_ENABLED` and refuse to
run after `REFUTE_LEDGER_AUTHORITATIVE=true`. Every source row is revalidated under lock
immediately before posting, every financial migration operation is idempotent, and a
rollback creates immutable reversal journals rather than editing original entries.

## Batch 2I — Ledger reconciliation and pre-cutover checkpoint

Batch 2I compares the still-authoritative LocalCoin `wallets.balance` source against
Refute ledger user balances at exact user/asset and platform/asset levels. It also
reconciles the shared System Migration control account to every still-active Batch 2H
migration item, checks migration/ledger/hold integrity, detects new or missing source
wallets, non-zero ledger wallets with no legacy source, active holds, non-zero Held
balances, and non-migration user-wallet journal activity.

`php artisan refute:ledger-reconcile` writes private JSON/CSV evidence under
`storage/app/refute/reconciliation` and returns success only when an unfiltered global
run passes every pre-cutover criterion. A passing result is a checkpoint only: this
batch never changes `REFUTE_LEDGER_AUTHORITATIVE`, never edits `wallets.balance`, and
never moves value. The source-of-truth switch remains a dedicated later cutover step.
