{"id":34550469,"url":"https://github.com/tigusigalpa/vtb-id-php","last_synced_at":"2026-01-13T23:34:01.671Z","repository":{"id":330248087,"uuid":"1122100023","full_name":"tigusigalpa/vtb-id-php","owner":"tigusigalpa","description":"PHP/Laravel пакет для интеграции OAuth2 авторизации через VTB ID. Поддерживает Laravel 8-12 и чистый PHP 8.0+. Включает полный OAuth2 flow (авторизация, обмен токенов, refresh, revoke), получение данных пользователя, встроенные маршруты, middleware, facade, события и helper-функции. Простая установка через Composer с подробной документацией.","archived":false,"fork":false,"pushed_at":"2025-12-24T05:30:35.000Z","size":22,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-12-25T18:51:38.573Z","etag":null,"topics":["api","laravel","oauth2","oauth2-client","php","vtb","vtb-id","vtbid"],"latest_commit_sha":null,"homepage":"","language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tigusigalpa.png","metadata":{"files":{"readme":"README-en.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-12-24T05:24:47.000Z","updated_at":"2025-12-24T05:28:22.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/tigusigalpa/vtb-id-php","commit_stats":null,"previous_names":["tigusigalpa/vtb-id-php"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/tigusigalpa/vtb-id-php","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tigusigalpa%2Fvtb-id-php","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tigusigalpa%2Fvtb-id-php/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tigusigalpa%2Fvtb-id-php/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tigusigalpa%2Fvtb-id-php/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tigusigalpa","download_url":"https://codeload.github.com/tigusigalpa/vtb-id-php/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tigusigalpa%2Fvtb-id-php/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28399813,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-13T14:36:09.778Z","status":"ssl_error","status_checked_at":"2026-01-13T14:35:19.697Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["api","laravel","oauth2","oauth2-client","php","vtb","vtb-id","vtbid"],"created_at":"2025-12-24T07:58:40.608Z","updated_at":"2026-01-13T23:34:01.666Z","avatar_url":"https://github.com/tigusigalpa.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# VTB ID PHP/Laravel Package\n\n![VTBID PHP](https://github.com/user-attachments/assets/681ae94b-51be-4c20-a1be-2ce6c8b5a802)\n\n[![Latest Version](https://img.shields.io/github/release/tigusigalpa/vtb-id-php.svg?style=flat-square)](https://github.com/tigusigalpa/vtb-id-php/releases)\n[![License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square)](LICENSE.md)\n\nPHP/Laravel package for VTB ID integration (OAuth2 authorization via VTB Bank account).\n\n**🌐 Language:** English | [Русский](README.md)\n\n## Table of Contents\n\n- [Requirements](#requirements)\n- [Installation](#installation)\n- [Quick Start](#quick-start)\n- [Configuration](#configuration)\n- [Usage](#usage)\n- [API Reference](#api-reference)\n- [Usage Examples](#usage-examples)\n- [Security](#security)\n- [Testing](#testing)\n\n## Requirements\n\n- PHP 8.0 or higher\n- Guzzle HTTP Client 7.x\n- Laravel 8.x, 9.x, 10.x, 11.x or 12.x (optional, for Laravel integration)\n\n## Installation\n\n### Via Composer (Recommended)\n\n```bash\ncomposer require tigusigalpa/vtb-id-php\n```\n\n### Publish Configuration\n\n```bash\nphp artisan vendor:publish --tag=vtbid-config\n```\n\n### Local Installation (for development)\n\nAdd repository to `composer.json`:\n\n```json\n{\n    \"repositories\": [\n        {\n            \"type\": \"path\",\n            \"url\": \"./packages/vtb-id-php\"\n        }\n    ],\n    \"require\": {\n        \"tigusigalpa/vtb-id-php\": \"@dev\"\n    }\n}\n```\n\nThen install:\n\n```bash\ncomposer require tigusigalpa/vtb-id-php:@dev\n```\n\n## Quick Start\n\n### For Laravel\n\n#### 1. Installation (1 minute)\n\n```bash\ncomposer require tigusigalpa/vtb-id-php\nphp artisan vendor:publish --tag=vtbid-config\n```\n\n#### 2. Configuration (2 minutes)\n\nAdd to `.env`:\n\n```env\nVTBID_CLIENT_ID=your_client_id_from_vtb\nVTBID_CLIENT_SECRET=your_client_secret_from_vtb\nVTBID_REDIRECT_URI=https://your-domain.com/auth/vtb/callback\n```\n\n#### 3. Create Routes (2 minutes)\n\nIn `routes/web.php`:\n\n```php\n// Success route\nRoute::get('/auth/vtb/success', function () {\n    $user = session('vtbid_user');\n    \n    // Create/update user in database\n    // and authenticate them in the system\n    \n    dd($user); // For testing\n})-\u003ename('vtbid.success');\n\n// Error route\nRoute::get('/auth/vtb/error', function () {\n    $error = session('error');\n    return redirect('/login')-\u003ewith('error', $error);\n})-\u003ename('vtbid.error');\n```\n\n#### 4. Add Login Button\n\nIn any blade template:\n\n```blade\n\u003ca href=\"{{ route('vtbid.redirect') }}\" class=\"btn btn-primary\"\u003e\n    Login with VTB ID\n\u003c/a\u003e\n```\n\n### For Plain PHP\n\n#### 1. Installation\n\n```bash\ncomposer require tigusigalpa/vtb-id-php\n```\n\n#### 2. Create Configuration File\n\n```php\n// config.php\nreturn [\n    'client_id' =\u003e 'your_client_id',\n    'client_secret' =\u003e 'your_client_secret',\n    'redirect_uri' =\u003e 'https://your-domain.com/callback.php',\n];\n```\n\n#### 3. Login Page (login.php)\n\n```php\n\u003c?php\nrequire_once 'vendor/autoload.php';\n\nuse Tigusigalpa\\VTBID\\VTBIDClient;\n\nsession_start();\n\n$config = require 'config.php';\n$client = new VTBIDClient($config);\n\n$state = $client-\u003egenerateState();\n$_SESSION['vtbid_state'] = $state;\n\n$authUrl = $client-\u003egetAuthorizationUrl($state);\n\nheader('Location: ' . $authUrl);\nexit;\n```\n\n#### 4. Callback Handler (callback.php)\n\n```php\n\u003c?php\nrequire_once 'vendor/autoload.php';\n\nuse Tigusigalpa\\VTBID\\VTBIDClient;\nuse Tigusigalpa\\VTBID\\Exceptions\\VTBIDException;\n\nsession_start();\n\n$config = require 'config.php';\n$client = new VTBIDClient($config);\n\n$code = $_GET['code'] ?? null;\n$state = $_GET['state'] ?? null;\n\n// Verify state\nif (!$code || !isset($_SESSION['vtbid_state']) || $_SESSION['vtbid_state'] !== $state) {\n    die('Invalid request');\n}\n\nunset($_SESSION['vtbid_state']);\n\ntry {\n    // Get token\n    $tokenData = $client-\u003egetAccessToken($code);\n    \n    // Get user data\n    $userData = $client-\u003egetUserData($tokenData['access_token']);\n    \n    // Save to session\n    $_SESSION['vtbid_access_token'] = $tokenData['access_token'];\n    $_SESSION['vtbid_refresh_token'] = $tokenData['refresh_token'] ?? null;\n    $_SESSION['vtbid_user'] = $userData-\u003etoArray();\n    \n    // Redirect to dashboard\n    header('Location: /dashboard.php');\n    exit;\n    \n} catch (VTBIDException $e) {\n    die('Authentication error: ' . $e-\u003egetMessage());\n}\n```\n\n#### 5. User Dashboard (dashboard.php)\n\n```php\n\u003c?php\nsession_start();\n\nif (!isset($_SESSION['vtbid_user'])) {\n    header('Location: /login.php');\n    exit;\n}\n\n$user = $_SESSION['vtbid_user'];\n?\u003e\n\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n    \u003ctitle\u003eDashboard\u003c/title\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n    \u003ch1\u003eWelcome, \u003c?= htmlspecialchars($user['name']) ?\u003e!\u003c/h1\u003e\n    \u003cp\u003eEmail: \u003c?= htmlspecialchars($user['email']) ?\u003e\u003c/p\u003e\n    \u003cp\u003ePhone: \u003c?= htmlspecialchars($user['mainMobilePhone']) ?\u003e\u003c/p\u003e\n    \u003ca href=\"/logout.php\"\u003eLogout\u003c/a\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\n### Done! 🎉\n\nUsers can now authenticate via VTB ID.\n\n## Configuration\n\n### Environment Variables\n\nAdd to `.env`:\n\n```env\nVTBID_CLIENT_ID=your_client_id\nVTBID_CLIENT_SECRET=your_client_secret\nVTBID_REDIRECT_URI=https://your-domain.com/auth/vtb/callback\n\n# Optional parameters\nVTBID_BASE_URL=https://id.vtb.ru\nVTBID_USER_DATA_URL=https://gost-id.vtb.ru\nVTBID_SCOPE=\"openid name surname patronymic gender mainMobilePhone email\"\nVTBID_RESPONSE_TYPE=code\n\n# Route settings\nVTBID_ROUTES_ENABLED=true\nVTBID_ROUTES_PREFIX=auth/vtb\n```\n\n### Getting VTB ID Credentials\n\n1. Register on VTB ID developer portal\n2. Create new application\n3. Set Redirect URI: `https://your-domain.com/auth/vtb/callback`\n4. Select required scopes\n5. Get `client_id` and `client_secret`\n\n### Available Scopes\n\n```\nopenid              - Required, basic authorization\nname                - First name\nsurname             - Last name\npatronymic          - Middle name (patronymic)\ngender              - Gender\nbirthdate           - Date of birth\nmainMobilePhone     - Primary phone\nemail               - Email\ninn                 - Tax identification number\nsnils               - Insurance number\n```\n\n## Usage\n\n### Basic Usage with Built-in Routes\n\nThe package automatically registers the following routes:\n\n- `GET /auth/vtb/redirect` - Redirect to VTB ID authorization page\n- `GET /auth/vtb/callback` - Handle callback from VTB ID\n\nTo start authorization, simply redirect the user to:\n\n```php\nreturn redirect()-\u003eroute('vtbid.redirect');\n```\n\n### Using VTBIDClient Directly\n\n```php\nuse Tigusigalpa\\VTBID\\VTBIDClient;\nuse Tigusigalpa\\VTBID\\Exceptions\\VTBIDException;\n\n// Via Dependency Injection\npublic function __construct(protected VTBIDClient $vtbid)\n{\n}\n\n// Or via Facade\nuse Tigusigalpa\\VTBID\\Facades\\VTBID;\n\n// Get authorization URL\n$state = VTBID::generateState();\nsession(['vtbid_state' =\u003e $state]);\n$authUrl = VTBID::getAuthorizationUrl($state);\n\nreturn redirect($authUrl);\n\n// Handle callback\ntry {\n    $tokenData = VTBID::getAccessToken($code);\n    // $tokenData contains: access_token, refresh_token, expires_in, token_type\n    \n    $userData = VTBID::getUserData($tokenData['access_token']);\n    \n    // Access user data\n    echo $userData-\u003ename;\n    echo $userData-\u003esurname;\n    echo $userData-\u003eemail;\n    echo $userData-\u003egetFullName(); // Surname Name Patronymic\n    \n} catch (VTBIDException $e) {\n    Log::error('VTB ID Error: ' . $e-\u003egetMessage());\n}\n```\n\n### Refresh Token\n\n```php\ntry {\n    $refreshToken = session('vtbid_refresh_token');\n    $newTokenData = VTBID::refreshAccessToken($refreshToken);\n    \n    session([\n        'vtbid_access_token' =\u003e $newTokenData['access_token'],\n        'vtbid_refresh_token' =\u003e $newTokenData['refresh_token'] ?? null,\n    ]);\n} catch (VTBIDException $e) {\n    // Token expired or invalid\n}\n```\n\n### Revoke Token\n\n```php\ntry {\n    $accessToken = session('vtbid_access_token');\n    VTBID::revokeToken($accessToken, 'access_token');\n    \n    session()-\u003eforget(['vtbid_access_token', 'vtbid_refresh_token', 'vtbid_user']);\n} catch (VTBIDException $e) {\n    Log::error('Failed to revoke token: ' . $e-\u003egetMessage());\n}\n```\n\n### Helper Functions\n\n```php\n// Check authentication\nif (vtbid_authenticated()) {\n    $user = vtbid_user();\n    $token = vtbid_access_token();\n}\n```\n\n## User Data\n\nThe `VTBIDUser` object contains the following fields:\n\n- `sub` - Unique user identifier\n- `name` - First name\n- `surname` - Last name\n- `patronymic` - Middle name (patronymic)\n- `gender` - Gender (male/female)\n- `birthdate` - Date of birth\n- `mainMobilePhone` - Primary mobile phone\n- `email` - Email address\n- `emailVerified` - Email verified (boolean)\n- `phoneNumberVerified` - Phone verified (boolean)\n- `inn` - Tax identification number\n- `snils` - Insurance number\n\n### VTBIDUser Methods\n\n```php\n$userData-\u003egetFullName(); // Returns \"Surname Name Patronymic\"\n$userData-\u003etoArray(); // Convert to array\n$userData-\u003egetRawData(); // Returns raw data from API\n```\n\n## Integration with Laravel Auth\n\n### Update User Migration\n\n```bash\nphp artisan make:migration add_vtb_id_fields_to_users_table\n```\n\n```php\npublic function up()\n{\n    Schema::table('users', function (Blueprint $table) {\n        $table-\u003estring('vtb_id')-\u003enullable()-\u003eunique()-\u003eafter('id');\n        $table-\u003estring('surname')-\u003enullable()-\u003eafter('name');\n        $table-\u003estring('patronymic')-\u003enullable()-\u003eafter('surname');\n        $table-\u003estring('phone')-\u003enullable()-\u003eafter('email');\n    });\n}\n```\n\n```bash\nphp artisan migrate\n```\n\n### Update User Model\n\n```php\nprotected $fillable = [\n    'name',\n    'surname',\n    'patronymic',\n    'email',\n    'phone',\n    'vtb_id',\n    'password',\n];\n```\n\n### Example Controller\n\n```php\nuse App\\Models\\User;\nuse Illuminate\\Support\\Facades\\Auth;\n\nRoute::get('/auth/vtb/success', function () {\n    $vtbUser = session('vtbid_user');\n    \n    $user = User::updateOrCreate(\n        ['vtb_id' =\u003e $vtbUser['sub']],\n        [\n            'name' =\u003e $vtbUser['name'],\n            'surname' =\u003e $vtbUser['surname'],\n            'patronymic' =\u003e $vtbUser['patronymic'],\n            'email' =\u003e $vtbUser['email'],\n            'phone' =\u003e $vtbUser['mainMobilePhone'],\n        ]\n    );\n    \n    Auth::login($user, true);\n    \n    return redirect('/dashboard');\n})-\u003ename('vtbid.success');\n```\n\n## API Reference\n\n### VTBIDClient\n\n#### Constructor\n\n```php\npublic function __construct(array $config = [])\n```\n\n**Parameters:**\n- `client_id` (string) - VTB ID application ID\n- `client_secret` (string) - Application secret key\n- `redirect_uri` (string) - Callback URL\n- `base_url` (string, optional) - Base API URL\n- `user_data_url` (string, optional) - User data URL\n- `scope` (string, optional) - Requested permissions\n- `response_type` (string, optional) - Response type\n\n#### getAuthorizationUrl()\n\n```php\npublic function getAuthorizationUrl(?string $state = null): string\n```\n\nGenerates URL for redirecting user to VTB ID authorization page.\n\n#### generateState()\n\n```php\npublic function generateState(): string\n```\n\nGenerates random string for state parameter (CSRF protection).\n\n#### getAccessToken()\n\n```php\npublic function getAccessToken(string $code): array\n```\n\nExchanges authorization code for access token.\n\n**Returns:**\n```php\n[\n    'access_token' =\u003e '...',\n    'refresh_token' =\u003e '...',\n    'expires_in' =\u003e 3600,\n    'token_type' =\u003e 'Bearer'\n]\n```\n\n#### refreshAccessToken()\n\n```php\npublic function refreshAccessToken(string $refreshToken): array\n```\n\nRefreshes access token using refresh token.\n\n#### getUserData()\n\n```php\npublic function getUserData(string $accessToken): VTBIDUser\n```\n\nGets user data using access token.\n\n#### revokeToken()\n\n```php\npublic function revokeToken(string $token, string $tokenTypeHint = 'access_token'): bool\n```\n\nRevokes (invalidates) token.\n\n### Facade: VTBID\n\n```php\nuse Tigusigalpa\\VTBID\\Facades\\VTBID;\n\n$authUrl = VTBID::getAuthorizationUrl();\n$state = VTBID::generateState();\n$tokenData = VTBID::getAccessToken($code);\n$userData = VTBID::getUserData($accessToken);\n$newTokenData = VTBID::refreshAccessToken($refreshToken);\nVTBID::revokeToken($token);\n```\n\n### Middleware\n\n#### VTBIDAuthenticated\n\nRegister in `app/Http/Kernel.php`:\n\n```php\nprotected $routeMiddleware = [\n    'vtbid.auth' =\u003e \\Tigusigalpa\\VTBID\\Middleware\\VTBIDAuthenticated::class,\n];\n```\n\nUsage:\n\n```php\nRoute::middleware(['vtbid.auth'])-\u003egroup(function () {\n    Route::get('/profile', function () {\n        return view('profile', ['user' =\u003e vtbid_user()]);\n    });\n});\n```\n\n## Usage Examples\n\n### Plain PHP Usage\n\n#### Complete Application Example\n\n**Project Structure:**\n```\n/\n├── vendor/\n├── config.php\n├── login.php\n├── callback.php\n├── dashboard.php\n├── logout.php\n└── composer.json\n```\n\n**logout.php:**\n```php\n\u003c?php\nrequire_once 'vendor/autoload.php';\n\nuse Tigusigalpa\\VTBID\\VTBIDClient;\nuse Tigusigalpa\\VTBID\\Exceptions\\VTBIDException;\n\nsession_start();\n\n$config = require 'config.php';\n$client = new VTBIDClient($config);\n\n// Revoke token\nif (isset($_SESSION['vtbid_access_token'])) {\n    try {\n        $client-\u003erevokeToken($_SESSION['vtbid_access_token']);\n    } catch (VTBIDException $e) {\n        error_log('Failed to revoke token: ' . $e-\u003egetMessage());\n    }\n}\n\n// Clear session\nsession_destroy();\n\nheader('Location: /login.php');\nexit;\n```\n\n**Token Refresh:**\n```php\n\u003c?php\nrequire_once 'vendor/autoload.php';\n\nuse Tigusigalpa\\VTBID\\VTBIDClient;\nuse Tigusigalpa\\VTBID\\Exceptions\\VTBIDException;\n\nsession_start();\n\n$config = require 'config.php';\n$client = new VTBIDClient($config);\n\nif (isset($_SESSION['vtbid_refresh_token'])) {\n    try {\n        $newTokenData = $client-\u003erefreshAccessToken($_SESSION['vtbid_refresh_token']);\n        \n        $_SESSION['vtbid_access_token'] = $newTokenData['access_token'];\n        $_SESSION['vtbid_refresh_token'] = $newTokenData['refresh_token'] ?? $_SESSION['vtbid_refresh_token'];\n        \n        echo \"Token refreshed successfully!\";\n    } catch (VTBIDException $e) {\n        echo \"Token refresh error: \" . $e-\u003egetMessage();\n        header('Location: /login.php');\n        exit;\n    }\n}\n```\n\n**Database Integration:**\n```php\n\u003c?php\n// callback.php with database storage\nrequire_once 'vendor/autoload.php';\n\nuse Tigusigalpa\\VTBID\\VTBIDClient;\nuse Tigusigalpa\\VTBID\\Exceptions\\VTBIDException;\n\nsession_start();\n\n$config = require 'config.php';\n$client = new VTBIDClient($config);\n\n$code = $_GET['code'] ?? null;\n$state = $_GET['state'] ?? null;\n\nif (!$code || !isset($_SESSION['vtbid_state']) || $_SESSION['vtbid_state'] !== $state) {\n    die('Invalid request');\n}\n\nunset($_SESSION['vtbid_state']);\n\ntry {\n    $tokenData = $client-\u003egetAccessToken($code);\n    $userData = $client-\u003egetUserData($tokenData['access_token']);\n    \n    // Database connection\n    $pdo = new PDO('mysql:host=localhost;dbname=myapp', 'username', 'password');\n    \n    // Find or create user\n    $stmt = $pdo-\u003eprepare(\"\n        INSERT INTO users (vtb_id, name, surname, patronymic, email, phone, created_at, updated_at)\n        VALUES (:vtb_id, :name, :surname, :patronymic, :email, :phone, NOW(), NOW())\n        ON DUPLICATE KEY UPDATE\n            name = :name,\n            surname = :surname,\n            patronymic = :patronymic,\n            email = :email,\n            phone = :phone,\n            updated_at = NOW()\n    \");\n    \n    $stmt-\u003eexecute([\n        'vtb_id' =\u003e $userData-\u003esub,\n        'name' =\u003e $userData-\u003ename,\n        'surname' =\u003e $userData-\u003esurname,\n        'patronymic' =\u003e $userData-\u003epatronymic,\n        'email' =\u003e $userData-\u003eemail,\n        'phone' =\u003e $userData-\u003emainMobilePhone,\n    ]);\n    \n    $userId = $pdo-\u003elastInsertId() ?: $pdo-\u003equery(\"SELECT id FROM users WHERE vtb_id = '{$userData-\u003esub}'\")-\u003efetchColumn();\n    \n    // Save to session\n    $_SESSION['user_id'] = $userId;\n    $_SESSION['vtbid_access_token'] = $tokenData['access_token'];\n    $_SESSION['vtbid_user'] = $userData-\u003etoArray();\n    \n    header('Location: /dashboard.php');\n    exit;\n    \n} catch (VTBIDException $e) {\n    die('Authentication error: ' . $e-\u003egetMessage());\n} catch (PDOException $e) {\n    die('Database error: ' . $e-\u003egetMessage());\n}\n```\n\n### Custom Routes (Laravel)\n\nDisable built-in routes in `.env`:\n\n```env\nVTBID_ROUTES_ENABLED=false\n```\n\nCreate your own routes:\n\n```php\nuse Tigusigalpa\\VTBID\\VTBIDClient;\nuse Tigusigalpa\\VTBID\\Exceptions\\VTBIDException;\n\nRoute::get('/custom/vtb/login', function (VTBIDClient $vtbid) {\n    $state = $vtbid-\u003egenerateState();\n    session(['vtbid_state' =\u003e $state]);\n    return redirect($vtbid-\u003egetAuthorizationUrl($state));\n});\n\nRoute::get('/custom/vtb/callback', function (Request $request, VTBIDClient $vtbid) {\n    $code = $request-\u003einput('code');\n    $state = $request-\u003einput('state');\n    \n    if (!$code || session('vtbid_state') !== $state) {\n        return redirect()-\u003eroute('login')-\u003ewith('error', 'Invalid request');\n    }\n    \n    try {\n        $tokenData = $vtbid-\u003egetAccessToken($code);\n        $userData = $vtbid-\u003egetUserData($tokenData['access_token']);\n        \n        // Your logic\n        \n        return redirect()-\u003eroute('dashboard');\n    } catch (VTBIDException $e) {\n        return redirect()-\u003eroute('login')-\u003ewith('error', $e-\u003egetMessage());\n    }\n});\n```\n\n### Error Handling\n\nGlobal handler in `app/Exceptions/Handler.php`:\n\n```php\nuse Tigusigalpa\\VTBID\\Exceptions\\VTBIDException;\n\npublic function register()\n{\n    $this-\u003erenderable(function (VTBIDException $e, $request) {\n        \\Log::error('VTB ID Exception', [\n            'message' =\u003e $e-\u003egetMessage(),\n            'code' =\u003e $e-\u003egetCode(),\n        ]);\n\n        if ($request-\u003eexpectsJson()) {\n            return response()-\u003ejson([\n                'error' =\u003e 'vtb_id_error',\n                'message' =\u003e $e-\u003egetMessage(),\n            ], 400);\n        }\n\n        return redirect()-\u003eroute('login')\n            -\u003ewith('error', 'VTB ID Error: ' . $e-\u003egetMessage());\n    });\n}\n```\n\n### Auto-refresh Token\n\nMiddleware for automatic token refresh:\n\n```php\nnamespace App\\Http\\Middleware;\n\nuse Closure;\nuse Tigusigalpa\\VTBID\\Facades\\VTBID;\nuse Tigusigalpa\\VTBID\\Exceptions\\VTBIDException;\n\nclass RefreshVTBIDToken\n{\n    public function handle(Request $request, Closure $next)\n    {\n        if (!session()-\u003ehas('vtbid_access_token')) {\n            return redirect()-\u003eroute('vtbid.redirect');\n        }\n\n        $expiresAt = session('vtbid_token_expires_at');\n        \n        if ($expiresAt \u0026\u0026 now()-\u003eaddMinutes(5)-\u003egreaterThan($expiresAt)) {\n            $refreshToken = session('vtbid_refresh_token');\n            \n            if ($refreshToken) {\n                try {\n                    $newTokenData = VTBID::refreshAccessToken($refreshToken);\n                    \n                    session([\n                        'vtbid_access_token' =\u003e $newTokenData['access_token'],\n                        'vtbid_refresh_token' =\u003e $newTokenData['refresh_token'] ?? $refreshToken,\n                        'vtbid_token_expires_at' =\u003e now()-\u003eaddSeconds($newTokenData['expires_in'] ?? 3600),\n                    ]);\n                } catch (VTBIDException $e) {\n                    session()-\u003eforget(['vtbid_access_token', 'vtbid_refresh_token']);\n                    return redirect()-\u003eroute('vtbid.redirect');\n                }\n            }\n        }\n\n        return $next($request);\n    }\n}\n```\n\n## Security\n\n### Required Measures\n\n1. **HTTPS is mandatory** - VTB ID doesn't work with HTTP\n2. **Protect client_secret** - never commit to Git\n3. **Verify state** - CSRF protection (already implemented in package)\n4. **Validate redirect_uri** - must exactly match registered URI\n\n### Recommendations\n\n```php\n// Verify email_verified\nif (!$vtbUser['email_verified']) {\n    return redirect()-\u003eback()\n        -\u003ewith('warning', 'Email not verified');\n}\n\n// Log authentication attempts\n\\Log::info('VTB ID login attempt', [\n    'vtb_id' =\u003e $vtbUser['sub'],\n    'email' =\u003e $vtbUser['email'],\n]);\n```\n\n### Production Setup\n\n#### HTTPS Required\n\n```env\nAPP_URL=https://your-domain.com\nVTBID_REDIRECT_URI=https://your-domain.com/auth/vtb/callback\n```\n\n#### Session Security\n\nIn `config/session.php`:\n\n```php\n'secure' =\u003e env('SESSION_SECURE_COOKIE', true),\n'http_only' =\u003e true,\n'same_site' =\u003e 'lax',\n```\n\n#### Cache Configuration\n\n```bash\nphp artisan config:cache\nphp artisan route:cache\n```\n\n## Testing\n\n```bash\ncomposer test\n```\n\n### Verify Installation\n\n```bash\nphp artisan tinker\n```\n\n```php\n\u003e\u003e\u003e config('vtbid.client_id')\n=\u003e \"your_client_id\"\n\n\u003e\u003e\u003e app(Tigusigalpa\\VTBID\\VTBIDClient::class)\n=\u003e Tigusigalpa\\VTBID\\VTBIDClient {#...}\n\n\u003e\u003e\u003e route('vtbid.redirect')\n=\u003e \"http://your-domain.test/auth/vtb/redirect\"\n```\n\n### Check Routes\n\n```bash\nphp artisan route:list | grep vtb\n```\n\n## Troubleshooting\n\n### \"Class VTBIDServiceProvider not found\"\n\n```bash\ncomposer dump-autoload\nphp artisan config:clear\nphp artisan cache:clear\n```\n\n### \"Route [vtbid.success] not defined\"\n\nCreate `vtbid.success` route in `routes/web.php`\n\n### \"invalid_client\"\n\nCheck `VTBID_CLIENT_ID` and `VTBID_CLIENT_SECRET` in `.env`\n\n### \"redirect_uri_mismatch\"\n\nEnsure `VTBID_REDIRECT_URI` exactly matches registered URI in VTB ID\n\n## VTB ID Documentation\n\n- [Official Documentation](https://www.vtb.ru/malyj-biznes/vtb-id/integraciya/)\n- [User Data (PDF)](https://www.vtb.ru/media-files/vtb.ru/sitepages/malyj-biznes/vtb-id/integraciya/Dannye_polzovatelya.pdf)\n- [Error List (PDF)](https://www.vtb.ru/media-files/vtb.ru/sitepages/malyj-biznes/vtb-id/integraciya/Perechen_oshibok.pdf)\n\n## Endpoints\n\n- **Authorization URL**: `https://id.vtb.ru/`\n- **Token URL**: `https://id.vtb.ru/oauth2/token`\n- **User Data URL**: `https://gost-id.vtb.ru/oauth2/me`\n- **Revoke Token URL**: `https://id.vtb.ru/oauth2/revoke`\n\n## License\n\nMIT License. See [LICENSE.md](LICENSE.md) for details.\n\n## Author\n\n**Igor Sazonov**\n- Email: sovletig@gmail.com\n- GitHub: [@tigusigalpa](https://github.com/tigusigalpa)\n\n## Support\n\nIf you have any issues or questions, please create an [issue on GitHub](https://github.com/tigusigalpa/vtb-id-php/issues).\n\n---\n\n\u003cp align=\"center\"\u003eMade with ❤️ for Laravel community\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftigusigalpa%2Fvtb-id-php","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftigusigalpa%2Fvtb-id-php","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftigusigalpa%2Fvtb-id-php/lists"}