Crea prenotazioni (Dialogflow)

Questa guida ti illustrerà il processo di sviluppo di un progetto Actions che utilizza l'API Orders per effettuare le prenotazioni.

Flusso delle transazioni

Quando il progetto Actions gestisce le prenotazioni, utilizza il seguente flusso:

  1. (Facoltativo) Convalidare i requisiti delle transazioni: utilizza l'helper dei requisiti delle transazioni all'inizio della conversazione per assicurarti che l'utente sia in grado di effettuare una transazione.
  2. Creare l'ordine: guida l'utente attraverso un "assemblaggio del carrello" in cui crea i dettagli della prenotazione.
  3. Proponi l'ordine: una volta completato il "carrello", proponi "ordine" di prenotazione all'utente in modo che possa confermare che è corretto. Se la prenotazione viene confermata, riceverai una risposta con i dettagli della prenotazione.
  4. Finalizzare l'ordine e inviare una ricevuta: dopo aver confermato l'ordine, aggiorna il sistema di prenotazione e invia una ricevuta all'utente.
  5. Inviare aggiornamenti sull'ordine: nel corso della durata della prenotazione, fornisci all'utente aggiornamenti dello stato di prenotazione inviando richieste PATCH all'API Orders.

Limitazioni e linee guida per le recensioni

Tieni presente che le norme aggiuntive si applicano alle azioni che utilizzano l'API Transaction e Orders. Potrebbero essere necessarie fino a sei settimane per esaminare le azioni con transazioni, quindi tieni conto di questo tempo quando pianifichi la pianificazione della pubblicazione. Per facilitare la procedura di revisione, assicurati di rispettare le norme e linee guida per le transazioni prima di inviare l'Azione per la revisione.

Puoi eseguire il deployment di Actions che utilizzano l'API Orders solo nei seguenti paesi:

Australia
Brasile
Canada
Indonesia
Giappone
Messico
Qatar
Russia
Singapore
Svizzera
Thailandia
Turchia
Regno Unito
Stati Uniti

Crea il tuo progetto

Per esempi completi di conversazioni transazionali, visualizza i nostri esempi di transazioni in Node.js e Java.

Configurazione del progetto

Quando crei l'Azione, devi specificare che vuoi eseguire transazioni nella Console di Actions. Inoltre, se utilizzi la libreria client Node.JS, configura il fulfillment in modo che utilizzi la versione più recente dell'API Orders.

Per configurare il progetto e il fulfillment:

  1. Crea un nuovo progetto o importa un progetto esistente.
  2. Vai a Deployment > Informazioni sulla directory.
  3. In Informazioni aggiuntive > Transazioni > seleziona la casella "Le tue azioni utilizzano l'API Transactions per eseguire transazioni di beni fisici?".

  4. Se utilizzi la libreria client Node.JS per creare il fulfillment dell'azione, apri il codice di fulfillment e aggiorna la descrizione dell'app per impostare il flag ordersv3 su true. Lo snippet di codice riportato di seguito mostra una dichiarazione di esempio dell'app per Orders versione 3.

Node.js

const {dialogflow} = require('actions-on-google');
let app = dialogflow({
  clientId, // If using account linking
  debug: true,
  ordersv3: true,
});

Node.js

const {actionssdk} = require('actions-on-google');
let app = actionssdk({
  clientId, // If using account linking
  debug: true,
  ordersv3: true,
});

1. (Facoltativo) Convalida i requisiti della transazione

Esperienza utente

Non appena l'utente ha indicato di voler configurare una prenotazione, ti consigliamo di attivare l'intent actions.intent.TRANSACTION_REQUIREMENTS_CHECK per assicurarti che possa richiedere una prenotazione. Ad esempio, quando viene richiamata, l'Azione potrebbe chiedere "Vuoi prenotare un posto?" Se l'utente risponde "sì", devi richiedere subito questo intent. In questo modo, potrà procedere e l'utente avrà l'opportunità di correggere eventuali impostazioni che impediscono di continuare la transazione.

La richiesta dei requisiti delle transazioni controlla l'intent in uno dei seguenti risultati:

  • Se i requisiti sono soddisfatti, l'evasione degli ordini riceve un intent con una condizione di successo e puoi procedere con la creazione dell'ordine dell'utente.
  • In caso contrario, il fulfillment riceve l'intent con una condizione di errore. In questo caso, termina la conversazione o esci dal flusso di prenotazione.

    Se l'utente è in grado di correggere l'errore, gli verrà chiesto automaticamente di risolvere il problema sul dispositivo. Se la conversazione si svolge su una piattaforma con comandi vocali come uno smart speaker, la conversazione viene consegnata al telefono dell'utente.

Evasione dell'ordine

Per assicurarti che un utente soddisfi i requisiti delle transazioni, richiedi il fulfillment dell'intent actions.intent.TRANSACTION_REQUIREMENTS_CHECK con un oggetto TransactionRequirementsCheckSpec.

Controlla i requisiti

Verifica se un utente soddisfa i requisiti di prenotazione con la libreria client:

Node.js
conv.ask(new TransactionRequirements());
Java
return getResponseBuilder(request)
    .add("Placeholder for transaction requirements text")
    .add(new TransactionRequirements())
    .build();
JSON Dialogflow

Tieni presente che il JSON di seguito descrive una risposta webhook.

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "systemIntent": {
        "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK",
        "data": {
          "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec"
        }
      }
    }
  }
}
JSON SDK Actions

Tieni presente che il JSON di seguito descrive una risposta webhook.

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK",
          "inputValueData": {
            "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec"
          }
        }
      ]
    }
  ]
}
Ricevere il risultato di un controllo dei requisiti

Una volta che l'assistente ha soddisfatto l'intent, invia una richiesta al fulfillment con l'intent actions.intent.TRANSACTION_REQUIREMENTS_CHECK con il risultato del controllo.

Per gestire correttamente questa richiesta, dichiara un intent di Dialogflow attivato dall'evento actions_intent_TRANSACTION_REQUIREMENTS_CHECK. Quando viene attivato, gestisci questo intent nel fulfillment:

Node.js
const arg = conv.arguments.get('TRANSACTION_REQUIREMENTS_CHECK_RESULT');
if (arg && arg.resultType === 'CAN_TRANSACT') {
  // Normally take the user through cart building flow
  conv.ask(`Looks like you're good to go!`);
} else {
  conv.close('Transaction failed.');
}
Java
Argument transactionCheckResult = request
    .getArgument("TRANSACTION_REQUIREMENTS_CHECK_RESULT");
boolean result = false;
if (transactionCheckResult != null) {
  Map<String, Object> map = transactionCheckResult.getExtension();
  if (map != null) {
    String resultType = (String) map.get("resultType");
    result = resultType != null && resultType.equals("CAN_TRANSACT");
  }
}
ResponseBuilder responseBuilder = getResponseBuilder(request);
if (result) {
  responseBuilder.add("Looks like you're good to go! Now say 'confirm transaction'");
} else {
  responseBuilder.add("Transaction failed");
}
return responseBuilder.build();
JSON Dialogflow

Tieni presente che il JSON di seguito descrive una richiesta webhook.

{
  "responseId": "",
  "queryResult": {
    "queryText": "",
    "action": "",
    "parameters": {},
    "allRequiredParamsPresent": true,
    "fulfillmentText": "",
    "fulfillmentMessages": [],
    "outputContexts": [],
    "intent": {
      "name": "reservation_transaction_check_complete_df",
      "displayName": "reservation_transaction_check_complete_df"
    },
    "intentDetectionConfidence": 1,
    "diagnosticInfo": {},
    "languageCode": ""
  },
  "originalDetectIntentRequest": {
    "source": "google",
    "version": "2",
    "payload": {
      "isInSandbox": true,
      "surface": {
        "capabilities": [
          {
            "name": "actions.capability.SCREEN_OUTPUT"
          },
          {
            "name": "actions.capability.AUDIO_OUTPUT"
          },
          {
            "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
          },
          {
            "name": "actions.capability.WEB_BROWSER"
          }
        ]
      },
      "inputs": [
        {
          "rawInputs": [],
          "intent": "",
          "arguments": [
            {
              "extension": {
                "@type": "type.googleapis.com/google.transactions.v3.TransactionRequirementsCheckResult",
                "resultType": "CAN_TRANSACT"
              },
              "name": "TRANSACTION_REQUIREMENTS_CHECK_RESULT"
            }
          ]
        }
      ],
      "user": {},
      "conversation": {},
      "availableSurfaces": [
        {
          "capabilities": [
            {
              "name": "actions.capability.SCREEN_OUTPUT"
            },
            {
              "name": "actions.capability.AUDIO_OUTPUT"
            },
            {
              "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
            },
            {
              "name": "actions.capability.WEB_BROWSER"
            }
          ]
        }
      ]
    }
  },
  "session": ""
}
JSON SDK Actions

Tieni presente che il JSON di seguito descrive una richiesta webhook.

{
  "user": {},
  "device": {},
  "surface": {
    "capabilities": [
      {
        "name": "actions.capability.SCREEN_OUTPUT"
      },
      {
        "name": "actions.capability.AUDIO_OUTPUT"
      },
      {
        "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
      },
      {
        "name": "actions.capability.WEB_BROWSER"
      }
    ]
  },
  "conversation": {},
  "inputs": [
    {
      "rawInputs": [],
      "intent": "reservation_transaction_check_complete_asdk",
      "arguments": [
        {
          "extension": {
            "@type": "type.googleapis.com/google.transactions.v3.TransactionRequirementsCheckResult",
            "resultType": "CAN_TRANSACT"
          },
          "name": "TRANSACTION_REQUIREMENTS_CHECK_RESULT"
        }
      ]
    }
  ],
  "availableSurfaces": [
    {
      "capabilities": [
        {
          "name": "actions.capability.SCREEN_OUTPUT"
        },
        {
          "name": "actions.capability.AUDIO_OUTPUT"
        },
        {
          "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
        },
        {
          "name": "actions.capability.WEB_BROWSER"
        }
      ]
    }
  ]
}

2. Crea l'ordine

Esperienza utente

Una volta che disponi delle informazioni utente necessarie, crea un'esperienza di "assemblaggio del carrello" che guidi l'utente nella creazione della prenotazione. Ogni azione avrà un flusso di assemblaggio del carrello leggermente diverso in base alle esigenze del servizio.

In un'esperienza di assemblaggio del carrello di base, un utente seleziona da un elenco le opzioni da aggiungere alla prenotazione, anche se puoi progettare la conversazione per semplificare l'esperienza utente. Ad esempio, puoi creare un'esperienza di assemblaggio del carrello che consenta all'utente di pianificare una prenotazione mensile con una semplice domanda sì o no. Puoi anche presentare all'utente un carosello o una scheda elenco di prenotazioni "consigliate".

Ti consigliamo di utilizzare risposte dettagliate per presentare visivamente le opzioni dell'utente, ma anche di progettare la conversazione in modo che l'utente possa creare il carrello utilizzando solo la voce. Per alcune best practice ed esempi di esperienze di assemblaggio del carrello, consulta le linee guida per la progettazione delle transazioni.

Evasione dell'ordine

Nel corso della conversazione, raccogli i dettagli della prenotazione che un utente vuole acquistare, quindi crea un oggetto Order.

Order deve contenere almeno quanto segue:

  • buyerInfo: informazioni sull'utente che ha programmato la prenotazione.
  • transactionMerchant: informazioni sul commerciante che gestisce la prenotazione.
  • contents: i dettagli effettivi della prenotazione indicati come lineItems.

Per realizzare il tuo carrello, consulta la documentazione relativa alle risposte Order. Tieni presente che potresti dover includere campi diversi a seconda della prenotazione.

Il codice di esempio riportato di seguito mostra un ordine di prenotazione completo, inclusi i campi facoltativi:

Node.js
app.intent('build_reservation_df', (conv) => {
  const now = new Date().toISOString();
  const order = {
    createTime: now,
    lastUpdateTime: now,
    merchantOrderId: 'UNIQUE_ORDER_ID',
    userVisibleOrderId: 'USER_VISIBLE_ORDER_ID',
    transactionMerchant: {
      id: 'https://www.example.com',
      name: 'Example Merchant',
    },
    contents: {
      lineItems: [
        {
          id: 'LINE_ITEM_ID',
          name: 'Dinner reservation',
          description: 'A world of flavors all in one destination.',
          reservation: {
            status: 'PENDING',
            userVisibleStatusLabel: 'Reservation is pending.',
            type: 'RESTAURANT',
            reservationTime: {
              timeIso8601: '2020-01-16T01:30:15.01Z',
            },
            userAcceptableTimeRange: {
              timeIso8601: '2020-01-15/2020-01-17',
            },
            partySize: 6,
            staffFacilitators: [
              {
                name: 'John Smith',
              },
            ],
            location: {
              zipCode: '94086',
              city: 'Sunnyvale',
              postalAddress: {
                regionCode: 'US',
                postalCode: '94086',
                administrativeArea: 'CA',
                locality: 'Sunnyvale',
                addressLines: [
                  '222, Some other Street',
                ],
              },
            },
          },
        },
      ],
    },
    buyerInfo: {
      email: 'janedoe@gmail.com',
      firstName: 'Jane',
      lastName: 'Doe',
      displayName: 'Jane Doe',
    },
    followUpActions: [
      {
        type: 'VIEW_DETAILS',
        title: 'View details',
        openUrlAction: {
          url: 'https://example.com',
        },
      },
      {
        type: 'CALL',
        title: 'Call us',
        openUrlAction: {
          url: 'tel:+16501112222',
        },
      },
      {
        type: 'EMAIL',
        title: 'Email us',
        openUrlAction: {
          url: 'mailto:person@example.com',
        },
      },
    ],
    termsOfServiceUrl: 'https://www.example.com',
  };

Java
private static OrderV3 createOrder() {
  // Transaction Merchant
  MerchantV3 transactionMerchant = new MerchantV3()
      .setId("http://www.example.com")
      .setName("Example Merchant");

  // Line Item

  // Reservation Item Extension
  ReservationItemExtension reservationItemExtension = new ReservationItemExtension()
      .setStatus("PENDING")
      .setUserVisibleStatusLabel("Reservation pending.")
      .setType("RESTAURANT")
      .setReservationTime(new TimeV3()
          .setTimeIso8601("2020-01-16T01:30:15.01Z"))
      .setUserAcceptableTimeRange(new TimeV3()
          .setTimeIso8601("2020-01-15/2020-01-17"))
      .setPartySize(6)
      .setStaffFacilitators(Collections.singletonList(new StaffFacilitator()
          .setName("John Smith")))
      .setLocation(new Location()
          .setZipCode("94086")
          .setCity("Sunnyvale")
          .setPostalAddress(new PostalAddress()
              .setRegionCode("US")
              .setPostalCode("94086")
              .setAdministrativeArea("CA")
              .setLocality("Sunnyvale")
              .setAddressLines(
                  Collections.singletonList("222, Some other Street"))));

  LineItemV3 lineItem = new LineItemV3()
      .setId("LINE_ITEM_ID")
      .setName("Dinner reservation")
      .setDescription("A world of flavors all in one destination.")
      .setReservation(reservationItemExtension);

  // Order Contents
  OrderContents contents = new OrderContents()
      .setLineItems(Collections.singletonList(lineItem));

  // User Info
  UserInfo buyerInfo = new UserInfo()
      .setEmail("janedoe@gmail.com")
      .setFirstName("Jane")
      .setLastName("Doe")
      .setDisplayName("Jane Doe");

  // Follow up actions
  Action viewDetails = new Action()
      .setType("VIEW_DETAILS")
      .setTitle("View details")
      .setOpenUrlAction(new OpenUrlAction()
          .setUrl("https://example.com"));

  Action call = new Action()
      .setType("CALL")
      .setTitle("Call us")
      .setOpenUrlAction(new OpenUrlAction()
          .setUrl("tel:+16501112222"));

  Action email = new Action()
      .setType("EMAIL")
      .setTitle("Email us")
      .setOpenUrlAction(new OpenUrlAction()
          .setUrl("mailto:person@example.com"));

  // Terms of service and order note
  String termsOfServiceUrl = "https://example.com";

  String now = Instant.now().toString();

  OrderV3 order = new OrderV3()
      .setCreateTime(now)
      .setLastUpdateTime(now)
      .setMerchantOrderId("UNIQUE_ORDER_ID")
      .setUserVisibleOrderId("UNIQUE_USER_VISIBLE_ORDER_ID")
      .setTransactionMerchant(transactionMerchant)
      .setContents(contents)
      .setBuyerInfo(buyerInfo)
      .setFollowUpActions(Arrays.asList(
          viewDetails,
          call,
          email
      ))
      .setTermsOfServiceUrl(termsOfServiceUrl);

  return order;
}
JSON

Tieni presente che il JSON di seguito descrive una risposta webhook.

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "systemIntent": {
        "intent": "actions.intent.TRANSACTION_DECISION",
        "data": {
          "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec",
          "order": {
            "createTime": "2019-07-17T18:25:30.182Z",
            "lastUpdateTime": "2019-07-17T18:25:30.182Z",
            "merchantOrderId": "UNIQUE_ORDER_ID",
            "userVisibleOrderId": "USER_VISIBLE_ORDER_ID",
            "transactionMerchant": {
              "id": "https://www.example.com",
              "name": "Example Merchant"
            },
            "contents": {
              "lineItems": [
                {
                  "id": "LINE_ITEM_ID",
                  "name": "Dinner reservation",
                  "description": "A world of flavors all in one destination.",
                  "reservation": {
                    "status": "PENDING",
                    "userVisibleStatusLabel": "Reservation is pending.",
                    "type": "RESTAURANT",
                    "reservationTime": {
                      "timeIso8601": "2020-01-16T01:30:15.01Z"
                    },
                    "userAcceptableTimeRange": {
                      "timeIso8601": "2020-01-15/2020-01-17"
                    },
                    "partySize": 6,
                    "staffFacilitators": [
                      {
                        "name": "John Smith"
                      }
                    ],
                    "location": {
                      "zipCode": "94086",
                      "city": "Sunnyvale",
                      "postalAddress": {
                        "regionCode": "US",
                        "postalCode": "94086",
                        "administrativeArea": "CA",
                        "locality": "Sunnyvale",
                        "addressLines": [
                          "222, Some other Street"
                        ]
                      }
                    }
                  }
                }
              ]
            },
            "buyerInfo": {
              "email": "janedoe@gmail.com",
              "firstName": "Jane",
              "lastName": "Doe",
              "displayName": "Jane Doe"
            },
            "followUpActions": [
              {
                "type": "VIEW_DETAILS",
                "title": "View details",
                "openUrlAction": {
                  "url": "https://example.com"
                }
              },
              {
                "type": "CALL",
                "title": "Call us",
                "openUrlAction": {
                  "url": "tel:+16501112222"
                }
              },
              {
                "type": "EMAIL",
                "title": "Email us",
                "openUrlAction": {
                  "url": "mailto:person@example.com"
                }
              }
            ],
            "termsOfServiceUrl": "https://www.example.com"
          },
          "orderOptions": {
            "requestDeliveryAddress": false,
            "userInfoOptions": {
              "userInfoProperties": [
                "EMAIL"
              ]
            }
          },
          "presentationOptions": {
            "actionDisplayName": "RESERVE"
          }
        }
      }
    }
  }
}

3. Proponi l'ordine

Presenta il tuo ordine di prenotazione all'utente in modo che possa confermare o rifiutare. Richiedi l'intent actions.intent.TRANSACTION_DECISION e fornisci il Order che hai creato.

Esperienza utente

Quando richiedi l'intent actions.intent.TRANSACTION_DECISION, l'assistente avvia un'esperienza integrata in cui la Order viene visualizzata direttamente in una "scheda di anteprima del carrello". L'utente può dire "Prenota la prenotazione", rifiutare la transazione o richiedere di modificare i dettagli della prenotazione.

A questo punto, l'utente può anche richiedere modifiche all'ordine. In questo caso, devi assicurarti che l'evasione degli ordini sia in grado di gestire le richieste di modifica dell'ordine dopo aver completato l'esperienza di assemblaggio del carrello.

Evasione dell'ordine

Quando richiedi l'intent actions.intent.TRANSACTION_DECISION, crea un TransactionDecision che contenga Order e orderOptions

Il codice seguente mostra un esempio di TransactionsDecision per un ordine:

Node.js
conv.ask(new TransactionDecision({
  orderOptions: {
    requestDeliveryAddress: 'false',
  },
  presentationOptions: {
    actionDisplayName: 'RESERVE',
  },
  order: order,
}));
Java
// Create order options
OrderOptionsV3 orderOptions = new OrderOptionsV3()
    .setRequestDeliveryAddress(false)
    .setUserInfoOptions(new UserInfoOptions()
        .setUserInfoProperties(Collections.singletonList("EMAIL")));

// Create presentation options
PresentationOptionsV3 presentationOptions = new PresentationOptionsV3()
    .setActionDisplayName("RESERVE");

// Ask for transaction decision
return getResponseBuilder(request)
    .add("Placeholder for transaction decision text")
    .add(new TransactionDecision()
        .setOrder(order)
        .setOrderOptions(orderOptions)
        .setPresentationOptions(presentationOptions)
    )
    .build();
JSON Dialogflow

Tieni presente che il JSON di seguito descrive una risposta webhook.

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "systemIntent": {
        "intent": "actions.intent.TRANSACTION_DECISION",
        "data": {
          "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec",
          "orderOptions": {
            "requestDeliveryAddress": "false"
          },
          "presentationOptions": {
            "actionDisplayName": "RESERVE"
          },
          "order": {
            "createTime": "2019-07-17T18:25:30.184Z",
            "lastUpdateTime": "2019-07-17T18:25:30.184Z",
            "merchantOrderId": "UNIQUE_ORDER_ID",
            "userVisibleOrderId": "USER_VISIBLE_ORDER_ID",
            "transactionMerchant": {
              "id": "https://www.example.com",
              "name": "Example Merchant"
            },
            "contents": {
              "lineItems": [
                {
                  "id": "LINE_ITEM_ID",
                  "name": "Dinner reservation",
                  "description": "A world of flavors all in one destination.",
                  "reservation": {
                    "status": "PENDING",
                    "userVisibleStatusLabel": "Reservation is pending.",
                    "type": "RESTAURANT",
                    "reservationTime": {
                      "timeIso8601": "2020-01-16T01:30:15.01Z"
                    },
                    "userAcceptableTimeRange": {
                      "timeIso8601": "2020-01-15/2020-01-17"
                    },
                    "partySize": 6,
                    "staffFacilitators": [
                      {
                        "name": "John Smith"
                      }
                    ],
                    "location": {
                      "zipCode": "94086",
                      "city": "Sunnyvale",
                      "postalAddress": {
                        "regionCode": "US",
                        "postalCode": "94086",
                        "administrativeArea": "CA",
                        "locality": "Sunnyvale",
                        "addressLines": [
                          "222, Some other Street"
                        ]
                      }
                    }
                  }
                }
              ]
            },
            "buyerInfo": {
              "email": "janedoe@gmail.com",
              "firstName": "Jane",
              "lastName": "Doe",
              "displayName": "Jane Doe"
            },
            "followUpActions": [
              {
                "type": "VIEW_DETAILS",
                "title": "View details",
                "openUrlAction": {
                  "url": "https://example.com"
                }
              },
              {
                "type": "CALL",
                "title": "Call us",
                "openUrlAction": {
                  "url": "tel:+16501112222"
                }
              },
              {
                "type": "EMAIL",
                "title": "Email us",
                "openUrlAction": {
                  "url": "mailto:person@example.com"
                }
              }
            ],
            "termsOfServiceUrl": "https://www.example.com"
          }
        }
      }
    }
  }
}
JSON SDK Actions

Tieni presente che il JSON di seguito descrive una risposta webhook.

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TRANSACTION_DECISION",
          "inputValueData": {
            "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec",
            "orderOptions": {
              "requestDeliveryAddress": "false"
            },
            "presentationOptions": {
              "actionDisplayName": "RESERVE"
            },
            "order": {
              "createTime": "2019-07-17T18:25:30.057Z",
              "lastUpdateTime": "2019-07-17T18:25:30.057Z",
              "merchantOrderId": "UNIQUE_ORDER_ID",
              "userVisibleOrderId": "USER_VISIBLE_ORDER_ID",
              "transactionMerchant": {
                "id": "https://www.example.com",
                "name": "Example Merchant"
              },
              "contents": {
                "lineItems": [
                  {
                    "id": "LINE_ITEM_ID",
                    "name": "Dinner reservation",
                    "description": "A world of flavors all in one destination.",
                    "reservation": {
                      "status": "PENDING",
                      "userVisibleStatusLabel": "Reservation is pending.",
                      "type": "RESTAURANT",
                      "reservationTime": {
                        "timeIso8601": "2020-01-16T01:30:15.01Z"
                      },
                      "userAcceptableTimeRange": {
                        "timeIso8601": "2020-01-15/2020-01-17"
                      },
                      "partySize": 6,
                      "staffFacilitators": [
                        {
                          "name": "John Smith"
                        }
                      ],
                      "location": {
                        "zipCode": "94086",
                        "city": "Sunnyvale",
                        "postalAddress": {
                          "regionCode": "US",
                          "postalCode": "94086",
                          "administrativeArea": "CA",
                          "locality": "Sunnyvale",
                          "addressLines": [
                            "222, Some other Street"
                          ]
                        }
                      }
                    }
                  }
                ]
              },
              "buyerInfo": {
                "email": "janedoe@gmail.com",
                "firstName": "Jane",
                "lastName": "Doe",
                "displayName": "Jane Doe"
              },
              "followUpActions": [
                {
                  "type": "VIEW_DETAILS",
                  "title": "View details",
                  "openUrlAction": {
                    "url": "https://example.com"
                  }
                },
                {
                  "type": "CALL",
                  "title": "Call us",
                  "openUrlAction": {
                    "url": "tel:+16501112222"
                  }
                },
                {
                  "type": "EMAIL",
                  "title": "Email us",
                  "openUrlAction": {
                    "url": "mailto:person@example.com"
                  }
                }
              ],
              "termsOfServiceUrl": "https://www.example.com"
            }
          }
        }
      ]
    }
  ]
}
Gestire la decisione dell'utente

Dopo che l'utente risponde all'ordine proposto, il fulfillment riceve l'intent actions_intent_TRANSACTION_DECISION con un argomento contenente un TransactionDecisionValue. Questo valore conterrà quanto segue:

  • transactionDecision: la decisione dell'utente in merito all'ordine proposto. I valori possibili sono ORDER_ACCEPTED, ORDER_REJECTED, CART_CHANGE_REQUESTED e USER_CANNOT_TRANSACT.

Per gestire questa richiesta, dichiara un intent di Dialogflow attivato dall'evento actions_intent_TRANSACTION_DECISION. Gestisci questo intento nel completamento:

Node.js
const arg = conv.arguments.get('TRANSACTION_DECISION_VALUE');
if (arg && arg.transactionDecision === 'ORDER_ACCEPTED') {
  console.log('order accepted');
  const order = arg.order;
}
Java
Argument transactionDecisionValue = request
    .getArgument("TRANSACTION_DECISION_VALUE");
Map<String, Object> extension = null;
if (transactionDecisionValue != null) {
  extension = transactionDecisionValue.getExtension();
}

String transactionDecision = null;
if (extension != null) {
  transactionDecision = (String) extension.get("transactionDecision");
}
if ((transactionDecision != null && transactionDecision.equals("ORDER_ACCEPTED"))) {
  OrderV3 order = ((OrderV3) extension.get("order"));
}
JSON Dialogflow

Tieni presente che il JSON di seguito descrive una richiesta webhook.

{
  "responseId": "",
  "queryResult": {
    "queryText": "",
    "action": "",
    "parameters": {},
    "allRequiredParamsPresent": true,
    "fulfillmentText": "",
    "fulfillmentMessages": [],
    "outputContexts": [],
    "intent": {
      "name": "reservation_get_transaction_decision_df",
      "displayName": "reservation_get_transaction_decision_df"
    },
    "intentDetectionConfidence": 1,
    "diagnosticInfo": {},
    "languageCode": ""
  },
  "originalDetectIntentRequest": {
    "source": "google",
    "version": "2",
    "payload": {
      "isInSandbox": true,
      "surface": {
        "capabilities": [
          {
            "name": "actions.capability.SCREEN_OUTPUT"
          },
          {
            "name": "actions.capability.AUDIO_OUTPUT"
          },
          {
            "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
          },
          {
            "name": "actions.capability.WEB_BROWSER"
          }
        ]
      },
      "inputs": [
        {
          "rawInputs": [],
          "intent": "",
          "arguments": []
        }
      ],
      "user": {},
      "conversation": {},
      "availableSurfaces": [
        {
          "capabilities": [
            {
              "name": "actions.capability.SCREEN_OUTPUT"
            },
            {
              "name": "actions.capability.AUDIO_OUTPUT"
            },
            {
              "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
            },
            {
              "name": "actions.capability.WEB_BROWSER"
            }
          ]
        }
      ]
    }
  },
  "session": ""
}
JSON SDK Actions

Tieni presente che il JSON di seguito descrive una richiesta webhook.

{
  "user": {},
  "device": {},
  "surface": {
    "capabilities": [
      {
        "name": "actions.capability.SCREEN_OUTPUT"
      },
      {
        "name": "actions.capability.AUDIO_OUTPUT"
      },
      {
        "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
      },
      {
        "name": "actions.capability.WEB_BROWSER"
      }
    ]
  },
  "conversation": {},
  "inputs": [
    {
      "rawInputs": [],
      "intent": "reservation_get_transaction_decision_asdk",
      "arguments": []
    }
  ],
  "availableSurfaces": [
    {
      "capabilities": [
        {
          "name": "actions.capability.SCREEN_OUTPUT"
        },
        {
          "name": "actions.capability.AUDIO_OUTPUT"
        },
        {
          "name": "actions.capability.MEDIA_RESPONSE_AUDIO"
        },
        {
          "name": "actions.capability.WEB_BROWSER"
        }
      ]
    }
  ]
}

4. Finalizza la prenotazione e invia una ricevuta

Quando l'intent actions.intent.TRANSACTION_DECISION viene restituito con un valore transactionDecision pari a ORDER_ACCEPTED, esegui l'elaborazione richiesta per pianificare la prenotazione (ad esempio, memorizzandola nel tuo database).

Invia una risposta semplice per mantenere viva la conversazione. L'utente riceve una "scheda di ricevuta compressa" insieme alla tua risposta.

Evasione dell'ordine

Node.js
// Set lastUpdateTime and update status of reservation
order.lastUpdateTime = new Date().toISOString();
order.reservation.status = 'CONFIRMED';
order.reservation.userVisibleStatusLabel = 'Reservation confirmed';
order.reservation.confirmationCode = '123ABCDEFGXYZ';

// Send synchronous order update
conv.ask(`Transaction completed! You're all set!`);
conv.ask(new OrderUpdate({
  type: 'SNAPSHOT',
  reason: 'Reason string',
  order: order,
}));
Java
ResponseBuilder responseBuilder = getResponseBuilder(request);
order.setLastUpdateTime(Instant.now().toString());

// Set reservation status to confirmed and provide confirmation code
LineItemV3 lineItem = order.getContents().getLineItems().get(0);
ReservationItemExtension reservationItemExtension = lineItem.getReservation();
reservationItemExtension.setStatus("CONFIRMED");
reservationItemExtension.setUserVisibleStatusLabel("Reservation confirmed.");
reservationItemExtension.setConfirmationCode("123ABCDEFGXYZ");
lineItem.setReservation(reservationItemExtension);
order.getContents().getLineItems().set(0, lineItem);

// Order update
OrderUpdateV3 orderUpdate = new OrderUpdateV3()
    .setType("SNAPSHOT")
    .setReason("Reason string")
    .setOrder(order);

responseBuilder
    .add("Transaction completed! You're all set! Would you like to do anything else?")
    .add(new StructuredResponse().setOrderUpdateV3(orderUpdate));
return responseBuilder.build();
JSON Dialogflow

Tieni presente che il JSON di seguito descrive una risposta webhook.

{
  "payload": {
    "google": {
      "expectUserResponse": true,
      "richResponse": {
        "items": [
          {
            "simpleResponse": {
              "textToSpeech": "Transaction completed! You're all set!"
            }
          },
          {
            "structuredResponse": {
              "orderUpdateV3": {
                "type": "SNAPSHOT",
                "reason": "Reason string",
                "order": {
                  "merchantOrderId": "UNIQUE_ORDER_ID",
                  "reservation": {
                    "status": "CONFIRMED",
                    "userVisibleStatusLabel": "Reservation confirmed",
                    "confirmationCode": "123ABCDEFGXYZ"
                  },
                  "lastUpdateTime": "2019-07-17T18:25:30.187Z"
                }
              }
            }
          }
        ]
      }
    }
  }
}
JSON SDK Actions

Tieni presente che il JSON di seguito descrive una risposta webhook.

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TEXT"
        }
      ],
      "inputPrompt": {
        "richInitialPrompt": {
          "items": [
            {
              "simpleResponse": {
                "textToSpeech": "Transaction completed! You're all set!"
              }
            },
            {
              "structuredResponse": {
                "orderUpdateV3": {
                  "type": "SNAPSHOT",
                  "reason": "Reason string",
                  "order": {
                    "merchantOrderId": "UNIQUE_ORDER_ID",
                    "reservation": {
                      "status": "CONFIRMED",
                      "userVisibleStatusLabel": "Reservation confirmed",
                      "confirmationCode": "123ABCDEFGXYZ"
                    },
                    "lastUpdateTime": "2019-07-17T18:25:30.059Z"
                  }
                }
              }
            }
          ]
        }
      }
    }
  ]
}

5. Invia aggiornamenti sull'ordine

Lo stato della prenotazione cambia nel corso della sua durata. Invia gli aggiornamenti dell'ordine di prenotazione dell'utente con richieste HTTP PATCH all'API Orders, contenente lo stato e i dettagli dell'ordine.

Configurare le richieste asincrone all'API Orders

Le richieste di aggiornamento degli ordini all'API Orders sono autorizzate da un token di accesso. Per eseguire il PATCH di un aggiornamento di un ordine all'API Orders, scarica una chiave dell'account di servizio JSON associata al tuo progetto Actions Console, quindi scambia la chiave dell'account di servizio con un token di connessione che può essere trasmesso all'intestazione Authorization della richiesta HTTP.

Per recuperare la chiave dell'account di servizio, segui questi passaggi:

  1. Nella console Google Cloud, vai a Menu Activate > API e servizi > Credenziali > Crea credenziali > Chiave dell'account di servizio.
  2. In Service Account (Account di servizio), seleziona New Service Account (Nuovo account di servizio).
  3. Imposta l'account di servizio su service-account.
  4. Imposta Ruolo su Progetto > Proprietario.
  5. Imposta il tipo di chiave su JSON.
  6. Seleziona Crea.
  7. Verrà scaricata sulla macchina locale una chiave JSON privata dell'account di servizio.

Nel codice di aggiornamento dell'ordine, scambia la chiave di servizio con un token di connessione utilizzando la libreria client delle API di Google e l'ambito "https://www.googleapis.com/auth/actions.order.developer". Puoi trovare i passaggi e gli esempi di installazione nella pagina GitHub della libreria client API.

Fai riferimento a order-update.js nei nostri esempi di Node.js e Java per uno scambio di chiavi di esempio.

Invia aggiornamenti sull'ordine

Dopo aver scambiato la chiave dell'account di servizio con un token di connessione OAuth, invia gli aggiornamenti degli ordini come richieste PATCH autorizzate all'API Orders.

URL API Orders: PATCH https://actions.googleapis.com/v3/orders/${orderId}

Fornisci le seguenti intestazioni nella richiesta:

  • "Authorization: Bearer token" con il token di connessione OAuth per cui hai scambiato la chiave dell'account di servizio.
  • "Content-Type: application/json".

La richiesta PATCH deve avere un corpo JSON con il seguente formato:

{ "orderUpdate": OrderUpdate }

L'oggetto OrderUpdate è costituito dai seguenti campi di primo livello:

  • updateMask. I campi dell'ordine che stai aggiornando. Per aggiornare lo stato della prenotazione, imposta il valore su reservation.status, reservation.userVisibleStatusLabel.
  • order: i contenuti dell'aggiornamento. Se aggiorni i contenuti della prenotazione, imposta il valore sull'oggetto Order aggiornato. Se stai solo aggiornando lo stato della prenotazione (ad esempio da "PENDING" a "FULFILLED"), l'oggetto contiene i seguenti campi:

    • merchantOrderId: lo stesso ID impostato nell'oggetto Order.
    • lastUpdateTime: il timestamp di questo aggiornamento.
    • purchase: un oggetto contenente i seguenti elementi:
      • status: lo stato dell'ordine come ReservationStatus, ad esempio "CONFIRMED" o "CANCELLED".
      • userVisibleStatusLabel: un'etichetta rivolta agli utenti che fornisce dettagli sullo stato dell'ordine, ad esempio "La prenotazione è confermata".
  • userNotification che può essere visualizzato sul dispositivo dell'utente quando viene inviato questo aggiornamento. Tieni presente che l'inclusione di questo oggetto non garantisce che venga visualizzata una notifica sul dispositivo dell'utente.

Il seguente codice campione mostra un esempio OrderUpdate che aggiorna lo stato dell'ordine di prenotazione a FULFILLED:

Node.js
// Import the 'googleapis' module for authorizing the request.
const {google} = require('googleapis');
// Import the 'request' module for sending an HTTP POST request.
const request = require('request');
// Import the OrderUpdate class from the Actions on Google client library.
const {OrderUpdate} = require('actions-on-google');
// Import the service account key used to authorize the request. Replace the string path with a path to your service account key.
const key = require('./service-account.json');
// Create a new JWT client for the Actions API using credentials from the service account key.
let jwtClient = new google.auth.JWT(
    key.client_email,
    null,
    key.private_key,
    ['https://www.googleapis.com/auth/actions.order.developer'],
    null
);
// Authorize the client asynchronously, passing in a callback to run upon authorization.
jwtClient.authorize((err, tokens) => {
    if (err) {
        console.log(err);
        return;
    }
    // Declare the ID of the order to update.
    const orderId = '<UNIQUE_MERCHANT_ORDER_ID>';

    const orderUpdateJson = new OrderUpdate({
        updateMask: [
          'lastUpdateTime',
          'contents.lineItems.reservation.status',
          'contents.lineItems.reservation.userVisibleStatusLabel',
      ].join(','),
        order: {
          merchantOrderId: orderId,
          lastUpdateTime: new Date().toISOString(),
          contents: {
            lineItems: [
              {
                reservation: {
                  status: 'FULFILLED',
                  userVisibleStatusLabel: 'Reservation fulfilled',
                },
              }
            ]
          }
        },
        reason: 'Reservation status was updated to fulfilled.',
    });

    // Set up the PATCH request header and body, including the authorized token
    // and order update.
    const bearer = 'Bearer ' + tokens.access_token;
    const options = {
        method: 'PATCH',
        url: `https://actions.googleapis.com/v3/orders/${orderId}`,
        headers: {
          'Authorization': bearer,
        },
        body: {
          header: {
            'isInSandbox': true,
          },
          orderUpdate: orderUpdateJson,
        },
        json: true,
      };
    // Send the PATCH request to the Orders API.
    request.patch(options, (err, httpResponse, body) => {
        if (err) {
            console.log('There was an error...');
            console.log(err);
            return;
        }
    });
});
Java
// Create order update
FieldMask fieldMask = FieldMask.newBuilder().addAllPaths(Arrays.asList(
    "lastUpdateTime",
    "contents.lineItems.reservation.status",
    "contents.lineItems.reservation.userVisibleStatusLabel"))
    .build();

OrderUpdateV3 orderUpdate = new OrderUpdateV3()
    .setOrder(new OrderV3()
        .setMerchantOrderId(orderId)
        .setLastUpdateTime(Instant.now().toString())
        .setContents(new OrderContents()
        .setLineItems(Collections.singletonList(new LineItemV3()
            .setReservation(new ReservationItemExtension()
                .setStatus("FULFILLED")
                .setUserVisibleStatusLabel("Reservation fulfilled."))))))
    .setUpdateMask(FieldMaskUtil.toString(fieldMask))
    .setReason("Reservation status was updated to fulfilled.");

// Setup JSON body containing order update
JsonParser parser = new JsonParser();
JsonObject orderUpdateJson =
    parser.parse(new Gson().toJson(orderUpdate)).getAsJsonObject();
JsonObject body = new JsonObject();
body.add("orderUpdate", orderUpdateJson);
JsonObject header = new JsonObject();
header.addProperty("isInSandbox", true);
body.add("header", header);
StringEntity entity = new StringEntity(body.toString());
entity.setContentType(ContentType.APPLICATION_JSON.getMimeType());
request.setEntity(entity);

// Make request
HttpClient httpClient = HttpClientBuilder.create().build();
HttpResponse response = httpClient.execute(request);
Impostare lo stato della prenotazione

Il valore ReservationStatus di un aggiornamento dell'ordine deve descrivere lo stato attuale dell'ordine. Nel campo order.ReservationStatus dell'aggiornamento, utilizza uno dei seguenti valori:

  • PENDING: la prenotazione è stata "creata" dall'Azione, ma richiede un'ulteriore elaborazione nel tuo backend.
  • CONFIRMED - La prenotazione è confermata nel back-end della programmazione.
  • CANCELLED: l'utente ha annullato la prenotazione.
  • FULFILLED: la prenotazione dell'utente è stata evasa dal servizio.
  • CHANGE_REQUESTED: l'utente ha richiesto una modifica alla prenotazione ed è in corso l'elaborazione della modifica.
  • REJECTED - Se non sei riuscito a elaborare o confermare in altro modo la prenotazione.

Invia aggiornamenti sull'ordine per ogni stato pertinente alla tua prenotazione. Ad esempio, se la prenotazione richiede l'elaborazione manuale per confermare la prenotazione dopo la richiesta, invia un aggiornamento dell'ordine PENDING fino al completamento dell'elaborazione aggiuntiva. Non tutte le prenotazioni richiedono ogni valore di stato.

Risolvere i problemi

Se riscontri problemi durante il test, consulta la nostra procedura per la risoluzione dei problemi relativa alle transazioni.