Liên kết nguồn cấp dữ liệu thực đơn và các mặt hàng trong giỏ hàng
Khi khách hàng thêm các mặt hàng trong nguồn cấp dữ liệu Thực đơn vào giỏ hàng và thanh toán, Google sẽ gửi các mặt hàng đó đến điểm cuối của phương thức thực hiện đơn hàng để xác minh giá và tình trạng còn hàng. Sau khi xác thực giá và tình trạng còn hàng, khách hàng có thể đặt hàng. Phần này minh hoạ cách liên kết các mục trong nguồn cấp dữ liệu thực đơn với các mặt hàng trong giỏ hàng.
Các mẫu trong phần này là các phiên bản rút gọn của nguồn cấp dữ liệu Trình đơn và giản đồ Giỏ hàng. Chỉ các trường có liên quan để minh hoạ mối liên kết giữa nguồn cấp dữ liệu Thực đơn và đối tượng Giỏ hàng mới được hiển thị. Để xem giản đồ đầy đủ, hãy xem Menu
và Cart
.
Các mặt hàng trong nguồn cấp dữ liệu Menu
được thêm vào giỏ hàng sẽ được gửi trong đối tượng Cart
để dùng cho cả quy trình thanh toán và gửi đơn đặt hàng.
- Một
MenuItem
đơn giản được biểu thị dưới dạngLineItem
trong mảnglineItems
vớiofferId
làoffer.id
của mục trong trình đơn đã chọn trong nguồn cấp dữ liệu Trình đơn. MenuItem
cóMenuItemOption
bắt buộc sẽ được biểu thị dưới dạngLineItem
trong mảnglineItems
vớiofferId
làoffer.id
của lựa chọn mục trong trình đơn đã chọn từ nguồn cấp dữ liệu Trình đơn.AddOnMenuItem
củaLineItem
được biểu thị dưới dạngFoodItemOption
trong mảngoptions
củaFoodItemExtension
. Mỗi tuỳ chọn có mộtofferId
tương ứng vớioffer.id
của mục trong trình đơn bổ sung đã chọn trong nguồn cấp dữ liệu Thực đơn. Lưu ý rằng AddOnMenuItem cũng có thể có(các) AddOnMenuItem lồng nhau được biểu thị dưới dạngsubOptions
bên trong mỗi tuỳ chọn.
Các ví dụ sau đây liên kết các mục trong trình đơn giữa nguồn cấp dữ liệu trình đơn và giỏ hàng thực hiện.
JSON
Ví dụ này chứa một danh sách các mục đơn giản trong trình đơn.
Các món trong thực đơn trong nguồn cấp dữ liệu thực đơn:
{ "@type": "Menu", "@id": "menu_id", "hasMenuItem": [ { "@type": "MenuItem", "@id": "menuitem_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_offer_id_1", "price": "p_1", "priceCurrency": "USD" } ] }, { "@type": "MenuItem", "@id": "menuitem_id_2", "offers": [ { "@type": "Offer", "@id": "menuitem_offer_id_2", "price": "p_2", "priceCurrency": "USD" } ] } ] }
Các mục trong trình đơn được liên kết với giỏ hàng thực hiện đơn hàng:
{ "@type": "Cart", "lineItems": [ { "offerId": "menuitem_offer_id_1", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_1*p_1)", "nanos": "cent(q_1*p_1)" } }, "quantity": "q_1" }, { "offerId": "menuitem_offer_id_2", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_2*p_2)", "nanos": "cent(q_2*p_2)" } }, "quantity": "q_2" } ] }
JSON
Ví dụ này chứa một mục trong trình đơn có một hoặc nhiều AddOnMenuItems.
Các món trong thực đơn trong nguồn cấp dữ liệu thực đơn:
{ "@type": "Menu", "@id": "menu_id", "hasMenuItem": [ { "@type": "MenuItem", "@id": "menuitem_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_offer_id_1", "price": "p_1", "priceCurrency": "USD" } ], "menuAddOn": [ { "@type": "MenuAddOnSection", "@id": "menuaddon_section_id_1", "hasMenuItem": [ { "@type": "AddOnMenuItem", "@id": "menuitem_addon_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_addon_offer_id_1", "price": "addon_p_1", "priceCurrency": "USD" } ] }, { "@type": "AddOnMenuItem", "@id": "menuitem_addon_id_2", "offers": [ { "@type": "Offer", "@id": "menuitem_addon_offer_id_2", "price": "addon_p_2", "priceCurrency": "USD" } ] } ] } ] }, { "@type": "MenuItem", "@id": "menuitem_id_2", "offers": [ { "@type": "Offer", "@id": "menuitem_offer_id_2", "price": "p_2", "priceCurrency": "USD" } ] } ] }
Các mục trong trình đơn được liên kết với giỏ hàng thực hiện đơn hàng:
{ "@type": "Cart", "lineItems": [ { "offerId": "menuitem_offer_id_1", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_1*(p_1 + addon_q_1*addon_p_1 + addon_q_2*addon_p_2))", "nanos": "cent(q_1*(p_1 + addon_q_1*addon_p_1 + addon_q_2*addon_p_2))" } }, "quantity": "q_1", "extension": { "@type": "FoodItemExtension", "options": [ { "offerId": "menuitem_addon_offer_id_1", "price": { "currencyCode": "USD", "units": "dollar(addon_q_1*addon_p_1)", "nanos": "cent(addon_q_1*addon_p_1)" }, "quantity": "addon_q_1" }, { "offerId": "menuitem_addon_offer_id_2", "price": { "currencyCode": "USD", "units": "dollar(addon_q_2*addon_p_2)", "nanos": "cent(addon_q_2*addon_p_2)" }, "quantity": "addon_q_2" } ] } }, { "offerId": "menuitem_offer_id_2", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_2*p_2)", "nanos": "cent(q_2*p_2)" } }, "quantity": "q_2" } ] }
JSON
Ví dụ này chứa một mục trong trình đơn có các tuỳ chọn mục trong trình đơn, AddOnMenuItems và AddOnMenuItems lồng nhau
Các món trong thực đơn trong nguồn cấp dữ liệu thực đơn:
{ "@type": "MenuItem", "@id": "menuitem_id_1", "hasMenuItemOptions": [ { "@type": "MenuItemOption", "value": { "@type": "PropertyValue", "name": "OPTION", "value": "Large", "offers": [ { "@type": "Offer", "@id": "menuitem_option_offer_id_1", "price": "p_1", "priceCurrency": "USD" } ], "menuAddOn": [ { "@type": "AddOnMenuSection", "@id": "menuitem_option_addon_section_id_1", "hasMenuItem": [ { "@type": "AddOnMenuItem", "@id": "menuitem_option_addon_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_option_addon_offer_id_1", "price": "addon_p_1", "priceCurrency": "USD" } ] }, { "@type": "AddOnMenuItem", "@id": "menuitem_option_addon_id_2", "offers": [ { "@type": "Offer", "@id": "menuitem_option_addon_offer_id_2", "price": "addon_p_2", "priceCurrency": "USD" } ], "menuAddOn": [ { "@type": "AddOnMenuSection", "@id": "menuitem_option_subaddon_section_id_1", "hasMenuItem": [ { "@type": "AddOnMenuItem", "@id": "menuitem_option_subaddon_id_1", "offers": [ { "@type": "Offer", "@id": "menuitem_option_subaddon_offer_id_1", "price": "subaddon_p_1", "priceCurrency": "USD" } ] } ] } ] } ] } ] } } ] }
Các mục trong trình đơn được liên kết với giỏ hàng thực hiện đơn hàng:
{ "@type": "Cart", "lineItems": [ { "offerId": "menuitem_option_offer_id_1", "price": { "amount": { "currencyCode": "USD", "units": "dollar(q_1*(p_1 + addon_q_1*addon_p_1 + addon_q_2*(addon_p_2 + subaddon_q_1*subaddon_p_1)))", "nanos": "cent(q_1*(p_1 + addon_q_1*addon_p_1 + addon_q_2*(addon_p_2 + subaddon_q_1*subaddon_p_1)))" } }, "quantity": "q_1", "extension": { "@type": "FoodItemExtension", "options": [ { "offerId": "menuitem_option_addon_offer_id_1", "price": { "currencyCode": "USD", "units": "dollar(addon_q_1*addon_p_1)", "nanos": "cent(addon_q_1*addon_p_1)" }, "quantity": "addon_q_1" }, { "offerId": "menuitem_option_addon_offer_id_2", "price": { "currencyCode": "USD", "units": "dollar(addon_q_2*(addon_p_2 + subaddon_q_1*subaddon_p_1))", "nanos": "cent(addon_q_2*(addon_p_2 + subaddon_q_1*subaddon_p_1))" }, "quantity": "addon_q_2", "subOptions": [ { "offerId": "menuitem_option_subaddon_offer_id_1", "price": { "currencyCode": "USD", "units": "dollar(subaddon_q_1*subaddon_p_1)", "nanos": "cent(subaddon_q_1*subaddon_p_1)" }, "quantity": "subaddon_q_1" } ] } ] } } ] }
Xử lý lỗi
Nếu gặp vấn đề khi xử lý CheckoutRequestMessage
, bạn có thể phản hồi bằng một CheckoutResponseMessage
chứa FoodErrorExtension
thay vì CheckoutResponse. Bạn có thể sử dụng phản hồi này để xác định một hoặc nhiều lỗi xảy ra trong quá trình xử lý.
Có 2 cách xử lý lỗi:
- Lỗi có thể khôi phục: Người dùng không cần phải chỉnh sửa giỏ hàng để gửi đơn đặt hàng. Ví dụ: nếu xác định một mặt hàng trong
Cart
có thay đổi về giá, thì bạn có thể phản hồi bằngFoodOrderError
thuộc loại lỗiPRICE_CHANGED
, cùng vớicorrectedProposedOrder
vàpaymentOptions
. Google sẽ thông báo cho người dùng về thay đổi này nhưng cho phép người dùng gửi bằngcorrectedProposedOrder
. Người dùng cũng có thể quay lại và chỉnh sửa giỏ hàng nếu muốn. Bạn sẽ nhận được mộtCheckoutRequestMessage
mới hoặc mộtSubmitOrderRequestMessage
. - Lỗi không thể khôi phục: Người dùng phải chỉnh sửa giỏ hàng trước khi gửi đơn đặt hàng. Ví dụ: nếu xác định nhà hàng đã đóng cửa, bạn có thể phản hồi bằng
FoodOrderError
thuộc loại lỗiCLOSED
. Google sẽ thông báo cho người dùng và quản lý hoạt động tương tác để cập nhật thành một nhà hàng mới. Bạn sẽ nhận được mộtCheckoutRequestMessage
mới cho một giỏ hàng mới.
Nhìn chung, bạn nên làm cho các lỗi ở cấp giỏ hàng không thể khôi phục và các lỗi ở cấp mặt hàng có thể khôi phục được. Để xem danh sách đầy đủ các loại lỗi và ý nghĩa của chúng, hãy xem FoodOrderError
.
Xử lý các thay đổi về giá
Giá thay đổi trong quy trình thanh toán
Nếu bạn gặp vấn đề về giá trong khi xử lý yêu cầu thanh toán của khách hàng, hãy làm như sau:
- Phản hồi
CheckoutRequestMessage
bằngCheckoutResponseMessage
chứaFoodErrorExtension
, như mô tả trong phần Xử lý lỗi. - Trong phản hồi lỗi, sử dụng
correctedProposedOrder.cart
để cập nhật giá thành đúng giá trị. Google nhận được yêu cầu đã chỉnh sửa và có thể phát hành mộtCheckoutRequestMessage
mới.
Sau khi thanh toán, Google sẽ hiển thị trang xác nhận đơn đặt hàng cho người dùng cuối, bất kể ProposedOrder
có thay đổi hay không.
Nếu đề xuất đã được sửa, Google có thể hiển thị thêm cảnh báo để người dùng biết về các thay đổi này. Nếu người dùng đồng ý đặt hàng, sẽ không có thêm yêu cầu thanh toán nào nữa. Quy trình này sẽ tiếp tục gửi đơn đặt hàng với ProposedOrder
đã được sửa.
Tuy nhiên, người dùng luôn có thể đổi ý và chỉnh sửa lại giỏ hàng. Khi giỏ hàng của họ cập nhật theo cách này, Google sẽ gửi một CheckoutRequestMessage
mới.
Thay đổi giá trong khi gửi đơn đặt hàng
Nếu bạn gặp vấn đề về giá trong khi xử lý gửi đơn đặt hàng (ý định actions.intent.TRANSACTION_DECISION
đã được kích hoạt), đừng phản hồi lỗi hoặc cập nhật giá trong phản hồi. Nếu giá, số lượng hoặc các thông tin chi tiết khác trong SubmitOrderRequestMessage
không tương ứng với dữ liệu của bạn, hãy phản hồi bằng orderState
được đặt thành REJECTED
để cho biết rằng không thể đặt đơn đặt hàng theo yêu cầu.
Sau đó, nếu đơn đặt hàng và thông tin thanh toán hợp lệ, hãy đặt orderState
thành CREATED
hoặc CONFIRMED
. Ngoài ra, hãy thêm một actionOrderId
để đại diện cho mã đơn hàng trong hệ thống của bạn. Bạn phải sử dụng mã này khi gửi nội dung cập nhật tiếp theo.
Nếu không thể xử lý khoản thanh toán và đã gửi SubmitOrderRequestMessage
, bạn có thể gửi AsyncOrderUpdateRequestMessage
với orderState
được đặt thành REJECTED
để người dùng biết rằng đơn đặt hàng sẽ không thành công.
Giá thay đổi sau khi gửi đơn đặt hàng
Nếu xác định rằng giá đã thay đổi so với giá được sử dụng khi khách hàng
gửi đơn đặt hàng, bạn có thể đưa ra AsyncOrderUpdateRequestMessage
, như
mô tả trong phần Triển khai cập nhật đơn đặt hàng không đồng bộ với giá mới.
Cách cập nhật giá bằng tính năng cập nhật đơn đặt hàng không đồng bộ:
- Thay đổi giá trong
lineItemUpdates[x].price
. Giá trị này phản ánh tổng chi phí của mặt hàng, bao gồm cả tiện ích bổ sung và nhân với số lượng. (Để biết thêm thông tin, hãy xem nội dung mô tả về trườngprice
củaLineItem
.) - Nhập nội dung giải thích bằng
lineItemUpdates[x].reason
. - Đặt
lineItemUpdates[x].orderState
thànhCONFIRMED
.
Bạn có thể tuỳ ý tính phí vào phương thức thanh toán này trước hoặc sau khi gửi AsyncOrderUpdateRequestMessage. Nếu giao dịch không thành công (có thể do delta giá quá cao), hãy gửi AsyncOrderUpdateRequestMessage với các chế độ cài đặt sau trong OrderUpdate
để thông báo cho Google về lỗi này:
- Đặt
orderState
thànhREJECTED
. - Mô tả lỗi trong trường
label
.
Xác thực quy trình thanh toán
Như đã thảo luận trong Bước 4: Triển khai thanh toán, điểm cuối của phương thức thực hiện sẽ tiến hành xác thực trên mọi CheckoutRequestMessage
được gửi đến và phản hồi bằng một CheckoutResponseMessage
.
Dưới đây là ví dụ về CheckoutResponseMessage
để xác thực thành công:
Trường hợp sử dụng | Cách triển khai |
---|---|
Trường hợp sử dụng 1: Xác thực thành công | Trả lại CheckoutResponse . Thuộc tính này phải có ProposedOrder và PaymentOptions .
ProposedOrder bao gồm thuế, phí và tổng giá của giỏ hàng. |
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "checkoutResponse": { "proposedOrder": { "id": "sample_proposed_order_id_1", "otherItems": [ { "name":"New customer discount", "price": { "type":"ESTIMATE", "amount": { "currencyCode":"USD", "units":"-5", "nanos": -500000000 } }, "type": "DISCOUNT" }, { "name": "Delivery fee", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "3", "nanos": 500000000 } }, "type": "DELIVERY" }, { "name": "Tax", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "1", "nanos": 500000000 } }, "type": "TAX" } ], "cart": { "merchant": { "id": "https://www.exampleprovider.com/merchant/id1", "name": "Falafel Bite" }, "lineItems": [ { "name": "Pita Chips", "type": "REGULAR", "id": "sample_item_offer_id_1", "offerId": "https://www.exampleprovider.com/menu/item/offer/id1", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "2", "nanos": 750000000 } }, "subLines": [ { "note": "Notes for this item." } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension", "options": [ { "id": "sample_addon_offer_id_1", "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id1", "name": "Honey Mustard", "price": { "currencyCode": "USD" }, "quantity": 1 }, { "id": "sample_addon_offer_id_2", "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id2", "name": "BBQ Sauce", "price": { "currencyCode": "USD", "nanos": 500000000 }, "quantity": 1 } ] } }, { "name": "Chicken Shwarma Wrap", "type": "REGULAR", "id": "sample_item_offer_id_2", "offerId": "https://www.exampleprovider.com/menu/item/offer/id2", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "8" } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Greek Salad", "type": "REGULAR", "id": "sample_item_offer_id_3", "offerId": "https://www.exampleprovider.com/menu/item/offer/id3", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Prawns Biryani", "type": "REGULAR", "id": "sample_item_offer_id_4", "offerId": "https://www.exampleprovider.com/menu/item/offer/id4", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "15", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension", "fulfillmentPreference": { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } } }, "location": { "coordinates": { "latitude": 37.788783, "longitude": -122.41384 }, "formattedAddress": "1350 CHARLESTON ROAD, MOUNTAIN VIEW, CA, United States", "zipCode": "94043", "city": "Mountain View", "postalAddress": { "regionCode": "US", "postalCode": "94043", "administrativeArea": "CA", "locality": "Mountain View", "addressLines": [ "1350 Charleston Road" ] }, "notes": "Gate code is #111" } } }, "totalPrice": { "type": "ESTIMATE", "amount": { // Represents $36.73 "currencyCode": "USD", "units": "36", "nanos": 730000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension", "availableFulfillmentOptions": [ { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } }, "expiresAt": "2017-07-17T12:30:00Z" } ] } }, "paymentOptions": { "googleProvidedOptions": { "tokenizationParameters": { "tokenizationType": "PAYMENT_GATEWAY", "parameters": { "gateway": "stripe", "stripe:publishableKey": "pk_live_stripe_client_key", "stripe:version": "2017-04-06" } }, "supportedCardNetworks": [ "AMEX", "DISCOVER", "MASTERCARD", "JCB", "VISA" ], "prepaidCardDisallowed": true } } } } } ] } } }
Xác thực địa chỉ giao hàng
Điểm cuối của phương thức thực hiện cần xác thực địa chỉ giao hàng có trong mỗi CheckoutRequestMessage
.
Nếu có vấn đề về địa chỉ giao hàng, chẳng hạn như địa chỉ nằm ngoài phạm vi của dịch vụ giao hàng, thì CheckoutResponseMessage
mà phương thức thực hiện của bạn trả về phải chứa FoodOrderError
thuộc loại thích hợp.
Trường hợp sử dụng | Cách triển khai |
---|---|
Trường hợp sử dụng 1: Xác thực không thành công do địa chỉ giao hàng nằm ngoài phạm vi hoặc có vấn đề với địa chỉ giao hàng | Trả về FoodErrorExtension với FoodOrderError thuộc loại lỗi OUT_OF_SERVICE_AREA . |
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "OUT_OF_SERVICE_AREA", "description": "Sorry, the restaurant cannot deliver to your address." } ] } } } ] } } }
Xác thực giá trị đặt hàng tối thiểu
Điểm cuối của phương thức thực hiện phải xác thực giá trị đơn đặt hàng tối thiểu của mỗi CheckoutRequestMessage
.
Nếu không đáp ứng được giá trị đặt hàng tối thiểu, CheckoutResponseMessage
mà phương thức thực hiện của bạn trả về sẽ chứa FoodOrderError
thuộc loại lỗi REQUIREMENTS_NOT_MET
.
Trường hợp sử dụng | Cách triển khai |
---|---|
Trường hợp sử dụng 1: Xác thực không thành công vì không đáp ứng giá trị đơn đặt hàng tối thiểu | Trả về FoodErrorExtension với FoodOrderError thuộc loại lỗi REQUIREMENTS_NOT_MET . |
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "REQUIREMENTS_NOT_MET", "description": "The cart subtotal must be over $20." } ] } } } ] } } }
Xác thực thời lượng đặt hàng
Điểm cuối của phương thức thực hiện phải xác thực mọi yếu tố có thể ảnh hưởng đến cửa sổ sắp xếp của từng CheckoutRequestMessage
.
Ví dụ: nếu nhà hàng đã đóng cửa hoặc không còn nhận đơn gọi món nữa, thì CheckoutResponseMessage
mà phương thức thực hiện trả về sẽ tương ứng chứa FoodOrderError
thuộc loại lỗi CLOSED
hoặc NO_CAPACITY
.
Trường hợp sử dụng | Cách triển khai |
---|---|
Trường hợp sử dụng 1: Xác thực không thành công do nhà hàng đã đóng cửa hoặc không còn được hỗ trợ | Trả về FoodErrorExtension với FoodOrderError thuộc loại lỗi CLOSED . |
Trường hợp sử dụng 2: Xác thực không thành công vì nhà hàng đang bận và hiện không nhận đơn đặt hàng | Trả về FoodErrorExtension với FoodOrderError thuộc loại lỗi NO_CAPACITY . |
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "CLOSED", "description": "The restaurant is closed." } ] } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "NO_CAPACITY", "description": "Sorry, the restaurant is busy at the moment." } ] } } } ] } } }
Xác thực mặt hàng trong giỏ hàng
Điểm cuối của phương thức thực hiện sẽ xác thực giá và tình trạng còn hàng của từng mặt hàng trong giỏ hàng có trong CheckoutRequestMessage
.
Nếu tình trạng còn hàng hoặc mức giá thay đổi, thì CheckoutResponseMessage
mà phương thức thực hiện mà bạn trả về phải chứa FoodOrderError
thuộc loại lỗi AVAILABILITY_CHANGED
hoặc PRICE_CHANGED
tương ứng.
Trường hợp sử dụng | Cách triển khai |
---|---|
Trường hợp sử dụng 1: Xác thực không thành công vì một số mục trong trình đơn và/hoặc phần tuỳ chỉnh của các mục đó không hợp lệ hoặc đã hết hàng | Trả về FoodErrorExtension có correctedProposedOrder , PaymentOptions và FoodOrderError thuộc loại lỗi AVAILABILITY_CHANGED . Phải xoá các mục không hợp lệ khỏi
CorrectedProposedOrder . |
Trường hợp sử dụng 2: Xác thực không thành công vì một số mục trong trình đơn và/hoặc phần tuỳ chỉnh của các mục đó không hợp lệ hoặc đã hết hàng. Giỏ hàng mà bạn sửa không còn đáp ứng yêu cầu về giá trị đơn đặt hàng tối thiểu. | Trả về FoodErrorExtension với FoodOrderError thuộc các loại lỗi AVAILABILITY_CHANGED và REQUIREMENTS_NOT_MET . |
Trường hợp sử dụng 3: Xác thực không thành công vì một số mục trong trình đơn và/hoặc mức giá tuỳ chỉnh đã thay đổi | Trả về FoodErrorExtension có correctedProposedOrder , PaymentOptions và FoodOrderError thuộc loại lỗi PRICE_CHANGED . Bạn phải cập nhật những mức giá cũ bằng
CorrectedProposedOrder . |
Trường hợp sử dụng 4: Xác thực không thành công vì một số mục trong trình đơn và/hoặc mức giá tuỳ chỉnh đã thay đổi. Giỏ hàng mà bạn sửa không còn đáp ứng yêu cầu về giá trị đơn đặt hàng tối thiểu | Trả về FoodErrorExtension với FoodOrderError thuộc các loại lỗi PRICE_CHANGED và REQUIREMENTS_NOT_MET . |
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "AVAILABILITY_CHANGED", "id": "sample_item_offer_id_1", "description": "The item is no longer available." }, { "error": "AVAILABILITY_CHANGED", "id": "sample_item_offer_id_2", "description": "The item is no longer available." } ], "correctedProposedOrder": { "id": "sample_corrected_proposed_order_id_1", "otherItems": [ { "name":"New customer discount", "price": { "type":"ESTIMATE", "amount": { "currencyCode":"USD", "units":"-5", "nanos": -500000000 } }, "type": "DISCOUNT" }, { "name": "Delivery fee", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "3", "nanos": 500000000 } }, "type": "DELIVERY" }, { "name": "Tax", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "1", "nanos": 500000000 } }, "type": "TAX" } ], "cart": { "merchant": { "id": "https://www.exampleprovider.com/merchant/id1", "name": "Falafel Bite" }, "lineItems": [ { "name": "Greek Salad", "type": "REGULAR", "id": "sample_item_offer_id_3", "offerId": "https://www.exampleprovider.com/menu/item/offer/id3", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Prawns Biryani", "type": "REGULAR", "id": "sample_item_offer_id_4", "offerId": "https://www.exampleprovider.com/menu/item/offer/id4", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "15", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension", "fulfillmentPreference": { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } } }, "location": { "coordinates": { "latitude": 37.788783, "longitude": -122.41384 }, "formattedAddress": "1350 CHARLESTON ROAD, MOUNTAIN VIEW, CA, United States", "zipCode": "94043", "city": "Mountain View", "postalAddress": { "regionCode": "US", "postalCode": "94043", "administrativeArea": "CA", "locality": "Mountain View", "addressLines": [ "1350 Charleston Road" ] }, "notes": "Gate code is #111" } } }, "totalPrice": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "36", "nanos": 730000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension", "availableFulfillmentOptions": [ { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } }, "expiresAt": "2017-07-17T12:30:00Z" } ] } }, "paymentOptions": { "googleProvidedOptions": { "tokenizationParameters": { "tokenizationType": "PAYMENT_GATEWAY", "parameters": { "gateway": "stripe", "stripe:publishableKey": "pk_live_stripe_client_key", "stripe:version": "2017-04-06" } }, "supportedCardNetworks": [ "AMEX", "DISCOVER", "MASTERCARD", "JCB", "VISA" ], "prepaidCardDisallowed": true } } } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "REQUIREMENTS_NOT_MET", "description": "The cart subtotal must be over $20." }, { "error": "AVAILABILITY_CHANGED", "id": "cart_lineitem_id" "description": "cart_lineitem_id is no longer available." } ] } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "PRICE_CHANGED", "id": "sample_item_offer_id_1", "description": "The price has changed.", "updatedPrice": { "currencyCode": "USD", "units": "2", "nanos": 750000000 } }, { "error": "PRICE_CHANGED", "id": "sample_item_offer_id_2", "description": "The price has changed.", "updatedPrice": { "currencyCode": "USD", "units": "8" } } ], "correctedProposedOrder": { "id": "sample_corrected_proposed_order_id_1", "otherItems": [ { "name":"New customer discount", "price": { "type":"ESTIMATE", "amount": { "currencyCode":"USD", "units":"-5", "nanos": -500000000 } }, "type": "DISCOUNT" }, { "name": "Delivery fee", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "3", "nanos": 500000000 } }, "type": "DELIVERY" }, { "name": "Tax", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "1", "nanos": 500000000 } }, "type": "TAX" } ], "cart": { "merchant": { "id": "https://www.exampleprovider.com/merchant/id1", "name": "Falafel Bite" }, "lineItems": [ { "name": "Pita Chips", "type": "REGULAR", "id": "sample_item_offer_id_1", "offerId": "https://www.exampleprovider.com/menu/item/offer/id1", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "2", "nanos": 750000000 } }, "subLines": [ { "note": "Notes for this item." } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension", "options": [ { "id": "sample_addon_offer_id_1", "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id1", "name": "Honey Mustard", "price": { "currencyCode": "USD" }, "quantity": 1 }, { "id": "sample_addon_offer_id_2", "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id2", "name": "BBQ Sauce", "price": { "currencyCode": "USD", "nanos": 500000000 }, "quantity": 1 } ] } }, { "name": "Chicken Shwarma Wrap", "type": "REGULAR", "id": "sample_item_offer_id_2", "offerId": "https://www.exampleprovider.com/menu/item/offer/id2", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "8" } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Greek Salad", "type": "REGULAR", "id": "sample_item_offer_id_3", "offerId": "https://www.exampleprovider.com/menu/item/offer/id3", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } }, { "name": "Prawns Biryani", "type": "REGULAR", "id": "sample_item_offer_id_4", "offerId": "https://www.exampleprovider.com/menu/item/offer/id4", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "15", "nanos": 990000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension", "fulfillmentPreference": { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } } }, "location": { "coordinates": { "latitude": 37.788783, "longitude": -122.41384 }, "formattedAddress": "1350 CHARLESTON ROAD, MOUNTAIN VIEW, CA, United States", "zipCode": "94043", "city": "Mountain View", "postalAddress": { "regionCode": "US", "postalCode": "94043", "administrativeArea": "CA", "locality": "Mountain View", "addressLines": [ "1350 Charleston Road" ] }, "notes": "Gate code is #111" } } }, "totalPrice": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "36", "nanos": 730000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension", "availableFulfillmentOptions": [ { "fulfillmentInfo": { "delivery": { "deliveryTimeIso8601": "P90M" } }, "expiresAt": "2017-07-17T12:30:00Z" } ] } }, "paymentOptions": { "googleProvidedOptions": { "tokenizationParameters": { "tokenizationType": "PAYMENT_GATEWAY", "parameters": { "gateway": "stripe", "stripe:publishableKey": "pk_live_stripe_client_key", "stripe:version": "2017-04-06" } }, "supportedCardNetworks": [ "AMEX", "DISCOVER", "MASTERCARD", "JCB", "VISA" ], "prepaidCardDisallowed": true } } } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension", "foodOrderErrors": [ { "error": "REQUIREMENTS_NOT_MET", "description": "The cart subtotal must be over $20." }, { "error": "PRICE_CHANGED", "id": "cart_lineitem_id" "description": "cart_lineitem_id price has been updated." "updatedPrice": { "currencyCode": "USD", "units": "2", "nanos": 750000000 } } ] } } } ] } } }
Gửi xác thực đơn đặt hàng
Như đã thảo luận trong Bước 7: Triển khai đơn đặt hàng gửi, điểm cuối của phương thức thực hiện sẽ thực hiện việc xác thực đối với mọi SubmitOrderRequestMessage
được gửi đến và phản hồi bằng một SubmitOrderResponseMessage
.
Dưới đây là ví dụ về SubmitOrderResponseMessage
để xác thực thành công:
Trường hợp sử dụng | Cách triển khai |
---|---|
Trường hợp sử dụng 1: Đơn đặt hàng được tạo thành công | SubmitOrderResponseMessage với trạng thái đơn đặt hàng CREATED . Tệp này phải có actionOrderId , userVisibleId , orderManagementActions và estimatedFulfillmentTime . |
Trường hợp sử dụng 2: Đơn đặt hàng bị từ chối do vấn đề về việc thanh toán | SubmitOrderResponseMessage với trạng thái đơn đặt hàng REJECTED . Tệp này phải có actionOrderId , userVisibleId , orderManagementActions và rejectionInfo thuộc loại PAYMENT_DECLINED . |
Trường hợp sử dụng 3: Đơn đặt hàng bị từ chối do người dùng bị gắn cờ là bị cấm | Một SubmitOrderResponseMessage có trạng thái đơn đặt hàng là REJECTED . Tệp này phải có actionOrderId , userVisibleId , orderManagementActions và rejectionInfo thuộc loại INELIGIBLE . |
Trường hợp sử dụng 4: Đơn đặt hàng bị từ chối vì thông tin người dùng không đầy đủ hoặc không hợp lệ | SubmitOrderResponseMessage với trạng thái đơn đặt hàng REJECTED . Tệp này phải có actionOrderId , userVisibleId , orderManagementActions và rejectionInfo thuộc loại INELIGIBLE . |
Trường hợp sử dụng 5: Đơn đặt hàng bị từ chối vì lý do không xác định | SubmitOrderResponseMessage với trạng thái đơn đặt hàng REJECTED . Tệp này phải có actionOrderId , userVisibleId , orderManagementActions và rejectionInfo thuộc loại UNKNOWN . |
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "CREATED", "label": "Order received" }, "updateTime": "2017-05-10T02:30:00.000Z", "orderManagementActions": [ { "type": "CUSTOMER_SERVICE", "button": { "title": "Contact customer service", "openUrlAction": { "url": "mailto:support@example.com" } } }, { "type": "EMAIL", "button": { "title": "Email restaurant", "openUrlAction": { "url": "mailto:person@example.com" } } }, { "type": "CALL", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "REJECTED", "label": "Order rejected" }, "updateTime": "2017-05-10T02:30:00.000Z", "rejectionInfo": { "type": "PAYMENT_DECLINED", "reason": "Insufficient funds" }, "orderManagementActions": [ { "type": "CUSTOMER_SERVICE", "button": { "title": "Contact customer service", "openUrlAction": { "url": "mailto:support@example.com" } } }, { "type": "EMAIL", "button": { "title": "Email restaurant", "openUrlAction": { "url": "mailto:person@example.com" } } }, { "type": "CALL", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "REJECTED", "label": "Order rejected" }, "updateTime": "2017-05-10T02:30:00.000Z", "rejectionInfo": { "type": "INELIGIBLE", "reason": "Sorry, we are not able to take orders from this user" }, "orderManagementActions": [ { "type": "CUSTOMER_SERVICE", "button": { "title": "Contact customer service", "openUrlAction": { "url": "mailto:support@example.com" } } }, { "type": "EMAIL", "button": { "title": "Email restaurant", "openUrlAction": { "url": "mailto:person@example.com" } } }, { "type": "CALL", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "REJECTED", "label": "Order rejected" }, "updateTime": "2017-05-10T02:30:00.000Z", "rejectionInfo": { "type": "INELIGIBLE", "reason": "Sorry, the phone number must not be blank" }, "orderManagementActions": [ { "type": "CUSTOMER_SERVICE", "button": { "title": "Contact customer service", "openUrlAction": { "url": "mailto:support@example.com" } } }, { "type": "EMAIL", "button": { "title": "Email restaurant", "openUrlAction": { "url": "mailto:person@example.com" } } }, { "type": "CALL", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }
JSON
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "REJECTED", "label": "Order rejected" }, "updateTime": "2017-05-10T02:30:00.000Z", "rejectionInfo": { "type": "UNKNOWN", "reason": "Sorry, there is something wrong with this order." }, "orderManagementActions": [ { "type": "CUSTOMER_SERVICE", "button": { "title": "Contact customer service", "openUrlAction": { "url": "mailto:support@example.com" } } }, { "type": "EMAIL", "button": { "title": "Email restaurant", "openUrlAction": { "url": "mailto:person@example.com" } } }, { "type": "CALL", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+16505554679" } } }, { "type": "VIEW_DETAILS", "button": { "title": "View order", "openUrlAction": { "url": "https://orderview.partner.com?orderid=sample_action_order_id" } } } ] } } } ] } } }