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

تتمثل إحدى المشكلات عند العمل باستخدام الدفع عبر الويب في أن إطلاق رسالة الدفع يُعد أمرًا صعبًا للغاية "كفوئ". لتشغيل رسالة فورية، يحتاج أحد التطبيقات إلى إرسال طلب 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 وتنفيذ الرمز تؤدي إلى تشغيل رسالة الدفع.

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

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

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

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

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

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

npm install web-push --save

ثم، في النص البرمجي للعقدة، نحتاج إلى الوحدة 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، ...)، فإن خطوات تنفيذ الدفع ستكون هي نفسها.

بعد ذلك، ما الذي تفعله مكتبات دفع الويب هذه بالضبط؟

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

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