Promotions provide a way for you and Google to incentivize customers to try your food ordering service with discount pricing. Google supports integrating your Ordering End-to-End Actions with your promotions management system.
The following types of discounts are supported:
- Google-sponsored promotion codes: Promotion codes that are automatically pre-filled by Google or entered by users.
- Third-party-sponsored promotion codes: Promotion codes for users to enter, supplied by your food ordering service.
- Third-party-sponsored automatic discounts: Discounts that your food ordering service automatically applies without a promotion code.
Regardless of the type of discount, Google makes a checkout call to your food ordering fulfillment to verify and apply the discount.
As a developer of a food ordering service, you need to make some changes to your implementation to compute discounts for valid promotion codes or send errors for invalid promotion codes, manage promotion code redemption limitations, and track accounting data for reimbursement.
How to process promotions
To implement a fulfillment that supports promotions, do the following:
- Set up promotions integration. (Skip this step if you aren't using Google-sponsored promotion codes.)
- Implement checkout with promotions.
- Implement submit order with promotions.
Set up promotions integration
This section describes how to set up promotions integration if you plan to use Google-sponsored promotion codes. If you only want to support promotion codes or discounts that a third party sponsors, you can specify your own setup and skip this section.
Google specifies the type of promotion to sponsor, and contacts you to set up the integration. We provide the following details:
- The discount amount.
- The minimum cart value.
- The start and end date for using the promotion codes.
- The maximum dollar amount budgeted for the promotion campaign.
- The number of times the promotion codes can be used.
Examples of promotion codes:
FopaNewUser
: 10% (fixed percent) with maximum of $50 off.FopaMoreThan50
: $10 (fixed amount off).
Should Google decide to halt the application of the code, you will be contacted.
Set up disbursements
Reach out to your Google EAP consultant to set up the disbursement process. Google only reimburses transactions involving Google-sponsored promotion codes if the final order status is one of the following:
CONFIRMED
IN_TRANSIT
READY_FOR_PICKUP
IN_PREPARATION
FULFILLED
Implement checkout with promotions
This section describes implementing checkout processing when you support
promotion codes (Google-sponsored or third-party-sponsored). For
third-party-sponsored automatic discounts, you only need to return the discount
line item in the CheckoutResponseMessage
(no promotion code checking is
needed).
During food order fulfillment, Google sends a single promotion code in the
CheckoutRequestMessage
to your fulfillment. Users may change their cart or
promotion code on repeated checkout requests.
To check if this is the first time the user has applied a promotion code, do the following:
- Google-sponsored promotion codes: Google checks if a returning user is trying to use the same promotion code again; you don't need to do anything.
- Third-party-sponsored promotion codes or automatic discounts: If you have
not implemented account linking and user opt-in, you will be unable to check
the user's details during checkout request processing. Instead, check for this
during
SubmitOrderRequestMessage
processing, using theContact
details (such as the user's email address) from theFoodCartExtension
object.
Identify errors or compute discounts with your fulfillment based on the latest checkout request. When doing so, ensure that your system does not keep stale state information.
Check promotion code validity
Your fulfillment should check the validity or eligibility of a given promotion
code against stipulated terms, such as the expiration date, maximum usage, and
maximum discount. Then, respond appropriately in the CheckoutResponseMessage
with the computed discount, or with foodOrderErrors
if the promotion code
cannot be applied. If you detect errors with the promotion code, follow the
process described in Handle errors with promotions.
The following snippet shows an example foodOrderErrors
for a promotion code.
Make sure that the correctedProposedOrder
does not include the promotions
node.
"foodOrderErrors": [
{
"error": "PROMO_NOT_APPLICABLE",
// Copy promotions.coupon string from CheckoutRequest as the ID
"id": "GoogleNewUser",
"description": "Promotion could not be applied"
}
],
"correctedProposedOrder": {// required ...},
"paymentOptions": {// required ...}
Compute discounts
If the promotion code is valid, your fulfillment should compute the discount
dollar value and send back a CheckoutResponseMessage
with the computed
discount value in the otherItems
array. The total order price must not be
negative. If the amount of the discount exceeds the cart amount, send back the
maximum dollar amount to render the total order price to $0.
The following snippet shows an example CheckoutResponseMessage
section for
the promotional discount:
"proposedOrder": {
"otherItems": [
. . .
{
"name": "Discount",
// copy promotions.coupon field from CheckoutRequest as the id
"id": "GoogleNewUser",
"price": {
"type": "ESTIMATE",
"amount": {
"currencyCode": "USD",
"units": "-3",
"nanos": -500000000
}
},
"type": "DISCOUNT",
}
]
}
Release unused promotions
Not every checkout request leads to a submit order request. If your fulfillment puts a hold on a promotion at the checkout call time, make sure you have a mechanism in place to release the hold if the promotion is not claimed via submit order after a certain period of time. This ensures that your food ordering service maintains the correct campaign quota.
Handle errors with promotions
If your fulfillment determines that the promotion code from a
CheckoutRequestMessage
is not valid (for example, it is expired, invalid,
or not recognized), send a CheckoutResponseMessage
with a foodOrderError
that contains the applicable error code and reason text, along with
correctedProposedOrder
and paymentOptions
objects.
If your fulfillment finds multiple promotion code errors from the same request, send back the unrecoverable errors before sending back recoverable errors. Prioritize your checks as follows (from high to low priority):
PROMO_NOT_RECOGNIZED
PROMO_EXPIRED
PROMO_USER_INELIGIBLE
PROMO_ORDER_INELIGIBLE
PROMO_NOT_APPLICABLE
Examples
Here's an example of a checkout request with a promotion code:
{ "accessToken": "test_access_token", "lastSeen": "2018-06-22T19:25:39Z" }, "conversation": { "conversationId": "XYZ" }, "inputs": [ { "intent": "actions.foodordering.intent.CHECKOUT", "arguments": [ { "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.Cart", "merchant": { "id": "https://www.exampleprovider.com/merchant/id1", "name": "Falafel Bite" }, "lineItems": [ { "name": "Falafel Tray", "type": "REGULAR", "id": "sample_item_offer_id_1", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 950000000 } }, "offerId": "https://www.exampleprovider.com/menu/item/offer/id1", "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } } ], "promotions": [ { "coupon": "FOPAACTIVECODE" } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension", "fulfillmentPreference": { "fulfillmentInfo": { "pickup": { "pickupTimeIso8601": "P0M" } } } } } } ] } ], "directActionOnly": true, "isInSandbox": true }
Here's the corresponding checkout response from the fulfillment if the promotion code is valid:
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "checkoutResponse": { "proposedOrder": { "otherItems": [ { "name": "Delivery Fees", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "3", "nanos": 500000000 } }, "type": "DELIVERY" }, { "name": "Tax", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "1", "nanos": 370000000 } }, "type": "TAX" }, { "name": "Promotion", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "-5", "nanos": 0 } }, "id": "FOPAACTIVECODE", "type": "DISCOUNT" } ], "cart": { "merchant": { "id": "https://www.exampleprovider.com/merchant/id1", "name": "Falafel Bite" }, "lineItems": [ { "name": "Falafel Tray", "type": "REGULAR", "id": "2529103", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 950000000 } }, "offerId": "https://www.exampleprovider.com/menu/item/offer/id1", "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } } ], "promotions": [ { "coupon": "FOPAACTIVECODE" } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension", "fulfillmentPreference": { "fulfillmentInfo": { "pickup": { "pickupTimeIso8601": "P0M" } } } } }, "totalPrice": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 820000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension", "availableFulfillmentOptions": [ { "fulfillmentInfo": { "pickup": { "pickupTimeIso8601": "P0M" } }, "expiresAt": "2018-06-22T19:30:52.596Z" } ] } }, "orderOptions": {}, "paymentOptions": { "googleProvidedOptions": { "tokenizationParameters": { "tokenizationType": "PAYMENT_GATEWAY", "parameters": { "gateway": "stripe", "stripe:publishableKey": "example_stripe_client_key", "stripe:version": "2017-04-06" } }, "supportedCardNetworks": [ "AMEX", "DISCOVER", "MASTERCARD", "VISA", "JCB" ], "prepaidCardDisallowed": true } } } } } ], "suggestions": [] } } }
Here's an example of a checkout response if the promotion code is invalid:
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "error": { "foodOrderErrors": [ { "error": "PROMO_NOT_RECOGNIZED", "id": "SOMEPROMO", "description": "Coupon not found" } ], "correctedProposedOrder": { "cart": { "merchant": { "id": "https://www.exampleprovider.com/merchant/id1", "name": "Falafel Bite" }, "lineItems": [ { "id": "sample_item_offer_id_4", "name": "Prawns Biryani", "type": "REGULAR", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "18", "nanos": 750000000 } }, "offerId": "https://www.exampleprovider.com/menu/item/offer/id4", "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } } ], "extension": { "fulfillmentPreference": { "fulfillmentInfo": { "pickup": { "pickupTimeIso8601": "P0M" } } }, "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension" }, "promotions": [] }, "otherItems": [ { "name": "Tax", "type": "TAX", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "1", "nanos": 650000000 } } } ], "termsOfServiceUrl": "https://exampleprovider.com/terms", "totalPrice": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "20", "nanos": 400000000 } }, "extension": { "availableFulfillmentOptions": [ { "fulfillmentInfo": { "pickup": { "pickupTimeIso8601": "PT0M" } } } ], "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension" } }, "paymentOptions": { "googleProvidedOptions": { "prepaidCardDisallowed": false, "billingAddressRequired": true, "tokenizationParameters": { "tokenizationType": "PAYMENT_GATEWAY", "parameters": { "gateway": "braintree", "braintree:apiVersion": "v1", "braintree:sdkVersion": "1.4.0", "braintree:merchantId": "example_braintree_merchant_ID", "braintree:clientKey": "example_braintree_client_key", "braintree:authorizationFingerprint": "example_braintree_fingerprint" } } } }, "@type": "type.googleapis.com/google.actions.v2.orders.FoodErrorExtension" } } } ] } } }
Implement submit order with promotions
In your submit order fulfillment, check for whether this is the first time the
user is applying a promotion code. During SubmitOrderRequestMessage
processing, you can check for this using the Contact
details (such as the
user's email address) from the FoodCartExtension
object.
You should also re-check the applicability of the promotion code:
- If the code is applicable: Confirm the order and mark the coupon redeemed.
- If the code is no longer applicable: Reject the order with the
PROMO_NOT_APPLICABLE
error. You can provide a specific rejection reason using the same mechanism as forFoodOrderUpdateExtension
.
Examples
Here's an example of a submit order request with promotions:
{ "conversation": { "conversationId": "example_conversation_ID" }, "inputs": [ { "intent": "actions.intent.TRANSACTION_DECISION", "arguments": [ { "transactionDecisionValue": { "order": { "finalOrder": { "cart": { "merchant": { "id": "https://www.exampleprovider.com/merchant/id1", "name": "Falafel Bite" }, "lineItems": [ { "name": "Falafel Tray", "type": "REGULAR", "id": "sample_item_offer_id_1", "quantity": 1, "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 950000000 } }, "offerId": "https://www.exampleprovider.com/menu/item/addon/offer/id1", "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodItemExtension" } } ], "promotions": [ { "coupon": "FOPAACTIVECODE" } ], "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodCartExtension", "fulfillmentPreference": { "fulfillmentInfo": { "pickup": { "pickupTimeIso8601": "P0M" } } }, "contact": { "displayName": "Food Ordering", "email": "example.provider@gmail.com", "phoneNumber": "+19993334444", "firstName": "Food", "lastName": "Ordering" } } }, "otherItems": [ { "name": "Delivery Fees", "type": "DELIVERY", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "3", "nanos": 500000000 } } }, { "name": "Tax", "type": "TAX", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "1", "nanos": 370000000 } } }, { "name": "Promotion", "type": "DISCOUNT", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "-5" } }, "id": "FOPAACTIVECODE" }, { "name": "Subtotal", "type": "SUBTOTAL", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 950000000 } } }, { "name": "Tip", "type": "GRATUITY", "price": { "type": "ESTIMATE", "amount": { "currencyCode": "USD" } } } ], "totalPrice": { "type": "ESTIMATE", "amount": { "currencyCode": "USD", "units": "9", "nanos": 820000000 } }, "extension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderExtension" } }, "googleOrderId": "example_google_order_ID", "orderDate": "2018-06-22T19:30:59.502Z", "paymentInfo": { "displayName": "example_display_name", "googleProvidedPaymentInstrument": { "instrumentToken": "example_instrument_token" }, "paymentType": "PAYMENT_CARD" }, "locale": "en" } } } ] } ], "directActionOnly": true, "isInSandbox": true }
Here's an example of the corresponding submit order response from a fulfillment if the promotion code is valid:
{ "expectUserResponse": false, "finalResponse": { "richResponse": { "items": [ { "structuredResponse": { "orderUpdate": { "actionOrderId": "example_action_order_ID", "orderState": { "state": "CREATED", "label": "Order is created with partner." }, "updateTime": "2018-06-22T19:31:01.556Z", "orderManagementActions": [ { "type": "CALL_RESTAURANT", "button": { "title": "Call Us", "openUrlAction": { "url": "tel:+1-111-111-1111" } } }, { "type": "EMAIL", "button": { "title": "Email Us", "openUrlAction": { "url": "mailto:example.provider@gmail.com" } } }, { "type": "CUSTOMER_SERVICE", "button": { "title": "Customer Service", "openUrlAction": { "url": "http://www.google.com" } } } ] } } } ], "suggestions": [] } } }
Here's an example of the submit order response if the promotion code is invalid:
"orderUpdate": { "actionOrderId": "sample_action_order_id", "orderState": { "state": "REJECTED", "label": "Order rejected." }, "updateTime": "2017-05-10T02:30:00.000Z", "rejectionInfo": { "type": "PROMO_NOT_APPLICABLE", "reason": "Sorry, there's something wrong. Try another code?" }, "orderManagementActions": [ { "type": "CUSTOMER_SERVICE", "button": { "title": "Contact customer service", "openUrlAction": { "url": "mailto:support@example.com" } } }, { "type": "EMAIL", "button": { "title": "Email restaurant", "openUrlAction": { "url": "mailto:example.provider@example.com" } } }, { "type": "CALL_RESTAURANT", "button": { "title": "Call restaurant", "openUrlAction": { "url": "tel:+19993334444" } } } ], "infoExtension": { "@type": "type.googleapis.com/google.actions.v2.orders.FoodOrderUpdateExtension", "foodOrderErrors": [ { "error": "PROMO_USER_INELIGIBLE", "description": "Sorry, you can only use this promotion once." } ] } }