当用户创建购物车时,系统会调用结账流程。用户购物车中的内容和订单详情会发送到您的端到端订购 Web 服务。您的网站服务会验证这些信息,然后您可以根据需要继续操作或调整其购物车。
Web 服务的结账处理脚本必须响应 POST 请求。当客户选择结账时,Google 会向订购端到端 Web 服务发送 JSON 请求正文,该正文采用 CheckoutRequestMessage
的形式,其中包含客户 Cart
的详细信息。然后,您的 Web 服务会使用 CheckoutResponseMessage
进行响应。下图演示了该流程。
收到结账请求后,您的端到端订购 Web 服务必须执行以下操作:
- 根据当前商品价格、库存状况和提供商服务,检查购物车的有效性。
- 计算总价(包括所有折扣、税费和运费)。
- 如果成功,请响应未修改的购物车。
- 如果失败,请返回错误消息和新的建议顺序。
在开始实现结账功能之前,我们建议您先查看履单概览文档。
结账请求消息
为了验证客户的购物车,当客户选择结账时,Google 会向您的 Web 服务发送请求,其中包含采用 CheckoutRequestMessage
格式的 JSON 正文。只有在端到端订餐流程的后续阶段,系统才会提交客户订单。
CheckoutRequestMessage
中包含的数据包括:
- 意图:每个结账请求正文的
inputs[0].intent
字段都包含actions.foodordering.intent.CHECKOUT
字符串值。 - 购物车:结账请求的
inputs[0].arguments[0].extension
字段包含一个Cart
对象,该对象代表客户的购物车。 - 外卖或自提:
Cart
对象的扩展字段包含一个FoodCartExtension
对象,用于指定外卖或自提的属性:- 对于送货订单,
FoodCartExtension
对象包含送货地址。 - 对于自提或外卖订单,
FoodCartExtension
对象不包含任何位置信息。
- 对于送货订单,
- 沙盒:结账请求的
isInSandbox
字段包含一个布尔值,用于指示交易是否使用沙盒付款。
结账请求示例
下面是一个 CheckoutRequestMessage
示例:
{
"user": {},
"conversation": {
"conversationId": "CTZbZfUlHCybEdcz_5PB3Ttf"
},
"inputs": [
{
"intent": "actions.foodordering.intent.CHECKOUT",
"arguments": [
{
"extension": {
"@type": "type.googleapis.com/google.actions.v2.orders.Cart",
"merchant": {
"id": "restaurant/Restaurant/QWERTY",
"name": "Tep Tep Chicken Club"
},
"lineItems": [
{
"name": "Spicy Fried Chicken",
"type": "REGULAR",
"id": "299977679",
"quantity": 2,
"price": {
"type": "ESTIMATE",
"amount": {
"currencyCode": "AUD",
"units": "39",
"nanos": 600000000
}
},
"offerId": "MenuItemOffer/QWERTY/scheduleId/496/itemId/143",
"extension": {
"@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension"
}
}
],
"extension": {
"@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension",
"fulfillmentPreference": {
"fulfillmentInfo": {
"delivery": {
"deliveryTimeIso8601": "P0M"
}
}
},
"location": {
"coordinates": {
"latitude": -33.8376441,
"longitude": 151.0868736
},
"formattedAddress": "Killoola St, 1, Concord West NSW 2138",
"zipCode": "2138",
"city": "Concord West",
"postalAddress": {
"regionCode": "AU",
"postalCode": "2138",
"administrativeArea": "NSW",
"locality": "Concord West",
"addressLines": [
"Killoola St",
"1"
]
}
}
}
}
}
]
}
],
"directActionOnly": true,
"isInSandbox": true
}
结账响应消息
收到来自端到端订购服务的请求后,您的结账 Web 服务必须对其进行处理,并以 CheckoutResponseMessage
进行响应。CheckoutResponseMessage
需要涵盖成功或失败的请求。
成功请求
如果结账请求成功,CheckoutResponseMessage
需要包含 ProposedOrder
和 PaymentOptions
:
ProposedOrder
cart
:与CheckoutRequestMessage
中提供的购物车相同的cart
对象。如果购物车中的任何内容都需要更改,CheckoutResponseMessage
应改为包含具有更正的ProposedOrder
的FoodErrorExtension
。otherItems
:提供商添加的项目,例如运费、税费和其他费用。还可能包含用户添加的小费。totalPrice
:订单的总金额。extension
:用于定义订单履单信息(例如送货时间)的FoodOrderExtension
。
PaymentOptions
- 如需了解如何设置付款处理,请参阅下文中的设置 Google Pay。在准备好实现付款处理之前,您可以在
CheckoutResponseMessage
中使用占位符 JSON。 - 如需在
CheckoutResponseMessage
中添加占位符付款选项,请参阅下面的示例,其中使用了PaymentOptions
的示例付款网关。
- 如需了解如何设置付款处理,请参阅下文中的设置 Google Pay。在准备好实现付款处理之前,您可以在
成功响应示例
{
"finalResponse": {
"richResponse": {
"items": [
{
"structuredResponse": {
"checkoutResponse": {
"proposedOrder": {
"cart": {
"merchant": {
"id": "restaurant/Restaurant/QWERTY",
"name": "Tep Tep Chicken Club"
},
"lineItems": [
{
"name": "Spicy Fried Chicken",
"type": "REGULAR",
"id": "299977679",
"quantity": 2,
"price": {
"type": "ESTIMATE",
"amount": {
"currencyCode": "AUD",
"units": "39",
"nanos": 600000000
}
},
"offerId": "MenuItemOffer/QWERTY/scheduleId/496/itemId/143",
"extension": {
"@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension"
}
}
],
"extension": {
"@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension",
"fulfillmentPreference": {
"fulfillmentInfo": {
"delivery": {
"deliveryTimeIso8601": "P0M"
}
}
},
"location": {
"coordinates": {
"latitude": -33.8376441,
"longitude": 151.0868736
},
"formattedAddress": "Killoola St, 1, Concord West NSW 2138",
"zipCode": "2138",
"city": "Concord West",
"postalAddress": {
"regionCode": "AU",
"postalCode": "2138",
"administrativeArea": "NSW",
"locality": "Concord West",
"addressLines": [
"Killoola St",
"1"
]
}
}
}
},
"totalPrice": {
"type": "ESTIMATE",
"amount": {
"currencyCode": "AUD",
"units": "43",
"nanos": 100000000
}
},
"extension": {
"@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension",
"availableFulfillmentOptions": [
{
"fulfillmentInfo": {
"delivery": {
"deliveryTimeIso8601": "P0M"
}
}
}
]
},
"otherItems": [
{
"name": "Delivery fee",
"price": {
"type": "ESTIMATE",
"amount": {
"currencyCode": "AUD",
"units": "3",
"nanos": 500000000
}
},
"type": "DELIVERY"
}
]
},
"paymentOptions": {
"googleProvidedOptions": {
"facilitationSpecification": "{\"apiVersion\":2,\"apiVersionMinor\":0,\"merchantInfo\":{\"merchantName\":\"merchantName\"},\"allowedPaymentMethods\":[{\"type\":\"CARD\",\"parameters\":{\"allowedAuthMethods\":[\"PAN_ONLY\"],\"allowedCardNetworks\":[\"VISA\",\"MASTERCARD\"],\"billingAddressRequired\":true,\"cvcRequired\":false},\"tokenizationSpecification\":{\"type\":\"PAYMENT_GATEWAY\",\"parameters\":{\"gatewayMerchantId\":\"YOUR_MERCHANT_ID\",\"gateway\":\"cybersource\"}}}],\"transactionInfo\":{\"currencyCode\":\"AUD\",\"totalPriceStatus\":\"ESTIMATED\",\"totalPrice\":\"43.1\"}} "
}
},
"additionalPaymentOptions": [
{
"actionProvidedOptions": {
"paymentType": "ON_FULFILLMENT",
"displayName": "Pay when you get your food.",
"onFulfillmentPaymentData": {
"supportedPaymentOptions": []
}
}
}
]
}
}
}
]
}
}
}
请求失败
如果结账请求失败,CheckoutResponseMessage
需要包含 FoodErrorExtension
,其中包含用于描述发生的任何错误的 FoodOrderError
项列表。如果订单存在任何可恢复的错误(例如购物车中商品的价格发生变化),FoodErrorExtension
必须包含 correctedProposedOrder
。
失败响应示例
{
"expectUserResponse": false,
"finalResponse": {
"richResponse": {
"items": [
{
"structuredResponse": {
"error": {
"@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension",
"foodOrderErrors": [
{
"error": "CLOSED",
"description": "The restaurant is closed."
}
]
}
}
}
]
}
}
}
结账流程实现
实现结账时,应执行以下步骤。
验证服务
针对找到的第一个服务错误条件返回 FoodOrderError。这些错误无法恢复,因此应返回遇到的第一个错误。如需了解可恢复的错误,请参阅处理错误。
- 读取请求中的 FulfillmentOptionInfo 属性,以确定执行方式类型是
delivery
还是pickup
。 请根据需要返回以下错误类型:
错误类型 使用场景 INVALID 执行方式类型无效。 NOT_FOUND 找不到履单类型。 已解决 - 订单没有 OperationHours 时间范围。
- 该订单是“尽快”订单,目前没有任何“尽快”ServiceHours可用。
- 发生紧急关闭或服务
isDisabled
为 true。
UNAVAILABLE_SLOT 无法执行提前下单。 NO_CAPACITY 餐厅很忙,目前不接受订单。 OUT_OF_SERVICE_AREA 订单无法配送到用户的地址。如需查看示例,请参阅送货地址验证。 NO_COURIER_AVAILABLE 由于配送人员有限,订单无法配送。
验证购物车并为其定价
查询每个购物车。
lineItems
并使用您系统或商家系统中的当前数据进行验证。Feed 实体中的 MenuItemOffer.sku
值会作为 LineItem.offerId
包含在内。根据需要,为每个订单项创建 FoodOrderError。每个项最多只能创建一个错误。如有需要,返回以下错误类型:错误类型 使用场景 可恢复 INVALID 商品数据或任何选项数据无效。 否 NOT_FOUND 找不到相应内容或任何选项。 否 PRICE_CHANGED 商品或附加内容组合的价格发生了变化。此错误可视为可恢复的错误。 是 AVAILABILITY_CHANGED 系统无法提供为订单项或任何选项请求的金额。 是 REQUIREMENTS_NOT_MET 未达到最低或最高订单金额要求。您可以通过检查购物车价格是否低于 Fee. eligibleTransactionVolumeMin
或高于 Fee.eligibleTransactionVolumeMax
来确定这一点。请参阅最低订单金额验证中的示例。否 返回包含 LineItemType
REGULAR
且已通过验证的 LineItem 列表。购物车中所有商品订单项价格的总和即为购物车价格或SUBTOTAL
。
请参阅购物车商品验证中的示例。
计算服务费
- 根据
eligibleRegion
、validFrom
、validThrough
和priority
为服务查找正确的费用实体。 - 根据实体是使用
price
、percentageOfCart
还是pricePerMeter
属性定义的来计算费用金额。 - 将外卖或外带服务费作为 LineItem 返回,其 LineItemType 分别为
DELIVERY
或FEE
。将费用添加到购物车。otherItems
列表中。
使用促销优惠
- 根据将 Promotion.
coupon
值与 Deal.dealCode
进行匹配,找到 Deal 实体。 验证交易,并根据需要返回 FoodOrderError。 这些错误可以视为可恢复的错误。根据需要返回以下错误类型:
错误类型 使用场景 PROMO_NOT_RECOGNIZED 系统无法识别优惠券代码。 PROMO_EXPIRED 特惠已过期。 PROMO_ORDER_INELIGIBLE 订单不符合优惠券使用条件。 PROMO_NOT_APPLICABLE 任何其他原因。 根据特惠,使用购物车总金额或费用总金额应用特惠价格金额。
dealType
返回包含已应用促销的 Cart.
promotions
。将促销活动作为 LineItem 返回,其 LineItemType 为
DISCOUNT
。将折扣添加到价格为负的 Cart.otherItems
列表中。
返回响应
- 创建 ProposedOrder。
cart
如果验证期间未遇到错误,则响应购物车与请求购物车相同。 - 返回 ProposedOrder.
otherItems
列表,其中包含税费、费用、小费和折扣(如果适用)。如需详细了解如何配置小费项,请参阅小费。 - 添加购物车价格、费用、折扣、税费和小费,以包含 ProposedOrder。
totalPrice
- 返回 FoodOrderExtension.
availableFulfillmentOptions
及其相应的 FulfillmentOption。将预计取件时间或送货时间更新为预计时间。 - 如果之前的验证检查生成了 FoodOrderError:
- 在 FoodErrorExtension.
foodOrderErrors
中添加 StructuredResponse.error
和错误列表。 - 如果所有错误均可恢复,则在
correctedProposedOrder
字段中返回 ProposedOrder。 - 如果所有错误均可恢复,请在
paymentOptions
字段中返回 PaymentOptions。 - 如果有其他付款方式可用且所有错误均可恢复,可以选择添加
additionalPaymentOptions
。
- 在 FoodErrorExtension.
- 如果没有验证错误,请在 CheckoutResponse 对象中返回
proposedOrder
、paymentOptions
。(可选)如果有其他付款方式可用,请添加additionalPaymentOptions
。