pelagia-portal/EpfoService/README.md
Hardik e193e26368 feat(crewing): EPFO/UAN assisted verification (GstService pattern, flagged)
Scaffolds EPFO/UAN verification the same way GST works — a standalone Playwright
proxy microservice + an /api proxy + an assisted affordance that records the
result. Aadhaar stays manual (UIDAI-restricted). Stacks on the follow-ups branch.
Behind NEXT_PUBLIC_CREWING_ENABLED.

What's in
- EpfoService/ (new microservice, GstService pattern): Express + Playwright.
  POST /otp {uan} → session + OTP request; POST /verify {sessionId,uan,otp} →
  member record; GET /health. EPFO is OTP-gated (no anonymous captcha lookup like
  GST), so the handshake is two steps. Live portal navigation is gated behind
  EPFO_LIVE (default STUB: OTP 000000 → matched) until real selectors/OTP are
  validated. README documents the differences + that Aadhaar is out of scope.
- App: /api/epfo/otp + /api/epfo proxies (gated by verify_bank_epf) to
  EPFO_SERVICE_URL. EpfDetail += epfoMemberName + epfoCheckedAt (migration
  crewing_epfo_check). recordEpfoCheck action persists the EPFO result + audit.
- UI: an "EPFO check" affordance on the verification EPF rows — request OTP →
  enter OTP → matched member → record. Aadhaar noted as manual-only.

Tests & docs
- Integration: verification.test.ts gains recordEpfoCheck (records name+timestamp,
  Accounts-only gating). type-check clean; full unit (245) + integration (213)
  green (RESEND_API_KEY unset).
- .env.example (EPFO_SERVICE_URL/EPFO_LIVE), CLAUDE.md, EpfoService/README.

Note: the EpfoService live portal selectors/OTP are stubbed and must be validated
against a real EPFO session before enabling EPFO_LIVE.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 22:43:24 +05:30

51 lines
1.7 KiB
Markdown

# EpfoService
EPFO / UAN **assisted-lookup** proxy for PPMS crewing — mirrors `GstService`.
Drives the EPFO member portal headlessly (Playwright) to fetch a member record
for a UAN, so Accounts can confirm a crew member's EPF details against the source.
## Why it differs from GstService
- The GST portal has an anonymous **captcha** lookup. The EPFO member portal does
not — "Know your UAN" is gated by an **OTP to the member's registered mobile**.
So the handshake is two steps (`/otp` then `/verify`).
- **Aadhaar is out of scope.** UIDAI restricts Aadhaar verification to licensed
AUA/KUA via consented e-KYC; it cannot be portal-scraped. PPMS keeps Aadhaar
**assisted-manual** (stores only the last 4 digits, masked).
## Endpoints
| Method | Path | Body | Returns |
|---|---|---|---|
| GET | `/health` | — | `{ status, mode, sessionCount }` |
| POST | `/otp` | `{ uan }` | `{ sessionId, mobileHint }` |
| POST | `/verify` | `{ sessionId, uan, otp }` | `{ matched, name, status }` |
## Modes
- **Stub (default):** `EPFO_LIVE` unset/`false`. Deterministic responses — OTP
`000000` → matched member, anything else → not matched. Lets the app
integration run end-to-end in dev/CI without the live portal.
- **Live:** `EPFO_LIVE=true`. Drives the real portal. **The page selectors and the
OTP/captcha flow are marked `TODO(live)` and must be validated against a real
session before enabling** — the portal layout is the source of truth.
## Env
```
PORT=3004
SESSION_TTL_MS=300000
EPFO_LIVE=false
EPFO_PORTAL_URL=https://unifiedportal-mem.epfindia.gov.in/memberinterface/
```
## Run
```
pnpm install
pnpm dev # tsx watch
# or
pnpm build && pnpm start
```
The PPMS app reaches it via `EPFO_SERVICE_URL` (proxied through `/api/epfo`).