sarkhanrasimoghlu / laravel-asanimza
Laravel package for Asan İmza mobile authentication and digital signing via JSON-RPC 2.0 proxy API
v1.0.0
2026-03-16 09:37 UTC
Requires
- php: ^8.3
- guzzlehttp/guzzle: ^7.8
- laravel/framework: ^12.0
This package is auto-updated.
Last update: 2026-03-16 09:41:47 UTC
README
Laravel package for Asan İmza — Azerbaijan's mobile authentication and digital signing system. Communicates with the Asan İmza proxy server via JSON-RPC 2.0 protocol.
Requirements
- PHP ^8.3
- Laravel ^12.0
- Asan İmza proxy server running (default:
http://localhost:8090)
Installation
composer require sarkhanrasimoghlu/laravel-asanimza
Publish the configuration file:
php artisan vendor:publish --tag=asanimza-config
Configuration
Add the following to your .env file:
ASANIMZA_BASE_URL=http://localhost:8090 ASANIMZA_SERVICE_NAME=AsanDoc ASANIMZA_LANGUAGE=aze ASANIMZA_TIMEOUT=30 ASANIMZA_LOG_ENABLED=true ASANIMZA_LOG_CHANNEL=stack
| Variable | Default | Description |
|---|---|---|
ASANIMZA_BASE_URL |
http://localhost:8090 |
Asan İmza proxy server URL |
ASANIMZA_SERVICE_NAME |
AsanDoc |
Registered service name |
ASANIMZA_LANGUAGE |
aze |
Default language (aze, rus, eng) |
ASANIMZA_TIMEOUT |
30 |
HTTP request timeout (seconds) |
ASANIMZA_LOG_ENABLED |
true |
Enable/disable request logging |
ASANIMZA_LOG_CHANNEL |
stack |
Laravel log channel |
Usage
Inject AsanImzaInterface via constructor or method injection:
use Sarkhanrasimoghlu\AsanImza\Contracts\AsanImzaInterface;
Authentication
Step 1: Start authentication
public function login(AsanImzaInterface $asanImza) { $result = $asanImza->authenticate('+994501234567', 'userId123'); // Show verification code to the user // $result->verificationCode — user must confirm this on their phone // $result->transactionId — needed for status check // $result->certificate — needed for status check // $result->challenge — needed for status check return response()->json([ 'transactionId' => $result->transactionId, 'verificationCode' => $result->verificationCode, 'certificate' => $result->certificate, 'challenge' => $result->challenge, ]); }
Step 2: Check authentication status (polling)
public function checkAuth(AsanImzaInterface $asanImza, Request $request) { $status = $asanImza->getAuthStatus( transactionId: $request->input('transactionId'), certificate: $request->input('certificate'), challenge: $request->input('challenge'), ); if ($status->status === \Sarkhanrasimoghlu\AsanImza\Enums\AuthStatus::USER_AUTHENTICATED) { // Authentication successful // $status->signature contains the base64-encoded signature } return response()->json([ 'status' => $status->status->value, ]); }
Get Certificate Data
Extract user information from a certificate:
$certData = $asanImza->getCertificateData($certificate); $certData->firstName; // "John" $certData->surname; // "Doe" $certData->personalCode; // "1234567" $certData->organizationName; // nullable $certData->certType; // CertType enum (AUTHENTICATION, CITIZEN_SIGNING, etc.)
Digital Signing
Step 1: Get signing certificates
$certs = $asanImza->getSigningCertificates('+994501234567', 'userId123'); // Returns: ['certId1' => 'base64cert1', 'certId2' => 'base64cert2']
Step 2: Sign files
use Sarkhanrasimoghlu\AsanImza\DTOs\DataFile; $dataFiles = [ new DataFile( content: base64_encode(file_get_contents('/path/to/document.pdf')), fileName: 'document.pdf', mimeType: 'application/pdf', ), ]; $result = $asanImza->signFiles( dataFiles: $dataFiles, phoneNumber: '+994501234567', userId: 'userId123', certIdentifier: 'certId1', certificate: $certs['certId1'], ); // $result->transactionId // $result->verificationCode — show to user // $result->hash — needed for status check // $result->container — needed for status check
Step 3: Check signing status (polling)
$status = $asanImza->getSignFileStatus( transactionId: $result->transactionId, hash: $result->hash, container: $result->container, certificate: $certs['certId1'], ); if ($status->status === \Sarkhanrasimoghlu\AsanImza\Enums\SignStatus::SIGNATURE_CREATED) { // $status->container — signed container (base64) $signedContent = base64_decode($status->container); }
Error Handling
All exceptions extend AsanImzaException:
use Sarkhanrasimoghlu\AsanImza\Exceptions\AsanImzaException; use Sarkhanrasimoghlu\AsanImza\Exceptions\JsonRpcException; try { $result = $asanImza->authenticate('+994501234567', 'userId123'); } catch (JsonRpcException $e) { // JSON-RPC error from Asan İmza proxy $e->getErrorCode(); // e.g., 2004 $e->getErrorMessage(); // e.g., "Unknown phone number" $e->getContext(); // ['error_code' => 2004, 'error_message' => '...'] } catch (AsanImzaException $e) { // HTTP or parsing error $e->getMessage(); $e->getContext(); }
Common Error Codes
| Code | Description |
|---|---|
| 1002 | Service overloaded |
| 2004 | Unknown phone number |
| 2013 | No authentication certificate |
| 2017 | Phone number and user ID are not related |
| 2018 | Authentication certificate is not valid |
| 2020 | No signing certificate |
| 2021 | Signing certificate is not valid |
| 2022 | Forbidden service name |
| -32700 | JSON-RPC parse error |
| -32600 | Invalid request |
| -32601 | Method not found |
Enums
AuthStatus
| Value | Description |
|---|---|
USER_AUTHENTICATED |
Authentication successful |
OUTSTANDING_TRANSACTION |
Waiting for user confirmation |
NOT_VALID |
Transaction not valid |
EXPIRED_TRANSACTION |
Transaction expired |
USER_CANCEL |
User cancelled on phone |
MID_NOT_READY |
Mobile ID not ready |
SENDING_ERROR |
Error sending to phone |
SIM_ERROR |
SIM card error |
INTERNAL_ERROR |
Internal system error |
SignStatus
Same values as AuthStatus, except USER_AUTHENTICATED is replaced by SIGNATURE_CREATED.
CertType
| Value | Description |
|---|---|
| 0 | Authentication |
| 1 | Citizen signing |
| 2 | Business signing |
| 3 | Governmental signing |
| 4 | Youth personal |
| 5 | Digital stamp |
License
MIT