# Admin CRC full credit reports

Admins open **Credit report** beside Due diligence on the loan page or either loan audit
modal. Opening/reloading reads saved data only. **Fetch CRC report** requires confirmation
that the admin is authorized to send the borrower's BVN to Zeeh and incur Zeeh's current
report fee. **Fetch a new CRC report** explicitly buys a replacement. Prices are not
hardcoded: a historical dashboard charge is not a guaranteed current tariff.

The provider contract is documented at
https://zeehdocs.zeeh.africa/endpoints/endpoints-desk/credit-report-individual/crc-full-credit-report:
`POST /credit_reports/crc_premium`, JSON `{ "bvn": "..." }`, header `Secret_Key`.
The full successful provider response is encrypted in `borrower_credit_reports.payload`.
The UI renders all `data` fields, including nested loan history and payment history.
BVN fields and embedded copies of the borrower's BVN are masked in API responses.
Empty values are not presented as zero. This endpoint is not the separate FICO product.

## Production setup (owner executes)

1. Deploy the API and run the new migration through the normal deployment process:
   `2026_10_28_120000_create_borrower_credit_reports_table.php`.
   It creates a separate table; it does not update existing loans, wallets or schedules.
2. Set these **API/backend** environment variables:

   ```dotenv
   ZEEH_BASE_URL=https://v2.api.zeeh.africa
   ZEEH_SECRET_KEY=<live Secret_Key from Nextpayday_Credit_Report>
   ```

3. Rebuild Laravel's configuration cache through the normal deployment process
   (`php artisan config:cache`); restart long-running application workers if applicable.
4. Deploy the admin app. No frontend environment variable, public key, webhook or queue
   is required. Keep `APP_KEY` stable and backed up: Laravel uses it to decrypt reports.
   `BVN_HASH_KEY` must already be configured (existing BVN storage requirement).
5. Ensure the Zeeh business wallet is funded and the credit-report app can access CRC.
   The dashboard's historical success under another app does not prove this app's key
   works. A real paid request, using an authorized borrower, remains an owner validation.

Without a secret, fetching returns 503 and does not call Zeeh. The default URL is
`https://sandbox.api.zeeh.africa`. Sandbox reports carry a warning and are never reused
as production reports. Only these two HTTPS hosts are accepted; redirects are disabled.

## Behaviour and safeguards

- `GET /api/v1/admin/loans/{id}/credit-report`: loans.read plus actual Admin account;
  returns the current saved report and last attempt; writes both view audit logs when
  returning a report. Responses use `Cache-Control: no-store, private`.
- `POST` at the same path: loans.write plus actual Admin account, throttled 10/minute;
  requires UUID `request_id`, accepted `confirm_charge`, optional boolean `refresh`.
- Reports belong to the borrower and are reused across applications, matching both
  environment and a keyed BVN fingerprint. Changing a BVN hides old reports. Neither a
  GET nor a normal POST with an existing valid report buys another report.
- The borrower row is locked briefly to persist a request claim before HTTP. Concurrent
  requests across loans/admin sessions cannot initiate another pull. UUID replays never
  call Zeeh again, including after failures. Claims survive application crashes.
- HTTP timeout: 45 seconds; connection timeout: 10 seconds. No automatic retries.
  Provider failure, malformed JSON, missing data/score, non-success response, or mismatched
  BVN is not a successful report. Previous successful data remains available with the
  latest failure warning. Failed response bodies and HTTP exceptions are not logged.
- A five-minute cooldown applies even after failure; a failed/timeout request may have
  been charged. A subsequent deliberate attempt must use a new UUID. Never automatically
  retry to obtain a clean response.
- A request left `pending` after a server crash blocks new requests. An operator must
  reconcile the request with Zeeh before resolving that claim through an authorized
  operational process. This feature intentionally has no automatic expiry/retry or
  unaudited admin reset. The saved previous report stays readable.
- Request and outcome logs are written to `admin_action_logs` and `user_activity_logs`;
  they contain report IDs/outcomes, not BVNs, credentials or report contents.
- Report retention follows borrower retention (foreign key cascade on hard deletion).
  No scheduled purge is introduced. Restrict access to the database and its backups.

## Flows and callers reviewed

The only existing code callers changed are `LoanAuditInsights` and the admin route table.
`LoanAuditInsights` is shared by `app/loans/[id]/page.tsx`,
`components/dashboard/LoanAuditModal.tsx`, and `components/borrowers/LoanAuditModal.tsx`.
All three gain the new tab. Existing audit service, audit PDF, decision snapshots,
collection readiness, approval/decline/manual payout services and caller behaviour stay
the same. The existing encrypted `User.bvn`, `Bvn::hash`, Admin model, admin audit log and
borrower activity log helpers are called without modifying their existing contracts.

Read `approve-and-pay-checks.md`, `loan-repayment-flows.md`, the admin audit endpoint,
LoanAuditService, LoanRequest decision snapshot hooks, LoanReviewService approval entry,
and the salary disbursement claim/guard references. Git history preserves deliberate
BVN masking and historical decision/payout evidence separation; this change preserves both.

Scope: admin review UI for borrower-backed salary, retail, local-government and imported
loans. Partner-credit routes retain their existing middleware restrictions; no partner
portal exposure is added. The new API never changes a loan, repayment, offer, mandate,
NextPayDay wallet or payment status. Paystack/Mono/Monnify/manual/wallet rails and borrower,
affiliate, scheduler, WhatsApp and automatic lending flows are unaffected.

## Verification

`AdminZeehCreditReportTest` uses fake HTTP only: configuration off, missing BVN, encrypted
success, masking, audit logs, unchanged loans/wallets, cross-loan reuse, explicit refresh,
cooldown, idempotent requests, pending claims, provider failures, timeouts, malformed
payloads, wrong borrower, environment/BVN changes, authentication/authorization and charge
confirmation. Run the migration only on `nextpayday_test` before the test suite, then:

```sh
php -d memory_limit=1G vendor/bin/phpunit
```

Admin validation: `npx tsc --noEmit -p .`.
Real credentials, live provider response compatibility, live pricing and production data
are not tested by this work. No real `.env`, production settings or dashboard settings
are changed; no live borrower details are submitted.
