Tip Extension¶
Overview¶
Gratuity for couriers and counter staff is part of the checkout itself,
not an afterthought. The Food Tip Extension (dev.ucp.food.tip) adds a tip
field to food checkout sessions for courier and counter-staff gratuity,
separating what the Business suggests from what the buyer chooses.
Key features:
- Business-suggested tip presets (
options[]) in eitherpercentageorabsoluteunits - Buyer choice expressed via a preset selection (
selected_option_id) or a custom amount (custom_amount) in minor currency units - Mutual exclusion (
oneOf) between preset selection, custom amount, and an unset state - Authoritative resolution of the selected tip into checkout
totals[]as atype: "tip"entry
tip contains:
options[]โ Business-authored tip suggestions, each with anid,unit(percentageorabsolute), andamountselected_option_idโ Platform-authoredidof a selected preset option fromoptions[](available on Update Checkout)custom_amountโ Platform-authored custom tip amount in ISO 4217 minor units (available on Create and Update Checkout;0= no tip)
Mental model:
options[]๐ก Business suggestionsoptions[0]๐15%(unit: "percentage",amount: 15)options[1]๐โ18%(unit: "percentage",amount: 18)options[2]๐20%(unit: "percentage",amount: 20)options[3]๐$5.00(unit: "absolute",amount: 500)
selected_option_id=options[1].id(orcustom_amountfor custom input)totals[]๐งพ{ "type": "tip", "display_text": "Courier Tip (18%)", "amount": 900 }
Discovery¶
Businesses advertise food tip support in their profile:
{
"ucp": {
"version": "draft",
"capabilities": {
"dev.ucp.food.tip": [
{
"version": "draft",
"extends": "dev.ucp.food.checkout",
"spec": "https://ucp.dev/draft/specification/food/extensions/tip",
"schema": "https://ucp.dev/draft/schemas/food/tip.json"
}
]
}
}
}
Tip Selection & Resolution¶
Across a checkout session, gratuity flows through three stages: the Business
advertises preset Tip Options in tip.options[], the Platform
submits the buyer's choice on Tip via selected_option_id or
custom_amount, and the Business resolves the monetary amount into totals[].
Suggestions (tip.options[])¶
Business-authored. Each Tip Option has an id, a unit
(percentage or absolute), and an amount. With unit: "percentage",
amount is the percent (e.g., 18); with unit: "absolute", it is in
ISO 4217 minor units (e.g., 500 for $5.00). Tip options carry no label: the
Platform renders one from unit and amount (e.g., "18%", "$5").
A Platform renders the suggestions as quick-select chips:
Choice (tip.selected_option_id or tip.custom_amount)¶
Platform-authored. selected_option_id is omitted on Create Checkout
(create: omit) and available on Update Checkout (update: optional) once the
Business has returned tip.options[]; custom_amount MAY be sent on
either create or update. The schema's oneOf admits exactly three states:
| State | selected_option_id |
custom_amount |
Meaning |
|---|---|---|---|
| Preset | โ | โ | Buyer picked a suggested option (on update). |
| Custom | โ | โ | Buyer entered an amount in minor units (0 = no tip). |
| Unset | โ | โ | No choice yet; adds nothing to totals[] until the Platform submits an explicit selection via selected_option_id or custom_amount. |
| โ Invalid | โ | โ | Rejected by schema validation. |
Resolution (totals[])¶
Business-authored. The Business resolves the choice into money โ percentage
tip.options[] are computed against the pre-discount subtotal entry in
totals[] โ and MUST report any non-zero selected tip as a type: "tip"
entry in totals[]. Because tip is a food-defined total type and not in the
common well-known set, Businesses MUST include display_text on it. tip
holds the choice; totals[] holds the amount.
Fulfillment Changes & Checkout Handoff¶
- Fulfillment switch: When a fulfillment switch (e.g.,
deliveryโpickup) replacestip.options[]and invalidates a priorselected_option_id, the Business MUST clearselected_option_id(returningtiptoUnsetstate) and SHOULD surface anoticewarning. - Checkout handoff (
continue_url): When a Platform hands off a checkout session to the Business UI viacontinue_urlbefore a tip has been selected, it SHOULD label the displayed total as pre-tip so the buyer expects gratuity selection on the Business UI.
Guidelines¶
Platform¶
- MUST NOT send both
tip.selected_option_idandtip.custom_amount, and MUST render the tip from thetype: "tip"entry intotals[]. - Because the tip can change up to the moment of completion, MUST present
the final
totals[]โ includingtype: "tip"โ for buyer confirmation before submitting payment. - When handing off via
continue_urlbefore a tip has been selected, SHOULD label the displayed total as pre-tip so the buyer expects gratuity selection on the Business UI.
Business¶
- MUST include
display_textontype: "tip"totals and report any non-zero tip intotals[]. - MUST compute percentage
tip.options[]against the pre-discountsubtotalentry intotals[]and recompute percentage tips when the basket or fulfillment changes. - When a fulfillment switch replaces
tip.options[]and invalidates a priorselected_option_id, MUST clearselected_option_id(returningtiptoUnsetstate) and SHOULD surface anoticewarning. - MAY require an explicit choice before completion; where it does, a
custom_amountof0MUST satisfy that requirement.
Schema¶
Entities¶
Tip¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| options | Array[Tip Option] | Optional | Available tip options. |
| selected_option_id | string | Optional | The ID of the selected tip option. |
| custom_amount | Amount | Optional | Custom tip amount in ISO 4217 minor units. |
Tip Option¶
| Name | Type | Requirement | Description |
|---|---|---|---|
| id | string | Required | Identifier for the tip option. |
| unit | string | Required | The unit of the tip amount. Enum: percentage, absolute |
| amount | integer | Required | The amount of the tip. When unit is absolute, this is in the currency's minor unit as defined by ISO 4217. |
Example¶