构建预留

本指南将逐步介绍如何开发 Actions 项目 使用 Orders API 进行预订。

交易流程

当 Actions 项目处理预留时,它会 采用以下流程:

  1. 验证交易要求(可选)- 使用交易 需求小助手,确保能 用户能否进行交易。
  2. 建立订单 - 引导用户完成 "购物车组装"在这里构建预留的详细信息
  3. 提议订单 -“购物车”页面则提议预订“订单”更改为 以便确认其正确无误。如果预订得到确认 收到包含预留详情的响应
  4. 敲定订单并发送收据 - 订单已确认后,更新 并向用户发送收据。
  5. 发送订单更新 - 在预订的整个有效期内, 将 PATCH 请求发送到 Orders API。

限制和查看指南

请注意,使用 和 Orders API。我们最多可能需要 6 周时间来审核 Action 因此在规划发布时间表时,请将这部分时间考虑在内。 为了简化审核流程,请确保您遵守 交易政策和准则 然后再提交 Action 以供审核。

您只能在以下国家/地区部署使用 Orders API 的 Action:

澳大利亚
巴西
加拿大
印度尼西亚
日本
墨西哥
卡塔尔
俄罗斯
新加坡
瑞士
泰国
土耳其
英国
美国

构建您的项目

有关交易对话的详尽示例,请参阅我们的事务处理 Node.js 中的示例

设置

创建 Action 时,你必须指定要执行交易 在 Actions 控制台中操作。

如需设置项目和执行方式,请执行以下操作:

  1. 创建新项目或导入现有项目。
  2. 导航到部署 >目录信息
  3. 其他信息 >交易 >选中 使用 Transaction API 执行实体商品的交易?”。

验证交易要求(可选)

用户表示想要设置预订后,您应该立即选中 他们能够请求预订例如,在被调用时,您的 Action 可能会 询问:“要预订座位吗?”如果用户说“是”,您应该 确保他们可以继续操作,并给他们机会来修正所有设置 阻止客户继续处理交易为此,您应该 过渡到执行事务要求检查的场景。

创建交易要求检查场景

  1. Scenes 标签页中,添加一个名为 TransactionRequirementsCheck
  2. 槽填充下,点击 + 以添加新槽。
  3. 选择类型下,选择 actions.type.TransactionRequirementsCheckResult 作为广告位类型
  4. 在槽名称字段中,将槽命名为 TransactionRequirementsCheck
  5. 选中自定义槽值回写复选框(默认处于启用状态)。
  6. 点击保存

事务要求检查会产生以下结果之一:

  • 如果满足这些要求,系统就会成功设置会话参数 您可以继续构建用户订单。
  • 如果不能满足一项或多项要求,则会话参数为 带有失败条件的集合。在这种情况下,您应围绕 或结束对话。
    • 如果用户可以更正导致失败状态的任何错误, 系统会提示他们在设备上解决这些问题。如果 对话是在纯语音平台上进行的, 向用户的电话发起调用

处理交易要求检查结果

  1. Scenes 标签页中,选择您新创建的场景 TransactionRequirementsCheck 个场景。
  2. 条件下,点击 + 以添加新条件。
  3. 在文本字段中,输入以下条件语法,以检查 成功条件:

    scene.slots.status == "FINAL" && session.params.TransactionRequirementsCheck.resultType == "CAN_TRANSACT"
    
  4. 将光标悬停在您刚刚添加的条件上,然后点击向上箭头 并将其放置在 if scene.slots.status == "FINAL" 之前。

  5. 启用 Send prompts,并提供简单的提示让用户知道 他们已准备好进行交易:

    candidates:
      - first_simple:
          variants:
            - speech: >-
                Looks like you're good to go!.
    
  6. 过渡下选择其他场景,以便用户继续操作 对话并继续进行交易。

  7. 选择条件 else if scene.slots.status == "FINAL"

  8. 启用 Send prompts,并提供简单的提示让用户知道 他们无法进行交易:

    candidates:
      - first_simple:
          variants:
            - speech: Transaction requirements check failed.
    
  9. 转换下,选择结束对话以结束对话 如果用户无法进行交易。

创建订单

获得所需的用户信息后,即可构建“购物车” 组装"引导用户进行预订。每个 Action 的购物车装配流程将视情况略有不同 服务。

在基本的购物车组装体验中,用户从列表中选择要添加的选项 但您可以设计对话来简化 用户体验。例如,打造购物车组装体验, 通过简单的“是”或“非”问题来安排每月预订。 您还可以向用户显示“推荐”的轮播界面或列表卡片 预留。

我们建议使用富响应来 不仅要直观地呈现用户的选项 而且要妥善设计对话 用户只需使用语音即可构建购物车。一些最佳实践和 购物车组装体验示例,请参阅设计准则

创建一个订单

在整个对话过程中,收集用户的预订详情,然后 构建一个 Order 对象。

您的 Order 必须至少包含以下内容:

  • buyerInfo - 购买用户的相关信息。
  • transactionMerchant - 协助完成订单的商家的相关信息。
  • contents - 列为 lineItems 的订单的实际内容。

请参阅 Order 构建您的购物车所需的响应文档。请注意,您可能需要将 具体取决于预留。

以下示例代码展示了一个完整的预订订单,其中包括可选字段:

const order = {
   createTime: '2019-09-24T18:00:00.877Z',
   lastUpdateTime: '2019-09-24T18:00:00.877Z',
   merchantOrderId: orderId, // A unique ID String for the order
   userVisibleOrderId: orderId,
   transactionMerchant: {
     id: 'http://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: 'http://example.com',
       },
     },
     {
       type: 'CALL',
       title: 'Call us',
       openUrlAction: {
         url: 'tel:+16501112222',
       },
     },
     {
       type: 'EMAIL',
       title: 'Email us',
       openUrlAction: {
         url: 'mailto:person@example.com',
       },
     },
   ],
   termsOfServiceUrl: 'http://www.example.com'
 };

创建顺序和展示选项

const orderOptions = {
      'requestDeliveryAddress': false,
    };

const presentationOptions = {
      'actionDisplayName': 'RESERVE'
    };

在会话参数中保存订单数据

在 fulfillment 中,将订单数据保存到会话参数中。顺序 对象将用于同一会话的各个场景。

conv.session.params.order = {
    '@type': 'type.googleapis.com/google.actions.transactions.v3.TransactionDecisionValueSpec',
    order: order,
    orderOptions: orderOptions,
    presentationOptions: presentationOptions
};

提议订单

创建预订订单后,您必须将其呈现给用户 确认或拒绝为此,您应该过渡到执行事务的场景 决策。

创建交易决策场景

  1. Scenes 标签页中,添加一个名为 TransactionDecision 的新场景。
  2. 槽填充下,点击 + 以添加新槽。
  3. 选择类型下,选择 actions.type.TransactionDecisionValue 作为 广告位类型。
  4. 在槽名称字段中,将槽命名为 TransactionDecision
  5. 选中自定义槽值回写复选框(默认处于启用状态)。
  6. 配置槽下,从下拉菜单中选择使用会话参数
  7. 配置槽下,输入用于配置槽的会话参数的名称 将订单存储到文本字段中(即 $session.params.order)。
  8. 点击保存

为了尝试填充 TransactionDecisionValue 槽,Google 助理启动 一种内置体验,您传递的 Order 会直接渲染到 “购物车预览卡片”用户可以说“安排预订”、拒绝交易 或请求更改预留详情。

此时,用户也可以请求更改订单。在这种情况下, 应确保您的履单可以处理 完成购物车组装体验

处理交易决策结果

TransactionDecisionValue 槽被填充时,系统会针对 交易决策将存储在一个会话参数中。该值包含 以下:

  • ORDER_ACCEPTED
  • ORDER_REJECTED
  • CART_CHANGE_REQUESTED
  • USER_CANNOT_TRANSACT

如需处理事务决策结果,请执行以下操作:

  1. Scenes 标签页中,选择您新创建的 TransactionDecision 场景。
  2. 条件下,点击 + 以添加新条件。
  3. 在文本字段中,输入以下条件语法,以检查 成功条件:

    scene.slots.status == "FINAL" && session.params.TransactionDecision.transactionDecision == "ORDER_ACCEPTED"
    
  4. 将光标悬停在您刚刚添加的条件上,然后点击向上箭头 并将其放置在 if scene.slots.status == "FINAL" 之前。

  5. 启用 Send prompts,并提供简单的提示让用户知道 预订完成时:

    candidates:
      - first_simple:
          variants:
            - speech: >-
                Transaction completed! Your reservation
                $session.params.TransactionDecision.order.merchantOrderId is all
                set!
    
  6. 转换下,选择结束对话以结束对话。

  7. 条件下,点击 + 以添加新条件。

  8. 在文本字段中,输入以下条件语法,以检查 失败的情况:

      scene.slots.status == "FINAL" && session.params.TransactionDecision.transactionDecision == "ORDER_REJECTED"
    
  9. 将光标悬停在您刚刚添加的条件上,然后点击向上箭头 并将其放置在 if scene.slots.status == "FINAL" 之前。

  10. 启用 Send prompts,并提供一条简单提示让用户知道 订单已被拒绝:

    candidates:
      - first_simple:
          variants:
            - speech: Looks like you don't want to set up a reservation. Goodbye.
    
  11. 转换下,选择结束对话以结束对话。

  12. 选择条件 else if scene.slots.status == "FINAL"

  13. 启用 Send prompts,并提供简单的提示让用户知道 他们无法进行交易:

    candidates:
      - first_simple:
          variants:
            - speech: >-
                Transaction failed with status
                $session.params.TransactionDecision.transactionDecision
    
  14. 转换下,选择结束对话以结束对话 如果用户无法进行交易。

完成预订并发送收据

TransactionDecisionValue 槽返回 ORDER_ACCEPTED 的结果时, 您必须立即执行安排 (例如将其保留在您自己的数据库中)。

发送简单的回复,让对话继续下去。用户会收到 “收起的收据卡片”连同您的回复一起显示

要发送初始订单更新,请执行以下操作:

  1. Scenes 标签页中,选择您的 TransactionDecision 场景。
  2. 条件下,选择用于检查成功结果的条件。 ORDER_ACCEPTED:

    scene.slots.status == "FINAL" && session.params.TransactionDecision.transactionDecision == "ORDER_ACCEPTED"
    
  3. 对于此条件,请启用 Call your webhook, 并提供 intent 处理程序名称,例如 update_order

  4. 在网络钩子代码中,添加一个用于发送初始订单更新的 intent 处理程序:

    app.handle('update_order', conv => {
      const currentTime = new Date().toISOString();
      let order = conv.session.params.TransactionDecision.order;
      conv.add(new OrderUpdate({
        'updateMask': {
          'paths': [
            'reservation.status',
            'reservation.user_visible_status_label',
            'reservation.confirmation_code'
          ]
        },
        'order': {
          'merchantOrderId': order.merchantOrderId,
          'lastUpdateTime': currentTime,
          'reservation': {
            'status': 'CONFIRMED',
            'userVisibleStatusLabel': 'Reservation confirmed',
            'confirmationCode': '123ABCDEFGXYZ',
          },
        },
        'reason': 'Reason string'
      }));
    });
    

发送订单更新

预留状态在其整个生命周期内不断变化。向用户发送 通过发送到 Orders API 的 HTTP PATCH 请求更新预留订单,包括 订单状态和详情

设置向 Orders API 发出的异步请求

对 Orders API 的订单更新请求已获得访问权限授权 令牌。如需 PATCH 对 Orders API 的订单更新,请下载 JSON 与您的 Actions 控制台项目关联的服务账号密钥,然后交换 不记名令牌的服务账号密钥,该令牌可以传递到 Authorization 标头。

如需检索您的服务账号密钥,请执行以下步骤:

  1. Google Cloud 控制台中,执行以下操作: 转到菜单 ☰ >API 和服务 >凭据 >创建凭据 >服务账号密钥
  2. 服务账号下,选择新的服务账号
  3. 将服务账号设置为 service-account
  4. 角色设置为项目 >所有者
  5. 将密钥类型设置为 JSON
  6. 选择创建
  7. 系统会将一个专用 JSON 服务账号密钥下载到您的本地机器。

在订单更新代码中,使用服务密钥换取不记名令牌 使用 Google API 客户端库 "https://www.googleapis.com/auth/actions.order.developer" 作用域。您可以 有关 API 客户端库的安装步骤和示例 GitHub 页面

在我们的 Node.js 示例中引用 order-update.js 获取密钥交换示例。

发送订单更新

在用您的服务账号密钥交换 OAuth 不记名令牌之后, 订单更新,作为对 Orders API 的授权 PATCH 请求。

Orders API 网址 PATCH https://actions.googleapis.com/v3/orders/${orderId}

在请求中提供以下标头:

  • "Authorization: Bearer token" 替换为 OAuth 不记名令牌 您的服务账号密钥。
  • "Content-Type: application/json"

PATCH 请求应采用以下格式的 JSON 正文:

{ "orderUpdate": OrderUpdate }

OrderUpdate 对象包含以下顶级字段:

  • updateMask - 要更新的订单的字段。要更新 预订状态 将值设置为 reservation.status, reservation.userVisibleStatusLabel
  • order - 更新的内容。如果您要更新 预留的内容,将该值设置为更新后的 Order 对象。 如果您只想更新预留的状态(例如, "PENDING""FULFILLED"),该对象包含 以下字段:

    • merchantOrderId - 您在 Order 对象中设置的 ID。
    • lastUpdateTime - 此更新的时间戳。
    • purchase - 包含以下内容的对象: <ph type="x-smartling-placeholder">
        </ph>
      • status - 订单的状态,显示为 ReservationStatus。 例如“CONFIRMED”或“CANCELLED”。
      • userVisibleStatusLabel - 面向用户的标签,用于提供 订单状态,例如“您的预订已确认”。
  • userNotification(选填)- userNotification 对象。注意事项 添加此对象并不能保证 用户的设备

以下示例代码显示了一个示例 OrderUpdate,它会更新 FULFILLED

// Import the 'googleapis' module for authorizing the request.
const {google} = require('googleapis');
// Import the 'request-promise' module for sending an HTTP POST request.
const request = require('request-promise');
// Import the OrderUpdate class from the client library.
const {OrderUpdate} = require('@assistant/conversation');

// Import the service account key used to authorize the request.
// Replacing the string path with a path to your service account key.
// i.e. const serviceAccountKey = 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(
   serviceAccountKey.client_email,
   null,
   serviceAccountKey.private_key,
   ['https://www.googleapis.com/auth/actions.order.developer'],
   null,
);

// Authorize the client
let tokens = await jwtClient.authorize();

// Declare the ID of the order to update.
const orderId = '<UNIQUE_MERCHANT_ORDER_ID>';

// Declare order update
const orderUpdate = new OrderUpdate({
   updateMask: {
     paths: [
       'contents.lineItems.reservation.status',
       'contents.lineItems.reservation.userVisibleStatusLabel'
     ]
   },
   order: {
     merchantOrderId: orderId, // Specify the ID of the order to update
     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.
let options = {
 method: 'PATCH',
 uri: `https://actions.googleapis.com/v3/orders/${orderId}`,
 auth: {
   bearer: tokens.access_token,
 },
 body: {
   header: {
     isInSandbox: true,
   },
   orderUpdate,
 },
 json: true,
};

// Send the PATCH request to the Orders API.
try {
 await request(options);
} catch (e) {
 console.log(`Error: ${e}`);
}

设置预留状态

订单更新的 ReservationStatus 必须说明订单的当前状态。在您的更新的order.ReservationStatus中 字段中,请使用以下某个值:

  • PENDING - 预留“已创建”被您的 Action 触发,但需要 进行额外的处理
  • CONFIRMED - 此预订已在时间安排后端中确认。
  • CANCELLED - 用户取消了预订。
  • FULFILLED - 服务完成用户的预订。
  • CHANGE_REQUESTED - 用户请求更改预订,则更改 处理中。
  • REJECTED - 如果无法处理或其他情况 确认预留。

发送与您商家相关的每个状态的订单更新 预留。例如,如果您的预订需要手动处理 在收到预订后确认预订,发送 PENDING 订单更新,直到 系统会完成额外的处理并非每个预留都需要每个状态值。

测试您的项目

测试项目时,你可以在 Actions 控制台中启用沙盒模式 在不向付款方式收取任何费用的情况下测试您的 Action。 如需启用沙盒模式,请按以下步骤操作:

  1. 在 Actions 控制台中,点击导航栏中的 Test
  2. 点击设置
  3. 启用 Development Sandbox 选项。

对于实际交易,您还可以将字段 isInSandbox 设置为 true(位于 您的样本。此操作等同于在 Actions 控制台。如需查看使用 isInSandbox 的代码段,请参阅 发送订单更新部分。

问题排查

如果您在测试期间遇到任何问题,请参阅我们的问题排查步骤