คู่มือนี้จะแนะนำขั้นตอนการพัฒนาโปรเจ็กต์ Actions ที่ใช้ Orders API ในการจอง
ขั้นตอนการทำธุรกรรม
เมื่อโปรเจ็กต์ Actions จัดการการจอง จะใช้ขั้นตอนต่อไปนี้
- ตรวจสอบข้อกำหนดด้านธุรกรรม (ไม่บังคับ) - ใช้ผู้ช่วยข้อกำหนดของธุรกรรมเมื่อเริ่มการสนทนาเพื่อให้แน่ใจว่าผู้ใช้จะทำธุรกรรมได้
- สร้างคำสั่งซื้อ - พาผู้ใช้ไปดู "ชุดประกอบรถเข็น" ที่พวกเขาสร้างรายละเอียดการจอง
- เสนอคำสั่งซื้อ - เมื่อ "รถเข็น" เสร็จสมบูรณ์ ให้เสนอ "คำสั่งซื้อ" การจองแก่ผู้ใช้เพื่อให้ผู้ใช้ยืนยันว่าถูกต้อง หากการจองได้รับการยืนยันแล้ว คุณจะได้รับการตอบกลับพร้อมรายละเอียดการจอง
- สรุปคำสั่งซื้อและส่งใบเสร็จ - เมื่อยืนยันคำสั่งซื้อแล้ว ให้อัปเดตระบบการจองและส่งใบเสร็จให้ผู้ใช้
- ส่งการอัปเดตคำสั่งซื้อ - ให้การอัปเดตสถานะการจองของผู้ใช้ตลอดระยะเวลาอายุการใช้งานของการจองโดยส่งคำขอ Patch ไปยัง Orders API
ข้อจำกัดและหลักเกณฑ์การตรวจสอบ
โปรดทราบว่านโยบายเพิ่มเติมมีผลบังคับใช้กับการดำเนินการที่ใช้ Transaction และ Orders API เราอาจใช้เวลาถึง 6 สัปดาห์ในการตรวจสอบการดำเนินการที่มีธุรกรรม ดังนั้นโปรดคำนึงถึงเวลาดังกล่าวเมื่อวางแผนกำหนดการเผยแพร่ เพื่อช่วยให้กระบวนการตรวจสอบง่ายขึ้น โปรดตรวจสอบว่าคุณปฏิบัติตามนโยบายและหลักเกณฑ์สำหรับธุรกรรมก่อนส่งการดำเนินการเข้ารับการตรวจสอบ
คุณใช้งานการดำเนินการที่ใช้ Orders API ได้ในประเทศต่อไปนี้เท่านั้น
ออสเตรเลีย บราซิล แคนาดา อินโดนีเซีย |
ญี่ปุ่น เม็กซิโก กาตาร์ รัสเซีย |
สิงคโปร์ สวิตเซอร์แลนด์ ไทย ตุรกี สหราชอาณาจักร สหรัฐอเมริกา |
สร้างโปรเจ็กต์
ดูตัวอย่างธุรกรรมของเราใน Node.js และ Java เพื่อดูตัวอย่างการสนทนาเกี่ยวกับธุรกรรมทั้งหมด
ตั้งค่าโครงการ
เมื่อสร้างการดำเนินการ คุณต้องระบุว่าคุณต้องการดำเนินธุรกรรมในคอนโซล Actions นอกจากนี้ หากใช้ไลบรารีของไคลเอ็นต์ Node.JS ให้ตั้งค่า Fulfillment ให้ใช้ Orders API เวอร์ชันล่าสุดด้วย
หากต้องการตั้งค่าโปรเจ็กต์และการดำเนินการตามคำสั่งซื้อ ให้ทำตามขั้นตอนต่อไปนี้
- สร้างโปรเจ็กต์ใหม่หรือนำเข้าโปรเจ็กต์ที่มีอยู่
- ไปที่ทำให้ใช้งานได้ > ข้อมูลไดเรกทอรี
ในส่วนข้อมูลเพิ่มเติม > ธุรกรรม > เลือกช่องที่ระบุว่า "การดำเนินการของคุณใช้ Transaction API ในการทำธุรกรรมสินค้าที่จับต้องได้ไหม"
หากคุณใช้ไลบรารีของไคลเอ็นต์ Node.JS เพื่อสร้าง Fulfillment ของการดำเนินการ ให้เปิดโค้ด Fulfillment และอัปเดตการจัดการแอปเพื่อตั้งค่าแฟล็ก
ordersv3
เป็นtrue
ข้อมูลโค้ดต่อไปนี้แสดงตัวอย่างการประกาศแอป สำหรับคำสั่งซื้อเวอร์ชัน 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. ตรวจสอบข้อกำหนดของธุรกรรม (ไม่บังคับ)
ประสบการณ์ของผู้ใช้
ทันทีที่ผู้ใช้ระบุว่าต้องการตั้งค่าการจอง เราขอแนะนำให้ทริกเกอร์ Intent actions.intent.TRANSACTION_REQUIREMENTS_CHECK
เพื่อให้แน่ใจว่าจะขอจองได้ ตัวอย่างเช่น เมื่อเรียกใช้ การดำเนินการของคุณอาจถามว่า
"คุณต้องการสำรองที่นั่งไหม" หากผู้ใช้ตอบว่า "ใช่" คุณควรขอ Intent นี้ทันที ซึ่งจะช่วยให้มั่นใจว่าผู้ใช้จะดำเนินการต่อได้และเปิดโอกาสให้ผู้ใช้แก้ไขการตั้งค่าที่ขัดขวางการทำธุรกรรมต่อได้
การขอให้ตรวจสอบ Intent ของธุรกรรมจะส่งผลให้เกิดผลลัพธ์อย่างใดอย่างหนึ่งต่อไปนี้
- หากเป็นไปตามข้อกำหนด การดำเนินการตามคำสั่งซื้อจะได้รับ Intent ที่มีเงื่อนไข "สำเร็จ" และคุณจะสร้างคำสั่งซื้อของผู้ใช้ต่อไปได้
หากไม่เป็นไปตามข้อกำหนดอย่างน้อย 1 ข้อ การดำเนินการของคุณจะได้รับ Intent ที่มีเงื่อนไขล้มเหลว ในกรณีนี้ ให้จบการสนทนาหรือ สลับออกจากขั้นตอนการจอง
หากผู้ใช้แก้ไขข้อผิดพลาดได้ ระบบจะแจ้งให้แก้ไขปัญหาในอุปกรณ์โดยอัตโนมัติ ถ้าการสนทนาเกิดขึ้นบนพื้นผิวที่ใช้เสียงอย่างเดียว เช่น ลำโพงอัจฉริยะ เสียงนั้นจะส่งไปที่โทรศัพท์ของผู้ใช้
การดำเนินการตามคำสั่งซื้อ
โปรดส่งคำขอ Fulfillment ของ actions.intent.TRANSACTION_REQUIREMENTS_CHECK
ด้วยออบเจ็กต์ TransactionRequirementsCheckSpec เพื่อให้แน่ใจว่าผู้ใช้เป็นไปตามข้อกำหนดด้านธุรกรรม
ตรวจสอบข้อกำหนด
ตรวจสอบว่าผู้ใช้มีคุณสมบัติตรงตามข้อกำหนดการจองที่มีไลบรารีของไคลเอ็นต์หรือไม่ ดังนี้
conv.ask(new TransactionRequirements());
return getResponseBuilder(request) .add("Placeholder for transaction requirements text") .add(new TransactionRequirements()) .build();
โปรดทราบว่า JSON ด้านล่างอธิบายการตอบสนองของเว็บฮุค
{ "payload": { "google": { "expectUserResponse": true, "systemIntent": { "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK", "data": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec" } } } } }
โปรดทราบว่า JSON ด้านล่างอธิบายการตอบสนองของเว็บฮุค
{ "expectUserResponse": true, "expectedInputs": [ { "possibleIntents": [ { "intent": "actions.intent.TRANSACTION_REQUIREMENTS_CHECK", "inputValueData": { "@type": "type.googleapis.com/google.actions.transactions.v3.TransactionRequirementsCheckSpec" } } ] } ] }
รับผลการตรวจสอบข้อกำหนด
หลังจากที่ Assistant ดำเนินการตามความตั้งใจแล้ว ก็จะส่งคำขอสำหรับการดำเนินการตามเจตนารมณ์ actions.intent.TRANSACTION_REQUIREMENTS_CHECK
ของคุณมาพร้อมกับผลการตรวจสอบ
โปรดประกาศ Intent ของ Dialogflow ที่ทริกเกอร์โดยเหตุการณ์ actions_intent_TRANSACTION_REQUIREMENTS_CHECK
เพื่อจัดการคำขอนี้อย่างเหมาะสม เมื่อทริกเกอร์ ให้จัดการ Intent นี้ใน Fulfillment
const arg = conv.arguments.get('TRANSACTION_REQUIREMENTS_CHECK_RESULT'); if (arg && arg.resultType === 'CAN_TRANSACT') { // Normally take the user through cart building flow conv.ask(`Looks like you're good to go!`); } else { conv.close('Transaction failed.'); }
Argument transactionCheckResult = request .getArgument("TRANSACTION_REQUIREMENTS_CHECK_RESULT"); boolean result = false; if (transactionCheckResult != null) { Map<String, Object> map = transactionCheckResult.getExtension(); if (map != null) { String resultType = (String) map.get("resultType"); result = resultType != null && resultType.equals("CAN_TRANSACT"); } } ResponseBuilder responseBuilder = getResponseBuilder(request); if (result) { responseBuilder.add("Looks like you're good to go! Now say 'confirm transaction'"); } else { responseBuilder.add("Transaction failed"); } return responseBuilder.build();
โปรดทราบว่า JSON ด้านล่างอธิบายคำขอเว็บฮุค
{ "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 ด้านล่างอธิบายคำขอเว็บฮุค
{ "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. สร้างคำสั่งซื้อ
ประสบการณ์ของผู้ใช้
เมื่อคุณมีข้อมูลผู้ใช้ที่ต้องการแล้ว ให้สร้างการใช้งาน "การประกอบรถเข็น" ที่จะช่วยแนะนำการสร้างการจอง การทำงานทุกครั้งจะมีขั้นตอนการประกอบรถเข็นที่แตกต่างกันเล็กน้อยตามที่เหมาะสมสำหรับการบริการ
ในการประกอบรถเข็นแบบพื้นฐาน ผู้ใช้จะเลือกตัวเลือกจากรายการเพื่อเพิ่มลงในการจอง แม้ว่าคุณจะออกแบบการสนทนาเพื่อลดความซับซ้อนของประสบการณ์ผู้ใช้ได้ก็ตาม เช่น สร้างประสบการณ์การประกอบรถเข็นที่ช่วยให้ผู้ใช้กำหนดเวลาการจองรายเดือนได้ด้วยคำถามใช่หรือไม่ง่ายๆ คุณยังแสดงภาพสไลด์หรือการ์ดลิสต์ของการจอง "แนะนำ" แก่ผู้ใช้ได้ด้วย
เราขอแนะนำให้ใช้การตอบกลับที่สมบูรณ์เพื่อนำเสนอตัวเลือกของผู้ใช้ให้เห็นเป็นภาพ แต่ก็ออกแบบการสนทนาเพื่อให้ผู้ใช้สร้างรถเข็นได้โดยใช้เพียงเสียงเท่านั้น ดูแนวทางปฏิบัติแนะนำและตัวอย่างประสบการณ์ประกอบรถเข็นได้ที่หลักเกณฑ์การออกแบบธุรกรรม
การดำเนินการตามคำสั่งซื้อ
ระหว่างการสนทนา ให้รวบรวมรายละเอียดการจองที่ผู้ใช้ต้องการซื้อ แล้วสร้างออบเจ็กต์ Order
Order
ของคุณต้องมีข้อมูลต่อไปนี้เป็นอย่างน้อย
buyerInfo
- ข้อมูลเกี่ยวกับผู้ใช้ที่กำหนดเวลาจองtransactionMerchant
- ข้อมูลเกี่ยวกับผู้ขายที่อำนวยความสะดวกในการจองcontents
- รายละเอียดจริงของการจองที่แสดงเป็นlineItems
โปรดดูเอกสารประกอบการตอบกลับของ Order
เพื่อสร้างรถเข็นของคุณ โปรดทราบว่าคุณอาจต้องใส่ช่องที่ต่างกัน
ทั้งนี้ขึ้นอยู่กับการจอง
โค้ดตัวอย่างด้านล่างแสดงคำสั่งซื้อการจองที่สมบูรณ์ รวมถึงช่องที่ไม่บังคับ
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 ด้านล่างอธิบายการตอบสนองของเว็บฮุค
{ "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. เสนอคำสั่งซื้อ
แสดงคำสั่งการจองให้ผู้ใช้ยืนยันหรือปฏิเสธ ขอ Intent actions.intent.TRANSACTION_DECISION
และระบุ Order
ที่คุณสร้างไว้
ประสบการณ์ของผู้ใช้
เมื่อคุณขอ Intent actions.intent.TRANSACTION_DECISION
Assistant จะเริ่มต้นประสบการณ์การใช้งานในตัวโดยการแสดงผล Order
ลงใน "การ์ดตัวอย่างรถเข็น" โดยตรง ผู้ใช้พูดว่า "กำหนดเวลาการจอง"
ปฏิเสธธุรกรรม หรือขอเปลี่ยนรายละเอียดการจองได้
ผู้ใช้ยังอาจขอเปลี่ยนแปลงคำสั่งซื้อในขั้นตอนนี้ ในกรณีนี้ คุณควรตรวจสอบว่าการดำเนินการตามคำสั่งซื้อสามารถจัดการคำขอเปลี่ยนแปลงคำสั่งซื้อได้หลังจากที่ดำเนินการประกอบรถเข็นเสร็จแล้ว
การดำเนินการตามคำสั่งซื้อ
เมื่อคุณขอ Intent actions.intent.TRANSACTION_DECISION
ให้สร้าง TransactionDecision
ที่มี Order
และ 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 ด้านล่างอธิบายการตอบสนองของเว็บฮุค
{ "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 ด้านล่างอธิบายการตอบสนองของเว็บฮุค
{ "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" } } } ] } ] }
จัดการการตัดสินใจของผู้ใช้
หลังจากที่ผู้ใช้ตอบกลับคำสั่งซื้อที่เสนอ การดำเนินการของคุณจะได้รับ Intent actions_intent_TRANSACTION_DECISION
ด้วยอาร์กิวเมนต์ที่มี TransactionDecisionValue
โดยค่านี้จะมีข้อมูลต่อไปนี้
transactionDecision
- การตัดสินใจของผู้ใช้เกี่ยวกับคำสั่งซื้อที่เสนอ ค่าที่เป็นไปได้คือORDER_ACCEPTED
,ORDER_REJECTED
,CART_CHANGE_REQUESTED
และUSER_CANNOT_TRANSACT
หากต้องการจัดการคำขอนี้ ให้ประกาศ Intent ของ Dialogflow ที่ทริกเกอร์โดยเหตุการณ์ actions_intent_TRANSACTION_DECISION
ดำเนินการตามความตั้งใจนี้
เมื่อดำเนินการตามคำสั่งซื้อ
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 ด้านล่างอธิบายคำขอเว็บฮุค
{ "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 ด้านล่างอธิบายคำขอเว็บฮุค
{ "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. ดำเนินการจองให้เสร็จสิ้นและส่งใบเสร็จ
เมื่อ Intent actions.intent.TRANSACTION_DECISION
กลับมาพร้อม transactionDecision
ของ ORDER_ACCEPTED
ให้ดำเนินการตามขั้นตอนที่จำเป็นเพื่อกำหนดเวลาการจอง (เช่น ยืนยันในฐานข้อมูลของคุณเอง)
ส่งคำตอบง่ายๆ เพื่อให้การสนทนาดำเนินต่อไป ผู้ใช้จะได้รับ "การ์ดใบเสร็จแบบยุบ" พร้อมกับคำตอบของคุณ
การดำเนินการตามคำสั่งซื้อ
// 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 ด้านล่างอธิบายการตอบสนองของเว็บฮุค
{ "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 ด้านล่างอธิบายการตอบสนองของเว็บฮุค
{ "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 PATCH ไปยัง Orders API สถานะและรายละเอียดของคำสั่งซื้อ
ตั้งค่าคำขอแบบไม่พร้อมกันไปยัง Orders API
คำขออัปเดตคำสั่งซื้อไปยัง Orders API จะได้รับอนุญาตจากโทเค็นเพื่อการเข้าถึง หากต้องการแพตช์การอัปเดตคำสั่งซื้อไปยัง Orders API ให้ดาวน์โหลดคีย์บัญชีบริการ JSON ที่เชื่อมโยงกับโปรเจ็กต์คอนโซล Actions ของคุณ จากนั้นแลกเปลี่ยนคีย์บัญชีบริการกับโทเค็นสำหรับผู้ถือซึ่งส่งผ่านไปยังส่วนหัว Authorization
ของคำขอ HTTP ได้
หากต้องการเรียกข้อมูลคีย์บัญชีบริการ ให้ทำตามขั้นตอนต่อไปนี้
- ในคอนโซล Google Cloud ให้ไปที่เมนู ☰ > API และบริการ > ข้อมูลเข้าสู่ระบบ > สร้างข้อมูลเข้าสู่ระบบ > คีย์บัญชีบริการ
- ภายใต้บัญชีบริการ เลือกบัญชีบริการใหม่
- ตั้งค่าบัญชีบริการเป็น
service-account
- ตั้งค่าบทบาทเป็นโปรเจ็กต์ > เจ้าของ
- ตั้งค่าประเภทคีย์เป็น JSON
- เลือกสร้าง
- ระบบจะดาวน์โหลดคีย์บัญชีบริการ JSON ส่วนตัวไปยังเครื่องภายใน
แลกเปลี่ยนคีย์บริการกับโทเค็นสำหรับผู้ถือในโค้ดอัปเดตของคำสั่งซื้อ โดยใช้ไลบรารีของไคลเอ็นต์ Google APIs และขอบเขต "https://www.googleapis.com/auth/actions.order.developer" คุณดูขั้นตอนการติดตั้งและตัวอย่างได้ที่หน้า GitHub ของไคลเอ็นต์ API
การอ้างอิง order-update.js
ในตัวอย่าง Node.js และ Java สำหรับตัวอย่างการแลกเปลี่ยนคีย์
ส่งข้อมูลอัปเดตการสั่งซื้อ
เมื่อแลกเปลี่ยนคีย์บัญชีบริการกับโทเค็นสำหรับผู้ถือ OAuth แล้ว ให้ส่งการอัปเดตคำสั่งซื้อเป็นคำขอ Patch ที่ได้รับอนุญาตไปยัง Orders API
URL ของ 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
lastUpdateTime
- การประทับเวลาของการอัปเดตนี้purchase
- ออบเจ็กต์ที่มีสิ่งต่อไปนี้status
- สถานะของคำสั่งซื้อเป็นReservationStatus
เช่น "CONFIRMED
" หรือ "CANCELLED
"userVisibleStatusLabel
- ป้ายกำกับที่ผู้ใช้เห็นซึ่งมีรายละเอียดเกี่ยวกับสถานะการสั่งซื้อ เช่น "ยืนยันการจองของคุณแล้ว"
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
- การดำเนินการของคุณได้ "สร้าง" การจองไว้ แต่ต้องมีการประมวลผลเพิ่มเติมในส่วนหลังของคุณCONFIRMED
- การจองได้รับการยืนยันในส่วนหลังของกำหนดการCANCELLED
- ผู้ใช้ยกเลิกการจองFULFILLED
- บริการดำเนินการตามการจองของผู้ใช้แล้วCHANGE_REQUESTED
- ผู้ใช้ขอให้เปลี่ยนแปลงการจอง ซึ่งระบบกำลังประมวลผลการเปลี่ยนแปลงREJECTED
- หากคุณไม่สามารถดำเนินการหรือยืนยันการจอง
ส่งการอัปเดตคำสั่งซื้อสำหรับแต่ละสถานะที่เกี่ยวข้องกับการจองของคุณ เช่น หากการจองของคุณกำหนดให้ดำเนินการด้วยตนเองเพื่อยืนยันการจองหลังจากที่ขอไปแล้ว ให้ส่งการอัปเดตคำสั่งซื้อ PENDING
ไปจนกว่าจะประมวลผลเพิ่มเติมเสร็จ การจองบางรายการไม่จำเป็นต้องมีค่าสถานะทุกรายการ
การแก้ปัญหา
หากพบปัญหาระหว่างการทดสอบ โปรดอ่านขั้นตอนการแก้ปัญหาสำหรับธุรกรรม