📖
دليل ربط PRMGTK API
دليل شامل لربط منصة PRMGTK مع تطبيقاتك — Laravel، Node.js، Python أو أي لغة تدعم HTTP. كل مطوّر لديه instance_id وaccess_token خاص به للمصادقة.
١نظرة عامة
PRMGTK يوفّر REST API بسيط لإرسال رسائل واتساب نيابةً عن أي حساب مرتبط. كل عميل له instance_id ثابت وaccess_token فريد.
BASE URL
https://yourdomain.comخطوات التهيئة
- 1أنشئ حسابك على المنصة أو سجّل من صفحة التسجيل
- 2اربط رقم واتساب بمسح QR من لوحة التحكم
- 3انسخ instance_id و access_token من صفحة حسابك
- 4ضع البيانات في .env الخاص بتطبيقك
- 5ابدأ الإرسال فوراً باستخدام الأمثلة أدناه
٢المصادقة
كل طلب API يحتاج معاملين يُرسلان في query string أو body:
| المعامل | مثال | مكان الإرسال |
|---|---|---|
| instance_id | 0001 | query أو body |
| access_token | prmgtk0001 | query أو body |
⚠️أمان: لا تكشف
access_token في كود الـ frontend أبداً. اجعل جميع الطلبات من الـ backend.٣الـ Endpoints
GET
/api/wa/statusحالة الجلسة والاشتراكCURL
curl "https://yourdomain.com/api/wa/status?instance_id=0001&access_token=prmgtk0001"الرد عند النجاح
JSON
{
"ok": true,
"status": "connected",
"phone": "966501234567",
"subscription": {
"start": "2026-01-01T00:00:00.000Z",
"end": "2026-02-01T00:00:00.000Z",
"days_remaining": 25
}
}GET
/api/wa/qrcodeجلب كود QR لربط واتسابيرجع كود QR بصيغة base64 PNG. يفشل لو كانت الجلسة متصلة بالفعل.
CURL
curl "https://yourdomain.com/api/wa/qrcode?instance_id=0001&access_token=prmgtk0001"200QR جاهز
202lم يُولّد بعد — أعد بعد 3 ثوانٍ
409متصل بالفعل
JSON
{ "ok": true, "qr_base64": "data:image/png;base64,iVBORw0KGgo..." }POST
/api/wa/sendإرسال رسالة نصيةالرقم بصيغة دولية بدون + أو 00 (مثل: 966501234567)
| المعامل | مثال | وصف |
|---|---|---|
| instance_id | 0001 | مُعرّف الحساب |
| access_token | prmgtk0001 | رمز الوصول |
| number | 966501234567 | رقم المستلم |
| message | مرحباً | نص الرسالة |
CURL
curl -X POST "https://yourdomain.com/api/wa/send" \
-d "instance_id=0001" \
-d "access_token=prmgtk0001" \
-d "number=966501234567" \
-d "message=مرحباً من تطبيقي 👋"الرد عند النجاح
JSON
{
"ok": true,
"message_id": "3EB0F5F0A8F5B26402B11D",
"timestamp": 1700000000
}٤كلاس Service جاهز — Laravel
أأضف في .env
.env
WA_GATEWAY_URL=https://yourdomain.com
WA_GATEWAY_INSTANCE_ID=0001
WA_GATEWAY_ACCESS_TOKEN=prmgtk0001بأضف في config/services.php
PHP
'wa_gateway' => [
'base_url' => env('WA_GATEWAY_URL', 'https://yourdomain.com'),
'instance_id' => env('WA_GATEWAY_INSTANCE_ID'),
'access_token' => env('WA_GATEWAY_ACCESS_TOKEN'),
],جأنشئ app/Services/WaGateway.php
PHP
<?php
namespace App\Services;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
class WaGateway
{
protected string $baseUrl;
protected string $instanceId;
protected string $accessToken;
public function __construct(?string $instanceId = null, ?string $accessToken = null)
{
$this->baseUrl = rtrim(config('services.wa_gateway.base_url'), '/');
$this->instanceId = $instanceId ?? config('services.wa_gateway.instance_id');
$this->accessToken = $accessToken ?? config('services.wa_gateway.access_token');
}
/** حالة الجلسة */
public function status(): array
{
return $this->get('status');
}
/** جلب QR لربط جلسة جديدة */
public function qrcode(): array
{
return $this->get('qrcode');
}
/** إرسال رسالة نصية */
public function send(string $number, string $message): array
{
return $this->post('send', [
'number' => $this->cleanNumber($number),
'message' => $message,
]);
}
/** تنظيف الرقم: إزالة + و 00 والمسافات */
protected function cleanNumber(string $n): string
{
$d = preg_replace('/\D+/', '', $n);
if (str_starts_with($d, '00')) $d = substr($d, 2);
return $d;
}
protected function get(string $endpoint): array
{
try {
$r = Http::timeout(20)->acceptJson()
->get("{$this->baseUrl}/api/wa/{$endpoint}", $this->credentials());
return $r->json() ?? ['ok' => false, 'error' => 'Empty response'];
} catch (\Throwable $e) {
Log::error('WaGateway GET failed', ['endpoint' => $endpoint, 'msg' => $e->getMessage()]);
return ['ok' => false, 'code' => 'network_error', 'error' => $e->getMessage()];
}
}
protected function post(string $endpoint, array $data): array
{
try {
$r = Http::timeout(30)->acceptJson()->asForm()
->post("{$this->baseUrl}/api/wa/{$endpoint}", array_merge($this->credentials(), $data));
return $r->json() ?? ['ok' => false, 'error' => 'Empty response'];
} catch (\Throwable $e) {
Log::error('WaGateway POST failed', ['endpoint' => $endpoint, 'msg' => $e->getMessage()]);
return ['ok' => false, 'code' => 'network_error', 'error' => $e->getMessage()];
}
}
protected function credentials(): array
{
return [
'instance_id' => $this->instanceId,
'access_token' => $this->accessToken,
];
}
}٥أمثلة استخدام
إرسال رسالة بسيطة
PHP
use App\Services\WaGateway;
$wa = new WaGateway();
$res = $wa->send('966501234567', 'مرحباً من Laravel 👋');
if ($res['ok']) {
return "تم — message_id: {$res['message_id']}";
}
return "فشل: {$res['error']}";التحقق من حالة الاتصال قبل الإرسال
PHP
$wa = new WaGateway();
$status = $wa->status();
if (!$status['ok'] || $status['status'] !== 'connected') {
return back()->with('error', 'الجلسة غير متصلة، اربط واتساب أولاً');
}
$wa->send($patient->phone, "موعدك في {$appointment->date}");عميل مختلف لكل فرع / عيادة
PHP
// كل فرع لديه instance_id و access_token خاص به محفوظين في DB
$wa = new WaGateway($branch->wa_instance_id, $branch->wa_access_token);
$wa->send($customer->phone, $message);إرسال جماعي (Broadcast)
PHP
foreach ($patients as $p) {
$res = $wa->send($p->phone, "تذكير: موعدك غداً الساعة {$p->appt_time}");
if (!$res['ok']) {
Log::warning("فشل إرسال لـ {$p->phone}: {$res['error']}");
}
usleep(700_000); // 0.7 ثانية بين كل رسالة — لتفادي الحظر
}⚠️ضع تأخيراً لا يقل عن 500ms بين كل رسالتين عند الإرسال الجماعي. الإرسال السريع جداً قد يؤدي لحظر الرقم.
Node.js / JavaScript
JavaScript
async function sendWhatsApp(number, message) {
const res = await fetch('https://yourdomain.com/api/wa/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
instance_id: process.env.WA_INSTANCE_ID,
access_token: process.env.WA_ACCESS_TOKEN,
number,
message,
}),
});
return res.json();
}
// الاستخدام
const result = await sendWhatsApp('966501234567', 'مرحباً 👋');
if (result.ok) console.log('تم:', result.message_id);٦صفحة الربط الجاهزة (HTML)
قالب HTML كامل (HTML + CSS + JS في ملف واحد) يعرض QR ويراقب الاتصال تلقائياً. ضعه في موقعك واستبدل INSTANCE_ID_HERE وACCESS_TOKEN_HERE.
ℹ️القالب يستدعي
/api/wa/qrcode كل 3 ثوانٍ تلقائياً ويعرض شاشة "تم الربط" عند الاتصال.HTML
<!DOCTYPE html>
<html lang="ar" dir="rtl">
<head>
<meta charset="UTF-8">
<title>اربط واتسابك</title>
<link href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;600;700;800&display=swap" rel="stylesheet">
<style>
*{box-sizing:border-box;margin:0;padding:0}
body{font-family:'Cairo',sans-serif;background:linear-gradient(135deg,#f0fdf4,#ecfdf5);
min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px;color:#1f2937}
.box{background:#fff;border-radius:20px;box-shadow:0 20px 50px rgba(0,0,0,.1);
padding:36px;max-width:440px;width:100%;text-align:center}
h1{font-size:20px;font-weight:800;margin-bottom:6px}
.sub{color:#6b7280;font-size:13px;margin-bottom:24px}
.stage{min-height:300px;display:flex;flex-direction:column;align-items:center;justify-content:center}
.spinner{width:40px;height:40px;border:3px solid #e5e7eb;border-top-color:#16a34a;
border-radius:50%;animation:spin 1s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}
.qr-box{padding:12px;background:#fff;border:1px solid #e5e7eb;border-radius:12px;
box-shadow:0 4px 16px rgba(0,0,0,.06)}
.qr-box img{width:220px;height:220px;display:block}
.steps{text-align:right;margin:14px 0;padding:14px;background:#f5f3ff;border-radius:10px;
font-size:13px;line-height:2;color:#374151;list-style:none;counter-reset:s}
.steps li::before{content:counter(s)'. ';counter-increment:s;color:#6366f1;font-weight:700}
.badge{display:inline-flex;align-items:center;gap:6px;padding:4px 14px;border-radius:999px;
font-size:12px;font-weight:700;margin-bottom:10px}
.badge.wait{background:#fef3c7;color:#d97706}
.success{color:#16a34a}
.success svg{width:56px;height:56px;margin-bottom:10px}
.phone{font-size:22px;font-weight:800;direction:ltr;margin-top:6px}
.err{color:#dc2626;padding:12px;background:#fef2f2;border-radius:10px;font-size:13px}
</style>
</head>
<body>
<div class="box">
<div style="font-size:36px;margin-bottom:10px">📱</div>
<h1>اربط حساب واتساب</h1>
<p class="sub">امسح كود QR من هاتفك لتفعيل الإرسال الآلي</p>
<div id="stage" class="stage">
<div class="spinner"></div>
<p style="margin-top:12px;color:#6b7280;font-size:13px">جاري التحميل...</p>
</div>
</div>
<script>
(function(){
var API = "https://yourdomain.com";
var IID = "INSTANCE_ID_HERE";
var TOKEN = "ACCESS_TOKEN_HERE";
var el = document.getElementById('stage');
var lastQr = null;
function showQR(qr){
if(qr===lastQr) return; lastQr=qr;
el.innerHTML=
'<div class="badge wait">⏳ في انتظار المسح</div>'+
'<ol class="steps">'+
'<li>افتح <strong>واتساب</strong> على هاتفك</li>'+
'<li>اذهب لـ الإعدادات ← <strong>الأجهزة المرتبطة</strong></li>'+
'<li>اضغط <strong>ربط جهاز</strong> ووجّه الكاميرا للكود</li>'+
'</ol>'+
'<div class="qr-box"><img src="'+qr+'" alt="QR"></div>';
}
function showOK(phone){
el.innerHTML=
'<div class="success">'+
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">'+
'<path d="M22 11.08V12a10 10 0 11-5.93-9.14"/><polyline points="22 4 12 14.01 9 11.01"/>'+
'</svg>'+
'<h2 style="font-size:17px;margin-bottom:4px">تم الربط بنجاح!</h2>'+
(phone?'<div class="phone">+'+phone+'</div>':'')+
'<p style="margin-top:8px;color:#6b7280;font-size:13px">حسابك جاهز لاستقبال طلبات الإرسال.</p>'+
'</div>';
}
function showErr(msg){el.innerHTML='<div class="err">⚠ '+msg+'</div>';}
function poll(){
fetch(API+'/api/wa/qrcode?instance_id='+IID+'&access_token='+TOKEN,{cache:'no-store'})
.then(function(r){return r.json().then(function(j){return{s:r.status,b:j};});})
.then(function(res){
if(res.b.ok && res.b.qr_base64){ showQR(res.b.qr_base64); setTimeout(poll,3000); }
else if(res.s===409){
fetch(API+'/api/wa/status?instance_id='+IID+'&access_token='+TOKEN)
.then(function(r){return r.json();}).then(function(s){showOK(s.phone);});
}
else if(res.s===202){ setTimeout(poll,3000); }
else if(res.s===401||res.s===403){ showErr(res.b.error||'بيانات اعتماد غير صحيحة'); }
else{ showErr(res.b.error||'خطأ غير متوقع'); setTimeout(poll,5000); }
})
.catch(function(){ showErr('فشل الاتصال بالخادم'); setTimeout(poll,5000); });
}
poll();
})();
</script>
</body>
</html>٧أكواد الأخطاء
| HTTP | code | المعنى |
|---|---|---|
| 200 | — | نجاح |
| 202 | qr_not_ready | كود QR لم يُولّد بعد — أعد المحاولة بعد 3 ثوانٍ |
| 400 | invalid_input | المعاملات ناقصة أو غير صالحة |
| 400 | invalid_number | الرقم قصير أو طويل جداً |
| 400 | number_not_registered | الرقم غير مسجّل في WhatsApp |
| 400 | session_not_connected | الجلسة ليست متصلة — اربط QR أولاً |
| 400 | timeout | الإرسال استغرق وقتاً طويلاً |
| 401 | missing_credentials | instance_id أو access_token مفقود |
| 401 | invalid_credentials | بيانات الاعتماد غير صحيحة |
| 403 | subscription_expired | انتهى اشتراك العميل |
| 409 | already_connected | الجلسة متصلة بالفعل (عند طلب QR) |
٨أخطاء شائعة وحلولها
⚠ الرسالة لا تُرسل ولا يظهر خطأ
السبب: الرقم بصيغة خاطئة (يبدأ بـ + أو 00 أو فيه مسافات). الحل: مرّر رقماً بصيغة دولية كاملة مثل 966501234567. كلاس الـ Service ينظّف الرقم تلقائياً.
⚠ session_not_connected
السبب: العميل لم يمسح QR أو فُصلت الجلسة. الحل: ادخل لوحة التحكم → افتح صفحة العميل → امسح QR من جديد.
⚠ number_not_registered
السبب: الرقم غير مسجّل في WhatsApp. الحل: تأكد من صحة الرقم وكود الدولة. الـ API يفحص أولاً قبل الإرسال.
⚠ subscription_expired
السبب: انتهت مدة اشتراك هذا العميل. الحل: جدّد الاشتراك من لوحة التحكم.
⚠ أول رسالة لرقم جديد بطيئة (5–15 ثانية)
طبيعي. النظام يبني مفاتيح تشفير مع المستلم لأول مرة. الرسائل التالية لنفس الرقم فورية. زد timeout في Http إلى 30 ثانية على الأقل.
⚠ الإرسال الجماعي يتوقف أو يُحظر
ضع تأخيراً لا يقل عن 500ms بين كل رسالتين. تجنّب إرسال نفس النص حرفياً لجميع المستلمين — نوّع قليلاً باسم الشخص أو التاريخ.