Ce guide vous accompagne tout au long du 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 les réservations, utilise le flux suivant:
- Valider les exigences concernant les transactions (facultatif) : utilisez l'outil d'aide. au début de la conversation pour vous assurer que l'utilisateur est capable effectuer une transaction.
- Créer l'ordre : guidez l'utilisateur tout au long d'une "montage chariot" où il construit les détails de sa réservation.
- Proposez la commande : une fois le panier est terminée, proposez la réservation "order" à l’utilisateur, afin qu’il puisse confirmer qu’elle est correcte. Si la réservation est confirmée, reçoivent une réponse avec les détails de la réservation.
- Finalisez la commande et envoyez un reçu : une fois la commande confirmée, mettez à jour votre système de réservation et envoyer un reçu pour l'utilisateur.
- Envoyer des mises à jour de la commande : au cours de la durée de vie de la réservation, des mises à jour de l'état de la réservation de l'utilisateur en envoyant des requêtes PATCH au Orders API.
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 transactions peut prendre jusqu'à six semaines. Tenez compte de ce temps lors de la planification de votre calendrier de publication. Pour faciliter le processus d'examen, assurez-vous de respecter les Règles et consignes relatives aux transactions avant d'envoyer votre action pour examen.
Vous ne pouvez déployer des actions qui utilisent Orders API 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 dans Node.js et Java.
Configuration du projet
Lorsque vous créez votre action, vous devez indiquer que vous souhaitez effectuer des transactions dans la console Actions. De plus, si vous à l'aide de la bibliothèque cliente Node.JS, configurez votre fulfillment pour utiliser la dernière version d'Orders API.
Pour configurer votre projet et votre fulfillment, procédez comme suit:
- Créez un projet ou importez un projet existant.
- Accédez à Déployer > Informations de l'annuaire.
Sous Informations supplémentaires > Transactions > cochez la case "Do your Actions (Actions utiliser l'API Transactions pour effectuer des transactions de biens physiques.
Si vous utilisez la bibliothèque cliente Node.JS pour créer le fulfillment de votre action, ouvrez votre code de fulfillment et mettez à jour la délégation de votre application pour définir l'indicateur
ordersv3
surtrue
. L'extrait de code suivant montre un exemple 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 concernant les transactions (facultatif)
Expérience utilisateur
Dès que l'utilisateur a indiqué qu'il souhaite configurer une réservation, nous vous recommandons de déclencher
actions.intent.TRANSACTION_REQUIREMENTS_CHECK
pour s'assurer qu'ils peuvent
une réservation. Par exemple, lorsqu'elle est appelée, votre action peut demander :
« voudriez-vous réserver
une place ? » Si l'utilisateur dit
"yes", vous devez demander cet intent immédiatement. Ainsi, vous aurez la garantie
qu'il peut continuer et lui donner la possibilité de corriger les paramètres
l’empêchant de poursuivre la transaction.
Demander les transactions L'intent de vérification des exigences aboutit à l'un des résultats suivants:
- Si les conditions sont remplies, le fulfillment reçoit un intent avec une et vous pouvez passer à la création de la commande de l'utilisateur.
Si une ou plusieurs des conditions requises ne le sont pas, votre traitement reçoit l'intent avec une condition d'échec. Dans ce cas, mettez fin à la conversation ou ne s'éloigne pas du flux de réservation.
Si l'utilisateur peut corriger l'erreur, il est automatiquement invité à le faire. sur leur appareil. Si la conversation se déroule sur une surface vocale uniquement comme une enceinte intelligente, elle est transmise sur le téléphone de l'utilisateur.
Fulfillment
Pour s’assurer qu’un utilisateur répond aux
les exigences relatives aux transactions, le traitement des
l'intent actions.intent.TRANSACTION_REQUIREMENTS_CHECK
avec une
Un objet TransactionRequirementsCheckSpec.
Vérifier les exigences
Vérifiez si un utilisateur satisfait aux exigences de réservation à l'aide de la bibliothèque cliente:
<ph type="x-smartling-placeholder">conv.ask(new TransactionRequirements());
return getResponseBuilder(request) .add("Placeholder for transaction requirements text") .add(new TransactionRequirements()) .build();
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" } } } } }
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 une requête à votre traitement.
avec l'intent actions.intent.TRANSACTION_REQUIREMENTS_CHECK
et le résultat.
de la vérification.
Pour traiter correctement cette requête, déclarez un intent Dialogflow déclenché par
l'événement actions_intent_TRANSACTION_REQUIREMENTS_CHECK
. Lorsqu'elle est déclenchée,
gérez cet intent dans votre traitement:
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.'); }
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();
Notez que le code JSON ci-dessous décrit une requête 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": "" }
Notez que le code JSON ci-dessous décrit une requête 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 avez les informations utilisateur dont vous avez besoin, construisez un « panier assemblage" expérience qui guide l'utilisateur dans la création de sa réservation. Toutes les Le processus d'assemblage du chariot varie légèrement en fonction des Google Cloud.
Dans une expérience de base d'assemblage du panier, un utilisateur sélectionne des options dans une liste à ajouter à leur réservation, mais vous pouvez concevoir la conversation de façon à simplifier l'expérience utilisateur. Par exemple, créez une expérience d'assemblage de chariot qui permet au de planifier une réservation mensuelle avec une simple question par oui ou non. Vous pouvez également présenter à l'utilisateur une fiche de carrousel ou de liste "recommandé" des réservations.
Nous vous recommandons d'utiliser les des réponses pour présenter les options visuellement, mais aussi de concevoir la conversation afin que l’utilisateur puisse construire son panier par simple commande vocale. Pour découvrir quelques bonnes pratiques et des exemples d'assemblage du panier, consultez les consignes de conception des transactions.
Fulfillment
Tout au long de votre conversation, recueillez les détails de la réservation qu'un utilisateur souhaite
d'acheter, puis de construire un objet Order
.
Votre Order
doit au moins contenir les éléments suivants:
buyerInfo
: informations sur l'utilisateur qui planifie la réservation.transactionMerchant
: informations sur le marchand qui facilite la réservation.contents
: détails réels de la réservation indiqués en tant quelineItems
.
Consultez la documentation sur les réponses Order
pour créer votre panier. Notez que vous devrez peut-être inclure des champs différents
en fonction de la réservation.
L'exemple de code ci-dessous montre une commande de réservation complète, avec des champs facultatifs:
<ph type="x-smartling-placeholder">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', };
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; }
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 l'ordre
Présentez l'ordre de réservation à l'utilisateur pour qu'il la confirme ou
refuser. Demander le actions.intent.TRANSACTION_DECISION
et fournir 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
est
directement sur une "fiche d'aperçu du panier". L'utilisateur peut dire
« planifier une réservation »,
refuser la transaction ou demander
de modifier les détails de la réservation.
À ce stade, l'utilisateur peut également demander à modifier la commande. Dans ce cas, vous devez vous assurer que votre traitement peut traiter les demandes de modification de commande après terminer l’expérience d’assemblage du chariot.
Fulfillment
Lorsque vous demandez
Intent actions.intent.TRANSACTION_DECISION
, créer un
TransactionDecision
contenant le Order
et orderOptions
Le code suivant montre un exemple de TransactionsDecision
pour une commande:
conv.ask(new TransactionDecision({ orderOptions: { requestDeliveryAddress: 'false', }, presentationOptions: { actionDisplayName: 'RESERVE', }, order: order, }));
// 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();
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" } } } } } }
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 la
L'intent actions_intent_TRANSACTION_DECISION
avec un argument contenant un
TransactionDecisionValue
Cette valeur contiendra les éléments suivants:
transactionDecision
: décision de l'utilisateur concernant la proposition commande. Les valeurs possibles sontORDER_ACCEPTED
,ORDER_REJECTED
,CART_CHANGE_REQUESTED
etUSER_CANNOT_TRANSACT
.
Pour gérer cette requête, déclarez un intent Dialogflow déclenché par
l'événement actions_intent_TRANSACTION_DECISION
. Gérer cet intent dans
votre traitement:
const arg = conv.arguments.get('TRANSACTION_DECISION_VALUE'); if (arg && arg.transactionDecision === 'ORDER_ACCEPTED') { console.log('order accepted'); const order = arg.order; }
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")); }
Notez que le code JSON ci-dessous décrit une requête 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": "" }
Notez que le code JSON ci-dessous décrit une requête 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 une
transactionDecision
de ORDER_ACCEPTED
, vous pouvez effectuer n'importe quelle
un traitement est nécessaire pour planifier la réservation (comme sa persistance
votre propre base de données).
Envoyer une réponse simple pour alimenter la conversation. L'utilisateur reçoit une "fiche de reçu réduite" ainsi que votre réponse.
Fulfillment
<ph type="x-smartling-placeholder">// 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, }));
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();
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" } } } } ] } } } }
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. Envoyer des mises à jour de l'ordre de réservation de l'utilisateur avec HTTP Requêtes PATCH envoyées à Orders API, contenant l'état et les détails de la commande.
Configurer des requêtes asynchrones dans Orders API
Les requêtes de mise à jour de commandes envoyées à Orders API sont autorisées par un accès
à partir d'un jeton d'accès. Pour valider une mise à jour de commande dans Orders API, téléchargez un fichier JSON
la clé de compte de service associée à votre projet dans la console Actions, puis la clé
la clé du compte de service pour un jeton de support pouvant être transmis
En-tête Authorization
de la requête HTTP.
Pour récupérer la clé de votre compte de service, procédez comme suit:
- Dans la console Google Cloud : accédez à Menu ▾ > API et Services > Identifiants > Créer des identifiants > Clé de compte de service :
- Sous Compte de service, sélectionnez Nouveau compte de service.
- Définissez le compte de service sur
service-account
. - Définissez Rôle sur Projet > Propriétaire.
- Définissez le type de clé sur JSON.
- Sélectionnez Créer.
- Une clé de compte de service JSON privée sera téléchargée sur votre ordinateur local.
Dans votre code de mise à jour des commandes, é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 les étapes d'installation et sur la page GitHub de la bibliothèque cliente de l'API.
Référencer order-update.js
dans nos exemples Node.js et Java pour
est un exemple d'échange de clés.
Envoyer des mises à jour de commande
Après avoir échangé votre clé de compte de service contre un jeton de support OAuth, envoyez les mises à jour de commandes en tant que requêtes PATCH autorisées à l'API Orders
URL de l'API Orders :
PATCH https://actions.googleapis.com/v3/orders/${orderId}
Indiquez les en-têtes suivants dans votre requête:
"Authorization: Bearer token"
par le jeton de support OAuth contre laquelle vous avez échangé votre clé de compte de service."Content-Type: application/json"
.
La requête PATCH doit utiliser un corps JSON au format suivant:
{ "orderUpdate": OrderUpdate }
OrderUpdate
comporte 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 surreservation.status, reservation.userVisibleStatusLabel
.order
: contenu de la mise à jour. Si vous mettez à jour contenu de la réservation, définissez sa valeur sur l'objetOrder
mis à jour. Si vous ne faites que mettre à jour l'état de la réservation (par exemple,"PENDING"
à"FULFILLED"
), l'objet contient les champs suivants:merchantOrderId
: ID que vous avez défini dans l'objetOrder
.lastUpdateTime
: code temporel de cette mise à jour.purchase
: objet contenant les éléments suivants : <ph type="x-smartling-placeholder">- </ph>
status
: état de la commande (ReservationStatus
). tel que "CONFIRMED
" ou "CANCELLED
".userVisibleStatusLabel
: libellé visible par l'utilisateur fournissant des informations sur l'état de la commande, par exemple "Votre réservation est confirmée".
userNotification
(facultatif) – AuserNotification
qui peut s'afficher sur l'appareil de l'utilisateur lors de l'envoi de cette mise à jour. Remarque que l'inclusion de cet objet ne garantit pas l'affichage d'une notification l'appareil de l'utilisateur.
L'exemple de code suivant montre un exemple de OrderUpdate
qui met à jour le
l'état de la commande de réservation sur FULFILLED
:
// 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; } }); });
// 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
ReservationStatus
de la mise à jour d'une commande
doit décrire l'état actuel de l'ordonnance. Dans les 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é réalisée par le service.CHANGE_REQUESTED
: l'utilisateur a demandé à modifier la réservation. Cette modification est en cours de traitement.REJECTED
- Si vous n'avez pas pu traiter confirmer la réservation.
Envoyez des notifications de commande pour chaque état pertinent pour votre
réservation. Par exemple, si votre réservation nécessite un traitement manuel pour
confirmer la réservation après sa demande, envoyer une mise à jour de la commande PENDING
jusqu'au
qu'un traitement supplémentaire est effectué. Toutes les réservations ne nécessitent pas toutes les valeurs d'état.
Dépannage
Si vous rencontrez des problèmes lors des tests, consultez notre procédure de dépannage. pour les transactions.