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>
|
||
|---|---|---|
| .. | ||
| src | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
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 (
/otpthen/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_LIVEunset/false. Deterministic responses — OTP000000→ 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 markedTODO(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).