Este guia contém orientações sobre o processo de desenvolvimento de um projeto do Actions que usa a API Orders para fazer reservas.
Fluxo de transações
Quando seu projeto do Actions processa reservas, usa este fluxo:
- Validar os requisitos de transação (opcional): use o assistente de requisitos de transações. no início da conversa para garantir que o usuário seja capaz de para realizar uma transação.
- Elaborar o pedido: oriente o usuário durante um "montagem do carrinho" onde constroem os detalhes da reserva.
- Propor o pedido: assim que o "carrinho" for concluído, proponha o "pedido" da reserva para para que o usuário possa confirmar se está correta. Se a reserva estiver confirmada, você receber uma resposta com os detalhes da reserva.
- Finalizar o pedido e enviar um recibo: com o pedido confirmado, atualize seu sistema de reservas e enviar um comprovante para o usuário.
- Enviar atualizações do pedido: durante a vida útil da reserva, atualize o status de reserva do usuário enviando solicitações PATCH ao API Orders.
Restrições e diretrizes de avaliações
Lembre-se de que outras políticas se aplicam a ações que usam as transações e a API Orders. Pode levar até seis semanas para revisar as Ações com transações. Por isso, leve esse tempo para planejar seu cronograma de lançamentos. Para facilitar o processo de análise, obedeça às políticas e diretrizes de transações antes de enviar sua Ação para análise.
Só é possível implantar ações que usam a API Orders nos seguintes países:
Austrália Brasil Canadá Indonésia |
Japão México Catar Rússia |
Singapura Suíça Tailândia Turquia Reino Unido Estados Unidos |
Criar o projeto
Para ver exemplos abrangentes de conversas transacionais, confira nossos exemplos de transações em Node.js e Java.
Configurar o projeto
Ao criar sua ação, especifique que você quer realizar transações no Console do Actions. Além disso, se você estiver com a biblioteca de cliente Node.JS, configure o fulfillment para usar versão mais recente da API Orders.
Para configurar o projeto e o fulfillment, faça o seguinte:
- Crie um novo projeto ou importe um existente.
- Acesse Implantar > informações do diretório.
Em Informações adicionais > Transações > marque a caixa que diz "Faça suas ações usar a API Transactions para realizar transações de produtos físicos?".
Se você estiver usando a biblioteca de cliente Node.JS para criar o fulfillment da ação, abra o código de fulfillment e atualize a declaração do app para definir a sinalização
ordersv3
paratrue
. O snippet de código a seguir mostra um app de exemplo para a versão 3 dos Pedidos.
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. Valide os requisitos da transação (opcional)
Experiência do usuário
Assim que o usuário indicar que quer configurar uma reserva, recomendamos acionar o
actions.intent.TRANSACTION_REQUIREMENTS_CHECK
para garantir que eles possam
fazer uma reserva. Por exemplo, quando invocada, sua ação pode perguntar:
"você gostaria de reservar um assento?" Se o usuário diz
“yes”, solicite essa intent imediatamente. Isso garantirá
que ele pode prosseguir e dar a ele a oportunidade de corrigir as configurações
impedindo que ele prossiga com a transação.
Como solicitar as transações A intent de verificação de requisitos resulta em um dos seguintes resultados:
- Se os requisitos forem atendidos, o fulfillment receberá uma intent com uma e poderá prosseguir com a criação do pedido do usuário.
Se um ou mais requisitos não estiverem, o atendimento de pedidos receberá ao intent com uma condição de falha. Nesse caso, encerre a conversa ou se afastar do fluxo de reserva.
Se o usuário puder corrigir o erro, ele vai receber uma solicitação automática para resolver esses problemas no dispositivo. Se a conversa estiver ocorrendo em uma plataforma somente de voz como um alto-falante inteligente, ele é entregue ao smartphone do usuário.
Fulfillment
Para garantir que um usuário atenda
requisitos de transação, solicitar o cumprimento do
actions.intent.TRANSACTION_REQUIREMENTS_CHECK
com uma
TransactionRequirementsCheckSpec.
Verificar os requisitos
Verifique se um usuário atende aos requisitos de reserva com a biblioteca de cliente:
conv.ask(new TransactionRequirements());
return getResponseBuilder(request) .add("Placeholder for transaction requirements text") .add(new TransactionRequirements()) .build();
O JSON abaixo descreve uma resposta do webhook.
{ "payload": { "google": { "expectUserResponse": true, "systemIntent": { "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK", "data": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec" } } } } }
O JSON abaixo descreve uma resposta do webhook.
{ "expectUserResponse": true, "expectedInputs": [ { "possibleIntents": [ { "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK", "inputValueData": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec" } } ] } ] }
Receber o resultado de uma verificação de requisitos
Depois que o Google Assistente realiza a intent, ele envia uma solicitação ao seu fulfillment
com a intent actions.intent.TRANSACTION_REQUIREMENTS_CHECK
com o resultado
da verificação.
Para processar corretamente essa solicitação, declare uma intent do Dialogflow acionada por
o evento actions_intent_TRANSACTION_REQUIREMENTS_CHECK
. Quando ele é acionado,
processar esta intent no seu fulfillment:
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();
O JSON abaixo descreve uma solicitação 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": "" }
O JSON abaixo descreve uma solicitação 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. Criar o pedido
Experiência do usuário
Depois de ter as informações de usuário necessárias, crie um "carrinho" assembly" que orienta o usuário na criação da reserva. Todas A ação terá um fluxo de montagem de carrinho ligeiramente diferente, conforme apropriado para serviço.
Em uma experiência básica de montagem de carrinho, o usuário seleciona as opções de uma lista para adicionar para a reserva, embora você possa projetar a conversa para simplificar a experiência do usuário. Por exemplo, crie uma experiência de montagem de carrinho que permita ao faça uma reserva mensal com uma simples pergunta de "sim" ou "não". Também é possível apresentar ao usuário um carrossel ou card de lista de "recomendado" reservas.
Recomendamos o uso de recursos avançados respostas para apresentar as opções do usuário visualmente, mas também projetar a conversa para que o usuário possa construir carrinho usando apenas a voz. Para conferir algumas práticas recomendadas e exemplos de experiências de montagem de carrinho, consulte as Diretrizes de design de transações.
Fulfillment
Ao longo da conversa, colete os detalhes da reserva que um usuário quer
para comprar e, em seguida, construa um objeto Order
.
Seu Order
precisa conter pelo menos o seguinte:
buyerInfo
: informações sobre o usuário que faz a reserva.transactionMerchant
– Informações sobre o comerciante que está facilitando a reserva.contents
: os detalhes reais da reserva listados comolineItems
.
Consulte a documentação de resposta Order
para criar seu carrinho. Talvez seja necessário incluir campos diferentes
dependendo da reserva.
O exemplo de código abaixo mostra um pedido de reserva completo, incluindo campos opcionais:
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; }
O JSON abaixo descreve uma resposta do 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. Propor o pedido
Apresente seu pedido de reserva ao usuário para que ele confirme ou
rejeitar. Solicitar a actions.intent.TRANSACTION_DECISION
e fornecer a Order
que você criou.
Experiência do usuário
Quando você solicita a intent actions.intent.TRANSACTION_DECISION
, o Google Assistente
inicia uma experiência integrada em que o Order
é
renderizados diretamente em um "card de visualização do carrinho". O usuário pode dizer "agendar reserva",
Recusar a transação ou solicitar a alteração dos detalhes da reserva.
Nesse momento, o usuário também pode solicitar alterações no pedido. Nesse caso, verifique se o atendimento pode processar as solicitações de alteração do pedido após para finalizar a experiência de montagem do carrinho.
Fulfillment
Quando você solicita a
actions.intent.TRANSACTION_DECISION
, crie uma
TransactionDecision
que contém o Order
e orderOptions
O código a seguir mostra um exemplo de TransactionsDecision
para um pedido:
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();
O JSON abaixo descreve uma resposta do 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" } } } } } }
O JSON abaixo descreve uma resposta do 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" } } } ] } ] }
Lidar com a decisão do usuário
Depois que o usuário responder ao pedido proposto, o fulfillment recebe a
actions_intent_TRANSACTION_DECISION
com um argumento contendo uma
TransactionDecisionValue
. Esse valor conterá o seguinte:
transactionDecision
: a decisão do usuário em relação à proposta ordem. Os valores possíveis sãoORDER_ACCEPTED
,ORDER_REJECTED
,CART_CHANGE_REQUESTED
eUSER_CANNOT_TRANSACT
.
Para processar essa solicitação, declare uma intent do Dialogflow acionada por
o evento actions_intent_TRANSACTION_DECISION
. Processar essa intent no
processamento do pedido:
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")); }
O JSON abaixo descreve uma solicitação 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": "" }
O JSON abaixo descreve uma solicitação 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. Finalizar a reserva e enviar um comprovante
Quando a intent actions.intent.TRANSACTION_DECISION
retorna com uma
transactionDecision
de ORDER_ACCEPTED
, execute o que for
processamento é necessário para programar a reserva (por exemplo, armazená-la em
seu próprio banco de dados).
Enviar uma resposta simples para manter a conversa rolando. O usuário recebe um "cartão de comprovante recolhido". junto com sua resposta.
Fulfillment
// 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();
O JSON abaixo descreve uma resposta do 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" } } } } ] } } } }
O JSON abaixo descreve uma resposta do 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. Enviar atualizações de pedidos
O status da reserva muda ao longo por todo o ciclo de vida. Enviar atualizações do pedido de reserva do usuário com HTTP PATCH para a API Orders, contendo o status e os detalhes do pedido.
Configurar solicitações assíncronas para a API Orders
As solicitações de atualização de pedidos para a API Orders são autorizadas por um acesso
com base no token correto anterior. Para PATCH uma atualização de pedido para a API Orders, faça o download de um arquivo JSON
chave da conta de serviço associada ao seu projeto do Console do Actions e, em seguida, trocar
a chave da conta de serviço para um token do portador que pode ser passado ao
Cabeçalho Authorization
da solicitação HTTP.
Para recuperar a chave da conta de serviço, siga estas etapas:
- No console do Google Cloud, Ir para Menu ☰ > APIs e Serviços > Credenciais > Criar credenciais > Chave da conta de serviço.
- Em Conta de serviço, selecione Nova conta de serviço.
- Defina a conta de serviço como
service-account
. - Defina o Papel como Projeto > proprietário.
- Defina o tipo de chave como JSON.
- Selecione Criar.
- Será feito o download de uma chave privada da conta de serviço JSON para sua máquina local.
No seu código de atualizações do pedido, troque sua chave de serviço por um token do portador usando a biblioteca de cliente de APIs do Google e o escopo "https://www.googleapis.com/auth/actions.order.developer". Confira as etapas de instalação e na página do GitHub da biblioteca de cliente da API.
Consulte order-update.js
nas amostras de Node.js e Java para
um exemplo de troca de chaves.
Enviar atualizações de pedidos
Depois de trocar a chave da sua conta de serviço por um token do portador OAuth, envie atualizações de pedidos como solicitações PATCH autorizadas para a API Orders.
URL da API Orders:
PATCH https://actions.googleapis.com/v3/orders/${orderId}
Forneça os seguintes cabeçalhos na solicitação:
"Authorization: Bearer token"
pelo token do portador OAuth pela qual você trocou a chave da conta de serviço."Content-Type: application/json"
.
A solicitação PATCH precisa ter um corpo JSON no seguinte formato:
{ "orderUpdate": OrderUpdate }
O OrderUpdate
consiste nos seguintes campos de nível superior:
updateMask
: os campos do pedido que você está atualizando. Para atualizar o o status da reserva, Defina o valor comoreservation.status, reservation.userVisibleStatusLabel
.order
: o conteúdo da atualização. Se você estiver atualizando a conteúdo da reserva, defina o valor do objetoOrder
atualizado. Se você estiver apenas atualizando o status da reserva (por exemplo, de"PENDING"
a"FULFILLED"
), o objeto contém o seguintes campos:merchantOrderId
: o mesmo ID definido no objetoOrder
.lastUpdateTime
: carimbo de data/hora da atualização.purchase
: um objeto que contém o seguinte:status
: o status do pedido como umReservationStatus
, como "CONFIRMED
" ou "CANCELLED
".userVisibleStatusLabel
: um rótulo voltado para o usuário com detalhes sobre o status do pedido, como "Sua reserva foi confirmada".
userNotification
(opcional): AuserNotification
que pode ser exibido no dispositivo do usuário quando essa atualização é enviada. Observação que a inclusão deste objeto não garante que uma notificação seja exibida no dispositivo do usuário.
O exemplo de código a seguir mostra um exemplo de OrderUpdate
que atualiza a
do pedido de reserva para 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);
Definir o status da reserva
ReservationStatus
de uma atualização de pedido
deve descrever o estado atual do pedido. No order.ReservationStatus
da sua atualização
, use um dos seguintes valores:
PENDING
: a reserva foi "criada" pela sua Ação, mas exige processamento adicional em seu back-end.CONFIRMED
: a reserva é confirmada no seu back-end de programação.CANCELLED
: o usuário cancelou a reserva.FULFILLED
: a reserva do usuário foi atendida pelo serviço.CHANGE_REQUESTED
: o usuário solicitou uma alteração na reserva, e a alteração é que estão sendo processados.REJECTED
: se não foi possível processar ou de outra forma confirme a reserva.
Envie atualizações de pedidos para cada status relevante para seu
reserva. Por exemplo, se a reserva exigir o processamento manual para
Confirmar a reserva depois de ser solicitada, enviar uma atualização do pedido PENDING
até
que um processamento adicional seja feito. Nem toda reserva exige todos os valores de status.
Solução de problemas
Se você tiver problemas durante o teste, leia as etapas de solução de problemas. para transações.