← حالة البنوك🤝 بوابة الشركاء

دليل المطورين

كل ما يحتاجه فريقكم التقني لربط خدماتكم بمنصة مراقبة الحالة. الربط لا يتطلب أي تغيير في جداركم الناري ولا أي صلاحية على أنظمتكم.

عنوان الـAPIhttps://greenpay.app/status/api/v1
الترميزJSON · UTF-8
المصادقةAuthorization: Bearer <key>

1. نموذج الأمان باختصار

أين يوضع المفتاح؟

2. النمط الأول: النبضة (Heartbeat)

سيرفركم ينادينا كل فترة متفق عليها. لو انقطعت النبضة لأكثر من interval_s + grace_s نعتبر الخدمة متوقفة. هذا يُسمى Dead Man's Switch: لا يحتاج منفذاً وارداً، ويكشف أيضاً انقطاع السيرفر كاملاً أو انقطاع الإنترنت عنه.

الطلب

POST https://greenpay.app/status/api/v1/heartbeat
Authorization: Bearer gp_live_sk_<id>_<secret>
Content-Type: application/json

{
  "service":    "mobile-app",   // إلزامي — معرّف الخدمة عندكم
  "status":     "up",           // up | degraded | down   (افتراضي up)
  "version":    "2.4.1",        // اختياري — إصدار النظام
  "latency_ms": 38,             // اختياري — زمن فحصكم الداخلي
  "note":       "cache warm"    // اختياري — 160 حرفاً كحد أقصى
}

الرد

200 OK
{
  "ok": true,
  "service": "mobile-app",
  "state": "up",
  "live": true,
  "next_expected_in_s": 180,
  "received_at": 1758268800
}

قواعد مهمة

3. النمط الثاني: رابط الصحة (Health endpoint)

إن فضّلتم أن نفحص نحن، انشروا رابطاً واحداً يرجّع الشكل التالي. نفحصه دورياً (كل 5 دقائق حالياً)، ولا نُعلن العطل إلا بعد فشل متكرر.

GET https://your-bank.sd/gp-health
X-GP-Probe-Token: <رمز نسلّمه لكم>     // اختياري — ارفضوا الطلبات بدونه

200 OK
{
  "status": "up",
  "ts": 1758268800,
  "version": "2.4.1",
  "components": [
    { "key": "core",      "status": "up",       "latency_ms": 38 },
    { "key": "transfers", "status": "degraded", "latency_ms": 940 },
    { "key": "ussd",      "status": "down" }
  ]
}

شروط الرابط

معدّل طلباتنا لا يتجاوز طلباً واحداً كل 60 ثانية لكل رابط (الدورة الحالية كل 5 دقائق)، ووكيل المستخدم دائماً:
GreenPayStatus/1.0 (+https://greenpay.app/status/bot)
وعناوين المصدر منشورة في https://greenpay.app/status/api/v1/probe-ips لإضافتها في قائمة السماح.

4. بقية النقاط

GET /ping — التأكد من المفتاح

curl https://greenpay.app/status/api/v1/ping -H "Authorization: Bearer $GP_STATUS_KEY"

{
  "ok": true,
  "org": { "slug": "example-bank", "name": "بنك المثال", "state": "approved" },
  "key": { "id": "a1b2c3d4e5f6", "env": "live", "scopes": ["heartbeat:write","status:read"] },
  "your_ip": "41.223.0.1"
}

GET /status — حالة خدماتكم عندنا

curl https://greenpay.app/status/api/v1/status -H "Authorization: Bearer $GP_STATUS_KEY"

GET /uptime — نسبة التشغيل

curl "https://greenpay.app/status/api/v1/uptime?service=mobile-app&days=7" -H "Authorization: Bearer $GP_STATUS_KEY"

5. الويبهوك — إشعار أنظمتكم لحظياً

بدل أن تسألونا، نحن ننادي عنوانكم. عند كل تغيّر حالة نرسل POST بجسم JSON موقّع، فتوصلونه بنظام المناوبة أو مجموعة العمل عندكم.

الهيدرات التي نرسلها

POST https://ops.your-bank.sd/hooks/greenpay
Content-Type: application/json
User-Agent: GreenPayStatus/1.0 (+https://greenpay.app/status/partners/)
X-GP-Event: service.down
X-GP-Delivery: 8241                      // معرّف التسليم — استخدموه لمنع التكرار
X-GP-Signature: t=1758268800,v1=9f2a...  // التوقيع

جسم الطلب

{
  "id": "evt_312_1758268800",
  "type": "service.down",
  "created_at": 1758268800,
  "org": "example-bank",
  "data": {
    "service": "mobile-app",
    "name": "تطبيق الموبايل",
    "from": "up",
    "to": "down",
    "source": "missed"      // missed = انقطعت النبضة · beat = بلاغ منكم
  }
}

الأحداث

النوعمتى
service.upعودة الخدمة للعمل
service.degradedحالة متذبذبة / بطء
service.downتوقف — سواء بلّغتم به أو رصدناه بانقطاع النبضة
maintenance.startedبدء نافذة صيانة مجدولة
maintenance.endedانتهاء النافذة
webhook.testزر «إرسال تجريبي» في اللوحة

التحقق من التوقيع (إلزامي)

التوقيع = HMAC-SHA256 على النص "<t>.<جسم الطلب الخام>" بمفتاح التوقيع الذي ظهر لكم مرة واحدة. استخدموا الجسم الخام قبل أي تحويل JSON، وارفضوا أي طلب عمره أكثر من 5 دقائق (يمنع إعادة إرسال طلب قديم مُلتقط).

PHP
<?php
$raw    = file_get_contents('php://input');
$header = $_SERVER['HTTP_X_GP_SIGNATURE'] ?? '';
parse_str(str_replace(',', '&', $header), $parts);   // t=...&v1=...

$t   = (int) ($parts['t'] ?? 0);
$mine = hash_hmac('sha256', $t . '.' . $raw, getenv('GP_WEBHOOK_SECRET'));

if (!hash_equals($mine, (string) ($parts['v1'] ?? '')) || abs(time() - $t) > 300) {
    http_response_code(401);
    exit;
}

http_response_code(200);      // ردّوا بسرعة، ثم عالجوا الحدث في الخلفية
$event = json_decode($raw, true);
Node.js / Express
const crypto = require('crypto');

// لاحظ: express.raw حتى يصل الجسم كما هو
app.post('/hooks/greenpay', express.raw({ type: 'application/json' }), (req, res) => {
  const parts = Object.fromEntries(req.get('X-GP-Signature').split(',').map(p => p.split('=')));
  const mine  = crypto.createHmac('sha256', process.env.GP_WEBHOOK_SECRET)
                      .update(parts.t + '.' + req.body.toString('utf8')).digest('hex');

  const ok = crypto.timingSafeEqual(Buffer.from(mine), Buffer.from(parts.v1))
          && Math.abs(Date.now() / 1000 - Number(parts.t)) < 300;
  if (!ok) return res.sendStatus(401);

  res.sendStatus(200);
  handle(JSON.parse(req.body));
});
Python
import hmac, hashlib, time, os

def verify(raw: bytes, header: str) -> bool:
    parts = dict(p.split('=', 1) for p in header.split(','))
    mine  = hmac.new(os.environ['GP_WEBHOOK_SECRET'].encode(),
                     (parts['t'] + '.').encode() + raw, hashlib.sha256).hexdigest()
    return hmac.compare_digest(mine, parts['v1']) and abs(time.time() - int(parts['t'])) < 300

قواعد التسليم

6. الصيانة المجدولة

أبلغونا قبل أي عمل مخطط، فخلال النافذة: لا يُسجَّل عطل، ولا يُرسل تنبيه لفريقكم، ولا تتغير حالتكم على الصفحة العامة — حتى لو توقفت النبضة تماماً. تُجدول من اللوحة، أو برمجياً:

POST https://greenpay.app/status/api/v1/maintenance
Authorization: Bearer gp_live_sk_...        // صلاحية maintenance:write
Content-Type: application/json

{
  "service":   "mobile-app",   // احذفوه لتشمل كل الخدمات
  "starts_at": 1758268800,     // unix timestamp
  "ends_at":   1758276000,
  "note":      "ترقية النظام الأساسي"
}
قائمة النوافذ المفتوحة
curl "https://greenpay.app/status/api/v1/maintenance" -H "Authorization: Bearer $GP_STATUS_KEY"

7. الحوادث والتحديثات الرسمية

الحادثة ليست إشارة مراقبة — هي كلامكم أنتم عمّا يجري، يظهر في صفحة حالتكم الرسمية ويصل أنظمتكم عبر الويبهوك. تُدار من اللوحة أو برمجياً.

POST https://greenpay.app/status/api/v1/incidents            // صلاحية incidents:write

{
  "title":     "بطء في التحويلات بين البنوك",
  "severity":  "major",        // minor | major | critical
  "service":   "mobile-app",   // اختياري — احذفه لتشمل كل الخدمات
  "body":      "رصدنا بطئاً والفريق يعمل على المعالجة.",
  "is_public": true,           // عرضها في صفحة حالتكم
  "set_state": 3               // اختياري: سجّلوا الخدمة متوقفة أيضاً
}
POST https://greenpay.app/status/api/v1/incidents/update

{
  "id":    12,
  "state": "monitoring",   // investigating | identified | monitoring | resolved
  "body":  "تم تطبيق الإصلاح ونراقب الوضع."
}

8. تقرير التشغيل (SLA)

النسبة تُحسب من سجل تغيّر الحالة لا من عدّ النبضات، ونوافذ الصيانة المجدولة مستثناة من المقام — الصيانة التي أعلنتموها لا تُنقص رقمكم. الحالة «متذبذب» لا تُحتسب توقفاً.

curl "https://greenpay.app/status/api/v1/report?days=30" -H "Authorization: Bearer $GP_STATUS_KEY"

{
  "ok": true,
  "days": 30,
  "services": [
    {
      "service": "mobile-app",
      "uptime_pct": 99.842,
      "down_s": 4080,
      "degraded_s": 600,
      "planned_s": 7200,      // مستثناة من الحساب
      "outages": 2,
      "partial": false        // true = المراقبة بدأت بعد بداية المدة
    }
  ],
  "incidents": 3,
  "mttr_s": 2760            // متوسط زمن إغلاق الحادثة
}

نفس الأرقام في اللوحة بصيغة قابلة للطباعة PDF وللتصدير CSV.

9. بيئة التجربة (Sandbox)

أنشئوا مفتاحاً بنوع sandbox واربطوا الكود به أولاً. مفتاح التجربة:

curl -X POST https://greenpay.app/status/api/v1/heartbeat \n  -H "Authorization: Bearer gp_sandbox_sk_..." \n  -d '{"service":"mobile-app","status":"down"}'

{ "ok": true, "sandbox": true, "state": "down",
  "note": "Sandbox key: nothing was changed, alerted or published." }

بعد نجاح التجربة، أنشئوا مفتاح live وبدّلوا متغيّر البيئة فقط — لا تغيير في الكود.

10. ملفات جاهزة لفريقكم

OpenAPI 3.0https://greenpay.app/status/api/v1/openapi.json
Postman Collectionhttps://greenpay.app/status/api/v1/postman.json

كلاهما عام ولا يحتاج مفتاحاً. بعد الاستيراد في Postman عيّنوا المتغيّر api_key بمفتاح sandbox وابدأوا التجربة.

تحميل OpenAPI تحميل Postman

11. الفريق والصلاحيات

كل عضو يدخل برمز واتساب يصل رقمه هو — بلا كلمات مرور ولا حساب مشترك. أضيفوهم من صفحة «الفريق»:

الصلاحيةتستطيع
مالك الحسابكل شيء، وهو وحده يمنح صلاحية «مالك»
مديرالخدمات، الصيانة، الحوادث، المفاتيح، الويبهوك، الأرقام، الفريق
مهندسالخدمات، الصيانة، والحوادث
مشاهدالاطلاع والتقارير فقط

عند مغادرة موظف: احذفوه من «الفريق» (ينتهي دخوله فوراً)، وإن كان مفتاح API معه فدوّروه.

12. الأخطاء

كل خطأ يرجع بنفس الشكل، مع رمز ثابت يمكن التعامل معه برمجياً:

{
  "ok": false,
  "error": { "code": "invalid_key", "message": "Unknown or wrong API key." }
}
HTTPcodeالمعنى
401missing_keyلم يصل هيدر المصادقة
401invalid_keyالمفتاح غير صحيح أو شكله خاطئ
403revoked_keyالمفتاح ملغى — أنشئوا بديلاً
403ip_not_allowedعنوان السيرفر خارج قائمة السماح للمفتاح
403missing_scopeالمفتاح لا يحمل الصلاحية المطلوبة
403org_not_activeحساب الجهة غير مفعّل
400missing_service / bad_statusحقل ناقص أو قيمة غير مقبولة
429too_frequentنبضتان لنفس الخدمة خلال أقل من 5 ثوانٍ
400bad_windowنافذة صيانة غير صالحة (النهاية قبل البداية، أو في الماضي)
400bad_incident / bad_updateبيانات حادثة ناقصة (عنوان قصير، أو نص تحديث فارغ)
404unknown_incidentلا توجد حادثة بهذا الرقم عندكم

عند فشل الإرسال من طرفكم: أعيدوا المحاولة مرة أو مرتين بفاصل متزايد، ثم اتركوها — النبضة التالية تكفي. ولا تجعلوا فشل النبضة يؤثر على خدمتكم إطلاقاً: نفّذوها في وظيفة مستقلة بمهلة قصيرة.

13. تدوير المفاتيح

  1. أنشئوا مفتاحاً جديداً من اللوحة وانسخوه.
  2. حدّثوا متغيّر البيئة في سيرفركم وأعيدوا تشغيل الخدمة.
  3. تأكدوا من عمود «آخر استخدام» أن المفتاح الجديد يعمل.
  4. ألغوا القديم.

بهذا الترتيب لا يحدث أي انقطاع. عند الاشتباه في تسريب: ألغوا فوراً أولاً، ثم أنشئوا البديل.

14. قائمة تحقق قبل التشغيل

تسجيل جهتكم دخول اللوحة