Skip to content

feat(discount): add structured inapplicable discount codes#271

Open
tpindel wants to merge 1 commit intoUniversal-Commerce-Protocol:mainfrom
tpindel:discount-rejection
Open

feat(discount): add structured inapplicable discount codes#271
tpindel wants to merge 1 commit intoUniversal-Commerce-Protocol:mainfrom
tpindel:discount-rejection

Conversation

@tpindel
Copy link

@tpindel tpindel commented Mar 18, 2026

Summary

Adds an inapplicable array to the discount extension's discounts_object, providing structured rejection feedback when submitted discount codes cannot be applied.

Currently, rejected codes are communicated via the generic messages[] array. While functional, this requires platforms and agents to:

  1. Filter messages[] to find discount-specific warnings
  2. Parse the path field ($.discounts.codes[0]) to associate a rejection with a specific code
  3. Mix discount rejections with unrelated checkout messages

The new inapplicable array provides a clean success/failure partition directly on the discounts object:

discounts: {
  codes: ["SUMMER20", "EXPIRED50"],     // submitted
  applied: [{ code: "SUMMER20", ... }], // succeeded
  inapplicable: [{                      // failed
    code: "EXPIRED50",
    reason: "discount_code_expired",
    content: "Code 'EXPIRED50' expired on December 1st"
  }]
}

Motivation

In agent-driven commerce, discount code application is a high-frequency operation. Agents try multiple codes, stack promotions, and negotiate on behalf of customers. They need programmatic, structured rejection feedback — not text parsing.

The current messages[] model works well for human-facing warnings, but agents benefit from:

  • Direct code-to-rejection mapping without JSONPath parsing
  • Clean partitioning: applied[] for successes, inapplicable[] for failures
  • Machine-readable reason codes reusing the existing discount_code_* vocabulary
  • Co-located feedback: rejections live on the discounts object, not in a separate array

This is backward-compatible: messages[] warnings for rejected codes continue to work. inapplicable is an additional, more structured channel.

Design Details

New Type: inapplicable_discount

{
  "code": "EXPIRED50",
  "reason": "discount_code_expired",
  "content": "Code 'EXPIRED50' expired on December 1st"
}
Field Type Required Description
code string Yes The rejected discount code
reason error_code Yes Machine-readable rejection reason (reuses existing codes)
content string Yes Human-readable explanation

Reuses Existing Error Codes

The reason field references error_code.json and SHOULD use the standard discount error codes already documented in the spec:

Code Description
discount_code_expired Code has expired
discount_code_invalid Code not found or malformed
discount_code_already_applied Code is already applied
discount_code_combination_disallowed Cannot combine with another active discount
discount_code_user_not_logged_in Code requires authenticated user
discount_code_user_ineligible User does not meet eligibility criteria

Relationship to messages[]

inapplicable complements messages[], it does not replace it:

  • Businesses SHOULD include both inapplicable[] entries AND messages[] warnings for rejected codes (for backward compatibility)
  • Platforms that support inapplicable can use it directly; older platforms continue using messages[]
  • inapplicable is scoped to code-based rejections only; automatic discount issues remain in messages[]

Files

New Files (1)

File Location
inapplicable_discount.json source/schemas/shopping/types/

Modified Files (1)

File Change
discount.json Add inapplicable array to discounts_object, add $defs reference

Spec Doc Update (1)

File Change
docs/specification/discount.md New "Inapplicable Discounts" section, updated examples

Type of change

  • New feature (non-breaking change which adds functionality)

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant