本指南将逐步介绍如何开发 Actions 项目 使用 Orders API 进行预订。
交易流程
当 Actions 项目处理预留时,它会 采用以下流程:
- 验证交易要求(可选)- 使用交易要求帮助程序 ,以确保用户在对话开始时能够 执行交易
- 建立订单 - 引导用户完成 "购物车组装"在这里构建预留的详细信息
- 提议订单 -“购物车”页面则提议预订“订单”更改为 以便确认其正确无误。如果预订得到确认 收到包含预留详情的响应
- 敲定订单并发送收据 - 订单已确认后,更新 并发送收据 。
- 发送订单更新 - 在预订的整个有效期内, 将 PATCH 请求发送到 Orders API。
限制和查看指南
请注意,使用交易和 Orders API 的操作还需要遵守一些额外的政策。审核涉及交易的操作数最多可能需要 6 周时间,因此在规划发布时间表时,请将这部分时间考虑在内。为了简化审核流程,在提交你的 Action 以供审核之前,请确保你遵守交易政策和准则。
您只能在以下国家/地区部署使用 Orders API 的 Action:
澳大利亚 巴西 加拿大 印度尼西亚 |
日本 墨西哥 卡塔尔 俄罗斯 |
新加坡 瑞士 泰国 土耳其 英国 美国 |
构建您的项目
如需查看事务型对话的详细示例,请参阅 Node.js 和 Java 中的事务示例。
项目设置
创建 Action 时,你必须指定要执行交易 在 Actions 控制台中操作。此外,如果您是 使用 Node.JS 客户端库,请将执行方式设置为使用最新的 Orders API 版本。
如需设置项目和执行方式,请执行以下操作:
- 创建新项目或导入现有项目。
- 导航到部署 >目录信息。
在其他信息 >交易 >选中 使用 Transaction API 执行实体商品的交易?”。
如果你使用 Node.JS 客户端库构建 Action 的执行方式, 请打开您的执行代码,并更新应用软件包,以设置 将
ordersv3
标志设置为true
。以下代码段展示了一个示例应用 为订单版本 3 声明的 ID。
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. 验证交易要求(可选)
用户体验
我们建议,只要用户表示想要设置预订,
actions.intent.TRANSACTION_REQUIREMENTS_CHECK
intent,以确保
来请求预留例如,被调用后,您的 Action 可能会询问:
“您想要预订座位吗?”如果用户说
“yes”,则应立即请求此 intent。这样可以确保
以便可以继续操作,并给他们机会修正所有设置
阻止客户继续处理交易
请求交易 要求检查 intent 会产生以下结果之一:
- 如果满足这些要求,您的 fulfillment 会收到包含 成功条件,您可以继续构建用户订单。
如果其中一项或多项不符合要求,您的履单将获得 具有失败条件的 intent。在这种情况下,您可以结束对话或 并放弃预订流程
如果用户可以修正错误,系统会自动提示他们解决这些问题 。对话是在纯语音界面中进行的 它会像智能音箱一样传递给用户的手机。
履行情况
为了确保用户符合
交易要求、请求履行
actions.intent.TRANSACTION_REQUIREMENTS_CHECK
intent,其中包含
TransactionRequirementsCheckSpec 对象。
查看要求
检查用户是否满足客户端库的预留要求:
<ph type="x-smartling-placeholder">conv.ask(new TransactionRequirements());
return getResponseBuilder(request) .add("Placeholder for transaction requirements text") .add(new TransactionRequirements()) .build();
请注意,下面的 JSON 描述的是 webhook 响应。
{ "payload": { "google": { "expectUserResponse": true, "systemIntent": { "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK", "data": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec" } } } } }
请注意,下面的 JSON 描述的是 webhook 响应。
{ "expectUserResponse": true, "expectedInputs": [ { "possibleIntents": [ { "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK", "inputValueData": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec" } } ] } ] }
接收要求检查的结果
Google 助理执行 intent 后,会向你的执行方式发送一个请求
并将结果与 actions.intent.TRANSACTION_REQUIREMENTS_CHECK
intent 搭配使用
检查。
要正确处理此请求,请声明一个由
actions_intent_TRANSACTION_REQUIREMENTS_CHECK
事件。触发后,
在 fulfillment 中处理此 intent:
const arg = conv.arguments.get('TRANSACTION_REQUIREMENTS_CHECK_RESULT'); if (arg && arg.resultType === 'CAN_TRANSACT') { // Normally take the user through cart building flow conv.ask(`Looks like you're good to go!`); } else { conv.close('Transaction failed.'); }
Argument transactionCheckResult = request .getArgument("TRANSACTION_REQUIREMENTS_CHECK_RESULT"); boolean result = false; if (transactionCheckResult != null) { Map<String, Object> map = transactionCheckResult.getExtension(); if (map != null) { String resultType = (String) map.get("resultType"); result = resultType != null && resultType.equals("CAN_TRANSACT"); } } ResponseBuilder responseBuilder = getResponseBuilder(request); if (result) { responseBuilder.add("Looks like you're good to go! Now say 'confirm transaction'"); } else { responseBuilder.add("Transaction failed"); } return responseBuilder.build();
请注意,下面的 JSON 描述的是 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 描述的是 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. 创建订单
用户体验
获得所需的用户信息后,即可构建“购物车” 组装"引导用户进行预订。每个 Action 的购物车装配流程将视情况略有不同 服务。
在基本的购物车组装体验中,用户从列表中选择要添加的选项 但您可以设计对话来简化 用户体验。例如,打造购物车组装体验, 通过简单的“是”或“非”问题来安排每月预订。 您还可以向用户显示“推荐”的轮播界面或列表卡片 预留。
我们建议使用富媒体 响应来呈现用户的选项 而且要设计对话,以便用户 只使用他们的语音指令。一些最佳实践和 购物车组装体验,请参阅事务设计准则。
履行情况
在整个对话过程中,收集用户想要的预订详细信息
以便购买并构造 Order
对象。
您的 Order
必须至少包含以下内容:
buyerInfo
- 用户安排预订的相关信息。transactionMerchant
- 提供宣传服务的商家的相关信息 预留。contents
- 列为lineItems
的预留的实际详细信息。
请参阅 Order
响应文档
来构建购物车。请注意,您可能需要将不同的字段
具体取决于预留
以下示例代码展示了一个完整的预订订单,其中包括可选字段:
<ph type="x-smartling-placeholder">app.intent('build_reservation_df', (conv) => { const now = new Date().toISOString(); const order = { createTime: now, lastUpdateTime: now, merchantOrderId: 'UNIQUE_ORDER_ID', userVisibleOrderId: 'USER_VISIBLE_ORDER_ID', transactionMerchant: { id: 'https://www.example.com', name: 'Example Merchant', }, contents: { lineItems: [ { id: 'LINE_ITEM_ID', name: 'Dinner reservation', description: 'A world of flavors all in one destination.', reservation: { status: 'PENDING', userVisibleStatusLabel: 'Reservation is pending.', type: 'RESTAURANT', reservationTime: { timeIso8601: '2020-01-16T01:30:15.01Z', }, userAcceptableTimeRange: { timeIso8601: '2020-01-15/2020-01-17', }, partySize: 6, staffFacilitators: [ { name: 'John Smith', }, ], location: { zipCode: '94086', city: 'Sunnyvale', postalAddress: { regionCode: 'US', postalCode: '94086', administrativeArea: 'CA', locality: 'Sunnyvale', addressLines: [ '222, Some other Street', ], }, }, }, }, ], }, buyerInfo: { email: 'janedoe@gmail.com', firstName: 'Jane', lastName: 'Doe', displayName: 'Jane Doe', }, followUpActions: [ { type: 'VIEW_DETAILS', title: 'View details', openUrlAction: { url: 'https://example.com', }, }, { type: 'CALL', title: 'Call us', openUrlAction: { url: 'tel:+16501112222', }, }, { type: 'EMAIL', title: 'Email us', openUrlAction: { url: 'mailto:person@example.com', }, }, ], termsOfServiceUrl: 'https://www.example.com', };
private static OrderV3 createOrder() { // Transaction Merchant MerchantV3 transactionMerchant = new MerchantV3() .setId("http://www.example.com") .setName("Example Merchant"); // Line Item // Reservation Item Extension ReservationItemExtension reservationItemExtension = new ReservationItemExtension() .setStatus("PENDING") .setUserVisibleStatusLabel("Reservation pending.") .setType("RESTAURANT") .setReservationTime(new TimeV3() .setTimeIso8601("2020-01-16T01:30:15.01Z")) .setUserAcceptableTimeRange(new TimeV3() .setTimeIso8601("2020-01-15/2020-01-17")) .setPartySize(6) .setStaffFacilitators(Collections.singletonList(new StaffFacilitator() .setName("John Smith"))) .setLocation(new Location() .setZipCode("94086") .setCity("Sunnyvale") .setPostalAddress(new PostalAddress() .setRegionCode("US") .setPostalCode("94086") .setAdministrativeArea("CA") .setLocality("Sunnyvale") .setAddressLines( Collections.singletonList("222, Some other Street")))); LineItemV3 lineItem = new LineItemV3() .setId("LINE_ITEM_ID") .setName("Dinner reservation") .setDescription("A world of flavors all in one destination.") .setReservation(reservationItemExtension); // Order Contents OrderContents contents = new OrderContents() .setLineItems(Collections.singletonList(lineItem)); // User Info UserInfo buyerInfo = new UserInfo() .setEmail("janedoe@gmail.com") .setFirstName("Jane") .setLastName("Doe") .setDisplayName("Jane Doe"); // Follow up actions Action viewDetails = new Action() .setType("VIEW_DETAILS") .setTitle("View details") .setOpenUrlAction(new OpenUrlAction() .setUrl("https://example.com")); Action call = new Action() .setType("CALL") .setTitle("Call us") .setOpenUrlAction(new OpenUrlAction() .setUrl("tel:+16501112222")); Action email = new Action() .setType("EMAIL") .setTitle("Email us") .setOpenUrlAction(new OpenUrlAction() .setUrl("mailto:person@example.com")); // Terms of service and order note String termsOfServiceUrl = "https://example.com"; String now = Instant.now().toString(); OrderV3 order = new OrderV3() .setCreateTime(now) .setLastUpdateTime(now) .setMerchantOrderId("UNIQUE_ORDER_ID") .setUserVisibleOrderId("UNIQUE_USER_VISIBLE_ORDER_ID") .setTransactionMerchant(transactionMerchant) .setContents(contents) .setBuyerInfo(buyerInfo) .setFollowUpActions(Arrays.asList( viewDetails, call, email )) .setTermsOfServiceUrl(termsOfServiceUrl); return order; }
请注意,下面的 JSON 描述的是 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. 提议订单
将您的预订订单呈现给用户,以便他们确认或
拒绝。请求 actions.intent.TRANSACTION_DECISION
intent 并提供您构建的 Order
。
用户体验
当您请求 actions.intent.TRANSACTION_DECISION
intent 时,Google 助理
会启动 Order
的内置体验
直接渲染到“购物车预览卡片”上用户可以说“安排预订”
拒绝交易或请求更改预留详情。
此时,用户也可以请求更改订单。在此示例中 您应确保履单之后能处理订单更改请求 完成购物车组装体验
履行情况
当您请求
actions.intent.TRANSACTION_DECISION
intent,创建一个
包含 Order
的 TransactionDecision
和orderOptions
以下代码展示了订单的 TransactionsDecision
示例:
conv.ask(new TransactionDecision({ orderOptions: { requestDeliveryAddress: 'false', }, presentationOptions: { actionDisplayName: 'RESERVE', }, order: order, }));
// Create order options OrderOptionsV3 orderOptions = new OrderOptionsV3() .setRequestDeliveryAddress(false) .setUserInfoOptions(new UserInfoOptions() .setUserInfoProperties(Collections.singletonList("EMAIL"))); // Create presentation options PresentationOptionsV3 presentationOptions = new PresentationOptionsV3() .setActionDisplayName("RESERVE"); // Ask for transaction decision return getResponseBuilder(request) .add("Placeholder for transaction decision text") .add(new TransactionDecision() .setOrder(order) .setOrderOptions(orderOptions) .setPresentationOptions(presentationOptions) ) .build();
请注意,下面的 JSON 描述的是 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 描述的是 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" } } } ] } ] }
处理用户的决定
用户响应建议的订单后,您的履单将收到
actions_intent_TRANSACTION_DECISION
intent,其参数包含
TransactionDecisionValue
。此值将包含以下内容:
transactionDecision
- 用户对提议的决定 订单。可能的值包括ORDER_ACCEPTED
、ORDER_REJECTED
、CART_CHANGE_REQUESTED
和USER_CANNOT_TRANSACT
。
要处理此请求,请声明一个由
actions_intent_TRANSACTION_DECISION
事件。在以下位置处理此 intent:
您的执行方式:
const arg = conv.arguments.get('TRANSACTION_DECISION_VALUE'); if (arg && arg.transactionDecision === 'ORDER_ACCEPTED') { console.log('order accepted'); const order = arg.order; }
Argument transactionDecisionValue = request .getArgument("TRANSACTION_DECISION_VALUE"); Map<String, Object> extension = null; if (transactionDecisionValue != null) { extension = transactionDecisionValue.getExtension(); } String transactionDecision = null; if (extension != null) { transactionDecision = (String) extension.get("transactionDecision"); } if ((transactionDecision != null && transactionDecision.equals("ORDER_ACCEPTED"))) { OrderV3 order = ((OrderV3) extension.get("order")); }
请注意,下面的 JSON 描述的是 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 描述的是 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. 完成预订并发送收据
当 actions.intent.TRANSACTION_DECISION
intent 返回
transactionDecision
(共 ORDER_ACCEPTED
行),执行任意操作
则需要处理,才能安排预留
您自己的数据库)。
发送简单回复 让对话继续下去。用户收到“收起收据卡片” 连同您的回复一起显示
履行情况
<ph type="x-smartling-placeholder">// Set lastUpdateTime and update status of reservation order.lastUpdateTime = new Date().toISOString(); order.reservation.status = 'CONFIRMED'; order.reservation.userVisibleStatusLabel = 'Reservation confirmed'; order.reservation.confirmationCode = '123ABCDEFGXYZ'; // Send synchronous order update conv.ask(`Transaction completed! You're all set!`); conv.ask(new OrderUpdate({ type: 'SNAPSHOT', reason: 'Reason string', order: order, }));
ResponseBuilder responseBuilder = getResponseBuilder(request); order.setLastUpdateTime(Instant.now().toString()); // Set reservation status to confirmed and provide confirmation code LineItemV3 lineItem = order.getContents().getLineItems().get(0); ReservationItemExtension reservationItemExtension = lineItem.getReservation(); reservationItemExtension.setStatus("CONFIRMED"); reservationItemExtension.setUserVisibleStatusLabel("Reservation confirmed."); reservationItemExtension.setConfirmationCode("123ABCDEFGXYZ"); lineItem.setReservation(reservationItemExtension); order.getContents().getLineItems().set(0, lineItem); // Order update OrderUpdateV3 orderUpdate = new OrderUpdateV3() .setType("SNAPSHOT") .setReason("Reason string") .setOrder(order); responseBuilder .add("Transaction completed! You're all set! Would you like to do anything else?") .add(new StructuredResponse().setOrderUpdateV3(orderUpdate)); return responseBuilder.build();
请注意,下面的 JSON 描述的是 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 描述的是 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. 发送订单更新
预留状态在 整个有效期。使用 HTTP 发送用户预订订单更新 向 Orders API 发出的 PATCH 请求,其中包含订单状态和详细信息。
设置向 Orders API 发出的异步请求
对 Orders API 的订单更新请求已获得访问权限授权
令牌。如需 PATCH 对 Orders API 的订单更新,请下载 JSON
与您的 Actions 控制台项目关联的服务账号密钥,然后交换
不记名令牌的服务账号密钥,该令牌可以传递到
Authorization
标头。
如需检索您的服务账号密钥,请执行以下步骤:
- 在 Google Cloud 控制台中,执行以下操作: 转到菜单 ☰ >API 和服务 >凭据 >创建凭据 >服务账号密钥。
- 在服务账号下,选择新的服务账号。
- 将服务账号设置为
service-account
。 - 将角色设置为项目 >所有者。
- 将密钥类型设置为 JSON。
- 选择创建。
- 系统会将一个专用 JSON 服务账号密钥下载到您的本地机器。
在订单更新代码中,使用服务密钥换取不记名令牌 使用 Google API 客户端库和 "https://www.googleapis.com/auth/actions.order.developer" 作用域。您可在此处找到安装步骤和 API 客户端库 GitHub 页面上的示例。
在我们的 Node.js 和 Java 示例中引用 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' module for sending an HTTP POST request. const request = require('request'); // Import the OrderUpdate class from the Actions on Google client library. const {OrderUpdate} = require('actions-on-google'); // Import the service account key used to authorize the request. Replace the string path with a path to your service account key. const key = require('./service-account.json'); // Create a new JWT client for the Actions API using credentials from the service account key. let jwtClient = new google.auth.JWT( key.client_email, null, key.private_key, ['https://www.googleapis.com/auth/actions.order.developer'], null ); // Authorize the client asynchronously, passing in a callback to run upon authorization. jwtClient.authorize((err, tokens) => { if (err) { console.log(err); return; } // Declare the ID of the order to update. const orderId = '<UNIQUE_MERCHANT_ORDER_ID>'; const orderUpdateJson = new OrderUpdate({ updateMask: [ 'lastUpdateTime', 'contents.lineItems.reservation.status', 'contents.lineItems.reservation.userVisibleStatusLabel', ].join(','), order: { merchantOrderId: orderId, lastUpdateTime: new Date().toISOString(), contents: { lineItems: [ { reservation: { status: 'FULFILLED', userVisibleStatusLabel: 'Reservation fulfilled', }, } ] } }, reason: 'Reservation status was updated to fulfilled.', }); // Set up the PATCH request header and body, including the authorized token // and order update. const bearer = 'Bearer ' + tokens.access_token; const options = { method: 'PATCH', url: `https://actions.googleapis.com/v3/orders/${orderId}`, headers: { 'Authorization': bearer, }, body: { header: { 'isInSandbox': true, }, orderUpdate: orderUpdateJson, }, json: true, }; // Send the PATCH request to the Orders API. request.patch(options, (err, httpResponse, body) => { if (err) { console.log('There was an error...'); console.log(err); return; } }); });
// Create order update FieldMask fieldMask = FieldMask.newBuilder().addAllPaths(Arrays.asList( "lastUpdateTime", "contents.lineItems.reservation.status", "contents.lineItems.reservation.userVisibleStatusLabel")) .build(); OrderUpdateV3 orderUpdate = new OrderUpdateV3() .setOrder(new OrderV3() .setMerchantOrderId(orderId) .setLastUpdateTime(Instant.now().toString()) .setContents(new OrderContents() .setLineItems(Collections.singletonList(new LineItemV3() .setReservation(new ReservationItemExtension() .setStatus("FULFILLED") .setUserVisibleStatusLabel("Reservation fulfilled.")))))) .setUpdateMask(FieldMaskUtil.toString(fieldMask)) .setReason("Reservation status was updated to fulfilled."); // Setup JSON body containing order update JsonParser parser = new JsonParser(); JsonObject orderUpdateJson = parser.parse(new Gson().toJson(orderUpdate)).getAsJsonObject(); JsonObject body = new JsonObject(); body.add("orderUpdate", orderUpdateJson); JsonObject header = new JsonObject(); header.addProperty("isInSandbox", true); body.add("header", header); StringEntity entity = new StringEntity(body.toString()); entity.setContentType(ContentType.APPLICATION_JSON.getMimeType()); request.setEntity(entity); // Make request HttpClient httpClient = HttpClientBuilder.create().build(); HttpResponse response = httpClient.execute(request);
设置预留状态
订单更新的 ReservationStatus
必须说明订单的当前状态。在您的更新的order.ReservationStatus
中
字段中,请使用以下某个值:
PENDING
- 预留“已创建”被您的 Action 触发,但需要 进行额外的处理CONFIRMED
- 此预订已在时间安排后端中确认。CANCELLED
- 用户取消了预订。FULFILLED
- 服务完成用户的预订。CHANGE_REQUESTED
- 用户请求更改预订,则更改 处理中。REJECTED
- 如果无法处理或其他情况 确认预留。
发送与您商家相关的每个状态的订单更新
预留。例如,如果您的预订需要手动处理
在收到预订后确认预订,发送 PENDING
订单更新,直到
系统会完成额外的处理并非每个预留都需要每个状态值。
问题排查
如果您在测试期间遇到任何问题,请参阅我们的问题排查步骤 。