pelagia-portal/EpfoService/README.md
Hardik e193e26368
All checks were successful
PR checks / checks (pull_request) Successful in 40s
PR checks / integration (pull_request) Successful in 30s
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

1.7 KiB

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).