إشعارات Webhook

ترسل أنيس النتائج إلى عنوان HTTPS المتحقق منه لتطبيقك. يمكنك متابعة خدمة العملاء دون قراءة حالة كل عملية باستمرار. يحمل الإشعار قراءة العملية العامة كاملة، بما فيها رموز شحن العداد أو بيانات مستلم الشحن المباشر. تحقّق من توقيعه قبل الوثوق به.

تسجيل عنوانك والتحقق منه

لكل تطبيق عنوان واحد. اقرأه بواسطة GET /v1/webhook-endpoint وسجّله بواسطة PUT /v1/webhook-endpoint. لا تحتاج إلى صلاحية إضافية، لكن الاستدعاء يجب أن يكون موقّعًا ومقبولًا لتطبيقك. ردّ GET غير موقّع، وردّ PUT موقّع. وقّع التغييرات ببصمة المحتوى ورقم استخدام واحد جديد، دون Idempotency-Key. ارجع إلى مرجع العنوان للاطلاع على حقول الاستدعاء والردّ المنشورة.

تعيد قراءة العنوان url وstatus وgeneration. حين لا يوجد عنوان، تكون url وgeneration بالقيمة null. الحالات هي notConfigured وpendingVerification وverified وdisabled. يحافظ تكرار PUT بالعنوان نفسه على التسجيل والتحقق؛ ولا يلغي PUT تعطيل الموظفين. يحتاج تغيير العنوان إلى تحقق جديد. أرسل {} بالضبط للاختبار وإعادة الإرسال.

سجّل العنوان بمحتوى PUT هذا:

{"url":"https://pos.example.ly/hooks/anis"}

يعيد GET وPUT للعنوان المضبوط الردّ 200 التالي:

{"url":"https://pos.example.ly/hooks/anis","status":"pendingVerification","generation":7}

وإذا لم يوجد عنوان، يعيد GET الردّ 200 التالي:

{"url":null,"status":"notConfigured","generation":null}

يجب أن يستخدم العنوان HTTPS والمنفذ 443 واسم مضيف يحتوي على نقطة واحدة على الأقل، بلا معلومات مستخدم أو جزء fragment، وبحد أقصى 2048 حرفًا. ويجب أن تُحلّ وجهته إلى عناوين عامة؛ تُرفض العناوين المحلية والخاصة وlink-local والمشتركة ومتعددة البث وغير المحددة. ولا تُتبع إعادة التوجيه.

يبدأ التسجيل إشعار تحقق موقّعًا:

{"id":"11111111-1111-4111-8111-111111111111","type":"webhook_endpoint.verification","occurredAt":"2026-10-20T08:00:00Z","applicationId":"9b2d1241-413e-4ae8-ae32-b15c58dbe101","data":{"challenge":"MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY","generation":1}}

تحقّق من التوقيع أولًا. خلال عشر ثوانٍ، أعد حالة 2xx وكائن JSON التالي كما هو، بلا عناصر إضافية:

{"challenge":"MDEyMzQ1Njc4OWFiY2RlZjAxMjM0NTY3ODlhYmNkZWY"}

تنتهي صلاحية التحدّي بعد خمس دقائق، ويرتبط بتسجيل العنوان هذا. لا يمكن لتحدٍّ قديم التحقق من عنوان بديل. لا تصل نتائج العمليات إلا بعد اكتمال التحقق. يستطيع موظفو أنيس عرض العنوان وتعطيله.

من أين تصل الإشعارات

ترسل أنيس كل الإشعارات، ومنها إشعار التحقق ورسائل الاختبار، من عنوان ثابت واحد لكل بيئة. إن كان جدار الحماية لديك يقبل مصادر معروفة فقط، فاسمح لهذا العنوان على المنفذ 443:

البيئة العنوان الذي تتصل منه أنيس
بيئة الاختبار (Sandbox) 20.19.1.248
الإنتاج 20.71.82.53

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

اختبار الاستقبال

الاستدعاء POST /v1/webhook-endpoint/test تغيير موقّع وردّه موقّع. يرسل إشعار اختبار حقيقيًا ومسجّلًا إلى عنوانك المتحقق منه والمفعّل، دون إنشاء شراء. عند وضع الاختبار في الانتظار يعيد 202:

{"id":"22222222-2222-4222-8222-222222222222","type":"webhook_endpoint.test","status":"pending","queuedAt":"2026-10-20T08:00:00Z"}

ويكون الإشعار الموقّع:

{"id":"22222222-2222-4222-8222-222222222222","type":"webhook_endpoint.test","occurredAt":"2026-10-20T08:00:00Z","applicationId":"9b2d1241-413e-4ae8-ae32-b15c58dbe101","data":{"message":"Anis webhook test"}}

معالجة نتائج العمليات

نوع الإشعار متى يُرسل
meterCharge.succeeded نجح الشحن
meterCharge.failed عاد المال أو لم يُخصم شيء
directRecharge.paid دُفعت وأصبحت بانتظار الموظف
directRecharge.paymentFailed رفضت المحفظة الدفع
directRecharge.suspended توقفت لتصحيح بيانات المستلم
directRecharge.completed سُلّمت
directRecharge.rejected رُفضت وعاد المال
directRecharge.partiallyRefunded سُوّيت إعادة المال الجزئية؛ ولا تحدث إلا إذا فُعّلت للمنتج
{
  "id": "4bceba8e-55d1-59ec-b560-6c1e08f2ae3a",
  "type": "meterCharge.succeeded",
  "occurredAt": "2026-10-20T08:13:02Z",
  "applicationId": "9b2d1241-413e-4ae8-ae32-b15c58dbe101",
  "data": {
    "completedAt": "2026-10-20T08:13:02Z",
    "createdAt": "2026-10-20T08:12:44Z",
    "externalReference": "POS-77-INV-1093",
    "meterNumber": "0268000710262",
    "operationId": "3c1e9b7a-0d42-4f5e-8a61-2b9c7d0e5f13",
    "package": {
      "gecolPackageName": "PKG_100",
      "id": "5f3b8c21-5f3b-4c21-8f3b-5f3b8c215f3b",
      "name": "باقة 100 دينار"
    },
    "personalPrice": {
      "amount": "100.000",
      "currency": "LYD"
    },
    "receipt": {
      "creditedAmount": "88.640",
      "receiptNumber": "260020103744",
      "taxes": [
        {
          "amount": "5.000",
          "description": "ضريبة الاستهلاك"
        },
        {
          "amount": "3.860",
          "description": "رسوم الخدمة"
        }
      ],
      "tokens": [
        {
          "code": "1234 5678 9012 3456 7890",
          "description": "Key change token 1",
          "label": "KCT1",
          "sequence": 1
        },
        {
          "code": "2345 6789 0123 4567 8901",
          "description": "Key change token 2",
          "label": "KCT2",
          "sequence": 2
        },
        {
          "code": "5821 0034 7719 2265 0418",
          "description": "Credit token",
          "label": "STS",
          "sequence": 3
        }
      ]
    },
    "status": "succeeded",
    "total": {
      "amount": "97.500",
      "currency": "LYD"
    },
    "walletId": "71825266-7182-4182-8182-718252667182"
  }
}

يحمل data القراءة العامة الحالية للعملية بعد لحاق القراءة لدى المالك بتسلسل الحدث الذي سبّب الإشعار أو تجاوزه. لا يُرسل قبل ذلك ولا يُعاد بناء السجل السابق. يصف type ما حدث، وتكون الحقيقة في data.status؛ فقد يحمل إشعار directRecharge.suspended بعد تصحيح المستلم الحالة inProgress. معرّف العملية فيه هو الذي حفظته للشراء. أما معرّف الإشعار فمختلف؛ احفظه لمنع معالجة التسليم المكرر. يحتفظ الإشعار بالمعرّف والمحتوى نفسيهما عند المحاولات والإعادة اليدوية. ولكل محاولة أوقات توقيع جديدة وترويسة لرقم المحاولة.

تحقّق أولًا، ثم احفظ معرّف الإشعار وعالج النتيجة مرة واحدة. إذا وصل إشعار عالجته سابقًا، فأعد له أيضًا حالة 2xx. أعد 2xx خلال عشر ثوانٍ بعد حفظ الإشعار بصورة دائمة، ونفّذ العمل الأبطأ لاحقًا. لا تفترض وصول الإشعارات بالترتيب. استخدم قراءة العملية لمطابقة حالتها الحالية. يستمر التطبيق المعلّق في تلقي نتائج العمليات التي قُبلت قبل تعليقه.

المحاولات والاستعادة

تجري أنيس 12 محاولة خلال نحو ثلاثة أيام: فورًا، ثم بفواصل ثلاثين ثانية، ودقيقتين، وعشر دقائق، وثلاثين دقيقة، وساعة، وساعتين، وأربع ساعات، وثماني ساعات، واثنتي عشرة ساعة، وأربع وعشرين ساعة، وأربع وعشرين ساعة، مع تفاوت قدره عشرة بالمئة. وتراعي Retry-After الصالحة إن طلبت انتظارًا أطول مع أي ردّ HTTP فاشل، لمدة أقصاها أربع وعشرون ساعة بعد الفشل؛ وإن كانت القيمة أطول (بما في ذلك تاريخ بعيد في المستقبل) فالانتظار أربع وعشرون ساعة. تُعدّ أي 2xx خلال عشر ثوانٍ نجاحًا؛ وتفشل المحاولة عند إعادة التوجيه أو انتهاء المهلة أو أي حالة أخرى.

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

اعرضها بواسطة GET /v1/webhook-events?status=failed&since=2026-10-20T00:00:00Z. هذه قراءة موقّعة وردّها غير موقّع؛ أعد nextCursor كما هي في cursor.

{"items":[{"id":"0f8c3e1a-6c7d-5b1e-9a40-2a1f5c9e7d11","type":"meterCharge.succeeded","status":"failed","attempts":12,"lastAttemptAt":"2026-10-23T08:00:00Z","nextAttemptAt":null,"lastResponseCode":503,"lastFailureKind":"httpStatus"}],"nextCursor":null}

اطلب تسليمًا آخر بواسطة POST /v1/webhook-events/{eventId}/redeliver. هذا تغيير موقّع، وردّه 202 موقّع:

{"id":"0f8c3e1a-6c7d-5b1e-9a40-2a1f5c9e7d11","status":"pending","queuedAt":"2026-10-23T08:01:00Z"}

تعيد هذه الخطوة إرسال الإشعار، ولا تكرر الشراء. استخدم قراءة الشحن أو الشحن المباشر الموقّعة أثناء تعذّر الوصول إلى عنوانك.

العنوان · التسجيل · الاختبار · التسليم · الإعادة

يحمل هذا المثال حدث تعليق مع بيانات مصحّحة بالحالة inProgress. القراءة هي الحالة الحالية بعد اكتمال تحديثها:

{
  "id": "0ef5f62f-b3f0-5436-8e1f-5806629ad7c0",
  "type": "directRecharge.suspended",
  "occurredAt": "2026-10-20T10:00:00Z",
  "applicationId": "9b2d1241-413e-4ae8-ae32-b15c58dbe101",
  "data": {
    "commission": {
      "amount": "5.000",
      "currency": "LYD"
    },
    "exchangeRate": "0.155000",
    "externalReference": "POS-77-TR-551",
    "note": "Family support",
    "number": 30412,
    "operationId": "7d4e1a90-5b2c-4e7f-8a13-0c9d6e2b4f81",
    "payout": {
      "amount": "1500.000",
      "currency": {
        "name": "جنيه مصري"
      }
    },
    "price": {
      "amount": "232.500",
      "currency": "LYD"
    },
    "product": {
      "id": "e7c2a9d4-1b3f-4a85-9c60-8d2e4f7b1a93",
      "title": "فودافون كاش مصر"
    },
    "recipient": [
      {
        "key": "recipient_name",
        "name": {
          "ar": "اسم المستلم",
          "en": "Recipient name"
        },
        "value": "Ahmed Ali Hassan"
      },
      {
        "key": "recipient_number",
        "name": {
          "ar": "رقم المحفظة",
          "en": "Wallet number"
        },
        "value": "01098765432"
      }
    ],
    "requestedAt": "2026-10-20T09:00:00Z",
    "status": "inProgress",
    "total": {
      "amount": "237.500",
      "currency": "LYD"
    },
    "walletId": "71825266-7182-4182-8182-718252667182"
  }
}