tanemrahman / zkteco-biotime
ZKTeco BioTime (ZKBioTime) REST sync for Laravel — pull punches from BioTime into zkteco_transactions.
Requires
- php: ^8.2
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/http: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
This package is auto-updated.
Last update: 2026-08-12 13:48:38 UTC
README
Laravel package to pull attendance punches from a ZKTeco BioTime / ZKBioTime server into your app.
BioTime keeps talking to the devices; this package periodically syncs transactions via the BioTime REST API into zkteco_transactions.
Requirements
- PHP
^8.2 - Laravel
11/12/13 - A reachable ZKBioTime / BioTime instance (HTTP or HTTPS)
- BioTime API user (username + password)
Installation (Composer)
Option A — from GitHub (current)
composer.json
{
"repositories": [
{
"type": "vcs",
"url": "https://github.com/tanemrahman/zkteco-biotime.git"
}
],
"require": {
"tanemrahman/zkteco-biotime": "dev-main"
}
}
composer update tanemrahman/zkteco-biotime php artisan migrate php artisan vendor:publish --tag=zkteco-biotime-config
Or:
composer config repositories.zkteco-biotime vcs https://github.com/tanemrahman/zkteco-biotime.git composer require tanemrahman/zkteco-biotime:dev-main php artisan migrate php artisan vendor:publish --tag=zkteco-biotime-config
Option B — path / local copy
your-laravel-app/
packages/tanemrahman/zkteco-biotime/
{
"repositories": [
{
"type": "path",
"url": "packages/tanemrahman/zkteco-biotime",
"options": { "symlink": true }
}
],
"require": {
"tanemrahman/zkteco-biotime": "*"
}
}
composer update tanemrahman/zkteco-biotime php artisan migrate
Laravel auto-discovers TanemRahman\ZktecoBiotime\ZktecoBiotimeServiceProvider.
What this package gives you
| Piece | Purpose |
|---|---|
zkteco_biotime_settings |
Store BioTime URL + credentials |
zkteco_transactions |
Shared punch table (source = biotime) |
BioTimeClient |
Auth + paginated transactions API |
zkteco-biotime:sync |
Artisan sync command |
| Schedule | Auto sync during office hours |
Event TransactionsSynced |
Hook into your HRM |
No admin UI is included.
Build your own settings form + punch list in Blade / Inertia / Filament / Livewire.
Database tables
| Table | Purpose |
|---|---|
zkteco_biotime_settings |
Connection profiles (can have many) |
zkteco_transactions |
Punches (source = biotime) |
If you also install tanemrahman/zkteco-adms, both packages share zkteco_transactions safely (migration uses Schema::hasTable).
zkteco_biotime_settings columns
| Column | Meaning |
|---|---|
enabled |
Sync only when true |
name |
Label (e.g. Head Office) |
api_url |
BioTime base URL |
username / password |
API login (password encrypted) |
last_sync_at |
Incremental cursor |
last_sync_status |
Last OK / error message |
zkteco_transactions (BioTime rows)
| Column | Meaning |
|---|---|
device_id |
0 (no local ADMS device row) |
user_id |
BioTime emp_code (PIN) |
timestamp |
punch_time |
status |
punch_state |
verify |
verify_type |
source |
biotime |
terminal_sn |
BioTime terminal_sn |
Configure a BioTime connection
Via Tinker / seeder
use TanemRahman\ZktecoBiotime\Models\ZktecoBiotimeSetting; ZktecoBiotimeSetting::create([ 'enabled' => true, 'name' => 'Head Office', 'api_url' => 'http://192.168.1.50', // no trailing slash needed 'username' => 'admin', 'password' => 'your-password', ]);
Via your own settings UI (recommended)
Build a simple form that writes to ZktecoBiotimeSetting:
use TanemRahman\ZktecoBiotime\Models\ZktecoBiotimeSetting; public function store(Request $request) { $data = $request->validate([ 'name' => 'nullable|string|max:120', 'api_url' => 'required|url', 'username' => 'required|string', 'password' => 'required|string', 'enabled' => 'boolean', ]); ZktecoBiotimeSetting::create($data); return back()->with('success', 'BioTime connection saved.'); }
Show sync health on the same page:
@foreach (\TanemRahman\ZktecoBiotime\Models\ZktecoBiotimeSetting::all() as $s) <div> {{ $s->name }} — {{ $s->enabled ? 'ON' : 'OFF' }} Last sync: {{ $s->last_sync_at }} Status: {{ $s->last_sync_status }} </div> @endforeach
Sync punches
# Incremental (uses last_sync_at, or last 7 days on first run) php artisan zkteco-biotime:sync # Force last N days php artisan zkteco-biotime:sync --days=3 # One settings row only php artisan zkteco-biotime:sync --setting=1
Scheduler (built-in)
When zkteco-biotime.schedule.enabled=true (default):
- Every 10 minutes during office hours (
08:00–21:00, timezone configurable) - Daily catch-up at 21:30 for last 3 days
Ensure Laravel’s scheduler is running:
* * * * * cd /path/to/your-app && php artisan schedule:run >> /dev/null 2>&1
Config (.env)
php artisan vendor:publish --tag=zkteco-biotime-config
ZKTECO_BIOTIME_TOKEN_SCHEME=Token # Some BioTime builds need: JWT ZKTECO_BIOTIME_PAGE_SIZE=200 ZKTECO_BIOTIME_LOOKBACK_DAYS=7 ZKTECO_BIOTIME_SCHEDULE=true ZKTECO_BIOTIME_TIMEZONE=Asia/Dhaka
Token scheme:
- Most installs →
Token - Some ZKBioTime builds →
JWT
How to design your app around this package
This package is a sync engine, not a full HRM module.
Suggested screens (you build)
- BioTime settings — CRUD on
ZktecoBiotimeSetting(URL, user, pass, enable toggle, “Sync now” button) - Transactions — list/filter
zkteco_transactionswheresource = biotime - Employee mapping — map
user_id(emp_code) → your employee table - Attendance builder — convert punches into daily attendance / late / OT in your domain
Example: Sync now button
use Illuminate\Support\Facades\Artisan; public function syncNow(int $id) { Artisan::call('zkteco-biotime:sync', ['--setting' => $id]); return back()->with('status', Artisan::output()); }
Example: Show today’s BioTime punches
use TanemRahman\ZktecoBiotime\Models\ZktecoTransaction; $punches = ZktecoTransaction::query() ->where('source', 'biotime') ->whereDate('timestamp', today()) ->orderByDesc('timestamp') ->paginate(50);
Example: React after sync
use TanemRahman\ZktecoBiotime\Events\TransactionsSynced; Event::listen(TransactionsSynced::class, function (TransactionsSynced $e) { // $e->settingId // $e->fetched // $e->saved // $e->pins — emp_codes touched // $e->source — "biotime" // Roll up into your attendances table, notify, etc. });
Example: Map emp_code → employee
use App\Models\Employee; use TanemRahman\ZktecoBiotime\Models\ZktecoTransaction; foreach (ZktecoTransaction::where('source', 'biotime')->whereDate('timestamp', today())->cursor() as $txn) { $employee = Employee::where('biometric_emp_id', $txn->user_id)->first(); if (!$employee) { continue; } // upsert attendance for $employee on $txn->timestamp… }
Optional: use BioTimeClient directly
use TanemRahman\ZktecoBiotime\Models\ZktecoBiotimeSetting; use TanemRahman\ZktecoBiotime\Services\BioTimeClient; $setting = ZktecoBiotimeSetting::where('enabled', true)->firstOrFail(); $client = BioTimeClient::fromSetting($setting); foreach ($client->transactions('2026-08-01 00:00:00', '2026-08-10 23:59:59') as $page) { // $page = array of BioTime transaction rows } $employees = $client->employees();
Using with ADMS package
Install both if some offices use ADMS push and others use BioTime:
| Package | When |
|---|---|
zkteco-adms |
Device pushes directly to Laravel |
zkteco-biotime |
BioTime server already exists |
Shared table: zkteco_transactions
Separate with source = adms|biotime.
{
"repositories": [
{ "type": "vcs", "url": "https://github.com/tanemrahman/zkteco-adms.git" },
{ "type": "vcs", "url": "https://github.com/tanemrahman/zkteco-biotime.git" }
],
"require": {
"tanemrahman/zkteco-adms": "dev-main",
"tanemrahman/zkteco-biotime": "dev-main"
}
}
How BioTime API works (reference)
| Step | Endpoint |
|---|---|
| Auth | POST {base}/api-token-auth/ → { token } |
| Punches | GET {base}/iclock/api/transactions/?page=&start_time=&end_time= |
| Employees | GET {base}/personnel/api/employees/ |
Header: Authorization: Token <token> (or JWT <token> via config).
Publishable tags
php artisan vendor:publish --tag=zkteco-biotime-config php artisan vendor:publish --tag=zkteco-biotime-migrations
License
MIT