Inkress ADRsarchitecture decisions
Overview / Discounts
ADR 0002

Discounts are server-authoritative and modeled as a fee group

AcceptedDiscounts

Context

Inkress checkout needed real discount codes. The transaction calculator already expressed shipping, tax, platform and provider charges as fee groups applied in a defined order, and the original intent was for discounts to be "just another fee" — but that had never been built, so discounts did not exist end to end.

Two forces shaped the decision:

  • Trust. A discount lowers what the customer pays. If the client could send the discount amount, a tampered request would pay an arbitrary price — the amount must be decided by the server.
  • Uniformity. A discount has to behave identically whether the cart is a list of products or a single amount, appear consistently as discount_total on the fees quote, the order, and the transaction, and never drive a total below zero.

Decision

A discount code is a merchant-owned definition (percentage or fixed amount, with optional currency, minimum spend, expiry). The client sends only the code; the server resolves it, validates it, and computes the money itself.

The resolved discount enters the transaction calculator as a first-class "discount" fee group, sorted to run before tax, with the merchant as the fee payer so it reduces the total exactly once (surfaced as discount_total) rather than being counted as a customer-paid fee. A fixed discount is clamped so the total cannot go negative; a percentage is bounded to 100% at definition time.

The public quote endpoint, GET|POST /public/m/:username/discount, returns the discounted fee breakdown or a typed rejection reason so checkout can show the buyer a code's worth before they commit; order-create re-resolves the code against the authoritative subtotal and currency and freezes the applied amount and code onto the order.

Consequences

  • Positive: one code path prices discounts for every cart shape; discount_total is consistent across quote, order and transaction; a client-supplied amount is never trusted.
  • Positive: downstream money paths (payment adapters, refunds, reconciliation) operate on the discounted authoritative total, inheriting the discount with no special-casing.
  • Neutral / to revisit: new discount behaviour (caps, scoping) is added by extending the resolver and the discount fee group — see ADR 0003 and ADR 0005 — not by touching the rest of the calculator.

Alternatives considered

  • A separate discount subsystem outside the fee calculator: rejected — it would duplicate the ordering, currency and rounding the fee engine already owns, and risk the discount and the fees disagreeing.
  • Trusting a client-sent discount amount (validating only the code shape): rejected — it makes the price forgeable; the amount must be server-computed.
← 0001 Merchant `/verify` links use the risk-dashboard host 0003 Discount redemption limits are derived from orders, not a counter or ledger →