إرسال الرسائل باستخدام مكتبات الإشعارات الفورية على الويب

من المشاكل التي تواجهك عند استخدام إشعارات الدفع على الويب هي أنّ بدء إرسال رسالة دفع هو عملية "معقدة" للغاية. لتشغيل رسالة فورية، يجب أن يُرسل التطبيق طلب POST إلى خدمة إعلامات فورية باتّباع بروتوكول إعلامات الويب الفورية. لاستخدام ميزة الإرسال الفوري على جميع المتصفحات، عليك استخدام VAPID (المعروفة أيضًا باسم مفاتيح خادم التطبيقات)، ما يتطلّب بشكل أساسي ضبط عنوان يحتوي على قيمة تثبت أنّ تطبيقك يمكنه إرسال رسالة إلى المستخدم. لإرسال البيانات من خلال رسالة فورية، يجب أولاً تشفير البيانات وإضافة رؤوس معيّنة كي يتمكّن المتصفّح من فك تشفير الرسالة بشكل صحيح.

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

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

في هذا القسم، سنستخدم مكتبة web-push Node. ستكون هناك اختلافات في اللغات الأخرى، ولكن لن تكون مختلفة جدًا. نحن ننظر إلى Node لأنّه JavaScript ومن المفترض أن يكون التنسيق الذي يسهل على القرّاء استخدامه.

سنوضّح لك الخطوات التالية:

  1. أرسِل اشتراكًا إلى الخلفية واحفظه.
  2. استرداد الاشتراكات المحفوظة وتشغيل رسالة فورية

حفظ الاشتراكات

يختلف حفظ PushSubscription وطلبات البحث عنها من قاعدة بيانات استنادًا إلى اللغة على جانب الخادم واختيار قاعدة البيانات، ولكن قد يكون من المفيد الاطّلاع على مثال على كيفية إجراء ذلك.

في صفحة الويب التجريبية، يتم إرسال PushSubscription إلى الخلفية من خلال إجراء طلب POST بسيط:

function sendSubscriptionToBackEnd(subscription) {
  return fetch('/api/save-subscription/', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(subscription),
  })
    .then(function (response) {
      if (!response.ok) {
        throw new Error('Bad status code from server.');
      }

      return response.json();
    })
    .then(function (responseData) {
      if (!(responseData.data && responseData.data.success)) {
        throw new Error('Bad response from server.');
      }
    });
}

يحتوي خادم Express في العرض التوضيحي على مستمع طلبات مطابق لنقطة نهاية /api/save-subscription/:

app.post('/api/save-subscription/', function (req, res) {

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

const isValidSaveRequest = (req, res) => {
  // Check the request body has at least an endpoint.
  if (!req.body || !req.body.endpoint) {
    // Not a valid subscription.
    res.status(400);
    res.setHeader('Content-Type', 'application/json');
    res.send(
      JSON.stringify({
        error: {
          id: 'no-endpoint',
          message: 'Subscription must have an endpoint.',
        },
      }),
    );
    return false;
  }
  return true;
};

إذا كان الاشتراك صالحًا، علينا حفظه وعرض استجابة JSON مناسبة:

return saveSubscriptionToDatabase(req.body)
  .then(function (subscriptionId) {
    res.setHeader('Content-Type', 'application/json');
    res.send(JSON.stringify({data: {success: true}}));
  })
  .catch(function (err) {
    res.status(500);
    res.setHeader('Content-Type', 'application/json');
    res.send(
      JSON.stringify({
        error: {
          id: 'unable-to-save-subscription',
          message:
            'The subscription was received but we were unable to save it to our database.',
        },
      }),
    );
  });

يستخدم هذا العرض الترويجي nedb لتخزين الاشتراكات، وهي قاعدة بيانات بسيطة تستند إلى الملفات، ولكن يمكنك استخدام أي قاعدة بيانات من اختيارك. لا نستخدم هذا الإجراء إلا لأنّه لا يتطلّب أي إعداد. لاستخدام الإصدار العلني، عليك استخدام طريقة أكثر موثوقية. (أميل إلى استخدام MySQL القديم.)

function saveSubscriptionToDatabase(subscription) {
  return new Promise(function (resolve, reject) {
    db.insert(subscription, function (err, newDoc) {
      if (err) {
        reject(err);
        return;
      }

      resolve(newDoc._id);
    });
  });
}

إرسال رسائل فورية

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

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

سأشرح لك كل خطوة من خطوات تشغيل العرض الترويجي. ستكون هذه الخطوات بسيطة بحيث يمكن للجميع اتّباعها، بما في ذلك أي مستخدم جديد في Node.

عندما ناقشنا اشتراك أحد المستخدمين، غطّينا إضافة applicationServerKey إلى خيارات subscribe(). سنحتاج إلى هذا المفتاح الخاص في الخلفية.

في العرض الترويجي، تتم إضافة هذه القيم إلى تطبيق Node على النحو التالي (أعلم أنّ الرمز مملّ، ولكن أريد فقط إعلامك بأنّه ليس هناك أي سحر):

const vapidKeys = {
  publicKey:
    'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
  privateKey: 'UUxI4O8-FbRouAevSmBQ6o18hgE4nSG3qwvJTfKc-ls',
};

بعد ذلك، نحتاج إلى تثبيت وحدة web-push لخادم العقدة:

npm install web-push --save

بعد ذلك، في نص Node، نطلب وحدة web-push على النحو التالي:

const webpush = require('web-push');

يمكننا الآن بدء استخدام وحدة web-push. أولاً، علينا إخبار وحدة web-push بمفاتيح خادم التطبيقات. (تُعرف هذه المفاتيح أيضًا باسم مفاتيح VAPID لأنّه اسم المواصفة).

const vapidKeys = {
  publicKey:
    'BEl62iUYgUivxIkv69yViEuiBIa-Ib9-SkvMeAtA3LFgDzkrxZJjSgSnfckjBJuBkr3qBUYIHBQFLXYp5Nksh8U',
  privateKey: 'UUxI4O8-FbRouAevSmBQ6o18hgE4nSG3qwvJTfKc-ls',
};

webpush.setVapidDetails(
  'mailto:web-push-book@gauntface.com',
  vapidKeys.publicKey,
  vapidKeys.privateKey,
);

يُرجى العلم أنّنا أدرجنا أيضًا سلسلة "mailto:". يجب أن تكون هذه السلسلة إما عنوان URL أو عنوان بريد إلكتروني على mailto. سيتم إرسال هذه المعلومة إلى خدمة الإشعارات الفورية على الويب كجزء من طلب بدء إرسال إشعار فوري. ويعود سبب إجراء ذلك إلى أنّه إذا أرادت خدمة الإشعارات الفورية على الويب التواصل مع المُرسِل، ستتوفّر لديها بعض المعلومات التي ستمكّنها من ذلك.

بعد ذلك، تكون وحدة web-push جاهزة للاستخدام، والخطوة التالية هي تنشيط رسالة فورية.

يستخدم العرض الترويجي لوحة المشرف الوهمية لتشغيل رسائل الإشعارات الفورية.

لقطة شاشة لصفحة المشرف

سيؤدي النقر على الزر "تشغيل رسالة فورية" إلى إرسال طلب POST إلى /api/trigger-push-msg/، وهي إشارة لنظامنا الأساسي لإرسال الرسائل الفورية، لذا ننشئ المسار في express لهذه النقطة النهائية:

app.post('/api/trigger-push-msg/', function (req, res) {

عند تلقّي هذا الطلب، نحصل على الاشتراكات من قاعدة البيانات ونقوم بتشغيل رسالة دفع لكل اشتراك.

return getSubscriptionsFromDatabase().then(function (subscriptions) {
  let promiseChain = Promise.resolve();

  for (let i = 0; i < subscriptions.length; i++) {
    const subscription = subscriptions[i];
    promiseChain = promiseChain.then(() => {
      return triggerPushMsg(subscription, dataToSend);
    });
  }

  return promiseChain;
});

يمكن للدالة triggerPushMsg() بعد ذلك استخدام مكتبة web-push لإرسال رسالة إلى الاشتراك الذي تم تقديمه.

const triggerPushMsg = function (subscription, dataToSend) {
  return webpush.sendNotification(subscription, dataToSend).catch((err) => {
    if (err.statusCode === 404 || err.statusCode === 410) {
      console.log('Subscription has expired or is no longer valid: ', err);
      return deleteSubscriptionFromDatabase(subscription._id);
    } else {
      throw err;
    }
  });
};

ستؤدي الدعوة إلى webpush.sendNotification() إلى عرض وعد. إذا تم إرسال الرسالة بنجاح، سيتم حلّ المشكلة ولن نحتاج إلى اتّخاذ أي إجراء. إذا تم رفض الوعد، عليك فحص الخطأ، لأنّه سيُعلمك ما إذا كان PushSubscription لا يزال صالحًا أم لا.

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

في هذا المثال، يتم التحقّق من رموز الحالة 404 و410، وهما رمزا حالة HTTP لحالة "لم يتم العثور على الصفحة" و"الصفحة غير متاحة". إذا تلقّينا أحد هذه الإشعارات، يعني ذلك أنّ الاشتراك قد انتهت صلاحيته أو لم يعُد صالحًا. في هذه الحالات، نحتاج إلى إزالة الاشتراكات من قاعدة بياناتنا.

في حال حدوث خطأ آخر، ما عليك سوى throw err، ما سيؤدي إلى رفض الوعد الذي يعرضه triggerPushMsg().

سنتناول بعض رموز الحالة الأخرى في القسم التالي عندما نلقي نظرة على بروتوكول إعلامات الويب العميقة بمزيد من التفصيل.

بعد تصفُّح الاشتراكات، يجب عرض استجابة JSON.

.then(() => {
res.setHeader('Content-Type', 'application/json');
    res.send(JSON.stringify({ data: { success: true } }));
})
.catch(function(err) {
res.status(500);
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({
    error: {
    id: 'unable-to-send-messages',
    message: `We were unable to send messages to all subscriptions : ` +
        `'${err.message}'`
    }
}));
});

لقد اطّلعنا على الخطوات الرئيسية لتنفيذ الميزة:

  1. أنشئ واجهة برمجة تطبيقات لإرسال الاشتراكات من صفحة الويب إلى الخلفية كي تتمكّن من حفظها في قاعدة بيانات.
  2. أنشئ واجهة برمجة تطبيقات لبدء إرسال الرسائل الفورية (في هذه الحالة، واجهة برمجة تطبيقات يتمّ استدعاؤها من لوحة المشرف المزعومة).
  3. استرداد جميع الاشتراكات من الخلفية وإرسال رسالة إلى كل اشتراك باستخدام إحدى مكتبات web-push

بغض النظر عن الخلفية (Node أو PHP أو Python أو غير ذلك)، ستكون خطوات تنفيذ ميزة "الدفع" متماثلة.

بعد ذلك، ما هي الوظيفة التي تؤديها لنا مكتبات Web Push بالضبط؟

الخطوات التالية

الدروس التطبيقية حول الترميز