Réservations de compilation (Dialogflow)

Ce guide décrit le processus de développement d'un projet Actions qui utilise Orders API pour effectuer des réservations.

Flux de transactions

Lorsque votre projet Actions gère des réservations, il suit le flux suivant:

  1. Valider les exigences de transaction (facultatif) : Utilisez l'outil d'aide sur les exigences de transaction au début de la conversation pour vous assurer que l'utilisateur est en mesure d'effectuer une transaction.
  2. Créer la commande : guidez l'utilisateur à travers un "assemblage de panier" dans lequel il crée les détails de sa réservation.
  3. Proposez la commande : une fois le "panier" terminé, proposez la "commande" de la réservation à l'utilisateur afin qu'il puisse confirmer qu'elle est correcte. Si la réservation est confirmée, vous recevrez une réponse contenant les détails de la réservation.
  4. Finaliser la commande et envoyer un reçu : une fois la commande confirmée, mettez à jour votre système de réservation et envoyez un reçu à l'utilisateur.
  5. Envoyer des mises à jour de commande : au cours de la durée de vie de la réservation, informez l'utilisateur des mises à jour de l'état de la réservation en envoyant des requêtes PATCH à l'API Orders.

Restrictions et consignes relatives aux avis

N'oubliez pas que des règles supplémentaires s'appliquent aux actions qui utilisent les transactions et Orders API. L'examen des actions avec les transactions peut prendre jusqu'à six semaines. Tenez-en compte lorsque vous planifiez votre calendrier de mise en ligne. Pour faciliter le processus d'examen, assurez-vous de respecter les Règles et consignes concernant les transactions avant d'envoyer votre action pour examen.

Vous ne pouvez déployer des actions qui utilisent l'API Orders que dans les pays suivants:

Australie
Brésil
Canada
Indonésie
Japon
Mexique
Qatar
Russie
Singapour
Suisse
Thaïlande
Turquie
Royaume-Uni
États-Unis

Compiler votre projet

Pour obtenir des exemples complets de conversations transactionnelles, consultez nos exemples de transactions en Node.js et Java.

Configuration du projet

Lors de la création de votre action, vous devez indiquer que vous souhaitez effectuer des transactions dans la console Actions. De plus, si vous utilisez la bibliothèque cliente Node.JS, configurez votre traitement pour utiliser la dernière version de l'API Orders.

Pour configurer votre projet et votre traitement, procédez comme suit:

  1. Créez un projet ou importez un projet existant.
  2. Accédez à Déployer > Informations sur le répertoire.
  3. Sous Informations supplémentaires > Transactions, cochez la case "Vos actions utilisent-ils l'API Transactions pour effectuer des transactions de biens physiques ?".

  4. Si vous utilisez la bibliothèque cliente Node.JS pour créer le traitement de votre action, ouvrez votre code de traitement et mettez à jour la délégation d'application pour définir l'indicateur ordersv3 sur true. L'extrait de code suivant présente un exemple de déclaration d'application pour Orders version 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. Valider les exigences de transaction (facultatif)

Expérience utilisateur

Dès que l'utilisateur a indiqué vouloir configurer une réservation, nous vous recommandons de déclencher l'intent actions.intent.TRANSACTION_REQUIREMENTS_CHECK pour vous assurer qu'il peut demander une réservation. Par exemple, lorsqu'elle est appelée, votre action peut demander : "Voulez-vous réserver une place ?". Si l'utilisateur répond "oui", vous devez demander cet intent immédiatement. Il pourra ainsi continuer et pourra corriger les paramètres qui l'empêchent de poursuivre la transaction.

Demander la vérification de l'intent des exigences liées aux transactions aboutit à l'un des résultats suivants:

  • Si les conditions sont remplies, votre traitement reçoit un intent avec une condition de réussite. Vous pouvez alors créer la commande de l'utilisateur.
  • Si une ou plusieurs des exigences ne le sont pas, votre traitement reçoit l'intent avec une condition d'échec. Dans ce cas, mettez fin à la conversation ou quittez le flux de réservation.

    Si l'utilisateur peut corriger l'erreur, il est automatiquement invité à résoudre ces problèmes sur son appareil. Si la conversation a lieu sur une surface vocale uniquement, comme une enceinte intelligente, elle est transmise au téléphone de l'utilisateur.

Traitement

Pour vous assurer qu'un utilisateur répond aux exigences de transaction, demandez le traitement de l'intent actions.intent.TRANSACTION_REQUIREMENTS_CHECK avec un objet TransactionRequirementsCheckSpec.

Vérifier les exigences

Vérifiez si un utilisateur répond aux exigences de réservation avec la bibliothèque cliente:

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

Notez que le code JSON ci-dessous décrit une réponse webhook.

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

Notez que le code JSON ci-dessous décrit une réponse webhook.

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK",
          "inputValueData": {
            "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec"
          }
        }
      ]
    }
  ]
}
Recevoir le résultat d'une vérification des exigences

Une fois que l'Assistant a traité l'intent, il envoie au traitement une requête avec l'intent actions.intent.TRANSACTION_REQUIREMENTS_CHECK avec le résultat de la vérification.

Pour gérer correctement cette requête, déclarez un intent Dialogflow déclenché par l'événement actions_intent_TRANSACTION_REQUIREMENTS_CHECK. Lorsqu'il est déclenché, gérez cet intent dans votre traitement:

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

Notez que le code JSON ci-dessous décrit une requête de 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": ""
}
SDK Actions JSON

Notez que le code JSON ci-dessous décrit une requête de 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. Créer la commande

Expérience utilisateur

Une fois que vous disposez des informations utilisateur dont vous avez besoin, créez une expérience d'assemblage du panier qui guide l'utilisateur pour créer sa réservation. Chaque action est associée à un flux d'assemblage de panier légèrement différent en fonction du service.

Dans une expérience d'assemblage de panier de base, un utilisateur sélectionne des options dans une liste à ajouter à sa réservation, mais vous pouvez concevoir la conversation pour simplifier l'expérience utilisateur. Par exemple, vous pouvez créer une expérience d'assemblage de chariots qui permet à l'utilisateur de planifier une réservation mensuelle à l'aide d'une simple question "oui" ou "non". Vous pouvez également présenter à l'utilisateur une fiche de type carrousel ou liste de réservations "recommandées".

Nous vous recommandons d'utiliser des réponses enrichies pour présenter visuellement les options de l'utilisateur, mais également de concevoir la conversation de sorte que l'utilisateur puisse construire son panier en utilisant uniquement sa voix. Pour vous familiariser avec les bonnes pratiques et voir des exemples d'assemblages de paniers, consultez les Consignes de conception des transactions.

Traitement

Tout au long de votre conversation, rassemblez les détails de la réservation qu'un utilisateur souhaite acheter, puis créez un objet Order.

Votre Order doit au moins contenir les éléments suivants:

  • buyerInfo : informations sur l'utilisateur qui a planifié la réservation.
  • transactionMerchant : informations sur le marchand qui gère la réservation.
  • contents : les détails réels de la réservation répertoriée comme lineItems.

Consultez la documentation de réponse Order pour créer votre panier. Notez que vous devrez peut-être inclure différents champs en fonction de la réservation.

L'exemple de code ci-dessous montre un ordre de réservation complet, avec des champs facultatifs:

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

Notez que le code JSON ci-dessous décrit une réponse 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. Proposer la commande

Présentez votre ordre de réservation à l'utilisateur pour qu'il puisse le confirmer ou le refuser. Demandez l'intent actions.intent.TRANSACTION_DECISION et fournissez le Order que vous avez créé.

Expérience utilisateur

Lorsque vous demandez l'intent actions.intent.TRANSACTION_DECISION, l'Assistant lance une expérience intégrée dans laquelle Order s'affiche directement sur une "carte d'aperçu du panier". L'utilisateur peut dire "planifier une réservation", refuser la transaction ou demander à modifier les détails de la réservation.

L'utilisateur peut également demander à modifier la commande. Dans ce cas, assurez-vous que votre traitement peut traiter les demandes de modification de commande une fois l'expérience d'assemblage du panier terminée.

Traitement

Lorsque vous demandez l'intent actions.intent.TRANSACTION_DECISION, créez un TransactionDecision contenant les éléments Order et orderOptions.

Le code suivant montre un exemple de TransactionsDecision pour une commande:

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

Notez que le code JSON ci-dessous décrit une réponse 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"
          }
        }
      }
    }
  }
}
SDK Actions JSON

Notez que le code JSON ci-dessous décrit une réponse 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"
            }
          }
        }
      ]
    }
  ]
}
Gérer la décision de l'utilisateur

Une fois que l'utilisateur a répondu à la commande proposée, votre traitement reçoit l'intent actions_intent_TRANSACTION_DECISION avec un argument contenant un élément TransactionDecisionValue. Cette valeur contiendra les éléments suivants:

  • transactionDecision : décision de l'utilisateur concernant la commande proposée. Les valeurs possibles sont ORDER_ACCEPTED, ORDER_REJECTED, CART_CHANGE_REQUESTED et USER_CANNOT_TRANSACT.

Pour gérer cette requête, déclarez un intent Dialogflow déclenché par l'événement actions_intent_TRANSACTION_DECISION. Gérez cet intent dans votre traitement:

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

Notez que le code JSON ci-dessous décrit une requête de 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": ""
}
SDK Actions JSON

Notez que le code JSON ci-dessous décrit une requête de 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. Finaliser la réservation et envoyer un reçu

Lorsque l'intent actions.intent.TRANSACTION_DECISION est renvoyé avec un transactionDecision défini sur ORDER_ACCEPTED, effectuez le traitement nécessaire pour planifier la réservation (comme la conserver dans votre propre base de données).

Envoyez une réponse simple pour poursuivre la conversation. L'utilisateur reçoit alors une "carte de reçu réduite" avec votre réponse.

Traitement

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

Notez que le code JSON ci-dessous décrit une réponse 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"
                }
              }
            }
          }
        ]
      }
    }
  }
}
SDK Actions JSON

Notez que le code JSON ci-dessous décrit une réponse 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. Envoyer des mises à jour de commande

L'état de la réservation change au cours de sa durée de vie. Envoyez à l'API Orders des mises à jour de la commande de réservation de l'utilisateur avec des requêtes HTTP PATCH, contenant l'état et les détails de la commande.

Configurer des requêtes asynchrones vers Orders API

Les requêtes de mise à jour de commande adressées à Orders API sont autorisées par un jeton d'accès. Pour PATCH une mise à jour de commande dans l'API Orders, téléchargez une clé de compte de service JSON associée à votre projet dans la console Actions, puis échangez-la contre un jeton de support qui peut être transmis dans l'en-tête Authorization de la requête HTTP.

Pour récupérer la clé de votre compte de service, procédez comme suit:

  1. Dans la console Google Cloud, accédez à Menu Dataproc > API et services > Identifiants > Créer des identifiants > Clé de compte de service.
  2. Sous Compte de service, sélectionnez Nouveau compte de service.
  3. Définissez le compte de service sur service-account.
  4. Définissez Rôle sur Projet > Propriétaire.
  5. Définissez le type de clé sur JSON.
  6. Sélectionnez Créer.
  7. Une clé de compte de service JSON privée sera téléchargée sur votre ordinateur local.

Dans le code de mise à jour de votre commande, échangez votre clé de service contre un jeton de support à l'aide de la bibliothèque cliente des API Google et du champ d'application "https://www.googleapis.com/auth/actions.order.developer". Vous trouverez la procédure d'installation et des exemples sur la page GitHub de la bibliothèque cliente des API.

Reportez-vous à order-update.js dans nos exemples Node.js et Java pour obtenir un exemple d'échange de clés.

Envoyer des mises à jour de commande

Une fois que vous avez échangé votre clé de compte de service contre un jeton de support OAuth, envoyez des mises à jour de commande en tant que requêtes PATCH autorisées à Orders API.

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

Fournissez les en-têtes suivants dans votre requête:

  • "Authorization: Bearer token" par le jeton de support OAuth pour lequel vous avez échangé votre clé de compte de service.
  • "Content-Type: application/json".

La requête PATCH doit prendre un corps JSON au format suivant:

{ "orderUpdate": OrderUpdate }

L'objet OrderUpdate comprend les champs de niveau supérieur suivants:

  • updateMask : champs de la commande que vous mettez à jour. Pour mettre à jour l'état de la réservation, définissez la valeur sur reservation.status, reservation.userVisibleStatusLabel.
  • order : contenu de la mise à jour. Si vous mettez à jour le contenu de la réservation, définissez la valeur sur l'objet Order mis à jour. Si vous mettez simplement à jour l'état de la réservation (par exemple, de "PENDING" à "FULFILLED"), l'objet contient les champs suivants:

    • merchantOrderId : le même ID que celui défini dans l'objet Order.
    • lastUpdateTime : date et heure de cette mise à jour.
    • purchase : objet contenant les éléments suivants :
      • status : état de la commande sous la forme d'une ReservationStatus (par exemple, CONFIRMED ou CANCELLED).
      • userVisibleStatusLabel : libellé visible par l'utilisateur qui fournit des détails sur l'état de la commande, par exemple "Votre réservation est confirmée".
  • userNotification qui peut s'afficher sur l'appareil de l'utilisateur lorsque cette mise à jour est envoyée. Notez que l'inclusion de cet objet ne garantit pas l'affichage d'une notification sur l'appareil de l'utilisateur.

L'exemple de code suivant montre un exemple de OrderUpdate qui met à jour l'état de l'ordre de réservation sur 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);
Définir l'état de la réservation

Le ReservationStatus de la mise à jour d'une commande doit décrire l'état actuel de la commande. Dans le champ order.ReservationStatus de votre mise à jour, utilisez l'une des valeurs suivantes:

  • PENDING : la réservation a été "créée" par votre action, mais nécessite un traitement supplémentaire sur votre backend.
  • CONFIRMED : la réservation est confirmée dans votre backend de planification.
  • CANCELLED : l'utilisateur a annulé sa réservation.
  • FULFILLED : la réservation de l'utilisateur a été traitée par le service.
  • CHANGE_REQUESTED : l'utilisateur a demandé une modification de la réservation, et la modification est en cours de traitement.
  • REJECTED - Si vous n'avez pas pu traiter ou confirmer la réservation.

Envoyez des mises à jour de commande pour chaque état pertinent pour votre réservation. Par exemple, si votre réservation nécessite un traitement manuel pour la confirmer après sa demande, envoyez une mise à jour de la commande PENDING jusqu'à ce que ce traitement supplémentaire soit terminé. Toutes les réservations ne nécessitent pas toutes les valeurs d'état.

Dépannage

Si vous rencontrez des problèmes pendant les tests, consultez nos étapes de dépannage pour les transactions.