Aggiornamento dell'ordine asincrono

Dopo che un cliente ha inviato un ordine di cibo, puoi inviare un messaggio di aggiornamento dell'ordine al servizio end-to-end dell'ordine per comunicarci la modifica.

Di seguito sono riportati alcuni motivi comuni per l'invio di aggiornamenti sugli ordini:

  • Il tempo di evasione stimato per l'ordine diventa disponibile o cambia.
  • Lo stato di un ordine cambia.
  • L'ordine non può più essere completato.
  • Il prezzo di una voce del menu inclusa nell'ordine è cambiato.
  • Il cliente ha a disposizione un nuovo modo per gestire il proprio ordine, ad esempio un numero di telefono dell'assistenza clienti o un numero di telefono di un ristorante.
  • La ricevuta dell'ordine diventa disponibile.

Le sezioni successive forniscono dettagli su come affrontare questi diversi scenari utilizzando gli aggiornamenti degli ordini.

Transizione degli stati degli ordini

Un ordine presenta sei stati possibili. Questi stati e le loro possibili transizioni sono descritti nel seguente diagramma:

Transizioni di stato dell'ordine

Quando un cliente invia per la prima volta un ordine, questo inizia con lo stato CREATED, CONFIRMED o REJECTED. Puoi inviare un messaggio di aggiornamento dell'ordine per aggiornare lo stato di un ordine, purché la transizione sia valida. Lo stato CREATED viene utilizzato quando la piattaforma del partner non può confermare o rifiutare immediatamente l'ordine. Un caso d'uso di esempio è quando un cliente ordina tramite un aggregatore di pubblicazione. L'aggregatore di consegne riceve la consegna da Google e invia le informazioni al ristorante. Dopo che il ristorante ha ricevuto e confermato la disponibilità dell'ordine, lo stato può essere ora CONFIRMED, altrimenti REJECTED.

In seguito, un ordine con stato CONFIRMED passa allo stato IN_PREPARATION. A seconda che l'ordine sia per il ritiro o la consegna, utilizza lo stato READY_FOR_PICKUP o IN_TRANSIT. Quando il cibo viene consegnato o ritirato, l'ordine viene impostato sullo stato FULFILLED.

Se consenti ai clienti di annullare gli ordini, puoi utilizzare lo stato CANCELLED. Un ordine può essere annullato mentre è in stato CREATED, CONFIRMED, IN_PREPARATION, READY_FOR_PICKUP o IN_TRANSIT. Il tuo servizio end-to-end degli ordini dovrebbe emettere rimborsi in base alle tue norme relative all'annullamento e allo stato dei pagamenti al momento dell'annullamento.

Il tuo servizio end-to-end degli ordini non deve supportare tutti gli stati e le transizioni disponibili. Tuttavia, lo stato finale dell'ordine deve essere FULFILLED, REJECTED o CANCELLED.

Fornire un tempo di evasione stimato

Puoi fornire agli utenti un intervallo di tempo stimato in cui il loro ordine sarà pronto per il ritiro (o la consegna). Utilizza il campo estimatedFulfillmentTimeIso8601 di FoodOrderUpdateExtension per fornire un intervallo di tempo stimato entro il quale l'ordine di un cliente sarà pronto per il ritiro o la consegna.

Invia estimatedFulfillmentTimeIso8601 ai seguenti orari:

  • Quando il tempo stimato diventa disponibile, idealmente nell'ordine CREATED o CONFIRMED.
  • Quando cambia l'orario stimato, ad esempio viene aggiornato l'orario stimato per essere più preciso quando l'ordine è IN_TRANSIT.

Per gestire in modo efficace le aspettative degli utenti, adotta un approccio conservativo nelle stime e specifica un intervallo di date e ore anziché una data e un'ora fisse. Devi tenere conto delle variazioni, come le condizioni del traffico, quando possibile. Ad esempio, puoi inviare una stima dell'intervallo tra le 12:45 (limite inferiore) e le 13:15 (limite superiore) per un ordine il cui tempo di consegna stimato corrisponde alle 13:00.

Fornire azioni di gestione degli ordini

Quando invii l'aggiornamento di un ordine, puoi fornire ai clienti risorse che li aiutano a gestire il loro ordine sotto forma di OrderManagementAction. Dopo che un cliente ha effettuato un ordine, potrebbe dover contattare te o il ristorante che evade l'ordine per monitorare l'avanzamento, apportare modifiche o annullare l'ordine.

Un OrderManagementAction consente ai clienti di inviare email, chiamare o inserire un link a un URL direttamente dal proprio dispositivo. Utilizza le stesse informazioni in OrderManagementAction e nell'email di conferma dell'ordine che invii all'utente.

Le azioni di gestione degli ordini includono i seguenti tipi:

  • CUSTOMER_SERVICE: fornisci ai clienti un'azione per contattare l'assistenza clienti. Questo tipo di azione di gestione è obbligatorio per gli aggiornamenti degli ordini.
  • EMAIL: consente ai clienti di inviare un'email all'indirizzo fornito.
  • CALL: offri ai clienti un'azione per chiamare il numero di telefono fornito.
  • VIEW_DETAIL: consente ai clienti di visualizzare i dettagli dell'ordine.

Ogni aggiornamento dell'ordine deve contenere almeno un'azione di gestione degli ordini. Tuttavia, le azioni di gestione degli ordini fornite possono variare in base allo stato dell'ordine. Ad esempio, quando un ordine è nello stato CONFIRMED, l'azione CUSTOMER_SERVICE può puntare al numero di telefono dell'assistenza clienti. Quando lo stato dell'ordine viene aggiornato a IN_TRANSIT, l'azione CUSTOMER_SERVICE può puntare al numero di telefono del ristorante di evasione degli ordini.

Invio di aggiornamenti sull'ordine

Puoi utilizzare il tipo di messaggio AsyncOrderUpdateRequestMessage per inviare un aggiornamento dell'ordine al servizio end-to-end degli ordini. Google risponde con un AsyncOrderUpdateResponseMessage. Ad esempio, se vuoi comunicare a un cliente che il suo ordine è valido e accettato, puoi inviare un AsyncOrderUpdateRequestMessage per modificare lo stato dell'ordine in CONFIRMED con l'etichetta Accepted by restaurant.

Diagramma di aggiornamento dell'ordine

Impostazione del messaggio di aggiornamento dell'ordine

Quando invii un'istruzione AsyncOrderUpdateRequestMessage a Google, devi includere informazioni sullo stato dell'ordine utilizzando il campo OrderUpdate.

I seguenti esempi mostrano un esempio AsyncOrderUpdateRequestMessage per ogni stato dell'ordine:

CONFERMATO

Questo esempio mostra una richiesta di aggiornamento dell'ordine di esempio che informa l'utente che l'ordine è stato confermato con una ricevuta e i tempi di consegna stimati.

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "CONFIRMED",
        "label": "Provider confirmed"
      },
      "receipt": {
        "userVisibleOrderId": "userVisibleId1234"
      },
      "updateTime": "2017-07-17T12:00:00Z",
      "orderManagementActions": [
        {
          "type": "CUSTOMER_SERVICE",
          "button": {
            "title": "Contact customer service",
            "openUrlAction": {
              "url": "mailto:support@example.com"
            }
          }
        },
        {
          "type": "EMAIL",
          "button": {
            "title": "Email restaurant",
            "openUrlAction": {
              "url": "mailto:person@example.com"
            }
          }
        },
        {
          "type": "CALL_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension": {
        "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
        "estimatedFulfillmentTimeIso8601": "2017-07-17T13:00:00Z/2017-07-17T13:30:00Z"
      }
    }
  }
}
    

RIFIUTATA

Questo esempio mostra una richiesta di aggiornamento dell'ordine di esempio che informa l'utente che l'ordine è stato rifiutato con un motivo del rifiuto.

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "REJECTED",
        "label": "Order rejected"
      },
      "updateTime": "2017-05-10T02:30:00.000Z",
      "rejectionInfo": {
        "type": "UNKNOWN",
        "reason": "Sorry, the restaurant cannot take your order right now."
      },
      "orderManagementActions": [
        {
          "type": "CUSTOMER_SERVICE",
          "button": {
            "title": "Contact customer service",
            "openUrlAction": {
              "url": "mailto:support@example.com"
            }
          }
        },
        {
          "type": "EMAIL",
          "button": {
            "title": "Email restaurant",
            "openUrlAction": {
              "url": "mailto:person@example.com"
            }
          }
        },
        {
          "type": "CALL_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension": {
      "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
      "foodOrderErrors": [
        {
        "error": "NO_CAPACITY",
        "description": "Sorry, the restaurant cannot take your order right now."
        }
      ]
      }
    }
  }
}
    

CANCELLATO

Questo esempio mostra una richiesta di aggiornamento dell'ordine di esempio che informa l'utente che l'ordine è stato annullato con un motivo dell'annullamento.

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "CANCELLED",
        "label": "Order cancelled"
      },
      "updateTime": "2017-05-10T02:30:00.000Z",
      "cancellationInfo": {
        "reason": "Customer requested"
      },
      "orderManagementActions": [
        {
          "type": "CUSTOMER_SERVICE",
          "button": {
            "title": "Contact customer service",
            "openUrlAction": {
              "url": "mailto:support@example.com"
            }
          }
        },
        {
          "type": "EMAIL",
          "button": {
            "title": "Email restaurant",
            "openUrlAction": {
              "url": "mailto:person@example.com"
            }
          }
        },
        {
          "type": "CALL_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ]
    }
  }
}
    

IN_PREPARATION

Questo esempio mostra una richiesta di aggiornamento dell'ordine di esempio che informa l'utente che il cibo è in fase di preparazione.

{
  "isInSandbox":true,
  "customPushMessage":{
    "orderUpdate":{
      "actionOrderId":"sample_action_order_id",
      "orderState":{
        "state":"IN_PREPARATION",
        "label":"Order is being prepared"
      },
      "receipt": {
        "userVisibleOrderId": "userVisibleId1234"
      },
      "updateTime":"2018-04-15T11:30:00Z",
      "orderManagementActions": [
        {
          "type": "CUSTOMER_SERVICE",
          "button": {
            "title": "Contact customer service",
            "openUrlAction": {
              "url": "mailto:support@example.com"
            }
          }
        },
        {
          "type": "EMAIL",
          "button": {
            "title": "Email restaurant",
            "openUrlAction": {
              "url": "mailto:person@example.com"
            }
          }
        },
        {
          "type": "CALL_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension":{
        "@type":"type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
        "estimatedFulfillmentTimeIso8601":"PT20M"
      }
    }
  }
}
    

READY_FOR_PICKUP

Questo esempio mostra una richiesta di aggiornamento dell'ordine di esempio che informa l'utente che il cibo è pronto per il ritiro.

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "READY_FOR_PICKUP",
        "label": "Order is ready for pickup"
      },
      "receipt": {
        "userVisibleOrderId": "userVisibleId1234"
      },
      "updateTime": "2018-04-15T12:00:00Z",
      "orderManagementActions": [
        {
          "type": "CUSTOMER_SERVICE",
          "button": {
            "title": "Contact customer service",
            "openUrlAction": {
              "url": "mailto:support@example.com"
            }
          }
        },
        {
          "type": "EMAIL",
          "button": {
            "title": "Email restaurant",
            "openUrlAction": {
              "url": "mailto:person@example.com"
            }
          }
        },
        {
          "type": "CALL_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension": {
        "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
        "estimatedFulfillmentTimeIso8601": "PT20M"
      }
    }
  }
}
    

IN_TRANSIT

Questo esempio mostra una richiesta di aggiornamento dell'ordine di esempio che informa l'utente che l'ordine è in transito con i tempi di consegna stimati.

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
        "state": "IN_TRANSIT",
        "label": "Order is on the way"
      },
      "inTransitInfo": {
        "updatedTime": "2017-07-17T12:00:00Z"
      },
      "updateTime": "2017-07-17T12:00:00Z",
      "orderManagementActions": [
        {
          "type": "CUSTOMER_SERVICE",
          "button": {
            "title": "Contact customer service",
            "openUrlAction": {
              "url": "mailto:support@example.com"
            }
          }
        },
        {
          "type": "EMAIL",
          "button": {
            "title": "Email restaurant",
            "openUrlAction": {
              "url": "mailto:person@example.com"
            }
          }
        },
        {
          "type": "CALL_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ],
      "infoExtension": {
        "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension",
        "estimatedFulfillmentTimeIso8601": "PT20M"
      }
    }
  }
}
  

COMPLETATO

Questo esempio mostra una richiesta di aggiornamento dell'ordine di esempio che informa l'utente che l'ordine è stato ritirato o consegnato:

{
  "isInSandbox": true,
  "customPushMessage": {
    "orderUpdate": {
      "actionOrderId": "sample_action_order_id",
      "orderState": {
      "state": "FULFILLED",
      "label": "Order delivered"
      },
      "updateTime": "2017-05-10T02:30:00.000Z",
      "fulfillmentInfo": {
        "deliveryTime": "2017-05-10T02:30:00.000Z"
      },
      "orderManagementActions": [
        {
          "type": "CUSTOMER_SERVICE",
          "button": {
            "title": "Contact customer service",
            "openUrlAction": {
              "url": "mailto:support@example.com"
            }
          }
        },
        {
          "type": "EMAIL",
          "button": {
            "title": "Email restaurant",
            "openUrlAction": {
              "url": "mailto:person@example.com"
            }
          }
        },
        {
          "type": "CALL_RESTAURANT",
          "button": {
            "title": "Call restaurant",
            "openUrlAction": {
              "url": "tel:+16505554679"
            }
          }
        },
        {
          "type": "CALL_DRIVER",
          "button": {
            "title": "Call driver",
            "openUrlAction": {
              "url": "tel:+16505554681"
            }
          }
        }
      ]
    }
  }
}
    

Per altri esempi di richieste di aggiornamento degli ordini in diversi casi d'uso, consulta Implementare gli aggiornamenti avanzati degli ordini.

Genera il token di autorizzazione e invia il messaggio

Gli aggiornamenti degli ordini richiedono un token di autorizzazione per consentire al servizio end-to-end di ordinazione di verificare che il messaggio provenga dal tuo servizio web end-to-end di ordinazione.

Per implementare gli aggiornamenti degli ordini per il tuo progetto:

  1. Per generare un token di autorizzazione:
    1. Utilizza la libreria di autenticazione Google per leggere le credenziali dal file dell'account di servizio.
    2. Richiedi il token utilizzando il seguente ambito API: https://www.googleapis.com/auth/actions.fulfillment.conversation
  2. Utilizza questo token per inviare una richiesta POST HTTP autenticata al seguente endpoint: https://actions.googleapis.com/v2/conversations:send
  3. Imposta l'intestazione Content-Type su application/json come parte della richiesta.

I seguenti esempi mostrano come implementare gli aggiornamenti degli ordini:

Node.js

Questo codice utilizza la libreria di autenticazione di Google per Node.js.

const {auth} = require('google-auth-library')
const request = require('request');
// The service account client secret file downloaded from the Google Cloud Console
const serviceAccountJson = require('./service-account.json')
// order-update.json is a file that contains the payload
const jsonBody = require('./order-update.json')

/**
 * Get the authorization token using a service account.
 */
async function getAuthToken() {
  let client = auth.fromJSON(serviceAccountJson)
  client.scopes = ['https://www.googleapis.com/auth/actions.fulfillment.conversation']
  const tokens = await client.authorize()
  return tokens.access_token;
}

/**
 * Send an order update request
 */
async function sendOrderUpdate() {
  const token = await getAuthToken()
  request.post({
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    },
    url: 'https://actions.googleapis.com/v2/conversations:send',
    body: jsonBody,
    json: true
  },
  (err, res, body) => {
    if (err) { return console.log(err); }
    console.log(`Response: ${JSON.stringify(res)}`)
  })
}
    

Python

Questo codice utilizza la libreria di autenticazione di Google per Python.

from google.oauth2 import service_account
from google.auth.transport.requests import AuthorizedSession
import json

# service-account.json is the service account client secret file downloaded from the
# Google Cloud Console
credentials = service_account.Credentials.from_service_account_file(
    'service-account.json')

scoped_credentials = credentials.with_scopes(
    ['https://www.googleapis.com/auth/actions.fulfillment.conversation'])

authed_session = AuthorizedSession(scoped_credentials)

# order-update.json is a file that contains the payload
json_payload=json.load(open('order-update.json'))

response = authed_session.post(
    'https://actions.googleapis.com/v2/conversations:send',
    json=json_payload)
    

Java

Questo codice utilizza la libreria di autenticazione di Google per Java.

/**
 * Get the authorization token using a service account.
 */
private static String getAuthToken() {
  InputStream serviceAccountFile = Example.class.getClassLoader().getResourceAsStream("service-account.json");
  ServiceAccountCredentials.Builder credentialsSimpleBuilder =
      ServiceAccountCredentials.fromStream(serviceAccountFile).toBuilder();
  credentialsSimpleBuilder.setScopes(ImmutableList.of("https://www.googleapis.com/auth/actions.fulfillment.conversation"));
  AccessToken accessToken = credentialsSimpleBuilder.build().refreshAccessToken();
  return accessToken.getTokenValue();
}

/**
 * Send an order update request
 */
public void sendOrderUpdate() {
  String authToken = getAuthToken();
  // Execute POST request
  executePostRequest("https://actions.googleapis.com/v2/conversations:send",
      authToken, "update_order_example.json",);
}
    

Per gli aggiornamenti dell'ordine riusciti senza errori, Google restituisce una risposta HTTP 200 con un payload vuoto. Se si è verificato un problema, ad esempio un aggiornamento non corretto, Google restituisce un errore.