Search by

risqid / laravel-attendance-engine

risqid

Reusable Laravel attendance engine with stateless dynamic QR challenges.

Package info

github.com/risqid/laravel-attendance-engine

pkg:composer/risqid/laravel-attendance-engine

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

v1.0.0 2026-10-06 04:56 UTC

This package is auto-updated.

Last update: 2026-10-06 05:01:06 UTC


README

Reusable Laravel package for session-based attendance using stateless dynamic QR challenges and configurable action windows.

Features

  • Stateless Dynamic QR Challenges: HMAC-SHA256 signed payloads validated against server time windows without storing rotating QR codes in the database.
  • Compact 32-Character Binary Payload (v2 Default): 24-byte packed binary payload (1B header [version 2 << 4 | actionCode] + 16B raw binary UUID session ID + 4B big-endian uint32 window + 3B truncated HMAC-SHA256) encoded as exactly 32 characters Base64URL (no padding). Fits dense, high-contrast QR Version 2 (25x25 grid) for fast camera detection on mobile screens.
  • Legacy Payload Support (v1): Backward-compatible with JSON string payload (v, sid, act, win, exp, sig).
  • Granular Action Windows: Independent check_in and check_out scan windows inside a broader session lifecycle.
  • Event-Driven Architecture: Dispatches AttendanceRecorded event for host-side reconciliation (e.g. daily attendance bundling, audit logs, notifications).
  • Decoupled Architecture: 100% generic. Host systems provide their own participant models, authorization, and location rules via contracts.

Requirements

  • PHP ^8.1
  • Laravel / Illuminate ^10.10|^11.0|^12.0

Installation

Install via Composer:

composer require risqid/laravel-attendance-engine

(Or via GitHub VCS repository if hosted privately)

Publish and run migrations:

php artisan vendor:publish --tag=attendance-engine-migrations
php artisan migrate

Publish configuration (optional):

php artisan vendor:publish --tag=attendance-engine-config

Environment Configuration

Configure secret key in .env:

ATTENDANCE_ENGINE_SECRET=your-secure-random-secret

If ATTENDANCE_ENGINE_SECRET is not set, the package falls back to Laravel's APP_KEY.

Database Tables

  • attendance_sessions: stores session lifecycle, secret rotation metadata, and action_windows.
  • attendance_records: stores unique attendance transactions per (attendance_session_id, participant_type, participant_id, action).

Core Contracts

Host applications implement contracts to integrate domain logic:

Contract Purpose
Unwahas\AttendanceEngine\Contracts\ParticipantResolver Resolves authenticated user to participant identity.
Unwahas\AttendanceEngine\Contracts\EligibilityResolver Verifies whether a participant is authorized for the session.
Unwahas\AttendanceEngine\Contracts\LocationPolicy Validates client IP or GPS coordinates against location rules.
Unwahas\AttendanceEngine\Contracts\AttendanceContext Optional host model representation for attendance context.
Unwahas\AttendanceEngine\Contracts\AttendanceParticipant Optional host model representation for participants.

Basic Usage

1. Creating a Session

use Unwahas\AttendanceEngine\Services\AttendanceSessionService;

$sessionService = app(AttendanceSessionService::class);

$session = $sessionService->createFromContext([
    'name' => 'Rapat Koordinasi',
    'starts_at' => now(),
    'ends_at' => now()->addHours(4),
    'qr_rotation_seconds' => 15,
    'action_windows' => [
        'check_in' => [
            'from' => now()->toIso8601String(),
            'until' => now()->addHour()->toIso8601String(),
        ],
        'check_out' => [
            'from' => now()->addHours(3)->toIso8601String(),
            'until' => now()->addHours(4)->toIso8601String(),
        ],
    ],
]);

2. Generating Dynamic QR Challenge

use Unwahas\AttendanceEngine\Services\AttendanceQrService;

$qrService = app(AttendanceQrService::class);

// Returns QrChallenge with payload (32-char binary Base64URL by default), expiry, and remaining seconds
$challenge = $qrService->issueChallenge($session, 'check_in');
$payload = $challenge->payload;

3. Scanning and Recording Attendance

use Unwahas\AttendanceEngine\Services\AttendanceScanService;
use Unwahas\AttendanceEngine\Data\ScanRequestData;

$scanService = app(AttendanceScanService::class);

$result = $scanService->recordScan(new ScanRequestData(
    payload: $payload,
    participantId: auth()->id(),
    participantType: get_class(auth()->user()),
    ipAddress: request()->ip(),
    latitude: request()->input('latitude'),
    longitude: request()->input('longitude')
));

if ($result->isSuccess()) {
    // Attendance successfully recorded
}

4. Listening to Attendance Events

use Unwahas\AttendanceEngine\Events\AttendanceRecorded;

Event::listen(AttendanceRecorded::class, function (AttendanceRecorded $event) {
    $record = $event->record;
    // Execute host-specific side effects (e.g. bundling daily attendance)
});

License

The MIT License (MIT).