PHPFrista is a PHP library to integrate with BPJS Kesehatan FRISTA (Facial Recognition and Biometric Registration API).
It helps hospitals and clinics connect to BPJS FRISTA endpoints for authentication, facial verification, and biometric registration.
- Authentication using BPJS VClaim username & password.
- Facial data verification using a 128-number face encoding array.
- Biometric registration from either:
- JPEG file path, or
- JPEG image as Base64 string.
- Input validation for:
- Identity number (13 or 16 digits only).
- Encoding format (array of 128 decimal numbers).
- Image format (JPEG only).
- cURL-based API requests with JSON and multipart/form-data support.
If you are using a web-based application, navigator.mediaDevices.getUserMedia() only runs on localhost and/or using the https protocol.
For reference, you can use face-api.js and its models here:
- https://github.com/justadudewhohacks/face-api.js
- https://github.com/justadudewhohacks/face-api.js-models
Example:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Face Detection</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 Detection</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 overlay = document.getElementById('overlay');
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');
log('Starting camera...');
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
video.srcObject = stream;
video.addEventListener('loadedmetadata', () => {
overlay.width = video.videoWidth;
overlay.height = video.videoHeight;
log('Camera started');
btnCapture.disabled = false;
});
} catch (error) {
log(`Error: ${error.message}`);
}
}
async function captureAndDetect() {
const canvas = document.createElement('canvas');
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
const ctx = canvas.getContext('2d');
// Flip image horizontally for capture
ctx.translate(canvas.width, 0);
ctx.scale(-1, 1);
ctx.drawImage(video, 0, 0);
// Show captured image
capturedImage.src = canvas.toDataURL();
capturedImage.style.display = 'block';
log('Detecting face...');
// Detect face from captured image
const detection = await faceapi.detectSingleFace(canvas).withFaceLandmarks().withFaceDescriptor();
if (!detection) {
log('No face detected');
return;
}
log(`Face detected (score: ${detection.detection.score.toFixed(2)})`);
var encoding = Array.from(detection.descriptor);
log(`Face descriptor: ${JSON.stringify(encoding)}`);
}
btnCapture.onclick = captureAndDetect;
window.addEventListener('load', init);
log('Initializing...');
</script>
</body>
</html>You can install this library via Composer:
composer require banguncode/php-frista- PHP >= 5.5
- PHP extensions:
- curl
- fileinfo
- Internet connection to access BPJS FRISTA endpoints.
project/
├── src/
│ ├── FacialRecognition.php
│ ├── StatusCode.php
├── tests/
│ └── FacialRecognitionTest.php
├── composer.json
├── .gitignore
└── README.md- Authentication
<?php
require __DIR__ . '/vendor/autoload.php';
use PHPFrista\FacialRecognition;
use PHPFrista\StatusCode;
$frista = (new FacialRecognition())
->init('vclaim_username', 'vclaim_password');- Automatically authenticates and stores the token.
- Uses default Base URL: https://frista.bpjs-kesehatan.go.id
- Uses default API version: 3.0.2
- Verify Facial Data
$id = '1234567890123456'; // Identity number
$encoding = array_fill(0, 128, 0.1234); // Example encoding
$match = $frista->verify($id, $encoding);
if ($match['status'] === StatusCode::OK || $match['status'] === StatusCode::ALREADY_REGISTERED) {
echo "✅ Verification successful";
} else {
echo "❌ Failed: " . $match['message'];
}- Register Biometric Data
Register facial recognition only if the verify method returns UNREGISTERED
- From a JPEG File
$upload = $frista->register($id, '/path/to/photo.jpg', true);- From a Base64 String
$base64Image = base64_encode(file_get_contents('/path/to/photo.jpg'));
$upload = $frista->register($id, $base64Image, false);| Status Code | Description |
|---|---|
OK |
Success |
AUTH_FAILED |
Authentication to BPJS server failed |
INVALID_ID |
Identity number format invalid |
INVALID_ENCODING |
Face encoding format invalid |
INVALID_IMAGE |
Image format invalid |
ALREADY_REGISTERED |
Participant already registered today |
UNREGISTERED |
Biometric not yet registered |
INTEGRATION_ERROR |
Error from BPJS integration |
INTERNAL_SERVER_ERROR |
Internal server error |