Liên kết nguồn cấp dữ liệu thực đơn và các mục trong giỏ hàng để thực hiện đơn hàng
Khi khách hàng thêm các mặt hàng từ nguồn cấp dữ liệu Thực đơn của bạn vào giỏ hàng và thanh toán, Google gửi các mặt hàng đó đến điểm cuối thực hiện đơn hàng của bạn để xác minh giá và tình trạng còn hàng. Sau khi giá và tình trạng còn hàng được xác thực, 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 thực hiện đơn hàng.
Các mẫu trong mục này là phiên bản đơn giản của Nguồn cấp dữ liệu thực đơn và Giỏ hàng
giản đồ. Chỉ những 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à
thì đối tượng Giỏ hàng sẽ xuất hiện. Để xem giản đồ đầy đủ, hãy xem Menu
và Cart
.
Những 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 Cart
cho cả thanh toán và gửi đơn đặt hàng.
- Một
MenuItem
đơn giản được biểu thị dưới dạngLineItem
tronglineItems
mảng vớiofferId
làoffer.id
của mục trong trình đơn đã chọn trong Trình đơn nguồn cấp dữ liệu. MenuItem
cóMenuItemOption
bắt buộc được biểu thị dưới dạngLineItem
trong mảnglineItems
vớiofferId
được chọn tuỳ chọn mục trong trình đơnoffer.id
từ nguồn cấp dữ liệu Thực đơ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ới trình đơn tiện ích bổ sung đã chọnoffer.id
của mục từ 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 ghép được biểu thị dưới dạngsubOptions
bên trong mỗi .
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 thực đơn và phương thức thực hiện đơn hàng giỏ hàng.
JSON
Ví dụ này chứa một danh sách các mục trong trình đơn đơn giản.
Các món trong thực đơn trong một 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ặt hàng 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 một 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ặt hàng 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 lựa chọn cho mục trong trình đơn, AddOnMenuItems, và AddOnMenuItems được lồng
Các món trong thực đơn trong một 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ặt hàng 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 sự cố khi xử lý CheckoutRequestMessage
, bạn
có thể phản hồi bằng CheckoutResponseMessage
chứa
FoodErrorExtension
thay vì CheckoutResponse. Bạn có thể dùng
để 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 bắt buộc phải chỉnh sửa giỏ hàng của họ để gửi
đơn đặt hàng. Ví dụ: nếu bạn xác định rằng một mục trong
Cart
có khi giá thay đổi, bạn có thể phản hồi bằng loại lỗiFoodOrderError
PRICE_CHANGED
, cùng vớicorrectedProposedOrder
vàpaymentOptions
. Google thông báo cho người dùng về thay đổi nhưng cho phép người dùng gửi kèm theocorrectedProposedOrder
. Người dùng cũng có thể quay lại và chỉnh sửa giỏ hàng nếu mong muốn. Bạn sẽ nhận đượcCheckoutRequestMessage
mới hoặcSubmitOrderRequestMessage
. - Lỗi không thể khôi phục: Người dùng phải chỉnh sửa giỏ hàng trước
gửi đơn đặt hàng. Ví dụ: nếu bạn xác định rằng nhà hàng này
đã đóng, bạn có thể phản hồi bằng
FoodOrderError
thuộc loại lỗiCLOSED
. Google thông báo cho người dùng và quản lý quá trình tương tác để cập nhật lên nhà hàng của bạn. Bạn sẽ nhận được mộtCheckoutRequestMessage
mới để có giỏ hàng.
Nhìn chung, lỗi ở cấp giỏ hàng là không thể khôi phục và lỗi ở cấp mặt hàng
có thể khôi phục được. Để biết 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á
Thay đổi giá trong quy trình thanh toán
Nếu bạn gặp vấn đề về giá trong khi xử lý giao dịch thanh toán của khách hàng yêu cầu, 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, hãy sử dụng
correctedProposedOrder.cart
để cập nhật giá thành giá trị chính xác. Google nhận được đơn đặt hàng đã sửa và có thể phát hành một đơn đặt hàng mớiCheckoutRequestMessage
.
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ó bị thay đổi hay không.
Nếu suggesteddOrder đã được sửa, Google có thể hiển thị các cảnh báo bổ sung cho
thông báo cho người dùng về các thay đổi. 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 sẽ tiếp tục gửi đơn đặt hàng, với
ProposedOrder
đã sửa.
Tuy nhiên, người dùng luôn có thể đổi ý và chỉnh sửa lại giỏ hàng. Thời gian
bản cập nhật giỏ hàng theo cách này thì Google sẽ gửi một CheckoutRequestMessage
mới.
Giá thay đổi trong quá trình gửi đơn đặt hàng
Nếu bạn gặp vấn đề về giá trong quá trình xử lý việc gửi đơn đặt hàng
(ý định actions.intent.TRANSACTION_DECISION
đã được kích hoạt), đừng phản hồi
có lỗi hoặc cập nhật giá trong phản hồi của bạn. 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 hàng theo yêu cầu.
Sau đó, nếu đơn đặt hàng và thông tin thanh toán là hợp lệ, hãy đặt orderState
thành CREATED
hoặc CONFIRMED
. Ngoài ra, hãy thêm actionOrderId
để thể hiện 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 các bản cập nhật tiếp theo.
Nếu bạn 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
để cho phép
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 bạn xác định rằng một mức giá đã thay đổi so với giá được áp dụng khi khách hàng sử dụng
đã gửi đơn đặt hàng, bạn có thể tạo AsyncOrderUpdateRequestMessage
, như
được mô tả trong phần Triển khai bản cập nhật thứ tự không đồng bộ, với mức 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á bằng
lineItemUpdates[x].price
. Chiến dịch này giá trị phản ánh tổng chi phí của mặt hàng, bao gồm các dịch vụ bổ sung và nhân theo số lượng. (Để biết thêm thông tin, hãy xem phần mô tả của trườngprice
củaLineItem
.) - Nhập nội dung giải thích bằng
lineItemUpdates[x].reason
. - Đặt
lineItemUpdates[x].orderState
đếnCONFIRMED
.
Bạn có thể thử tính phí vào phương thức thanh toán trước hoặc sau khi gửi
AsyncOrderUpdateRequestMessage, tuỳ theo quyết định của bạn. 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 cài đặt sau trong phần
OrderUpdate
để thông báo cho Google về lỗi:
- Đặ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 phương thức thực hiện sẽ thực hiện xác thực trên mọi thiết bị đến
CheckoutRequestMessage
rồi trả lời bằng CheckoutResponseMessage
.
Dưới đây là ví dụ về CheckoutResponseMessage
để thành công
xác thực:
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 . 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 thực hiện đơn hàng phải 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
dịch vụ giao hàng, CheckoutResponseMessage
do
phương thức thực hiện phải chứa FoodOrderError
thuộc kiểu phù 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 vì không có địa chỉ giao hàng hoặc có vấn đề với địa chỉ giao hàng | Trả lại FoodErrorExtension bằng FoodOrderError
về 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ị đơn đặt hàng tối thiểu
Điểm cuối thực hiện đơn hàng phải xác thực giá trị đặt hàng tối thiểu của mỗi
CheckoutRequestMessage
.
Nếu bạn 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ề phải chứa FoodOrderError
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 do giá trị đặt hàng tối thiểu không được đáp ứng | Trả lại FoodErrorExtension bằng FoodOrderError
về 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 khoảng thời gian đặt hàng
Điểm cuối thực hiện đơn hàng của bạ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 đặt hàng tại
thời điểm, CheckoutResponseMessage
mà phương thức thực hiện của bạn trả về sẽ
chứa một 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: Không xác thực được vì nhà hàng đóng cửa hoặc không còn được hỗ trợ nữa | Trả lại FoodErrorExtension bằng FoodOrderError
về 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ả lại FoodErrorExtension bằng FoodOrderError
về 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 thực hiện đơn hàng phải 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 nằm trong CheckoutRequestMessage
.
Nếu tình trạng còn hàng hoặc giá đã thay đổi, CheckoutResponseMessage
mà phương thức thực hiện của bạn trả về phải chứa FoodOrderError
loại lỗi
AVAILABILITY_CHANGED
hoặc PRICE_CHANGED
.
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 các thông tin tuỳ chỉnh không hợp lệ hoặc đã hết hàng | Trả lại FoodErrorExtension bằng 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 do một số mục trong trình đơn và/hoặc các thông tin tuỳ chỉnh không hợp lệ hoặc đã hết hàng. Giỏ hàng đã chỉnh sửa không còn đáp ứng yêu cầu về giá trị đơn đặt hàng tối thiểu. | Trả lại FoodErrorExtension bằng FoodOrderError
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 do một số mục trong trình đơn và/hoặc giá tuỳ chỉnh đã thay đổi | Trả lại FoodErrorExtension bằng correctedProposedOrder ,
PaymentOptions và FoodOrderError thuộc loại lỗi
PRICE_CHANGED . Bạn phải cập nhật giá đã lỗi thời sau
CorrectedProposedOrder . |
Trường hợp sử dụng 4: Xác thực không thành công do một số mục trong trình đơn và/hoặc giá tuỳ chỉnh đã thay đổi. Giỏ hàng được chỉnh sửa không còn đáp ứng yêu cầu về giá trị đơn đặt hàng tối thiểu | Trả lại FoodErrorExtension bằng FoodOrderError
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 tính năng Gửi đơn đặt hàng,
điểm cuối phương thức thực hiện sẽ thực hiện xác thực trên mọi thiết bị đến
SubmitOrderRequestMessage
rồi trả lời bằng
SubmitOrderResponseMessage
.
Dưới đây là ví dụ về SubmitOrderResponseMessage
để thành công
xác thực:
Trường hợp sử dụng | Cách triển khai |
---|---|
Trường hợp sử dụng 1: Đã tạo thành công đơn đặt hàng | Một SubmitOrderResponseMessage có đơn đặt hàng CREATED
trạng thái. 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ề thanh toán | Một SubmitOrderResponseMessage có đơn đặt hàng REJECTED
trạng thái. 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ó đơn đặt hàng REJECTED
trạng thái. 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ệ | Một SubmitOrderResponseMessage có đơn đặt hàng REJECTED
trạng thái. 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 | Một SubmitOrderResponseMessage có đơn đặt hàng REJECTED
trạng thái. 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" } } } ] } } } ] } } }