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

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

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

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

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

Получите от продавца сообщение о запросе на оплату.

Когда клиент выбирает оплату через ваше веб-приложение для платежей, и продавец вызывает 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;

Верните платежные данные клиента.

Когда клиент авторизует платеж, фронтенд может отправить POST-запрос сервис-воркеру для продолжения. Вы можете разрешить промис, переданный в 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;
      

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

Чтобы позволить клиенту отменить транзакцию, фронтенд может отправить POST-запрос сервис-воркеру. Затем сервис-воркер может разрешить промис, переданный в 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;
      

Пример кода

Все примеры кода, которые вы видели в этом документе, являются выдержками из следующих материалов: Демонстрация веб-системы обработки платежей.

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

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