banguncode / php-frista
A simple PHP library for BPJS Kesehatan facial recognition integration.
Installs: 13
Dependents: 0
Suggesters: 0
Security: 0
Stars: 3
Watchers: 0
Forks: 6
Open Issues: 0
pkg:composer/banguncode/php-frista
Requires
- php: >=5.5
- ext-curl: *
- ext-openssl: *
This package is auto-updated.
Last update: 2025-11-26 07:15:54 UTC
README
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.
Features
- 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.
Important Information
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>
Installation
You can install this library via Composer:
composer require banguncode/php-frista
Requirements
- PHP >= 5.5
- PHP extensions:
- curl
- fileinfo
- Internet connection to access BPJS FRISTA endpoints.
Directory Structure
project/ ├── src/ │ ├── FacialRecognition.php │ ├── StatusCode.php ├── tests/ │ └── FacialRecognitionTest.php ├── composer.json ├── .gitignore └── README.md
Usage
- 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);
Appendix
| 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 |