أحداث Webhooks لعمليات الدفع

تحديثات فورية حول معاملاتك.

تعتبر أحداث Webhooks لعمليات الدفع (والتي كانت تعرف سابقًا باسم التحديثات الفورية) طريقة أساسية يتم من خلالها إبلاغك بالتغييرات في الطلبات التي تتم عبر عمليات الدفع من فيسبوك في تطبيقك.

نظرة عامة

تُعد أحداث Webhooks نظامًا يستند إلى الاشتراك متوفر بين فيسبوك والخادم لديك. حيث يشترك تطبيقك لتلقي التحديثات من فيسبوك عبر نقطة نهاية HTTPS محددة. وعند تحديث طلب تم إجراؤه داخل تطبيقك، سنصدر طلب POST HTTPS إلى نقطة النهاية هذه، لإخطار الخادم بالتغيير.

تتوفر 3 سيناريوهات أساسية يتم فيها إرسال التحديثات إلى خادم المطوّر:

الاشتراك في أحداث Webhooks

للاشتراك في أحداث Webhooks لعمليات الدفع، يجب أولاً إنشاء عنوان URL لنقطة نهاية عامة يتلقى كل من طلب GET HTTPS للتحقق من الاشتراك وPOST لطلبات تغيير البيانات. تم وصف بنية هذين النوعين من الطلبات أدناه. بعد ذلك، يمكنك إعداد الاشتراك في الكائن payment لدى تطبيقك. توجد طريقتان للقيام بذلك:

في كلتا الحالتين، ستتلقى نقطة النهاية البيانات ذاتها بالطريقة ذاتها. راجع خادم الاستدعاء لمزيد من المعلومات حول ما سيتلقاه الخادم.

الاشتراك عبر لوحة معلومات التطبيق

تُعد أسهل طريقة لإعداد تطبيقك لتلقي تحديثات Webhooks هي استخدام لوحة عمليات الدفع في لوحة معلومات التطبيق. ابحث عن تطبيقك في لوحة المعلومات ثم انقر على علامة التبويب Payments. سيكون قسم أحداث Webhooks أسفل قسم إعدادات الشركة مباشرةً.

أحداث Webhooks لعمليات الدفع

ستعرض هذه الشاشة بعد ذلك حالة اشتراك التطبيق، سواء تمت إضافته من خلال هذه اللوحة أو API. ومن هنا، يمكن تغيير عنوان URL الاستدعاء واختباره.

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

وأخيرًا، قم بتوفير "رمز تحقق". سيتم إرسال هذا الرمز فقط أثناء مرحلة التسجيل للتحقق من أن الاشتراك قد تم إنشاؤه من موقع آمن. لن يتم إرسال هذا الرمز في تحديثات Webhook المعتادة.

اختبار الإعدادات

يجب اختبار إعدادات الاستدعاء قبل حفظ الاشتراك. سيصدر ذلك طلب GET للتحقق إلى نقطة النهاية، يحتوي على المعلمات hub.mode وhub.challenge وhub.verify_token، وسيضمن معالجتها بشكل صحيح. على سبيل المثال، يجب أن تتأكد من أن نقطة النهاية تُرجع hub.challenge مرة أخرى إلى فيسبوك:

اختبار الإعدادات

بمجرد إدخال تفاصيل الاشتراك، احرص على النقر على الزر "حفظ التغييرات" أسفل الصفحة. يُعد تعديل الاشتراك أمرًا بسيطًا يتمثل في تغيير محتويات الحقول وإعادة الاختبار ثم حفظ النموذج مرة أخرى.

الاشتراك عبر واجهة Graph API

من الممكن إعداد الاشتراكات وإدراجها بشكل برمجي من خلال واجهة Graph API. ستحتاج إلى access token للتطبيق، والذي يتوفر من خلال أداة رمز الوصول أو من خلال استخدام نقطة النهاية /oauth لواجهة Graph API

تتوفر API الاشتراك في نقطة النهاية https://graph.facebook.com/[APP_ID]/subscriptions

يمكنك إجراء 3 مهمات من خلالها:

  • إضافة اشتراك أو تعديله (عن طريق إرسال طلب POST HTTPS)
  • إدراج كل الاشتراكات الحالية (عن طريق إرسال طلب GET HTTPS)

إضافة الاشتراكات وتعديلها

لإعداد اشتراك، أرسل طلب POST يتضمن المعلمات التالية. لاحظ أن هذه المعلمات تتوافق مع الحقول الموجودة في النموذج الموضح أعلاه:

  • object - كما هو موضح بالأعلى، تمثل نوع الكائن الذي تريد تلقي تحديثات حوله. حدّد payments.
  • fields - تمثل قائمة مفصولة بفاصلة تتضمن خصائص نوع الكائن الذي تود الحصول على تحديثات حول التغييرات التي تتم به. حدّد "الإجراءات" و"النزاعات".
  • callback_url - تمثل نقطة نهاية خادم صالحة وقابلة للوصول من خلال العامة.
  • verify_token - تمثل سلسلة عشوائية، يتم إرسالها إلى نقطة النهاية عند التحقق من الاشتراك.

عندما نتلقى هذا الطلب، كما هو الحال مع تكوين النموذج أعلاه، سنرسل طلب GET إلى الاستدعاء للتأكد من صلاحيته وجاهزيته لتلقي التحديثات. على وجه التحديد، يجب أن تتأكد من أن نقطة النهاية تُرجع hub.challenge مرة أخرى إلى فيسبوك.

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

إدراج الاشتراكات

يؤدي إرسال طلب GET HTTP إلى API الاشتراكات إلى إرجاع محتوى مشفّر بلغة JSON يدرج الاشتراكات. على سبيل المثال:

[
  {
    "object": "payments",
    "callback_url": "https://www.friendsmash.com/rtu.php",
    "fields": ["actions", "disputes"],
    "active": true
  }
]

يمكنك استخدام مستكشف Graph لاختبار API هذه مباشرةً، مع الحرص على استخدام رمز وصول التطبيق.

خادم الاستدعاء

يجب أن يتعامل خادم الاستدعاء مع نوعي طلبات. تأكد من وجوده على عنوان URL عام حتى نتمكن من إجراء هذه الطلبات بنجاح.

التحقق من الاشتراك

أولاً، ستقوم خوادم فيسبوك بإرسال طلب GET HTTPS إلى عنوان URL الاستدعاء عند محاولة إضافة اشتراك أو تعديله. سيتم إلحاق سلسلة استعلام بعنوان URL مع تضمين المعلمات التالية:

المعلمة الوصف

hub.mode

يتم إدخال السلسلة "subscribe" في هذه المعلمة

hub.challenge

سلسلة عشوائية

hub.verify_token

قيمة verify_token التي حددتها عند إنشاء الاشتراك

يجب على نقطة النهاية التحقق من hub.verify_token أولاً. يضمن ذلك أن يعرف الخادم أن الطلب يتم إجراؤه بواسطة فيسبوك ويربطه بالاشتراك الذي قمت بتكوينه للتو.

حينها يجب أن تُرجع نقطة النهاية القيمة hub.challenge فقط مرة أخرى، الأمر الذي يؤكد لفيسبوك أن هذا الخادم تم تكوينه لقبول الاستدعاءات، ويمنع الثغرات التي تسهل شن هجمات حجب الخدمة (DDoS).

ملاحظة لمطوّري PHP: في PHP، يتم تحويل النقاط والمسافات في أسماء معلمات الاستعلام إلى شرطات سفلية تلقائيًا. ولذلك، عليك الوصول إلى هذه المعلمات باستخدام $_GET['hub_mode'] و$_GET['hub_challenge'] و$_GET['hub_verify_token'] إذا كنت تكتب نقطة نهاية الاستدعاء بلغة PHP. راجع هذه الملاحظة في دليل لغة PHP لمزيد من المعلومات.

تلقي التحديثات

بعد نجاح الاشتراك، سنشرع في إرسال طلب POST HTTPS إلى نقطة نهاية الخادم في كل مرة تحدث فيها تغييرات (في الحقول أو جهات التواصل المختارة). يجب الاستجابة لهذا الطلب برمز HTTP 200.

ملاحظة - نعتبر أي استجابة HTTP بخلاف 200 خطأً. وفي هذه الحالات، سنواصل إعادة محاولة إرسال تحديث webhooks. إذا لم تستجب على النحو المطلوب، فقد تتلقى التحديث ذاته عدة مرات.

سيتوفر لدى الطلب نوع محتوى application/json وسيتألف النص الأساسي من سلسلة مشفّرة بلغة JSON تحتوي على تغيير واحد أو أكثر.

ملاحظة لمطوّري PHP: في PHP، للحصول على البيانات المشفرة، ستستخدم الرمز التالي:

$data = file_get_contents("php://input");
$json = json_decode($data);`

لاحظ أنه لا يتم إرسال المعلمات hub.mode وhub.challenge وhub.verify_token مرة أخرى بمجرد تأكيد الاشتراك.

فيما يلي مثال أساسي لاستدعاء تم إنشاؤه لاشتراك كائن payments:

{
  "object": "payments",
  "entry": [
    {
      "id": "296989303750203",
      "time": 1347996346,
      "changed_fields": [
        "actions"
      ]
    }
  ]
}

من المهم ملاحظة أن تحديثات Webhook تخبرك فقط أنه تم تغيير عملية دفع معينة، محددة بواسطة الحقل id. بعد تلقي التحديث، يُطلب منك بعد ذلك الاستعلام عن واجهة Graph API للحصول على تفاصيل المعاملة، لمعالجة التغيير بشكل مناسب.

ملاحظة - على الرغم من إمكانية تجميع تحديثات Webhooks لأنواع الكائنات الأخرى، فإنه لا يمكن مطلقًا تجميع تحديثات الدفع.

نضمن لك تلقي تحديث جديد في كل مرة يتم فيها تحديث معاملة، إما عن طريق إجراء المستخدم أو إجراء المطوّر.

إذا فشل تحديث الخادم، فسنعيد المحاولة مرة أخرى على الفور ثم نكرر المحاولة عدة مرات، بمعدل تكرار متناقص، على مدار الـ 24 ساعة التالية.

مع كل طلب، نرسل عنوان HTTP X-Hub-Signature-256 يحتوي على توقيع SHA256 لحمولة بيانات الطلب، باستخدام المفتاح السري للتطبيق كمفتاح، ومسبوقًا ببادئة sha256=. بإمكان نقطة نهاية الاستدعاء التحقق من هذا التوقيع للتحقق من سلامة حمولة البيانات وأصلها.

الاستجابة للتحديثات

بعد تلقي الخادم للتحديث، يجب الاستعلام عن واجهة Graph API باستخدام الحقل id للحصول على تفاصيل حول حالة المعاملة الجديدة. عليك حينها اتخاذ إجراء استنادًا إلى الحالة.

تسرد الأقسام التالية كل تغييرات الحالة المحتملة التي تؤدي إلى إرسال تحديث. وتنقسم هذه التغييرات عمومًا إلى:

  • تغييرات على مصفوفة الإجراءات، والتي تحدث عندما تكتمل عملية الدفع بشكل غير متزامن، حيث يتم إصدار طلب استرداد أموال (إما عن طريقك أو عن طريق فيسبوك) أو عند حدوث عملية رد المبالغ المدفوعة.
  • تغييرات على مصفوفة النزاعات، والتي تحدث عند بدء نزاع حول طلب من جانب العميل.

الإجراءات

يحتوي كل كائن payment على مصفوفة تُسمى actions، تتضمن مجموعة تغييرات الحالة التي مرت بها المعاملة. ويحتوي كل إدخال في المصفوفة actions على خاصية تُسمى type والتي تصف نوع الإجراء الذي تم. يمكن أن يحتوي type على القيم التالية: charge وrefund وchargeback وchargeback_reversal وdecline والتي يتم شرحه بالكامل هنا.

عينة من استجابة واجهة Graph API لكائن عملية دفع مع الإجراءات المرتبطة فيما يلي:

{
   "id": "3603105474213890",
   "user": {
      "name": "Marco Alvarez",
      "id": "500535225"
   },
   "application": {
      "name": "Friend Smash",
      "namespace": "friendsmashsample",
      "id": "241431489326925"
   },
   "actions": [
      {
         "type": "charge",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-22T21:18:54+0000",
         "time_updated": "2013-03-22T21:18:55+0000"
      },
      {
         "type": "refund",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-23T21:18:54+0000",
         "time_updated": "2013-03-23T21:18:55+0000"
      }
   ],
   "refundable_amount": {
      "currency": "USD",
      "amount": "0.00"
   },
   "items": [
      {
         "type": "IN_APP_PURCHASE",
         "product": "https://www.friendsmash.com/og/friend_smash_bomb.html",
         "quantity": 1
      }
   ],
   "country": "US",
   "created_time": "2013-03-22T21:18:54+0000",
   "payout_foreign_exchange_rate": 1,}`

بمجرد الاشتراك في حقل الإجراءات عند التسجيل في أحداث Webhooks، سنصدر تحديثًا عندما تتغير المصفوفة على النحو التالي:

تحصيل الرسوم

في البداية، تحتوي كل الطلبات على إدخال تحصيل رسوم بالحالة "status": "initiated". تشير عملية الدفع المبدئية إلى أن عملية الدفع بدأت فقط ولم تكتمل بعد. وبالتالي لن نرسل تحديثات حول عمليات الدفع غير المكتملة.

عند إكمال عملية دفع بنجاح، سيتم تغيير "status": "initiated" إلى "status": "completed" وسنرسل تحديثًا. بمجرد رؤية هذا التحديث يجب الرجوع إلى سجلات عملية الدفع للتحقق مما إذا كان هذا التحديث لمعاملة جديدة أم معاملة موجودة ثم الاستجابة على النحو التالي:

  • إذا كان الطلب معروفًا لك بالفعل، وتم تنفيذه بواسطة استدعاء JavaScript (يُفضل كخيار أول)، فيمكنك تجاهل التحديث بأمان، أو استخدامه كتأكيد إضافي.
  • إذا كان الطلب معروفًا لك، ولكنه موجود في حالة initiated، فيمكنك متابعة تنفيذ الطلب، وإرسال العنصر أو العملة الافتراضية المرتبطة به إلى المستهلك. ويمكن بعد ذلك تحديد عملية الدفع هذه بأمان على أنها مكتملة.
  • إذا كان الطلب غير معروف، فيشير ذلك إلى أن دفق المعالجة لم يكتمل من جانب العميل، والذي يرجع غالبًا إلى مشكلة في الاتصال بالإنترنت أو إغلاق المستهلك للمتصفح أثناء عملية إتمام الشراء. لا يزال بإمكانك تنفيذ هذا الطلب وإكماله بأمان، نظرًا لأن فيسبوك يظل المصدر الأساسي لتجميع البيانات فيما يتعلق بفواتير المستخدم.

ستتلقى تحديثات أيضًا حول عمليات الدفع المحددة بالحالة "status": "failed". يجب ألا يتم إتمام تلك العمليات.

استرداد الأموال

عند إصدار طلب استرداد أموال عبر واجهة Graph API، ستتلقى تحديثًا. كما هو الحال مع "type": "charge"، يمكن أن يحتوي طلب استرداد الأموال أيضًا على حالة متغيرة يجب أن تكون على دراية بها. والأهم من ذلك، أنه من الممكن أن تفشل عملية استرداد الأموال، عادةً بسبب خطأ في المعالجة أو الاتصال - وفي هذه الحالة يجب إعادة محاولة إرسال طلب استرداد الأموال.

عملية رد المبالغ المدفوعة وعملية إبطال رد المبالغ المدفوعة وعمليات الرفض

كما هو الحال مع عملية استرداد الأموال، سيتم إبلاغك أيضًا في حالة إصدار عملية رد المبالغ المدفوعة وعملية إبطال رد المبالغ المدفوعة وعملية الرفض. وستتم إضافة كائن عملية رد المبالغ المدفوعة أو عملية إبطال رد المبالغ المدفوعة أو عملية الرفض إلى مصفوفة الإجراءات في بيانات واجهة Graph API التي يتم إرجاعها حول عملية الدفع.

النزاعات

سنخطرك عن طريق إرسال تحديث عند بدء النزاع. في هذه الحالة، ستظهر مصفوفة "disputes" جديدة كجزء من الكائن payment. وستحتوي المصفوفة على وقت بدء النزاع، والسبب الذي دفع المستهلك لبدء الاستجابة وكذلك عنوان البريد الإلكتروني للمستهلك، والذي يمكنك استخدامه للاتصال به مباشرةً لحل النزاع.

فيما يلي عينة من استجابة كاملة من واجهة Graph API لمعاملة متنازع عليها:

{
   "id": "990361254213890",
   "user": {
      "name": "Marco Alvarez",
      "id": "500535225"
   },
   "application": {
      "name": "Friend Smash",
      "namespace": "friendsmashsample",
      "id": "241431489326925"
   },
   "actions": [
      {
         "type": "charge",
         "status": "completed",
         "currency": "USD",
         "amount": "0.99",
         "time_created": "2013-03-22T21:18:54+0000",
         "time_updated": "2013-03-22T21:18:55+0000"
      }
   ],
   "refundable_amount": {
      "currency": "USD",
      "amount": "0.99"
   },
   "items": [
      {
         "type": "IN_APP_PURCHASE",
         "product": "https://www.friendsmash.com/og/friend_smash_bomb.html",
         "quantity": 1
      }
   ],
   "country": "US",
   "created_time": "2013-03-22T21:18:54+0000",
   "payout_foreign_exchange_rate": 1,
   "disputes": [
      {
         "user_comment": "I didn't receive my item! I want a refund, please!",
         "time_created": "2013-03-24T18:21:02+0000",
         "user_email": "email\u0040domain.com",
         "status": "resolved", 
         "reason": "refunded_in_cash"
      }
   ]
}

لمزيد من المعلومات حول كيفية الاستجابة للنزاعات وإصدار عمليات استرداد الأموال، يُرجى مراجعة شروحات عمليات الدفع: كيفية معالجة النزاعات وعمليات استرداد الأموال.