tuantrinitri / zalo-laravel
Complete Laravel package for Zalo integration including OAuth authentication and ZNS (Zalo Notification Service) with automatic token management and queue support
Installs: 8
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/tuantrinitri/zalo-laravel
Requires
- php: ^7.4|^8.0
- guzzlehttp/guzzle: ^6.0|^7.0
- laravel/framework: ^8.0|^9.0|^10.0|^11.0
- zaloplatform/zalo-php-sdk: ^4.0
Requires (Dev)
- orchestra/testbench: ^6.0|^7.0|^8.0|^9.0
- phpunit/phpunit: ^9.5|^10.0
This package is auto-updated.
Last update: 2025-12-05 16:10:15 UTC
README
Package Laravel chuyên nghiệp cho tích hợp Zalo, cung cấp OAuth Authentication và ZNS (Zalo Notification Service) với quản lý token tự động, hỗ trợ queue background và tích hợp seamless với Laravel ecosystem.
📋 Mục lục
- 🚀 Tính năng
- 📋 Yêu cầu hệ thống
- ⚡ Cài đặt
- ⚙️ Cấu hình
- 🔐 OAuth Authentication
- 📱 ZNS Notification Service
- 🔑 Quản lý Token
- 🎯 Helper Functions
- 🚀 Advanced Usage
- ✨ Best Practices
- 🔧 Troubleshooting
- 🤝 Contributing
- 📄 License
🚀 Tính năng
🔐 Zalo OAuth Authentication
- ✅ OAuth 2.0 Flow hoàn chỉnh với PKCE support
- ✅ Lấy thông tin user từ Zalo profile
- ✅ Session management tự động
- ✅ Laravel-style configuration quen thuộc
- ✅ ZaloProviderUser class để xử lý user data
📱 Zalo Notification Service (ZNS)
- ✅ Gửi notification template theo chuẩn Zalo
- ✅ Auto token refresh không cần can thiệp thủ công
- ✅ Queue support cho background processing
- ✅ Bulk messaging gửi hàng loạt
- ✅ Database token storage an toàn
- ✅ Retry mechanisms và error handling thông minh
🛠️ Laravel Integration
- ✅ Auto-discovery service provider và facades
- ✅ Facade patterns dễ sử dụng (
Zalo,ZaloZns) - ✅ Helper functions tiện lợi
- ✅ Artisan commands quản lý token
- ✅ Migration files cho database
- ✅ Event system comprehensive logging
⚡ Performance & Security
- ✅ Smart caching token và template
- ✅ Phone number validation chuẩn Việt Nam
- ✅ Rate limiting tránh spam
- ✅ Error logging chi tiết cho debugging
- ✅ Health checks monitoring system
📋 Yêu cầu hệ thống
- PHP: 7.4+ (khuyến nghị 8.1+)
- Laravel: 8.0+ (hỗ trợ đến 11.x)
- Database: MySQL 5.7+, PostgreSQL 10+, hoặc SQLite 3.8+
- Queue Driver: Redis (khuyến nghị), Database, hoặc SQS
- Extensions:
ext-curl,ext-json,ext-mbstring
Tài khoản Zalo cần thiết:
- Zalo Developer Account với App ID/Secret
- Zalo Official Account (cho ZNS) với OA ID/Secret
- ZNS Service được kích hoạt và có template
⚡ Cài đặt
Bước 1: Install Package
composer require tuantrinitri/zalo-laravel-integrated
Bước 2: Laravel Auto-Discovery
Package sẽ tự động đăng ký. Với Laravel < 5.5, thêm vào config/app.php:
'providers' => [ // ... Tuantrinitri\\ZaloLaravel\\Providers\\ZaloServiceProvider::class, ], 'aliases' => [ // ... 'Zalo' => Tuantrinitri\\ZaloLaravel\\Facades\\Zalo::class, 'ZaloZns' => Tuantrinitri\\ZaloLaravel\\Facades\\ZaloZns::class, ],
Bước 3: Publish Files
# Publish configuration file php artisan vendor:publish --tag="zalo-config" # Publish và chạy migrations php artisan vendor:publish --tag="zalo-migrations" php artisan migrate
⚙️ Cấu hình
Environment Variables
Thêm vào file .env:
# === ZALO OAUTH CONFIGURATION === ZALO_APP_ID=1234567890123456789 ZALO_APP_SECRET=abcdef1234567890abcdef1234567890 ZALO_REDIRECT_URL=https://yourdomain.com/auth/zalo/callback # === ZALO OFFICIAL ACCOUNT (OPTIONAL) === ZALO_OA_ID=9876543210987654321 ZALO_OA_SECRET=fedcba0987654321fedcba0987654321 # === ZALO ZNS CONFIGURATION === ZALO_ZNS_APP_ID=1111222233334444 ZALO_ZNS_SECRET_KEY=xyzkeyabcd1234567890 ZALO_ZNS_BASE_URL=https://business.openapi.zalo.me ZALO_ZNS_TIMEOUT=30 # === QUEUE CONFIGURATION === ZALO_ZNS_QUEUE=zalo-notifications ZALO_ZNS_MAX_TRIES=3 QUEUE_CONNECTION=redis
Configuration File
File config/zalo.php được tạo với cấu trúc:
return [ 'oauth' => [ 'app_id' => env('ZALO_APP_ID'), 'app_secret' => env('ZALO_APP_SECRET'), 'url_callback' => env('ZALO_REDIRECT_URL') ], 'oa' => [ 'id' => env('ZALO_OA_ID'), 'secret' => env('ZALO_OA_SECRET'), ], 'zns' => [ 'app_id' => env('ZALO_ZNS_APP_ID'), 'secret_key' => env('ZALO_ZNS_SECRET_KEY'), 'base_url' => env('ZALO_ZNS_BASE_URL', 'https://business.openapi.zalo.me'), 'timeout' => env('ZALO_ZNS_TIMEOUT', 30), 'queue_name' => env('ZALO_ZNS_QUEUE', 'default'), 'max_tries' => env('ZALO_ZNS_MAX_TRIES', 3), // Template IDs 'templates' => [ 'order_confirmation' => 'your_order_template_id', 'appointment_reminder' => 'your_appointment_template_id', 'payment_confirmation' => 'your_payment_template_id', ], ], ];
Queue Setup (Khuyến nghị)
# Cài đặt Redis composer require predis/predis # Cấu hình trong config/queue.php 'connections' => [ 'redis' => [ 'driver' => 'redis', 'connection' => 'default', 'queue' => env('REDIS_QUEUE', 'default'), 'retry_after' => 90, 'block_for' => null, ], ],
🔐 OAuth Authentication
Basic Login Flow
Tạo Routes
// routes/web.php use Illuminate\Http\Request; use Tuantrinitri\ZaloLaravel\Facades\Zalo; // Redirect đến Zalo login Route::get('/auth/zalo', function () { try { $loginUrl = Zalo::loginZalo(); return redirect($loginUrl); } catch (Exception $e) { return redirect('/login') ->with('error', 'Không thể kết nối đến Zalo. Vui lòng thử lại.'); } })->name('auth.zalo'); // Xử lý callback từ Zalo Route::get('/auth/zalo/callback', function (Request $request) { try { $zaloUser = Zalo::getInfoUserThenLogin(); if (empty($zaloUser) || !isset($zaloUser['id'])) { throw new Exception('Không lấy được thông tin người dùng từ Zalo'); } // Tìm hoặc tạo user trong database $user = App\Models\User::updateOrCreate( ['zalo_id' => $zaloUser['id']], [ 'name' => $zaloUser['name'] ?? 'Zalo User', 'email' => $zaloUser['id'] . '@zalo.local', 'avatar' => $zaloUser['picture']['data']['url'] ?? null, 'zalo_profile' => json_encode($zaloUser), ] ); // Đăng nhập user Auth::login($user, true); return redirect() ->intended('/dashboard') ->with('success', 'Đăng nhập thành công với Zalo!'); } catch (Exception $e) { Log::error('Zalo OAuth Error: ' . $e->getMessage()); return redirect('/login') ->with('error', 'Đăng nhập thất bại. Vui lòng thử lại.'); } })->name('auth.zalo.callback');
Update User Model
Thêm migration cho Zalo fields:
// Migration: xxxx_add_zalo_fields_to_users_table.php Schema::table('users', function (Blueprint $table) { $table->string('zalo_id')->nullable()->unique(); $table->string('avatar')->nullable(); $table->json('zalo_profile')->nullable(); });
Update User model:
// app/Models/User.php protected $fillable = [ 'name', 'email', 'password', 'zalo_id', 'avatar', 'zalo_profile' ]; protected $casts = [ 'zalo_profile' => 'array', ]; public function hasZaloAccount(): bool { return !is_null($this->zalo_id); }
Login Button UI
<!-- resources/views/auth/login.blade.php --> <div class="social-login"> <a href="{{ route('auth.zalo') }}" class="btn btn-zalo"> <img src="/images/zalo-icon.png" alt="Zalo" width="20"> Đăng nhập bằng Zalo </a> </div> <style> .btn-zalo { background: #0068ff; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-flex; align-items: center; gap: 8px; } .btn-zalo:hover { background: #0056cc; } </style>
Advanced OAuth Usage
Custom Parameters
use Tuantrinitri\ZaloLaravel\Facades\Zalo; // Generate login URL with custom parameters $metadata = Zalo::getLoginUrl( 'https://yourdomain.com/custom-callback', // Custom callback URL 'custom-state-data', // Custom state 'custom-verifier' // Custom PKCE verifier ); $loginUrl = $metadata['url']; $state = $metadata['state']; $codeVerifier = $metadata['code_verifier']; // Store trong session để sử dụng sau session([ 'zalo_verifier' => $codeVerifier, 'zalo_state' => $state ]);
Get User Info từ Access Token
// Nếu đã có access token $accessToken = 'your_access_token'; $userProfile = Zalo::getInfoUserFromAccessToken($accessToken); // Sử dụng helper tạo user object $user = zalo_provider_user($userProfile); echo $user->getId(); echo $user->getName(); echo $user->getEmail();
📱 ZNS Notification Service
Setup Token Ban Đầu
Trước khi sử dụng ZNS, cần setup token:
# Tạo artisan command để setup token
php artisan make:command SetupZnsToken
// app/Console/Commands/SetupZnsToken.php <?php namespace App\Console\Commands; use Illuminate\Console\Command; use Tuantrinitri\ZaloLaravel\Facades\ZaloZns; class SetupZnsToken extends Command { protected $signature = 'zns:setup-token {access_token} {refresh_token} {--expires=3600}'; protected $description = 'Setup initial ZNS tokens'; public function handle() { $accessToken = $this->argument('access_token'); $refreshToken = $this->argument('refresh_token'); $expiresIn = $this->option('expires'); try { $token = ZaloZns::storeTokens($accessToken, $refreshToken, $expiresIn); $this->info("ZNS tokens stored successfully!"); $this->info("Expires at: " . $token->expires_at); } catch (Exception $e) { $this->error("Failed to store tokens: " . $e->getMessage()); } } }
Chạy command:
php artisan zns:setup-token "your_access_token" "your_refresh_token" --expires=86400
Basic ZNS Usage
Gửi Notification Ngay Lập Tức
use Tuantrinitri\ZaloLaravel\Facades\ZaloZns; // Gửi immediately (synchronous) $success = ZaloZns::sendNow( '0123456789', // Phone number [ 'customer_name' => 'Nguyễn Văn A', 'order_code' => 'ORDER123', 'amount' => '500,000 VND', 'order_date' => '05/10/2024 14:30' ], 'your_template_id' // Template ID từ Zalo ); if ($success) { echo "Thông báo đã được gửi thành công!"; } else { echo "Gửi thông báo thất bại!"; }
Queue Notification (Khuyến nghị)
// Queue để gửi background (asynchronous) ZaloZns::sendLater( '0123456789', [ 'customer_name' => 'Nguyễn Văn A', 'appointment_time' => '14:30 25/10/2024', 'service_name' => 'Cắt tóc + Gội đầu' ], 'appointment_template_id', 60 // Delay 60 giây ); // Hoặc gửi ngay trong queue ZaloZns::sendLater('0123456789', $data, 'template_id'); // No delay
Bulk Messaging
$recipients = [ [ 'phone' => '0123456789', 'data' => [ 'name' => 'Khách hàng 1', 'code' => 'ORDER001', 'amount' => '300,000 VND' ] ], [ 'phone' => '0987654321', 'data' => [ 'name' => 'Khách hàng 2', 'code' => 'ORDER002', 'amount' => '500,000 VND' ] ], // ... thêm nhiều recipients ]; // Gửi bulk với delay 30 giây ZaloZns::sendBulk($recipients, 'order_template_id', 30);
Real-world Examples
Order Confirmation Service
// app/Services/NotificationService.php <?php namespace App\Services; use Tuantrinitri\ZaloLaravel\Facades\ZaloZns; use Illuminate\Support\Facades\Log; class NotificationService { public function sendOrderConfirmation($order) { try { $customer = $order->customer; if (!$this->isValidVietnamesePhone($customer->phone)) { Log::warning("Invalid phone number for customer: " . $customer->id); return false; } $data = [ 'customer_name' => $customer->name, 'order_code' => $order->code, 'total_amount' => number_format($order->total) . ' VND', 'order_date' => $order->created_at->format('d/m/Y H:i'), 'items_count' => $order->items->count(), ]; // Queue để gửi sau 30 giây ZaloZns::sendLater( $customer->phone, $data, config('zalo.zns.templates.order_confirmation'), 30 ); Log::info("Order confirmation queued for: " . $customer->phone); return true; } catch (Exception $e) { Log::error("Failed to queue order confirmation: " . $e->getMessage()); return false; } } public function sendAppointmentReminder($appointment) { try { $customer = $appointment->customer; $data = [ 'customer_name' => $customer->name, 'service_name' => $appointment->service->name, 'appointment_time' => $appointment->start_time->format('H:i d/m/Y'), 'location' => $appointment->location, 'staff_name' => $appointment->staff->name ?? 'Nhân viên', ]; // Gửi trước giờ hẹn 2 tiếng $sendTime = $appointment->start_time->subHours(2); $delaySeconds = now()->diffInSeconds($sendTime, false); if ($delaySeconds > 0) { ZaloZns::sendLater( $customer->phone, $data, config('zalo.zns.templates.appointment_reminder'), $delaySeconds ); Log::info("Appointment reminder scheduled for: " . $sendTime); return true; } } catch (Exception $e) { Log::error("Failed to schedule appointment reminder: " . $e->getMessage()); return false; } } private function isValidVietnamesePhone($phone): bool { $phone = preg_replace('/[^\d]/', '', $phone); return preg_match('/^(0|84)(3[2-9]|5[689]|7[06-9]|8[1-689]|9[0-46-9])[0-9]{7}$/', $phone); } }
Tích hợp trong Controller
// app/Http/Controllers/OrderController.php <?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Services\NotificationService; use Illuminate\Http\Request; class OrderController extends Controller { protected $notificationService; public function __construct(NotificationService $notificationService) { $this->notificationService = $notificationService; } public function store(Request $request) { $validated = $request->validate([ 'customer_id' => 'required|exists:customers,id', 'items' => 'required|array', 'items.*.product_id' => 'required|exists:products,id', 'items.*.quantity' => 'required|integer|min:1', ]); $order = Order::create([ 'customer_id' => $validated['customer_id'], 'code' => 'ORD' . time(), 'total' => $this->calculateTotal($validated['items']), 'status' => 'pending', ]); // Tạo order items foreach ($validated['items'] as $item) { $order->items()->create($item); } // Gửi notification xác nhận đơn hàng $this->notificationService->sendOrderConfirmation($order); return response()->json([ 'message' => 'Đơn hàng đã được tạo thành công', 'order' => $order->load(['customer', 'items']) ], 201); } }
🔑 Quản lý Token
Automatic Token Refresh
Package tự động schedule refresh token hàng ngày:
// Trong ZaloServiceProvider.php - đã được setup $schedule->command('zns:refresh-token')->dailyAt('02:00');
Manual Token Management
# Refresh token thủ công php artisan zns:refresh-token # Kiểm tra token hiện tại php artisan tinker >>> ZaloZns::getCurrentToken() >>> ZaloZns::hasValidToken()
Token Health Check
// Tạo health check endpoint Route::get('/health/zns', function () { $status = [ 'has_valid_token' => ZaloZns::hasValidToken(), 'current_token' => ZaloZns::getCurrentToken(), 'queue_size' => Queue::size('zalo-notifications'), 'last_refresh' => ZaloZns::getCurrentToken()?->updated_at, ]; return response()->json($status); })->middleware('auth:api');
Custom Token Refresh Logic
// app/Console/Commands/MonitorZnsToken.php <?php namespace App\Console\Commands; use Illuminate\Console\Command; use Tuantrinitri\ZaloLaravel\Facades\ZaloZns; use Illuminate\Support\Facades\Log; class MonitorZnsToken extends Command { protected $signature = 'zns:monitor-token'; protected $description = 'Monitor ZNS token and refresh if needed'; public function handle() { if (!ZaloZns::hasValidToken()) { $this->warn('ZNS token is expired or invalid'); $refreshed = ZaloZns::refreshToken(); if ($refreshed) { $this->info('ZNS token refreshed successfully'); Log::info('ZNS token auto-refreshed by monitor'); } else { $this->error('Failed to refresh ZNS token'); Log::critical('ZNS token refresh failed - manual intervention required'); // Gửi alert đến admin (email, Slack, etc.) // Mail::to(config('app.admin_email'))->send(new ZnsTokenFailedMail()); } } else { $token = ZaloZns::getCurrentToken(); $expiresAt = $token->expires_at; $this->info("ZNS token is valid until: {$expiresAt}"); } } }
Schedule monitor:
// app/Console/Kernel.php protected function schedule(Schedule $schedule) { // Kiểm tra token mỗi 30 phút $schedule->command('zns:monitor-token') ->everyThirtyMinutes() ->withoutOverlapping(); // Kiểm tra queue health mỗi 5 phút $schedule->call(function () { $queueSize = Queue::size('zalo-notifications'); if ($queueSize > 100) { Log::warning("ZNS queue backlog: $queueSize jobs"); } })->everyFiveMinutes(); }
🎯 Helper Functions
Package cung cấp nhiều helper functions tiện lợi:
OAuth Helpers
// Tạo user object từ Zalo profile $profile = ['id' => '123', 'name' => 'John Doe', /* ... */]; $user = zalo_provider_user($profile); echo $user->getId(); // '123' echo $user->getName(); // 'John Doe' echo $user->getEmail(); // null (Zalo không cung cấp email)
ZNS Helpers
// Gửi notification nhanh $success = zns_send('0123456789', [ 'name' => 'Nguyễn Văn A', 'code' => 'ORDER123' ], 'template_id'); // Queue notification zns_send_later('0123456789', [ 'name' => 'Nguyễn Văn A', 'time' => '14:30 25/10/2024' ], 'template_id', 120); // Delay 2 phút // Bulk send $recipients = [ ['phone' => '0123456789', 'data' => ['name' => 'Customer 1']], ['phone' => '0987654321', 'data' => ['name' => 'Customer 2']], ]; zns_bulk($recipients, 'template_id'); // Get ZNS service instance $znsService = zalo_zns(); $token = $znsService->getCurrentToken();
Specialized Helpers
// Order notification helper zns_order_notification($order, $customer, 'order_template', 60); // Appointment notification helper zns_appointment_notification($schedule, 'appointment_template', 30); // Universal ZNS helper zalo_zns('0123456789', ['name' => 'John'], 'template_id'); // Auto queue $service = zalo_zns(); // Get service instance
🚀 Advanced Usage
Custom Queue Configuration
// Sử dụng custom queue và connection use Tuantrinitri\ZaloLaravel\Jobs\SendZnsNotification; $job = new SendZnsNotification('0123456789', $data, 'template_id'); // Custom queue settings $job->onQueue('high-priority-zns'); $job->onConnection('redis-cluster'); $job->delay(now()->addMinutes(5)); // Dispatch job dispatch($job);
Laravel Event Integration
// In your Order model class Order extends Model { protected static function booted() { static::created(function ($order) { if ($order->status === 'paid') { zns_order_notification($order, $order->customer); } }); } } // In your Schedule model class Schedule extends Model { protected static function booted() { static::created(function ($schedule) { zns_appointment_notification($schedule); }); } }
Laravel Observers
class OrderObserver { public function updated(Order $order) { if ($order->wasChanged('status') && $order->status === 'completed') { $data = [ 'customer_name' => $order->customer->name, 'order_code' => $order->code, 'completion_date' => now()->format('d/m/Y H:i') ]; ZaloZns::sendLater( $order->customer->phone, $data, 'order_completion' ); } } }
Complete Configuration Options
// config/zalo-zns.php return [ // API URLs 'api_url' => env('ZALO_ZNS_API_URL', 'https://business.openapi.zalo.me/message/template'), 'oauth_url' => env('ZALO_ZNS_OAUTH_URL', 'https://oauth.zaloapp.com/v4/oa/access_token'), // Credentials 'app_id' => env('ZALO_ZNS_APP_ID'), 'secret_key' => env('ZALO_ZNS_SECRET_KEY'), // Templates 'default_template_id' => env('ZALO_ZNS_DEFAULT_TEMPLATE_ID'), 'templates' => [ 'order_confirmation' => env('ZALO_ZNS_TEMPLATE_ORDER'), 'appointment_reminder' => env('ZALO_ZNS_TEMPLATE_APPOINTMENT'), // Add more templates... ], // Queue Configuration 'queue_connection' => env('ZALO_ZNS_QUEUE_CONNECTION', 'default'), 'queue_name' => env('ZALO_ZNS_QUEUE_NAME', 'zalo-zns'), 'max_tries' => env('ZALO_ZNS_MAX_TRIES', 3), 'retry_delay' => env('ZALO_ZNS_RETRY_DELAY', 60), // Phone Formatting 'phone_country_code' => env('ZALO_ZNS_PHONE_COUNTRY_CODE', '84'), 'phone_prefix_remove' => env('ZALO_ZNS_PHONE_PREFIX_REMOVE', '0'), // Rate Limiting 'rate_limit_per_minute' => env('ZALO_ZNS_RATE_LIMIT', 100), 'default_delay_seconds' => env('ZALO_ZNS_DEFAULT_DELAY', 5), // Logging 'log_channel' => env('ZALO_ZNS_LOG_CHANNEL', 'stack'), 'log_success' => env('ZALO_ZNS_LOG_SUCCESS', true), 'log_errors' => env('ZALO_ZNS_LOG_ERRORS', true), ];
✨ Best Practices
1. Queue Configuration for Production
# Using Supervisor sudo apt install supervisor # Create config file: /etc/supervisor/conf.d/zalo-zns-worker.conf [program:zalo-zns-worker] process_name=%(program_name)s_%(process_num)02d command=php /path/to/your/app/artisan queue:work redis --sleep=3 --tries=3 --max-time=3600 --queue=zalo-zns autostart=true autorestart=true user=www-data numprocs=2 redirect_stderr=true stdout_logfile=/path/to/your/app/storage/logs/zns-worker.log # Start supervisor sudo supervisorctl reread sudo supervisorctl update sudo supervisorctl start zalo-zns-worker:*
2. Cron Jobs
# Add to crontab * * * * * cd /path/to/your/app && php artisan schedule:run >> /dev/null 2>&1
3. Environment Variables cho Production
QUEUE_CONNECTION=redis REDIS_HOST=127.0.0.1 REDIS_PASSWORD=null REDIS_PORT=6379 ZALO_ZNS_APP_ID=your_production_app_id ZALO_ZNS_SECRET_KEY=your_production_secret ZALO_ZNS_DEFAULT_TEMPLATE_ID=your_production_template
4. Error Handling
use Tuantrinitri\ZaloLaravel\Jobs\SendZnsNotification; class CustomZnsJob extends SendZnsNotification { public function failed(\Throwable $exception): void { parent::failed($exception); // Your custom error handling Mail::to('admin@yourapp.com')->send( new ZnsFailureNotification($this->phone, $exception) ); } }
5. Database Schema
ZNS Tokens Table
CREATE TABLE `zns_tokens` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `access_token` text NOT NULL COMMENT 'Zalo access token for API calls', `refresh_token` text NOT NULL COMMENT 'Refresh token to get new access token', `expires_at` timestamp NULL DEFAULT NULL COMMENT 'Token expiration time', `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `zns_tokens_expires_at_index` (`expires_at`) );
ZNS Logs Table (Optional)
CREATE TABLE `zns_logs` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `phone` varchar(20) NOT NULL COMMENT 'Phone number that received the message', `template_id` varchar(100) NOT NULL COMMENT 'Template ID used', `template_data` json NOT NULL COMMENT 'Data sent with template', `tracking_id` varchar(100) DEFAULT NULL COMMENT 'Tracking ID for the message', `message_id` varchar(100) DEFAULT NULL COMMENT 'Zalo message ID if successful', `status` tinyint NOT NULL DEFAULT '0' COMMENT '0: pending, 1: sent, 2: failed', `response_data` text COMMENT 'Full response from Zalo API', `error_message` text COMMENT 'Error message if failed', `sent_at` timestamp NULL DEFAULT NULL COMMENT 'When message was actually sent', `created_at` timestamp NULL DEFAULT NULL, `updated_at` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), KEY `zns_logs_phone_created_at_index` (`phone`,`created_at`), KEY `zns_logs_status_created_at_index` (`status`,`created_at`), KEY `zns_logs_template_id_index` (`template_id`) );
🔧 Troubleshooting
Common Issues
Token Issues
# Check current token status php artisan zns:refresh-token # Clear cache if needed php artisan config:clear php artisan cache:clear
Queue Not Processing
# Check queue status php artisan queue:work --once # Check failed jobs php artisan queue:failed # Retry failed jobs php artisan queue:retry all
Phone Number Format
The package automatically formats Vietnamese phone numbers:
0901234567→84901234567+84901234567→84901234567
Debug Mode
Enable detailed logging:
ZALO_ZNS_LOG_SUCCESS=true ZALO_ZNS_LOG_ERRORS=true LOG_LEVEL=debug
Artisan Commands
# Refresh token if expired php artisan zns:refresh-token # Force refresh even if valid php artisan zns:refresh-token --force
Test Connection
use Tuantrinitri\ZaloLaravel\Services\ZaloZnsService; $znsService = app(ZaloZnsService::class); $result = $znsService->testConnection(); if ($result['success']) { echo "✅ ZNS connection is working!"; } else { echo "❌ Error: " . $result['message']; }
Job Failures
The package automatically handles failed jobs and logs errors:
// Check logs tail -f storage/logs/laravel.log | grep ZNS // Failed jobs table php artisan queue:failed
🤝 Contributing
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
📄 License
This package is open-sourced software licensed under the MIT license.
Credits
- Developed by Tuantrinitri Development Team
- Based on Zalo ZNS API documentation
- Inspired by Laravel notification patterns
Changelog
v1.0.0
- Initial release
- Basic ZNS sending functionality
- Token management
- Queue support
- Artisan commands
- Comprehensive documentation