תזמור עסקאות תשלום עם קובץ שירות (service worker)

איך להתאים את אפליקציית התשלומים מבוססת-האינטרנט ל'תשלומים באינטרנט' ולספק חוויית משתמש טובה יותר ללקוחות.

אחרי שאפליקציית התשלומים תירשם, תוכלו לקבל בקשות תשלום ממוכרים. בפוסט הזה מוסבר איך לתאם עסקת תשלום מ-Service Worker במהלך זמן ריצה (כלומר כשמוצג חלון והמשתמש מקיים איתו אינטראקציה).

תזמור עסקאות תשלום עם קובץ שירות (service worker)
תזמור עסקאות תשלום עם קובץ שירות (service worker)

'שינויים בפרמטר של תשלום בזמן הריצה' מתייחסים לקבוצת אירועים שמאפשרים למוכר ולגורם המטפל בתשלום להחליף הודעות בזמן שהמשתמש מנהל אינטראקציה עם ה-handler של התשלום. מידע נוסף על טיפול בפרטי תשלום אופציונליים עם Service Worker

קבלת אירוע של בקשת תשלום מהמוכר

כשלקוח בוחר לשלם באמצעות אפליקציית תשלומים מבוססת-אינטרנט והמוכר מפעיל PaymentRequest.show(), ה-Service Worker מקבל אירוע paymentrequest. כדי לתעד את האירוע ולהתכונן לפעולה הבאה, מוסיפים פונקציות event listener ל-Service Worker.

[payment handler] service-worker.js:

…
let payment_request_event;
let resolver;
let client;

// `self` is the global object in service worker
self.addEventListener('paymentrequest', async e => {
  if (payment_request_event) {
    // If there's an ongoing payment transaction, reject it.
    resolver.reject();
  }
  // Preserve the event for future use
  payment_request_event = e;
…

השדה PaymentRequestEvent שנשמר מכיל מידע חשוב לגבי העסקה הזו:

שם הנכס תיאור
topOrigin מחרוזת שמציינת את המקור של דף האינטרנט ברמה העליונה (בדרך כלל המוכר של מקבל התשלום). המאפיין הזה משמש לזיהוי מקור המוכר.
paymentRequestOrigin מחרוזת שמציינת את מקור ה-invoker. הערך הזה יכול להיות זהה לערך topOrigin כשהמוכר מפעיל את Payment Request API באופן ישיר. הוא יכול להיות שונה אם ה-API מופעל מתוך iframe על ידי צד שלישי, כמו שער תשלומים.
paymentRequestId המאפיין id של PaymentDetailsInit שסופק ל-Payment Request API. אם המוכר לא יענה, הדפדפן יספק מזהה שנוצר באופן אוטומטי.
methodData הנתונים הספציפיים לאמצעי התשלום שהמוכר סיפק כחלק מ-PaymentMethodData. אפשר להשתמש בה כדי לקבוע את פרטי עסקת התשלום.
total הסכום הכולל שהמוכר סיפק כחלק מ-PaymentDetailsInit. השתמשו בו כדי ליצור ממשק משתמש כדי ליידע את הלקוח מה הסכום הכולל לשלם.
instrumentKey מפתח האמצעי שנבחר על ידי המשתמש. הערך הזה משקף את instrumentKey שסיפקת מראש. מחרוזת ריקה מציינת שהמשתמש לא ציין אמצעי תשלום.

פתיחת חלון ה-handler של התשלומים כדי להציג את הקצה הקדמי של אפליקציית התשלומים מבוססת-האינטרנט

כשמתקבל אירוע paymentrequest, אפליקציית התשלומים יכולה לפתוח חלון של handler תשלומים על ידי קריאה ל-PaymentRequestEvent.openWindow(). בחלון המטפל בתשלומים יוצג ללקוחות הממשק של אפליקציית התשלומים שבו הם יכולים לבצע אימות, לבחור כתובת ואפשרויות למשלוח ולאשר את התשלום. במאמר טיפול בתשלומים בממשק התשלומים (בקרוב) מוסבר איך לכתוב את הקוד בחזית.

תהליך התשלום באמצעות אפליקציית תשלומים מבוססת-אינטרנט.

מעבירים הבטחה שנשמרה אל PaymentRequestEvent.respondWith() כדי שתוכלו לפתור את הבעיה עם תוצאת תשלום בעתיד.

[payment handler] service-worker.js:

…
self.addEventListener('paymentrequest', async e => {
…
  // Retain a promise for future resolution
  // Polyfill for PromiseResolver is provided below.
  resolver = new PromiseResolver();

  // Pass a promise that resolves when payment is done.
  e.respondWith(resolver.promise);
  // Open the checkout page.
  try {
    // Open the window and preserve the client
    client = await e.openWindow(checkoutURL);
    if (!client) {
      // Reject if the window fails to open
      throw 'Failed to open window';
    }
  } catch (err) {
    // Reject the promise on failure
    resolver.reject(err);
  };
});
…

אפשר להשתמש ב-polyfill PromiseResolver נוח כדי לפתור הבטחה בתזמון שרירותי.

class PromiseResolver {
  constructor() {
    this.promise_ = new Promise((resolve, reject) => {
      this.resolve_ = resolve;
      this.reject_ = reject;
    })
  }
  get promise() { return this.promise_ }
  get resolve() { return this.resolve_ }
  get reject() { return this.reject_ }
}

החלפת מידע עם הקצה הקדמי

ה-Service Worker של אפליקציית התשלום יכול להחליף הודעות עם הקצה הקדמי של אפליקציית התשלום באמצעות ServiceWorkerController.postMessage(). כדי לקבל הודעות מהחזית, צריך להאזין לאירועי message.

[payment handler] service-worker.js:

// Define a convenient `postMessage()` method
const postMessage = (type, contents = {}) => {
  if (client) client.postMessage({ type, ...contents });
}

קבלת האות המוכן מהחזית

אחרי שפותחים את חלון ה-handler של התשלום, ה-Service Worker צריך להמתין לאות מצב מוכן מהחזית של אפליקציית התשלומים. ה-Service Worker יכול להעביר מידע חשוב לממשק הקצה כשהוא מוכן.

[payment handler] Frontend:

navigator.serviceWorker.controller.postMessage({
  type: 'WINDOW_IS_READY'
});

[payment handler] service-worker.js:

…
// Received a message from the frontend
self.addEventListener('message', async e => {
  let details;
  try {
    switch (e.data.type) {
      // `WINDOW_IS_READY` is a frontend's ready state signal
      case 'WINDOW_IS_READY':
        const { total } = payment_request_event;
…

העברת פרטי העסקה לחזית

עכשיו צריך לשלוח בחזרה את פרטי התשלום. במקרה כזה, שולחים רק את הסכום הכולל של בקשת התשלום, אבל תוכלו להעביר פרטים נוספים אם תרצו.

[payment handler] service-worker.js:

…
        // Pass the payment details to the frontend
        postMessage('PAYMENT_IS_READY', { total });
        break;
…

[payment handler] Frontend:

let total;

navigator.serviceWorker.addEventListener('message', async e => {
  switch (e.data.type) {
      case 'PAYMENT_IS_READY':
        ({ total } = e.data);
        // Update the UI
        renderHTML(total);
        break;
…

החזרת פרטי הכניסה לתשלום של הלקוח

כשהלקוח מאשר את התשלום, החזית יכולה לשלוח ל-Service Worker הודעה בדואר כדי להמשיך. אפשר לטפל בהבטחה שהועברה אל PaymentRequestEvent.respondWith() כדי לשלוח את התוצאה חזרה למוכר. מעבירים אובייקט PaymentHandlerResponse.

שם הנכס תיאור
methodName מזהה אמצעי התשלום ששימש לביצוע התשלום.
details הנתונים הספציפיים של אמצעי התשלום שמספקים את המידע הנחוץ למוכר לצורך עיבוד תשלום.

[payment handler] Frontend:

  const paymentMethod = …

  postMessage('PAYMENT_AUTHORIZED', {
    paymentMethod,              // Payment method identifier
  });

[payment handler] service-worker.js:

…
// Received a message from the frontend
self.addEventListener('message', async e => {
  let details;
  try {
    switch (e.data.type) {
      …
      case 'PAYMENT_AUTHORIZED':
        // Resolve the payment request event promise
        // with a payment response object
        const response = {
          methodName: e.data.paymentMethod,
          details: { id: 'put payment credential here' },
        }
        resolver.resolve(response);
        // Don't forget to initialize.
        payment_request_event = null;
        break;
      …

לבטל את עסקת התשלום.

כדי לאפשר ללקוח לבטל את הטרנזקציה, החזית יכולה לשלוח ל-Service Worker הודעת פרסום כדי לעשות זאת. לאחר מכן, ה-Service Worker יכול לפתור את ההבטחה שהועברה ל-PaymentRequestEvent.respondWith() עם null כדי לציין למוכר שהעסקה בוטלה.

[payment handler] Frontend:

  postMessage('CANCEL_PAYMENT');

[payment handler] service-worker.js:

…
// Received a message from the frontend
self.addEventListener('message', async e => {
  let details;
  try {
    switch (e.data.type) {
      …
      case 'CANCEL_PAYMENT':
        // Resolve the payment request event promise
        // with null
        resolver.resolve(null);
        // Don't forget to initialize.
        payment_request_event = null;
        break;
      …

קוד לדוגמה

כל הקודים לדוגמה שראיתם במסמך הזה הם קטעים מהאפליקציה לדוגמה פועלת:

https://paymenthandler-demo.glitch.me

[payment handler] Service Worker

[payment handler]

כדי לנסות את הכלים:

  1. נכנסים לכתובת https://paymentrequest-demo.glitch.me/.
  2. עבור לתחתית הדף.
  3. לוחצים על הוספת לחצן תשלום.
  4. מזינים https://paymenthandler-demo.glitch.me בשדה מזהה אמצעי התשלום.
  5. לוחצים על הלחצן Pay (תשלום) ליד השדה.

השלבים הבאים

במאמר הזה למדנו איך לתזמר עסקת תשלום מעובדי שירות. בשלב הבא תלמדו איך להוסיף עוד תכונות מתקדמות ל-Service Worker.