Tworzenie rezerwacji (Dialogflow)

Ten przewodnik przeprowadzi Cię przez proces tworzenia projektu w Actions, który używa interfejsu Orders API do składania rezerwacji.

Przepływ transakcji

Gdy projekt Actions obsługuje rezerwacje, korzysta z tego procesu:

  1. Zweryfikuj wymagania dotyczące transakcji (opcjonalnie) – na początku rozmowy użyj asystenta wymagań dotyczących transakcji, aby sprawdzić, czy użytkownik może zrealizować transakcję.
  2. Stworzenie zamówienia – pokaż użytkownikowi „zestaw koszyka”, w którym tworzy szczegóły rezerwacji.
  3. Zaproponowanie zamówienia – po zakończeniu „koszyka” zaproponuj użytkownikowi zamówienie z rezerwacją, by mógł on potwierdzić, że jest prawidłowy. Jeśli rezerwacja zostanie potwierdzona, otrzymasz odpowiedź ze szczegółowymi informacjami.
  4. Sfinalizuj zamówienie i wyślij potwierdzenie – po potwierdzeniu zamówienia zaktualizuj system rezerwacji i wyślij użytkownikowi potwierdzenie.
  5. Wyślij aktualizacje zamówienia – w trakcie okresu ważności rezerwacji wysyłaj aktualizacje stanu rezerwacji użytkownika, wysyłając żądania PATCH do interfejsu Orders API.

Ograniczenia i wytyczne dotyczące sprawdzania

Pamiętaj, że w przypadku akcji, które korzystają z interfejsu Transaction i Orders API, obowiązują dodatkowe zasady. Sprawdzenie działań z transakcjami może nam zająć do 6 tygodni, więc uwzględnij ten czas podczas planowania harmonogramu publikowania. Aby ułatwić proces sprawdzania, przed przesłaniem akcji do sprawdzenia upewnij się, że przestrzegasz zasad i wytycznych dotyczących transakcji.

Akcje, które używają interfejsu Orders API, możesz wdrażać tylko w tych krajach:

Australia
Brazylia
Kanada
Indonezja
Japonia
Meksyk
Katar
Rosja
Singapur
Szwajcaria
Tajlandia
Turcja
Wielka Brytania
Stany Zjednoczone

Utwórz projekt

Obszerne przykłady rozmów transakcyjnych znajdziesz w naszych przykładach transakcji w językach Node.js i Java.

Konfigurowanie projektu

Podczas tworzenia akcji musisz określić, że chcesz przeprowadzać transakcje w Konsoli Actions. Jeśli korzystasz z biblioteki klienta Node.JS, skonfiguruj realizację tak, aby korzystała z najnowszej wersji interfejsu Orders API.

Aby skonfigurować projekt i realizację:

  1. Utwórz nowy projekt lub zaimportuj istniejący.
  2. Kliknij Wdróż > Informacje z katalogu.
  3. W sekcji Dodatkowe informacje > Transakcje zaznacz pole „Czy Twoje działania używają interfejsu Transaction API do realizacji transakcji towarów fizycznych?”.

  4. Jeśli do tworzenia realizacji akcji używasz biblioteki klienta Node.JS, otwórz kod realizacji i zaktualizuj deklaracja aplikacji, aby ustawić flagę ordersv3 na true. Poniższy fragment kodu zawiera przykładową deklaracja aplikacji dla usługi Orders w wersji 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. Zweryfikuj wymagania dotyczące transakcji (opcjonalnie)

Interfejs użytkownika

Gdy tylko użytkownik zgłosi, że chce utworzyć rezerwację, zalecamy aktywowanie intencji actions.intent.TRANSACTION_REQUIREMENTS_CHECK, aby mieć pewność, że może zażądać rezerwacji. Po wywołaniu akcja może np. zapytać „Czy chcesz zarezerwować miejsce?”. Jeśli użytkownik mówi „tak”, należy od razu poprosić o taką intencję. Dzięki temu klient będzie mógł kontynuować transakcję i poprawić ustawienia, które uniemożliwiają kontynuowanie transakcji.

Żądanie spełnienia wymagań dotyczących transakcji skutkuje jednym z tych wyników:

  • Jeśli wymagania są spełnione, realizacja otrzymuje intencję z warunkiem powodzenia, możesz więc kontynuować tworzenie zamówienia użytkownika.
  • Jeśli co najmniej jedno z wymagań nie jest spełnione, realizacja otrzymuje intencję z warunkem niepowodzenia. W takim przypadku zakończ rozmowę lub wyjdź z procesu rezerwacji.

    Jeśli użytkownik będzie w stanie naprawić błąd, zostanie automatycznie poproszony o rozwiązanie problemów na urządzeniu. Jeśli rozmowa odbywa się tylko na urządzeniu głosowym, np. na inteligentnym głośniku, jest ona przekazywana do telefonu użytkownika.

realizację,

Aby mieć pewność, że użytkownik spełnia wymagania dotyczące transakcji, poproś o realizację intencji actions.intent.TRANSACTION_REQUIREMENTS_CHECK za pomocą obiektu TransactionRequirementsCheckSpec.

Sprawdź wymagania

Sprawdź w bibliotece klienta, czy użytkownik spełnia wymagania dotyczące rezerwacji:

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

Pamiętaj, że poniższy kod JSON opisuje odpowiedź webhooka.

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

Pamiętaj, że poniższy kod JSON opisuje odpowiedź webhooka.

{
  "expectUserResponse": true,
  "expectedInputs": [
    {
      "possibleIntents": [
        {
          "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK",
          "inputValueData": {
            "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec"
          }
        }
      ]
    }
  ]
}
Otrzymuj wynik kontroli wymagań

Gdy Asystent zrealizuje intencję, wysyła realizację żądania z intencją actions.intent.TRANSACTION_REQUIREMENTS_CHECK z wynikiem kontroli.

Aby prawidłowo obsługiwać to żądanie, zadeklaruj intencję Dialogflow wywoływaną przez zdarzenie actions_intent_TRANSACTION_REQUIREMENTS_CHECK. Po wywołaniu tej intencji postępuj zgodnie z tą intencją w realizacji:

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();
Plik JSON Dialogflow

Pamiętaj, że poniższy kod JSON opisuje żądanie webhooka.

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

Pamiętaj, że poniższy kod JSON opisuje żądanie webhooka.

{
  "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. Tworzenie zamówienia

Interfejs użytkownika

Gdy masz potrzebne informacje o użytkowniku, utwórz proces składania koszyka, by pomógł mu utworzyć rezerwację. W przypadku każdego działania proces składania koszyka będzie przebiegał trochę inaczej, zależnie od usługi.

W prostym procesie składania koszyka użytkownik wybiera z listy opcje, które chce dodać do swojej rezerwacji. Możesz jednak zaprojektować rozmowę w taki sposób, aby zwiększyć wygodę użytkowników. Przykładem może być proces składania koszyka, który pozwala użytkownikowi zaplanować miesięczną rezerwację z prostym pytaniem „tak” lub „nie”. Możesz też pokazać użytkownikowi karuzelę lub kartę listy z rezerwacjami z „rekomendowanymi” rezerwacjami.

Zalecamy stosowanie odpowiedzi rozszerzonych, aby wizualnie przedstawić opcje użytkownika, a także tak zaprojektować rozmowę, aby mógł utworzyć koszyk tylko przy użyciu głosu. Niektóre sprawdzone metody i przykłady procesu składania koszyka znajdziesz w wytycznych dotyczących projektowania transakcji.

realizację,

W trakcie rozmowy zbierz szczegóły rezerwacji, które użytkownik chce kupić, a następnie utwórz obiekt Order.

Order musi zawierać co najmniej:

  • buyerInfo – informacje o planowaniu rezerwacji przez użytkownika.
  • transactionMerchant – informacje o sprzedawcy, który umożliwia rezerwację.
  • contents – rzeczywiste szczegóły rezerwacji wymienione jako lineItems.

Informacje o tworzeniu koszyka znajdziesz w dokumentacji odpowiedzi Order. Pamiętaj, że w zależności od rezerwacji konieczne może być dodanie innych pól.

Przykładowy kod poniżej pokazuje pełne zamówienie rezerwacji z opcjonalnymi polami:

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

Pamiętaj, że poniższy kod JSON opisuje odpowiedź webhooka.

{
  "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. Zaproponuj zamówienie

Pokaż użytkownikowi swoje zamówienie rezerwacji, aby mógł je potwierdzić lub odrzucić. Poproś o intencję actions.intent.TRANSACTION_DECISION i podaj utworzony przez siebie obiekt Order.

Zadowolenie użytkowników

Gdy poprosisz o intencję actions.intent.TRANSACTION_DECISION, Asystent inicjuje wbudowane środowisko, w którym element Order jest renderowany bezpośrednio na „karcie podglądu koszyka”. Użytkownik może powiedzieć „zaplanuj rezerwację”, odrzucić transakcję lub poprosić o zmianę szczegółów rezerwacji.

W tym momencie użytkownik może też poprosić o zmianę zamówienia. W takim przypadku musisz upewnić się, że po zakończeniu składania zamówienia Twoja realizacja może obsługiwać prośby o zmianę zamówienia.

realizację,

Gdy poprosisz o intencję actions.intent.TRANSACTION_DECISION, utwórz TransactionDecision zawierającą elementy Order i orderOptions

Poniżej znajduje się przykładowy kod TransactionsDecision zamówienia:

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();
Plik JSON Dialogflow

Pamiętaj, że poniższy kod JSON opisuje odpowiedź webhooka.

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

Pamiętaj, że poniższy kod JSON opisuje odpowiedź webhooka.

{
  "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"
            }
          }
        }
      ]
    }
  ]
}
Podejmowanie decyzji użytkownika

Gdy użytkownik odpowie na proponowane zamówienie, realizacja otrzyma intencję actions_intent_TRANSACTION_DECISION z argumentem zawierającym TransactionDecisionValue. Ta wartość będzie zawierać:

  • transactionDecision – decyzja użytkownika dotycząca proponowanego zamówienia. Możliwe wartości to ORDER_ACCEPTED, ORDER_REJECTED, CART_CHANGE_REQUESTED i USER_CANNOT_TRANSACT.

Aby obsłużyć to żądanie, zadeklaruj intencję Dialogflow wywołaną przez zdarzenie actions_intent_TRANSACTION_DECISION. Obsługuj tę intencję w realizacji:

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"));
}
Plik JSON Dialogflow

Pamiętaj, że poniższy kod JSON opisuje żądanie webhooka.

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

Pamiętaj, że poniższy kod JSON opisuje żądanie webhooka.

{
  "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. Dokończ rezerwację i wyślij rachunek

Gdy intencja actions.intent.TRANSACTION_DECISION zwraca intencję transactionDecision o wartości ORDER_ACCEPTED, wykonaj dowolne przetwarzanie wymagane do zaplanowania rezerwacji (np. pozostawienie jej we własnej bazie danych).

Wyślij prostą odpowiedź, aby podtrzymać rozmowę. Obok Twojej odpowiedzi użytkownik otrzymuje „zwiniętą kartę potwierdzenia”.

realizację,

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();
Plik JSON Dialogflow

Pamiętaj, że poniższy kod JSON opisuje odpowiedź webhooka.

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

Pamiętaj, że poniższy kod JSON opisuje odpowiedź webhooka.

{
  "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. Wysyłanie aktualizacji zamówienia

Stan rezerwacji zmienia się w trakcie jej życia. Wysyłaj do interfejsu Orders API aktualizacje dotyczące zamówień rezerwacji użytkownika wraz z żądaniami HTTP PATCH z informacjami o stanie i szczegółach zamówienia.

Konfigurowanie żądań asynchronicznych kierowanych do interfejsu Orders API

Żądania aktualizacji zamówień wysyłane do interfejsu Orders API są autoryzowane przez token dostępu. Aby ŁATWIĆ aktualizację zamówienia do interfejsu Orders API, pobierz klucz konta usługi JSON powiązany z projektem w Actions Console, a następnie wymień klucz konta usługi na token okaziciela, który można przekazać do nagłówka Authorization żądania HTTP.

Aby pobrać klucz konta usługi, wykonaj te czynności:

  1. W konsoli Google Cloud wybierz Menu />< > Interfejsy API i usługi > Dane logowania > Utwórz dane logowania > Klucz konta usługi.
  2. W sekcji Service Account (Konto usługi) wybierz New Service Account (Nowe konto usługi).
  3. Ustaw konto usługi na service-account.
  4. Ustaw Rolę na Projekt > Właściciel.
  5. Ustaw typ klucza na JSON.
  6. Wybierz Utwórz.
  7. Na Twój komputer zostanie pobrany prywatny klucz konta usługi JSON.

W kodzie aktualizacji zamówienia wymień klucz usługi na token okaziciela, korzystając z biblioteki klienta interfejsów API Google i zakresu "https://www.googleapis.com/auth/actions.order.developer". Instrukcje i przykłady instalacji znajdziesz na stronie w GitHubie w bibliotece klienta interfejsu API.

Przykładową wymianę kluczy znajdziesz w przykładach kodu Node.js i Java.order-update.js

Wysyłanie aktualizacji zamówienia

Po wymianie klucza konta usługi na token okaziciela OAuth przesyłaj aktualizacje zamówień jako autoryzowane żądania PATCH do interfejsu Orders API.

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

Podaj te nagłówki w żądaniu:

  • "Authorization: Bearer token" z tokenem okaziciela OAuth, na który wymieniono klucz konta usługi.
  • "Content-Type: application/json".

Żądanie PATCH powinno mieć treść JSON w tym formacie:

{ "orderUpdate": OrderUpdate }

Obiekt OrderUpdate składa się z tych pól najwyższego poziomu:

  • updateMask – pola zamówienia, które aktualizujesz. Aby zaktualizować stan rezerwacji, ustaw wartość na reservation.status, reservation.userVisibleStatusLabel.
  • order – zawartość aktualizacji. Jeśli aktualizujesz zawartość rezerwacji, ustaw wartość na zaktualizowany obiekt Order. Jeśli tylko aktualizujesz stan rezerwacji (na przykład z "PENDING" na "FULFILLED"), obiekt zawiera te pola:

    • merchantOrderId – identyfikator ustawiony w obiekcie Order.
    • lastUpdateTime – sygnatura czasowa aktualizacji.
    • purchase – obiekt zawierający:
      • status – stan zamówienia to ReservationStatus, np. „CONFIRMED” lub „CANCELLED”.
      • userVisibleStatusLabel – widoczna dla użytkowników etykieta ze szczegółowymi informacjami o stanie zamówienia, np. „Twoja rezerwacja została potwierdzona”.
  • userNotification, który może się wyświetlać na urządzeniu użytkownika po wysłaniu tej aktualizacji. Pamiętaj, że uwzględnienie tego obiektu nie gwarantuje, że powiadomienie pojawi się na urządzeniu użytkownika.

Poniżej znajduje się przykładowy kod OrderUpdate, który aktualizuje stan zamówienia rezerwacji na 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);
Ustawianie stanu rezerwacji

Pole ReservationStatus aktualizacji zamówienia musi zawierać opis jego bieżącego stanu. W polu order.ReservationStatus aktualizacji użyj jednej z tych wartości:

  • PENDING – rezerwacja została „utworzona” przez akcję, ale wymaga dodatkowego przetwarzania przez backend.
  • CONFIRMED – rezerwacja została potwierdzona w backendzie harmonogramu.
  • CANCELLED – użytkownik anulował rezerwację.
  • FULFILLED – rezerwacja użytkownika została zrealizowana przez usługę.
  • CHANGE_REQUESTED – użytkownik poprosił o zmianę rezerwacji, a zmiana jest przetwarzana.
  • REJECTED – jeśli nie udało Ci się przetworzyć rezerwacji lub potwierdzić ją w inny sposób.

Wysyłaj aktualizacje zamówień o każdym stanie, który ma znaczenie dla Twojej rezerwacji. Jeśli na przykład rezerwacja wymaga ręcznego przetworzenia w celu potwierdzenia rezerwacji po jej przesłaniu, wysyłaj aktualizację zamówienia PENDING do czasu zakończenia dodatkowego przetwarzania. Nie każda rezerwacja wymaga każdej wartości stanu.

Rozwiązywanie problemów

Jeśli podczas testowania napotkasz jakieś problemy, przeczytaj nasze instrukcje rozwiązywania problemów z transakcjami.