Организация платежных транзакций с помощью сервис-воркера

Как адаптировать ваше онлайн-платежное приложение к веб-платежам и обеспечить лучший пользовательский опыт для клиентов.

После регистрации платежного приложения вы сможете принимать платежные запросы от продавцов. В этом посте объясняется, как организовать платежную транзакцию от сервис-воркера во время выполнения (т. е. когда отображается окно и пользователь взаимодействует с ним).

Организация платежных транзакций с помощью сервис-воркера
Организация платежных транзакций с помощью сервис-воркера

«Изменения параметров оплаты во время выполнения» относятся к набору событий, которые позволяют продавцу и обработчику платежей обмениваться сообщениями, пока пользователь взаимодействует с обработчиком платежей. Дополнительные сведения см. в разделе «Обработка дополнительной платежной информации с помощью сервисного работника» .

Получить событие запроса платежа от продавца

Когда клиент решает оплатить с помощью вашего веб-платежного приложения, а продавец вызывает PaymentRequest.show() , ваш сервисный работник получит событие paymentrequest . Добавьте прослушиватель событий в сервис-воркер, чтобы перехватить событие и подготовиться к следующему действию.

[обработчик платежей] 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 Строка, указывающая происхождение инициатора. Это может быть то же самое, что и topOrigin когда продавец напрямую вызывает API запроса платежа, но может быть другим, если API вызывается из iframe третьей стороной, например платежным шлюзом.
paymentRequestId Свойство id объекта PaymentDetailsInit предоставленное API запроса платежа. Если продавец пропустит этот параметр, браузер предоставит автоматически сгенерированный идентификатор.
methodData Данные о способе оплаты, предоставленные продавцом как часть PaymentMethodData . Используйте это для определения деталей платежной транзакции.
total Общая сумма, предоставленная продавцом как часть PaymentDetailsInit . Используйте это для создания пользовательского интерфейса, позволяющего клиенту узнать общую сумму к оплате.
instrumentKey Ключ инструмента, выбранный пользователем. Это отражает instrumentKey который вы указали заранее. Пустая строка означает, что пользователь не указал никаких инструментов.

Откройте окно обработчика платежей, чтобы отобразить интерфейс веб-платежного приложения.

При получении события paymentrequest платежное приложение может открыть окно обработчика платежей, вызвав PaymentRequestEvent.openWindow() . В окне обработчика платежей клиентам будет представлен интерфейс вашего платежного приложения, где они смогут пройти аутентификацию, выбрать адрес и параметры доставки, а также авторизовать платеж. Мы расскажем, как написать код внешнего интерфейса в разделе «Обработка платежей» (скоро).

Процесс оформления заказа с помощью веб-приложения для оплаты.

Передайте сохраненное обещание в PaymentRequestEvent.respondWith() , чтобы в будущем можно было разрешить его с результатом платежа.

[обработчик платежей] 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);
  };
});
…

Вы можете использовать удобный полифил 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_ }
}

Обмен информацией с фронтендом

Сервис-воркер платежного приложения может обмениваться сообщениями с интерфейсом платежного приложения через ServiceWorkerController.postMessage() . Чтобы получать сообщения от внешнего интерфейса, прослушивайте события message .

[обработчик платежей] service-worker.js:

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

Получите сигнал готовности от фронтенда

После открытия окна обработчика платежей сервис-воркер должен дождаться сигнала о готовности от интерфейса платежного приложения. Работник службы может передать важную информацию во внешний интерфейс, когда она будет готова.

[обработчик платежей] интерфейс:

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

[обработчик платежей] 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;
…

Передайте детали транзакции во внешний интерфейс

Теперь отправьте платежные реквизиты обратно. В этом случае вы отправляете только общую сумму платежного запроса, но при желании можете передать более подробную информацию.

[обработчик платежей] service-worker.js:

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

[обработчик платежей] интерфейс:

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;
…

Возврат платежных данных клиента

Когда клиент авторизует платеж, интерфейс может отправить почтовое сообщение работнику службы для продолжения. Вы можете разрешить обещание, переданное в PaymentRequestEvent.respondWith() , чтобы отправить результат обратно продавцу. Передайте объект PaymentHandlerResponse .

Имя свойства Описание
methodName Идентификатор способа оплаты, используемый для совершения платежа.
details Данные о способе оплаты, которые предоставляют продавцу необходимую информацию для обработки платежа.

[обработчик платежей] интерфейс:

  const paymentMethod = …

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

[обработчик платежей] 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;
      …

Отменить платежную транзакцию

Чтобы клиент мог отменить транзакцию, интерфейсная часть может отправить для этого почтовое сообщение сервисному работнику. Затем сервисный работник может разрешить обещание, переданное в PaymentRequestEvent.respondWith() со значением null , чтобы указать продавцу, что транзакция была отменена.

[обработчик платежей] интерфейс:

  postMessage('CANCEL_PAYMENT');

[обработчик платежей] 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://payhandler-demo.glitch.me

[обработчик платежей] работник службы поддержки

[обработчик платежей] интерфейс

Чтобы попробовать:

  1. Перейдите на https://payrequest-demo.glitch.me/ .
  2. Перейдите в конец страницы.
  3. Нажмите кнопку «Добавить платеж» .
  4. Введите https://paymenthandler-demo.glitch.me в поле «Идентификатор способа оплаты» .
  5. Нажмите кнопку «Оплатить» рядом с полем.

Следующие шаги

В этой статье мы узнали, как организовать платежную транзакцию у сервис-воркера. Следующий шаг — научиться добавлять в сервис-воркера некоторые более продвинутые функции.