Reservas de build (Dialogflow)

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:

  1. 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.
  2. Elaborar o pedido: oriente o usuário durante um "montagem do carrinho" onde constroem os detalhes da reserva.
  3. 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.
  4. Finalizar o pedido e enviar um recibo: com o pedido confirmado, atualize seu sistema de reservas e enviar um comprovante para o usuário.
  5. 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:

  1. Crie um novo projeto ou importe um existente.
  2. Acesse Implantar > informações do diretório.
  3. 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?".

  4. 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 para true. 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:

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

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"
        }
      }
    }
  }
}
JSON do SDK do Actions

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:

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 do Dialogflow

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": ""
}
JSON do SDK do Actions

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 como lineItems.
.

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:

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

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:

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 do Dialogflow

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"
          }
        }
      }
    }
  }
}
JSON do SDK do Actions

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ão ORDER_ACCEPTED, ORDER_REJECTED, CART_CHANGE_REQUESTED e USER_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:

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 do Dialogflow

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": ""
}
JSON do SDK do Actions

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

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 do Dialogflow

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"
                }
              }
            }
          }
        ]
      }
    }
  }
}
JSON do SDK do Actions

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:

  1. No console do Google Cloud, Ir para Menu ☰ > APIs e Serviços > Credenciais > Criar credenciais > Chave da conta de serviço.
  2. Em Conta de serviço, selecione Nova conta de serviço.
  3. Defina a conta de serviço como service-account.
  4. Defina o Papel como Projeto > proprietário.
  5. Defina o tipo de chave como JSON.
  6. Selecione Criar.
  7. 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 &quot;https://www.googleapis.com/auth/actions.order.developer&quot;. 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 como reservation.status, reservation.userVisibleStatusLabel.
  • order: o conteúdo da atualização. Se você estiver atualizando a conteúdo da reserva, defina o valor do objeto Order 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 objeto Order.
    • lastUpdateTime: carimbo de data/hora da atualização.
    • purchase: um objeto que contém o seguinte:
      • status: o status do pedido como um ReservationStatus, 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): A userNotification 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:

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);
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.