Como orquestrar transações de pagamento com um service worker

Como adaptar seu app de pagamento baseado na Web para pagamentos na Web e oferecer uma experiência do usuário melhor aos clientes.

Depois que o app de pagamento for registrado, você poderá aceitar solicitações de pagamento de comerciantes. Esta postagem explica como orquestrar uma transação de pagamento de um service worker durante a execução (ou seja, quando uma janela é exibida e o usuário está interagindo com ela).

Orquestrar transações de pagamento com um service worker
Orquestração de transações de pagamento com um service worker

"Mudanças de parâmetros de pagamento em tempo de execução" se referem a um conjunto de eventos que permite que o comerciante e o manipulador de pagamentos troquem mensagens enquanto o usuário interage com o manipulador de pagamentos. Saiba mais em Como processar informações de pagamento opcionais com um service worker.

Receber um evento de solicitação de pagamento do comerciante

Quando um cliente escolhe pagar com seu app de pagamento baseado na Web e o comerciante invoca PaymentRequest.show(), o service worker recebe um evento paymentrequest. Adicione um listener de eventos ao service worker para capturar o evento e se preparar para a próxima ação.

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

O PaymentRequestEvent preservado contém informações importantes sobre esta transação:

Nome da propriedade Descrição
topOrigin Uma string que indica a origem da página da Web de nível superior (geralmente o comerciante beneficiário). Use isso para identificar a origem do comerciante.
paymentRequestOrigin Uma string que indica a origem do invocador. Pode ser o mesmo que topOrigin quando o comerciante invoca a API Payment Request diretamente, mas pode ser diferente se a API for invocada de um iframe por um terceiro, como um gateway de pagamento.
paymentRequestId A propriedade id do PaymentDetailsInit fornecido à API Payment Request. Se o comerciante omitir, o navegador vai fornecer um ID gerado automaticamente.
methodData Os dados específicos da forma de pagamento fornecidos pelo comerciante como parte de PaymentMethodData. Use isso para determinar os detalhes da transação de pagamento.
total O valor total fornecido pelo comerciante como parte de PaymentDetailsInit. Use isso para criar uma interface que informe ao cliente o valor total a ser pago.
instrumentKey A chave de instrumento selecionada pelo usuário. Isso reflete o instrumentKey que você forneceu com antecedência. Uma string vazia indica que o usuário não especificou nenhum instrumento.

Abra a janela do gerenciador de pagamentos para mostrar o front-end do app de pagamento baseado na Web

Quando um evento paymentrequest é recebido, o app de pagamento pode abrir uma janela de processador de pagamentos chamando PaymentRequestEvent.openWindow(). A janela do manipulador de pagamentos vai apresentar aos clientes a interface do seu app de pagamentos, em que eles podem autenticar, escolher o endereço e as opções de entrega e autorizar o pagamento. Vamos abordar como escrever o código do front-end em Como processar pagamentos no front-end de pagamento (em breve).

Fluxo de finalização da compra com um app de pagamento baseado na Web.

Transmita uma promessa preservada para PaymentRequestEvent.respondWith() para que você possa resolvê-la com um resultado de pagamento no futuro.

[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);
  };
});

Você pode usar um polyfill PromiseResolver conveniente para resolver uma promessa em um momento arbitrário.

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_ }
}

Trocar informações com o front-end

O service worker do app de pagamento pode trocar mensagens com o front-end do app de pagamento usando ServiceWorkerController.postMessage(). Para receber mensagens do front-end, detecte eventos message.

[payment handler] service-worker.js:

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

Receber o sinal de pronto do front-end

Quando a janela do gerenciador de pagamento for aberta, o service worker vai aguardar um sinal de estado pronto do front-end do app de pagamento. O service worker pode transmitir informações importantes para o front-end quando estiver pronto.

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

Transmitir os detalhes da transação para o front-end

Agora envie os detalhes de pagamento de volta. Nesse caso, você está enviando apenas o total do pedido de pagamento, mas pode transmitir mais detalhes se quiser.

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

Retornar as credenciais de pagamento do cliente

Quando o cliente autoriza o pagamento, o front-end pode enviar uma mensagem post para o service worker e continuar. Você pode resolver a promessa transmitida para PaymentRequestEvent.respondWith() e enviar o resultado de volta para o comerciante. Transmita um objeto PaymentHandlerResponse.

Nome da propriedade Descrição
methodName O identificador da forma de pagamento usada para fazer o pagamento.
details Os dados específicos da forma de pagamento que fornecem as informações necessárias para o comerciante processar o pagamento.

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

Cancelar a transação de pagamento

Para permitir que o cliente cancele a transação, o front-end pode enviar uma mensagem post para o service worker. Em seguida, o service worker pode resolver a promessa transmitida para PaymentRequestEvent.respondWith() com null para indicar ao comerciante que a transação foi cancelada.

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

Código de amostra

Todos os exemplos de código mostrados neste documento são trechos da seguinte demonstração: Payment Handler Demo

Próximas etapas

Neste artigo, aprendemos a orquestrar uma transação de pagamento de um service worker. A próxima etapa é aprender a adicionar alguns recursos mais avançados ao service worker.