# C-Store Features Convenience stores and fuel stations operate under regulatory and operational constraints that general-purpose restaurant or retail integrations rarely encounter. Payment method restrictions, time-limited menu availability, and per-transaction quantity caps are standard in this industry. Your online ordering integration must handle these constraints to avoid cart validation failures, payment rejections, and poor customer experiences. This guide covers three c-store-specific features exposed through the MenuItem schema: **tender restrictions**, **item time windows**, and **quantity limits**. Each section explains why the restriction exists, which API fields control it, and how to handle it in your integration with both a happy path and an error example. Age verification is a fourth c-store feature but is covered separately in the [Cart Lifecycle guide](/online-ordering/guides/04-cart-lifecycle#age-restricted-items). That section documents how `age_verification_required` and `minimum_age` work. This guide does not duplicate that content. All fields discussed below are defined in the `MenuItem` schema in the API specification. Example data uses items from the full menu example (`menu-full.json`). ## Tender Restrictions Some convenience store items are restricted to specific payment methods by regulation or store policy. SNAP/EBT regulations exclude non-food items such as tobacco and alcohol. Some jurisdictions further restrict certain product categories to cash or debit transactions only. The `tender_restriction_level` and `allowed_tenders` fields on a menu item enforce these rules by declaring which payment methods can be used to purchase the item. > **Planned feature:** The `tender_restriction_level` and `allowed_tenders` fields are marked `x-status: planned` in the MenuItem schema. The schema is defined and fields are present in menu responses, but server-side enforcement is not yet active. Plan your integration to support these fields now so you are ready when enforcement goes live. Items currently return these fields with populated values -- your client should respect them even before server enforcement is enabled. ### Fields | Field | Type | Description | | --- | --- | --- | | `tender_restriction_level` | `string` or `null` | `NONE` (all tenders accepted) or `RESTRICTED` (check `allowed_tenders`). Null is treated as `NONE`. | | `allowed_tenders` | `array` or `null` | List of accepted payment method strings when restricted. Null means all payment methods are accepted. Possible values: `CREDIT_CARD`, `DEBIT_CARD`, `CASH`, `GIFT_CARD`, `LOYALTY_POINTS`, `DIGITAL_WALLET`, `EBT`. | **CASH and EBT context:** - **CASH** means pay-at-counter. The order is placed online but payment is collected in-store. CASH is only meaningful for `PICKUP` and `DINE_IN` handoff modes. When the selected handoff mode is `DELIVERY`, filter out `CASH` from the available tenders since cash collection at the door is not supported. - **EBT** means SNAP/EBT card. Item eligibility applies per SNAP regulations -- only SNAP-eligible items (generally food items, excluding hot prepared foods and tobacco) can be paid with EBT. Check `allowed_tenders` on each menu item to determine EBT eligibility. ### Example: Restricted item in the menu The following menu item (a tobacco product) has tender restrictions -- only credit card, debit card, cash, and digital wallet payments are accepted. EBT, gift card, and loyalty points are excluded: ```json { "id": "d0000003-0000-0000-0000-000000000001", "name": "Premium Cigars", "base_price": { "amount": 1499, "currency": "USD" }, "available": true, "age_verification_required": true, "minimum_age": 21, "max_allowed_quantity": 2, "tender_restriction_level": "RESTRICTED", "allowed_tenders": ["CREDIT_CARD", "DEBIT_CARD", "CASH", "DIGITAL_WALLET"] } ``` When displaying payment options for a cart containing this item, your integration should only show the four allowed tenders. If the customer previously selected EBT or a gift card, those options should be disabled or hidden with an explanation. ### Integration guidance When building a cart that may contain restricted items, follow this logic at the payment step: 1. Iterate through all items in the cart. 2. For each item where `tender_restriction_level` is `RESTRICTED`, collect the `allowed_tenders` array. 3. Compute the **intersection** of all `allowed_tenders` arrays. This gives you the set of payment methods valid for the entire cart. 4. Display only the intersected set of payment methods to the customer. If the intersection is empty (two items with incompatible restrictions), alert the customer that the items cannot be purchased together in a single transaction. Items where `tender_restriction_level` is `NONE` or `null` accept all tenders and do not constrain the intersection. ### Example: Tender restriction error Attempting to pay for a cart containing a tender-restricted item with a disallowed payment method returns a 422 error: ```json { "error": { "code": "INVALID_REQUEST_ERROR", "message": "Payment method not allowed for restricted items in cart.", "detail": "Item 'Premium Cigars' does not accept EBT. Allowed tenders: CREDIT_CARD, DEBIT_CARD, CASH, DIGITAL_WALLET.", "request_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890" } } ``` ### Split Payment with Restricted Items When a cart contains both restricted and unrestricted items, the API validates tender restrictions at the cart level. The intersection of all items' `allowed_tenders` arrays determines which payment methods can be used for the entire cart. This is the most restrictive approach but the simplest to implement. **Example:** A cart contains Premium Cigars (tobacco, restricted to `[CREDIT_CARD, DEBIT_CARD, CASH, DIGITAL_WALLET]`) and a Bottled Water (unrestricted, all tenders accepted). The intersection is `[CREDIT_CARD, DEBIT_CARD, CASH, DIGITAL_WALLET]`. The customer cannot use `EBT` or `GIFT_CARD` for any part of the order, even though the Bottled Water alone would accept those tenders. Item-level tender allocation (where different items in the same cart could be paid with different tenders) is not currently supported. If this is needed for your use case, consider splitting the order into separate carts per tender restriction group. > **Tolerant Reader:** New values may be added to the `allowed_tenders` enum. Your integration should treat unknown enum values as unrestricted rather than failing. Log a warning for observability and continue processing the cart. ## Non-Discountable Items Some menu items have `non_discountable: true` on the MenuItem schema. The server excludes these items from all discount calculations -- promo codes, automatic discounts, and loyalty rewards are not applied to non-discountable items. When displaying a non-discountable item in your UI, show a label such as "Not eligible for discounts" so customers understand why a promotion did not reduce the price of that item. | Field | Type | Description | | --- | --- | --- | | `non_discountable` | `boolean` | `true` if the item is excluded from all discounts. Defaults to `false`. | > **Note:** This field is informational for now. It becomes actionable when promo codes and loyalty reward discounts ship in a future release. ## Item Time Windows Many convenience stores serve prepared food and beverages with limited availability windows. Coffee may only be brewed during morning hours. Hot food service may end in the evening. These windows reflect operational reality -- the store cannot prepare items outside of scheduled service times. The `daily_sales_start_time` and `daily_sales_end_time` fields define when each item can be ordered. ### Fields | Field | Type | Description | | --- | --- | --- | | `daily_sales_start_time` | `string` or `null` | Start of the availability window in `HH:MM` format (24-hour, location local time). Null means no start restriction -- the item is available from store open. | | `daily_sales_end_time` | `string` or `null` | End of the availability window in `HH:MM` format (24-hour, location local time). Null means no end restriction -- the item is available until store close. | ### Example: Time-windowed item in the menu The following beverage item is available from 5:00 AM to 2:00 PM local time: ```json { "id": "d0000002-0000-0000-0000-000000000002", "name": "Fresh Brewed Coffee", "description": "House blend, available in multiple sizes.", "base_price": { "amount": 179, "currency": "USD" }, "available": true, "daily_sales_start_time": "05:00", "daily_sales_end_time": "14:00", "max_allowed_quantity": null, "tender_restriction_level": "RESTRICTED", "allowed_tenders": ["CREDIT_CARD", "DEBIT_CARD", "CASH", "GIFT_CARD", "LOYALTY_POINTS", "DIGITAL_WALLET"] } ``` When displaying the menu, compare the current time against the item's time window. The current time must be in the **store's local timezone**, which is available from the Location object's `timezone` field (returned by `GET /locations` and `GET /locations/{id}`). Items outside their window should be hidden from the menu or displayed as unavailable with messaging such as "Available 5:00 AM - 2:00 PM." ### Integration guidance Apply time window filtering in two places: 1. **Menu display:** Before rendering the menu, filter or grey out items whose current local time falls outside the `daily_sales_start_time` to `daily_sales_end_time` window. This prevents the customer from browsing items they cannot order. 2. **Cart validation:** The server enforces time windows when items are added to the cart. If a customer somehow adds an out-of-window item (e.g., your menu cache is stale), the server rejects the request. Handle this error gracefully by refreshing the menu and informing the customer. When both `daily_sales_start_time` and `daily_sales_end_time` are `null`, the item has no time restriction and is available whenever the store is open. ### Example: Time window error Attempting to add an item to the cart outside its availability window returns a 422 error: ```json { "error": { "code": "INVALID_REQUEST_ERROR", "message": "Item is not currently available.", "detail": "Fresh Brewed Coffee is available from 05:00 to 14:00 (store local time). Current store time: 15:30.", "request_id": "b2c3d4e5-f6a7-8901-bcde-f23456789012" } } ``` ## Quantity Limits Convenience stores may limit purchase quantities for regulated or high-demand items. Alcohol and tobacco products commonly have per-transaction limits set by state or local regulation. Promotional items may be limited to prevent hoarding. The `max_allowed_quantity` field enforces these limits at the cart level, capping how many units of an item a single transaction can contain. ### Fields | Field | Type | Description | | --- | --- | --- | | `max_allowed_quantity` | `integer` or `null` | Maximum units of this item allowed per transaction. Null means no limit. | ### Example: Quantity-limited item in the menu The following tobacco product is limited to 2 units per transaction: ```json { "id": "d0000003-0000-0000-0000-000000000001", "name": "Premium Cigars", "base_price": { "amount": 1499, "currency": "USD" }, "available": true, "age_verification_required": true, "minimum_age": 21, "max_allowed_quantity": 2 } ``` Your integration should enforce this limit in the UI. When the customer adjusts the quantity, disable the increment control at the maximum and display a message such as "Limit 2 per order." This prevents unnecessary API errors and provides a better customer experience. ### Integration guidance Check `max_allowed_quantity` in two scenarios: 1. **Adding an item to the cart:** If the requested quantity exceeds `max_allowed_quantity`, reject it client-side before making the API call. The server enforces this limit as well and will return an error. 2. **Updating item quantity:** When the customer increases the quantity of an existing cart item, validate the new total against `max_allowed_quantity`. The limit applies to the total quantity of that item in the cart, not per-addition. Items where `max_allowed_quantity` is `null` have no quantity restriction. ### Example: Quantity limit error Attempting to add more than the allowed quantity returns a 422 error: ```json { "error": { "code": "INVALID_REQUEST_ERROR", "message": "Quantity exceeds limit.", "detail": "Premium Cigars has a maximum allowed quantity of 2 per transaction. Requested: 5.", "request_id": "c3d4e5f6-a7b8-9012-cdef-345678901234" } } ``` ## Combining Restrictions A single item may have multiple restrictions active simultaneously. For example, the Premium Cigars item in the examples above has age verification (`age_verification_required: true`, `minimum_age: 21`), a quantity limit (`max_allowed_quantity: 2`), and tender restrictions (`tender_restriction_level: "RESTRICTED"`). Your integration must check all applicable restrictions before allowing cart and payment operations. When processing items with combined restrictions, validate in this order: time window availability first (is the item orderable right now?), then quantity limits (is the requested quantity allowed?), then tender restrictions (can the selected payment method be used?). Age verification is informational and does not block any cart or payment operation -- see the [Cart Lifecycle guide](/online-ordering/guides/04-cart-lifecycle#age-restricted-items) for details. For the full cart validation flow including modifier validation, price calculation, and handoff mode requirements, see the [Cart Lifecycle guide](/online-ordering/guides/04-cart-lifecycle).