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>
51 lines
1.7 KiB
Markdown
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`).
|