Set up Checkout

The checkout process is invoked when a user creates a cart. The contents of the user's cart and details about the order are sent to your Ordering End-to-End web service. This information is validated by your web service, and then you can either proceed or make adjustments to their cart as needed.

The checkout handler for your web service must respond to POST requests. When a customer chooses to check out, Google sends the Ordering End-to-End web service a JSON request body in the form of a CheckoutRequestMessage, which contains the details of a customer's Cart. Your web service then responds back with a CheckoutResponseMessage. The following diagram illustrates the process.

The CheckoutResponseMessage returns the customer's unmodified cart or an
error.

Upon receiving a checkout request, your Ordering End-to-End web service must do the following:

  • Check the cart's validity based on the current item prices, availability, and provider service.
  • Calculate the total price (including any discounts, taxes, and delivery fees).
  • If successful, respond with an unmodified cart.
  • If unsuccessful, respond with an error message and a new proposed order.

Before you begin implementing checkout, we recommend reviewing the Fulfillment overview documentation.

Checkout Request Message

In order to validate the customer's cart, when a customer chooses to check out, Google sends a request to your web service with a JSON body in the form of a CheckoutRequestMessage. The customer order is not submitted until later in the Ordering End-to-End flow.

Data contained in a CheckoutRequestMessage includes the following:

  • Intent: The inputs[0].intent field of every checkout request body contains the actions.foodordering.intent.CHECKOUT string value.
  • Cart: The inputs[0].arguments[0].extension field of a checkout request contains a Cart object that represents the customer's cart.
  • Delivery or takeout: The Cart object's extension field contains a FoodCartExtension object that specifies properties for delivery or takeout:
    • For delivery orders, the FoodCartExtension object includes the delivery address.
    • For pickup or takeout orders, the FoodCartExtension object doesn't contain any location information.
  • Sandbox: The isInSandbox field of a checkout request contains a boolean value that indicates whether the transaction uses sandbox payments.

Checkout request example

Below is an example of a 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
}

Checkout Response Message

After receiving a request from the Ordering End-to-End service, your checkout web service must process it and respond with a CheckoutResponseMessage. The CheckoutResponseMessage needs to cover either a successful or unsuccessful request.

Successful request

If a check out request is successful, CheckoutResponseMessage needs to include ProposedOrder and PaymentOptions:

  • ProposedOrder

    • cart: A cart object identical to the cart provided in the CheckoutRequestMessage. If any of the contents of the cart need to be changed, the CheckoutResponseMessage should instead include a FoodErrorExtension with a corrected ProposedOrder.
    • otherItems: Items added by the provider, such as delivery charges, taxes, and other fees. May also contain gratuity added by the user.
    • totalPrice: The total price of the order.
    • extension: A FoodOrderExtension that defines fulfillment information for the order, such as delivery time.
  • PaymentOptions

    • Setting up payment processing is covered later in Set up Google Pay. You can use placeholder JSON in your CheckoutResponseMessage until you're ready to implement payment processing.
    • To add placeholder payment options in your CheckoutResponseMessage, refer to the example below, which uses an example payment gateway for PaymentOptions.

Successful response example

{
    "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": []
                                        }
                                    }
                                }
                            ]
                        }
                    }
                }
            ]
        }
    }
}

Unsuccessful request

If a checkout request is unsuccessful, CheckoutResponseMessage needs to include FoodErrorExtension, which contains a list of FoodOrderError items that describe any errors that occurred. If there are any recoverable errors to the order, like a price change of an item in the cart, the FoodErrorExtension must include the correctedProposedOrder.

Unsuccessful response example

{
  "expectUserResponse": false,
  "finalResponse": {
    "richResponse": {
      "items": [
        {
          "structuredResponse": {
            "error": {
              "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension",
              "foodOrderErrors": [
                {
                  "error": "CLOSED",
                  "description": "The restaurant is closed."
                }
              ]
            }
          }
        }
      ]
    }
  }
}

Checkout implementation

The following steps should be taken when implementing checkout.

Validate the service

Return a FoodOrderError for the first service error condition found. These errors are not recoverable so the first error encountered should be returned. See Handling errors for a description of recoverable errors.

  1. Read the FulfillmentOptionInfo property in the request to determine if the fulfillment type is for delivery or pickup.
  2. Return the following error types if needed:

    Error type Use case
    INVALID The fulfillment type is invalid.
    NOT_FOUND The fulfillment type is not found.
    CLOSED
    • There are no OperationHours windows for the order.
    • The order is an ASAP order and there are no ASAP ServiceHours available for the current time.
    • There is an emergency closure or the service isDisabled is true.
    Note that Special windows take precedence over regular windows. See examples in ordering window validation and remove service entities temporarily.
    UNAVAILABLE_SLOT The order ahead time cannot be fulfilled.
    NO_CAPACITY The restaurant is busy and not taking orders at the moment.
    OUT_OF_SERVICE_AREA The order cannot be delivered to the user's address. See Delivery address validation for an example.
    NO_COURIER_AVAILABLE The order cannot be delivered because of limited delivery personnel.

Validate and price the cart

  1. Look up each Cart.lineItems and validate with the current data in your system or in the merchant's system. The MenuItemOffer.sku value from the feed entity is included as the LineItem.offerId. Create a FoodOrderError for each line item if needed. Create a maximum of one error for each item. Return the following error types if needed:

    Error type Use case Recoverable
    INVALID The item data or any of the options data are invalid. No
    NOT_FOUND The item or any of the options are not found. No
    PRICE_CHANGED The price of an item or add-on combination has changed. This error can be treated as recoverable. Yes
    AVAILABILITY_CHANGED The amount requested for the line items or any of the options are not available. Yes
    REQUIREMENTS_NOT_MET The order minimum or order maximum is not met. This can be determined by checking if the cart price is below the Fee.eligibleTransactionVolumeMin or above the Fee.eligibleTransactionVolumeMax. See the example in minimum order value validation. No
  2. Return the validated list of lineItems with LineItemType REGULAR. The sum of all the cart line item prices is the cart price or SUBTOTAL.

See examples in cart items validation.

Calculate the service fees

  1. Find the correct Fee entity for the service based on the eligibleRegion, validFrom, validThrough and priority.
  2. Calculate the fee amount based on if the entity was defined with a price, percentageOfCart or pricePerMeter property.
  3. Return the delivery or takeout service fee as a LineItem with LineItemType DELIVERY or FEE respectively. Add the fee to the Cart.otherItems list.

Apply promotions

  1. Find the Deal entity based on matching the Promotion.coupon value with the Deal.dealCode.
  2. Validate the deal and return a FoodOrderError if needed. These errors can be treated as recoverable. Return the following error types if needed:

    Error type Use case
    PROMO_NOT_RECOGNIZED The coupon code was not recognized.
    PROMO_EXPIRED The deal validity is expired.
    PROMO_ORDER_INELIGIBLE The order is not eligible for the coupon.
    PROMO_NOT_APPLICABLE Any other reason.
  3. Calculate the deal price amount based on Deal.discount or Deal.discountPercentage.

  4. Apply deal price amount using the cart total or fee total depending on the Deal.dealType.

  5. Return the Cart.promotions with applied promotion.

  6. Return the promotion as a LineItem with LineItemType DISCOUNT. Add the discount to the Cart.otherItems list with a negative price.

Return the response

  1. Create the ProposedOrder.cart, the response cart is the same as the request cart if no errors are encountered during validation.
  2. Return the ProposedOrder.otherItems list including the tax, fees, gratuity and discount if applied. See Gratuity for more details on how to configure the gratuity item.
  3. Include the ProposedOrder.totalPrice by adding the cart price, fees, discount, taxes and gratuity.
  4. Return the FoodOrderExtension.availableFulfillmentOptions with the respective FulfillmentOption. Update the estimated pickup or delivery time to the expected time.
  5. If there are FoodOrderErrors generated from the previous validation checks:
    • Include StructuredResponse.error and the list of errors in FoodErrorExtension.foodOrderErrors.
    • Return the ProposedOrder in the correctedProposedOrder field if all errors are recoverable.
    • Return the PaymentOptions in the paymentOptions field if all errors are recoverable.
    • Optionally, include the additionalPaymentOptions if there are other payment options available and all errors are recoverable.
  6. If there are no validation errors, return the proposedOrder, paymentOptions in the CheckoutResponse object. Optionally, include the additionalPaymentOptions if there are other payment options available.