banguncode/php-frista
Composer 安装命令:
composer require banguncode/php-frista
包简介
A simple PHP library for BPJS Kesehatan facial recognition integration.
README 文档
README
PHPFrista is a PHP library for integrating with BPJS Kesehatan FRISTA (Facial Recognition Integrated System).
It covers the full biometric flow: authentication, face verification, and first-time enrollment.
Features
- Token-based authentication with server-side session validation — stale tokens are detected and refreshed automatically.
- Face identification from encoding alone, no ID required (
/face/recognition2). - Participant lookup by NIK (
/face/nik2). - Face verification by ID + encoding (
/face/match2) with all documented server responses mapped toStatusCodeconstants. - Biometric enrollment from a JPEG file, base64 string, or encoding array (
/face/upload,/face/upload2). - Input validation for identity number, encoding format, and image type.
- Single shared cURL wrapper — no duplicated request boilerplate.
Important
navigator.mediaDevices.getUserMedia() only works on localhost or over HTTPS.
For capturing face descriptors in the browser, face-api.js works well with the FRISTA encoding format. The detection.descriptor array from withFaceDescriptor() is a 128-element Float32Array that maps directly to the $encoding parameter.
Example capture page:
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Face Capture</title> <style> body { font-family: Arial; margin: 20px; } #videoWrap { position: relative; width: 640px; } video { width: 100%; background: #000; transform: scaleX(-1); } canvas { position: absolute; left: 0; top: 0; transform: scaleX(-1); } #capturedImage { max-width: 640px; margin-top: 20px; display: none; } button { padding: 8px 12px; margin: 10px 5px 0 0; } #log { margin-top: 10px; padding: 10px; background: #f5f5f5; font-family: monospace; font-size: 12px; max-height: 300px; overflow: auto; } </style> </head> <body> <h2>Face Capture</h2> <div id="videoWrap"> <video id="video" autoplay muted playsinline></video> <canvas id="overlay"></canvas> </div> <button id="btnCapture" disabled>Capture</button> <img id="capturedImage" /> <pre id="log"></pre> <script src="https://cdn.jsdelivr.net/npm/face-api.js@0.22.2/dist/face-api.min.js"></script> <script> const video = document.getElementById('video'); const logEl = document.getElementById('log'); const btnCapture = document.getElementById('btnCapture'); const capturedImage = document.getElementById('capturedImage'); function log(msg) { logEl.innerHTML += msg + '\n'; } async function init() { try { log('Loading models...'); await faceapi.nets.ssdMobilenetv1.loadFromUri('./models/ssd_mobilenetv1'); await faceapi.nets.faceLandmark68Net.loadFromUri('./models/face_landmark_68'); await faceapi.nets.faceRecognitionNet.loadFromUri('./models/face_recognition'); log('Models loaded'); const stream = await navigator.mediaDevices.getUserMedia({ video: true }); video.srcObject = stream; video.addEventListener('loadedmetadata', () => { log('Camera ready'); btnCapture.disabled = false; }); } catch (e) { log('Error: ' + e.message); } } async function captureAndDetect() { const canvas = document.createElement('canvas'); canvas.width = video.videoWidth; canvas.height = video.videoHeight; const ctx = canvas.getContext('2d'); // Mirror the frame to match the mirrored video display ctx.translate(canvas.width, 0); ctx.scale(-1, 1); ctx.drawImage(video, 0, 0); capturedImage.src = canvas.toDataURL(); capturedImage.style.display = 'block'; log('Detecting face...'); const detection = await faceapi .detectSingleFace(canvas) .withFaceLandmarks() .withFaceDescriptor(); if (!detection) { log('No face detected'); return; } log('Score: ' + detection.detection.score.toFixed(2)); log('Encoding: ' + JSON.stringify(Array.from(detection.descriptor))); } btnCapture.onclick = captureAndDetect; window.addEventListener('load', init); </script> </body> </html>
Installation
composer require banguncode/php-frista
Requirements
- PHP >= 5.5
- Extensions:
curl,fileinfo - Outbound HTTPS access to
frista.bpjs-kesehatan.go.id
Directory Structure
php-frista/
├── src/
│ ├── FacialRecognition.php
│ └── StatusCode.php
├── tests/
│ └── FacialRecognitionTest.php
├── composer.json
└── README.md
Usage
1. Authenticate
<?php require __DIR__ . '/vendor/autoload.php'; use PHPFrista\FacialRecognition; use PHPFrista\StatusCode; $frista = (new FacialRecognition())->init('vclaim_username', 'vclaim_password');
The token is cached in the PHP session. On each call, it is validated against the server via checkSession() before being reused. If the server reports the token as expired or invalid, a fresh login is performed automatically — no manual token management needed.
Default values:
- Base URL:
https://frista.bpjs-kesehatan.go.id - API version:
3.0.2
To override either:
$frista = (new FacialRecognition()) ->setBaseUrl('https://staging.example.com') ->setVersion('3.0.3') ->init('username', 'password');
2. Validate Token (optional)
checkSession() pings the server to confirm the current token is still active.
It is called automatically inside auth(), but you can also call it directly.
$valid = $frista->checkSession(); // true, false, or null (no token)
3. Identify by Face (no ID needed)
recognition() searches the entire FRISTA database using a face encoding alone.
Useful when the participant's ID is unknown — e.g. a walk-in patient.
$result = $frista->recognition($encoding); // raw decoded response or null
4. Look Up by NIK
nik() retrieves a participant's biometric record by their 16-digit NIK.
$result = $frista->nik('3212************'); // raw decoded response or null
5. Verify a Face
$id = '3212************'; // 16-digit NIK or 13-digit BPJS card number $encoding = $request->getJSON()->encoding; // 128 floats forwarded from face-api.js $result = $frista->verify($id, $encoding); switch ($result['status']) { case StatusCode::OK: // $result['data'] contains: match_id, nama, nik, nokartu, score echo 'Verified: ' . $result['data']['nama']; break; case StatusCode::ALREADY_REGISTERED: echo 'Already checked in today.'; break; case StatusCode::UNREGISTERED: // Participant is in BPJS but has no biometric yet. // Register via image file/base64: $frista->register($id, $file) // Register via encoding directly: $frista->uploadEncoding($id, $encoding) break; case StatusCode::FACE_MISMATCH: // $result['data']['distance'] is available for debugging echo 'Face did not match: ' . $result['message']; break; case StatusCode::FACE_UNPROCESSABLE: echo 'Photo quality too low, ask participant to retake.'; break; case StatusCode::DUPLICATE_PHOTO: echo 'Identical photo was used before — participant must take a new photo.'; break; default: echo 'Error: ' . $result['message']; break; }
6. Register a Face (first-time only)
Only call register() when verify() returns StatusCode::UNREGISTERED.
From a JPEG file:
$upload = $frista->register($id, '/path/to/photo.jpg', true);
From a base64 string (data URI prefix is handled automatically):
$b64 = base64_encode(file_get_contents('/path/to/photo.jpg')); // or a data URI: 'data:image/jpeg;base64,...' $upload = $frista->register($id, $b64, false);
Both forms return:
['status' => StatusCode::OK, 'message' => 'Pendaftaran biometrik berhasil'] // or ['status' => StatusCode::INTEGRATION_ERROR, 'message' => '...']
Register via encoding (when a JPEG is not available but the face descriptor is already known):
// kanal refers to the type of healthcare facility: // 'fktl' — Fasilitas Kesehatan Tingkat Lanjutan (hospital/specialist) // 'fktp' — Fasilitas Kesehatan Tingkat Pertama (clinic/puskesmas) $result = $frista->uploadEncoding($id, $encoding); // defaults to 'fktl' $result = $frista->uploadEncoding($id, $encoding, 'fktp'); // primary care facility // returns raw decoded response or null
API Response Reference — /face/match2
These are the raw server responses from the match2 endpoint and their corresponding StatusCode mappings.
| HTTP | Server status |
StatusCode constant | Notes |
|---|---|---|---|
| 400 | — | INTEGRATION_ERROR |
Malformed payload |
| 200 | false |
INVALID_ID |
"ID harus numeric" — caught locally before the request |
| 200 | false |
FACE_UNPROCESSABLE |
"Wajah tidak dapat diproses" — encoding sent but face extraction failed |
| 200 | false |
FACE_MISMATCH |
"Verifikasi wajah gagal. Foto tidak sesuai" — data.distance available |
| 200 | false |
UNREGISTERED |
code: "0" — participant exists but has no biometric on record |
| 200 | false |
ALREADY_REGISTERED |
"Peserta telah terdaftar hari ini" — treated as a pass |
| 200 | false |
DUPLICATE_PHOTO |
"Anda pernah melakukan upload dengan foto yang identik" |
| 200 | true |
OK |
data contains match_id, nama, nik, nokartu, score |
Example success response from the server:
{
"data": {
"match_id": "69fc********************",
"nama": "RUM HAIDAR FAUZAN",
"nik": "3212************",
"nokartu": "000*******245",
"score": 0.604043983216737
},
"message": "Data ditemukan",
"status": true
}
Example mismatch response (StatusCode::FACE_MISMATCH):
{
"data": { "distance": 99.30039597991038 },
"message": "Verifikasi wajah gagal. Foto tidak sesuai",
"status": false
}
Example unregistered response (StatusCode::UNREGISTERED):
{
"code": "0",
"message": "Peserta belum melakukan perekaman wajah. Harap Hubungi Admin.",
"status": false
}
Status Code Reference
| Constant | Description |
|---|---|
OK |
Operation successful |
ALREADY_REGISTERED |
Participant already verified today |
UNREGISTERED |
No biometric on record — enrol with register() |
FACE_MISMATCH |
Face recognised but score below threshold |
FACE_UNPROCESSABLE |
Server could not extract a face from the encoding |
DUPLICATE_PHOTO |
Identical photo was previously uploaded |
AUTH_FAILED |
Login to BPJS server failed |
INVALID_ID |
Identity number is not 13 or 16 digits |
INVALID_ENCODING |
Encoding is not 128 numeric values |
INVALID_IMAGE |
Image is not a valid JPEG or path does not exist |
INTEGRATION_ERROR |
Unexpected error from the BPJS server |
INTERNAL_SERVER_ERROR |
Server response was empty or unreadable |
SERVER_UNREACHABLE |
Connection timed out or server is down |
banguncode/php-frista 适用场景与选型建议
banguncode/php-frista 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 36 次下载、GitHub Stars 达 7, 最近一次更新时间为 2025 年 08 月 12 日, 在 PHP 生态内属于活跃度较高的组件。
它主要适用于以下技术方向: 「Facial Recognition」 「bpjs」 「frista」 「bridging」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。
我们在过去多个企业项目中使用过 banguncode/php-frista 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。
基于 banguncode/php-frista 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。
线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。
承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。
与 banguncode/php-frista 相关的其它包
同方向 / 同关键字的高下载量 PHP Composer 包推荐,方便对比选型:
Kairos Facial Recognition PHP SDK
Optical Mark Recognition from PHP
PHP-ML - Machine Learning library for PHP
A Laravel package for captcha recognition for popular services like rucaptcha.com, 2captcha.com, pixodrom.com, captcha24.com, socialink.ru, anti-captcha.com
Package for integrate with BPJS Trustmark
Use the famous CIFAR-10 dataset to train a multi-layer neural network to recognize images of cats, dogs, and other things.
统计信息
- 总下载量: 36
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 7
- 点击次数: 24
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2025-08-12