当用户创建购物车时,系统将调用结账流程。系统会将用户购物车中的内容和订单详情发送到您的订购端到端网络服务。此信息由您的网络服务进行验证,然后您可以根据需要继续或调整其购物车。
网络服务的结账处理程序必须响应 POST 请求。当客户选择结账时,Google 会向订购端到端网络服务发送 CheckoutRequestMessage
形式的 JSON 请求正文,其中包含客户 Cart
的详细信息。然后,您的网络服务会返回 CheckoutResponseMessage
进行响应。下图说明了该过程。
收到结账请求后,您的订购端到端网络服务必须执行以下操作:
- 根据当前商品价格、库存状况和提供商服务检查购物车的有效性。
- 计算总价(包括所有折扣、税费和运费)。
- 如果成功,则回应一个未经修改的购物车。
- 如果发送失败,请在回复中提供错误消息和新的提议订单。
在开始实现结账流程之前,我们建议您查看 Fulfillment 概览文档。
结账请求消息
为验证客户的购物车,当客户选择结账时,Google 会向您的网络服务发送采用 CheckoutRequestMessage
形式的 JSON 正文的请求。客户订单要等到稍后在端到端订购流程中提交。
CheckoutRequestMessage
中包含的数据包括以下内容:
- intent:每个结账请求正文的
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
}
结账响应消息
收到来自订购端到端服务的请求后,您的结账网络服务必须处理该请求,并返回 CheckoutResponseMessage
进行响应。CheckoutResponseMessage
需要涵盖成功或失败的请求。
请求成功
如果结账请求成功,CheckoutResponseMessage
需要包含 ProposedOrder
和 PaymentOptions
:
ProposedOrder
cart
:一个cart
对象,与CheckoutRequestMessage
中提供的购物车完全相同。如果需要更改购物车的任何内容,CheckoutResponseMessage
应改为包含FoodErrorExtension
,其中ProposedOrder
已更正。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 未达到最低订单金额或订单金额上限。可以通过查看购物车价格是否低于费用 eligibleTransactionVolumeMin
或高于费用eligibleTransactionVolumeMax
来确定价格。请参阅最低订单价值验证中的示例。否 返回经验证的订单项列表,其 LineItemType 为
REGULAR
。购物车所有订单项价格的总和为购物车价格或SUBTOTAL
。
请参阅购物车商品验证中的示例。
计算服务费
- 根据
eligibleRegion
、validFrom
、validThrough
和priority
查找服务的正确费用实体。 - 根据实体是通过
price
、percentageOfCart
还是pricePerMeter
属性计算费用金额。 - 将配送或外卖服务费作为 LineItem 返回,并且相应费用分别为 LineItemType
DELIVERY
或FEE
。将费用添加到 Cart.otherItems
列表中。
使用促销优惠
- 根据 Promotion.
coupon
值与 Deal 的匹配情况,找到 Deal 实体。dealCode
。 验证交易,并根据需要返回 FoodOrderError。 这些错误可以被视为可恢复的错误。如果需要,请返回以下错误类型:
错误类型 使用场景 PROMO_NOT_RECOGNIZED 无法识别优惠券代码。 PROMO_EXPIRED 交易有效期已过。 PROMO_ORDER_INELIGIBLE 该订单不符合使用优惠券的条件。 PROMO_NOT_APPLICABLE 任何其他原因。 根据交易,使用购物车总金额或总费用应用交易价格金额。
dealType
退回包含促销优惠的购物车
promotions
。将促销活动作为 LineItemType
DISCOUNT
的 LineItem 返回。将折扣添加到 Cart.otherItems
列表中,并将价格设为负数。
返回响应
- 创建 ProposedOrder.
cart
,如果验证期间未遇到错误,则响应购物车与请求购物车相同。 - 返回 ProposedOrder.
otherItems
列表,其中包含税费、费用、小费和折扣(如果适用)。如需详细了解如何配置免费项目,请参阅 Gratuity。 - 添加 ProposedOrder.
totalPrice
,方法是添加购物车价格、费用、折扣、税费和小费。 - 返回带有相应 FulfillmentOption 的 FoodOrderExtension.
availableFulfillmentOptions
。将预计取货或送货时间更新为预计时间。 - 如果之前的验证检查生成了 FoodOrderErrors,请执行以下操作:
- 添加 StructuredResponse.
error
和 FoodErrorExtension.foodOrderErrors
中的错误列表。 - 如果所有错误都可以恢复,请在
correctedProposedOrder
字段中返回 ProposedOrder。 - 如果所有错误都可以恢复,请在
paymentOptions
字段中返回 PaymentOptions。 - (可选)如果还有其他可用的付款方式并且所有错误都可以恢复,则添加
additionalPaymentOptions
。
- 添加 StructuredResponse.
- 如果没有验证错误,请在 CheckoutResponse 对象中返回
proposedOrder
、paymentOptions
。(可选)如果有其他可用的付款方式,请添加additionalPaymentOptions
。