Developer-focused toolkit for integrating Philippine National ID (PhilSys) QR verification. Built on Next.js with a mobile-first UI and server APIs that mirror the official PhilSys flows—so you can embed scanning and verification into your own applications quickly.
- Unified verification pipeline – Detect Version 1 legacy PhilIDs and Version 3 ePhilIDs, perform signature checks, and normalize the data.
- Server proxy to PhilSys –
/api/verifyforwards requests tohttps://verify.philsys.gov.ph/api/verifywith the required headers, user agent, and cookies. Optional automated session bootstrapping. - Image-to-QR endpoint –
/api/scan-imageaccepts uploads, extracts QR codes (Jimp + jsQR), and routes them through the same verifier. - Pragmatic logging –
[PhilSys]console diagnostics for every decoding and verification step. - Public eVerify support (auto-detect) – Automatically detects eVerify-style QR values (e.g., PCN or compact tokens), routes them to the public eVerify check, and handles eGovPH consent with polling.
| Path | Purpose |
|---|---|
src/app/page.tsx |
Mobile-first verification console UI |
src/hooks/usePhilSysVerification.ts |
Client hook orchestrating legacy/ePhilID flows |
src/lib/philsys/verification.ts |
Signature validation, CBOR decoding, formatting helpers |
src/lib/philsys/session.ts |
PhilSys session bootstrapper and cookie cache |
src/app/api/verify/route.ts |
Proxy to the official PhilSys /api/verify |
src/app/api/scan-image/route.ts |
Image upload & QR decoding endpoint |
src/app/api/cose/route.ts |
CBOR decoding smoke test |
src/lib/everify/index.ts |
eVerify helpers: type detection, normalization, UI mapping |
src/app/api/everify/check/route.ts |
Proxy to POST /api/pub/qr/check (public eVerify) |
src/app/api/everify/egov-ph/route.ts |
Proxy to GET /api/pub/qr/egov_ph with result normalization |
npm install
npm run devOpen http://localhost:3000
Type checking:
npx tsc --noEmitCreate .env.local (and .env.production for deployments) to override defaults:
| Variable | Description | Notes |
|---|---|---|
NEXT_PUBLIC_PHILSYS_PUBLIC_KEY |
Base64 Ed25519 public key for legacy PhilID verification | Falls back to the original dump’s key if unset. |
PHILSYS_VERIFY_COOKIE |
Optional PhilSys cookie string | Include __verify-token, _ga, _ga_9P2BTMLQFL, etc. If omitted, the proxy will fetch the site once and cache issued cookies for 5 minutes. |
Primary integration point. Send a multipart form with an image then the server extracts the QR code, runs the verification pipeline, and returns the result.
- Form field –
file(required) - Response –
{ qrString: string, result: VerificationResult }
curl -X POST http://localhost:3000/api/scan-image \
-F 'file=@/path/to/qr-photo.jpg'Forwards a normalized PhilID/ePhilID payload to the official PhilSys verifier.
curl -X POST http://localhost:3000/api/verify \
-H 'Content-Type: application/json' \
-d '{"d":"2022-05-11","i":"PSA","img":"","sb":{"BF":"[6,3]","DOB":"2003-05-27","PCN":"2795801750683042","POB":"City of Cabanatuan,Nueva Ecija","fn":"JARIEL","ln":"QUE","mn":"ATIENZA","s":"Male","sf":""}}'Quick CBOR sanity check—useful for gating requests before going upstream. Returns boolean JSON.
Proxy to the public eVerify QR classifier. Accepts the raw scanned value and returns a normalized shape plus display-friendly fields.
- Body –
{ "value": string } - Response –
{ normalized, personalInfo?, details[] }
curl -X POST http://localhost:3000/api/everify/check \
-H 'Content-Type: application/json' \
-d '{"value":"2795801750683042"}'Fetches full eGovPH profile after the holder accepts consent in the official app. The UI polls this endpoint for a short window when needed.
- Query –
tracking(required) - Response –
{ profile, personalInfo, details }
- Detect version (
checkVersion) – JSON payload ⇒ v1 (legacy), otherwise v3 (ePhilID).- Additionally, the client auto-detects public eVerify values (numeric PCNs or compact tokens) and routes them to the eVerify flow below.
- Legacy PhilID
- Normalize payload (
formatVersion1) and sanitize text. - Verify Ed25519 signature (
verifyEddsa). - Format data for display (
formatLegacyData/formatDisplayData). - Optional online check via
/api/verify(results: ACTIVATED, REVOKED, etc.).
- Normalize payload (
- ePhilID
- Strip prefix, base45 decode, inflate, and CBOR decode (
cborToJson). - Validate country code, convert embedded image to base64.
- Submit to
/api/verifyfor activation status.
- Strip prefix, base45 decode, inflate, and CBOR decode (
- Result packaging – UI receives the structured data, statuses, and helper messages.
- Public eVerify (auto-detected)
POST /api/everify/checkclassifies the QR (e.g., National ID, National ID Signed, ePhilId, eGovPH).- For
eGovPH, the UI enters aPENDINGstate and pollsGET /api/everify/egov-ph?tracking=...until the profile is populated or a short timeout elapses. - Other types render immediately using normalized fields.
- The production site uses Firebase Realtime Database to stream consent. This project uses short-lived HTTP polling instead (friendly to serverless deployments like Vercel).
- A 200 response with
{ verified: false }or no identity fields is treated as “not ready yet” and the UI remains pending.
- HTTP 400 from
/api/verify– Cookie missing/expired. Supply a freshPHILSYS_VERIFY_COOKIEor let the proxy bootstrap a new one (ensure outbound access). hashes.sha512 not set– Restart afternpm install; the verification module wires@noble/hasheson load./api/scan-imagereturns 422 – QR not detected. Provide clearer/larger images with the code centered.