Orchestrazione delle transazioni di pagamento con un service worker

Come adattare l'app per pagamenti basata sul web ai pagamenti web e offrire una migliore esperienza utente ai clienti.

Una volta registrata l'app per i pagamenti, puoi accettare le richieste di pagamento da parte dei commercianti. Questo post spiega come orchestrare una transazione di pagamento da un service worker durante il runtime, ovvero quando viene visualizzata una finestra e l'utente vi sta interagendo.

Orchestrare le transazioni di pagamento con un service worker
Orchestrare le transazioni di pagamento con un service worker

Le "modifiche ai parametri di pagamento durante l'esecuzione" si riferiscono a un insieme di eventi che consentono al commerciante e al gestore dei pagamenti di scambiare messaggi mentre l'utente interagisce con il gestore dei pagamenti. Scopri di più nella sezione Gestire i dati di pagamento facoltativi con un service worker.

Ricevere un evento di richiesta di pagamento dal commerciante

Quando un cliente sceglie di pagare con la tua app per pagamenti basata sul web e il commerciante rivoca PaymentRequest.show(), il service worker riceverà un evento paymentrequest. Aggiungi un listener di eventi al service worker per acquisire l'evento e prepararti all'azione successiva.

[gestore dei pagamenti] 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;
…

Il PaymentRequestEvent conservato contiene informazioni importanti su questa transazione:

Nome proprietà Descrizione
topOrigin Una stringa che indica l'origine della pagina web di primo livello (di solito il commerciante del beneficiario). Utilizzalo per identificare l'origine del commerciante.
paymentRequestOrigin Una stringa che indica l'origine dell'invocatore. Può essere uguale a topOrigin se il commerciante richiama direttamente l'API Payment Request, ma potrebbe essere diverso se l'API viene richiamata dall'interno di un iframe da una terza parte, ad esempio un gateway di pagamento.
paymentRequestId La proprietà id di PaymentDetailsInit fornita all'API Payment Request. Se il commerciante omette, il browser fornirà un ID generato automaticamente.
methodData I dati specifici del metodo di pagamento forniti dal commerciante nell'ambito di PaymentMethodData. Utilizzalo per determinare i dettagli della transazione di pagamento.
total L'importo totale fornito dal commerciante nell'ambito di PaymentDetailsInit. Utilizzalo per creare un'interfaccia utente per comunicare al cliente l'importo totale da pagare.
instrumentKey Il tasto dello strumento selezionato dall'utente. Questo valore riflette i instrumentKey che hai fornito in anticipo. Una stringa vuota indica che l'utente non ha specificato alcun strumento.

Apri la finestra del gestore dei pagamenti per visualizzare il frontend dell'app di pagamento basata sul web

Quando viene ricevuto un evento paymentrequest, l'app di pagamento può aprire una finestra del gestore dei pagamenti chiamando il numero PaymentRequestEvent.openWindow(). La finestra del gestore dei pagamenti mostrerà ai clienti l'interfaccia dell'app per i pagamenti, in cui potranno eseguire l'autenticazione, scegliere l'indirizzo di spedizione e le opzioni e autorizzare il pagamento. Vedremo come scrivere il codice frontend in Gestione dei pagamenti sul frontend di pagamento (disponibile a breve).

Flusso di pagamento con un'app per pagamenti basata sul web.

Passa una promessa mantenuta a PaymentRequestEvent.respondWith() in modo da poter risolvere il problema con un risultato di pagamento in futuro.

[gestore dei pagamenti] 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);
  };
});
…

Puoi utilizzare un pratico polyfill di PromiseResolver per risolvere una promessa in tempi arbitrari.

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

Scambiare informazioni con il frontend

Il service worker dell'app di pagamento può scambiare messaggi con il frontend dell'app di pagamento tramite ServiceWorkerController.postMessage(). Per ricevere messaggi dal frontend, ascolta gli eventi message.

[gestore dei pagamenti] service-worker.js:

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

Ricevi il segnale di pronto dal frontend

Una volta aperta la finestra del gestore dei pagamenti, il service worker deve attendere un segnale di stato di pronto dal frontend dell'app di pagamento. Il service worker può passare informazioni importanti al frontend quando è pronto.

frontend [gestore dei pagamenti]:

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

[gestore dei pagamenti] 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;
…

Passare i dettagli della transazione al frontend

Ora invia nuovamente i dettagli di pagamento. In questo caso, viene inviato solo il totale della richiesta di pagamento, ma se vuoi puoi fornire ulteriori dettagli.

[gestore dei pagamenti] service-worker.js:

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

frontend [gestore dei pagamenti]:

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

Restituire le credenziali di pagamento del cliente

Quando il cliente autorizza il pagamento, il frontend può inviare un messaggio postale al service worker per procedere. Puoi risolvere la promessa trasmessa a PaymentRequestEvent.respondWith() di restituire il risultato al commerciante. Trasmetti un oggetto PaymentHandlerResponse.

Nome proprietà Descrizione
methodName L'identificatore del metodo di pagamento utilizzato per effettuare il pagamento.
details I dati specifici del metodo di pagamento che forniscono al commerciante le informazioni necessarie per elaborare il pagamento.

frontend [gestore dei pagamenti]:

  const paymentMethod = …

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

[gestore dei pagamenti] 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;
      …

Annullare la transazione di pagamento

Per consentire al cliente di annullare la transazione, il frontend può inviare un messaggio successivo al service worker per farlo. Il service worker potrà quindi risolvere la promessa trasmessa a PaymentRequestEvent.respondWith() con null per indicare al commerciante che la transazione è stata annullata.

frontend [gestore dei pagamenti]:

  postMessage('CANCEL_PAYMENT');

[gestore dei pagamenti] 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;
      …

Codice campione

Tutti i codici di esempio che hai visto in questo documento sono estratti dalla seguente app di esempio funzionante:

https://paymenthandler-demo.glitch.me

Service worker [gestore dei pagamenti]

frontend [gestore dei pagamenti]

Per provarla:

  1. Vai alla pagina https://paymentrequest-demo.glitch.me/.
  2. Vai alla parte inferiore della pagina.
  3. Premi Aggiungi un pulsante di pagamento.
  4. Inserisci https://paymenthandler-demo.glitch.me nel campo Identificatore metodo di pagamento.
  5. Premi il pulsante Paga accanto al campo.

Passaggi successivi

In questo articolo abbiamo imparato a orchestrare una transazione di pagamento da un service worker. Il prossimo passaggio consiste nell'imparare ad aggiungere alcune funzionalità più avanzate al service worker.