Pluton.id
Notes

Why the Server Decides the Price of Every Order

Clients may display prices, but the server must calculate and authorize the final order total.


The client is an input device, not the pricing authority

When we build an ordering flow, we treat every value received from a browser, mobile app, integration, or POS terminal as input that must be validated. This includes unit prices, discounts, tax amounts, shipping charges, and the order total.

A client can display a price to help a customer decide what to buy. It cannot be trusted to decide what the customer must pay. The client may be modified, running an old version, disconnected from current pricing data, or calling an API directly with manually constructed requests.

If an API accepts a client-provided total as authoritative, a request can change unit_price from 100 to 1 before checkout. No flaw in the visual interface is required. The server has simply accepted a value it should have calculated itself.

What the client should send

We design checkout requests around purchase intent rather than financial results. A client should normally send:

  • Product or SKU identifiers
  • Selected variants, options, and quantities
  • Customer or account identity where relevant
  • Delivery or pickup details
  • Promotion or coupon codes supplied by the customer
  • A payment method token or payment reference

The server then loads the current eligible product data and calculates the payable amount. We may accept client price information for display comparison, diagnostics, or stale-cart detection, but never as the source of truth.

For example, the client can say, “the cart showed 24.00.” The server should respond with its own calculation and either continue or explain that the price has changed.

Pricing is more than a product price

The final price often depends on rules that the client should not implement independently. Even a straightforward catalog can require decisions about:

  • Currency and currency-specific rounding
  • Tax jurisdiction, tax inclusion, and exemptions
  • Customer-specific contracts or account pricing
  • Item availability and substitution rules
  • Delivery fees, service charges, and minimum-order rules
  • Refunds, credits, gift cards, and stored-value balances

If these rules are duplicated across web, mobile, and POS applications, they drift. One channel eventually applies an old rule, rounds differently, or misses an exception. We keep the calculation on the server so every channel uses the same pricing rules.

Tiered pricing requires an authoritative calculation

Tiered pricing is a common reason that client calculation becomes unreliable. A price may change based on quantity, customer group, subscription status, location, date, or the combined quantity of several products.

A client might show a lower price after a customer adds enough units to reach a tier. Before the order is accepted, the server must still verify that the tier is eligible and calculate it from the final cart state.

The calculation must also define its scope clearly. A tier can apply per line item, per SKU, per category, or across the full order. These are business rules, not presentation details. We put them in server-side pricing logic and keep the evaluated rule references with the order where auditability matters.

A promotion engine must run where it can be controlled

Promotion logic has similar requirements. A promotion engine may need to consider eligibility dates, usage limits, customer segments, exclusions, product combinations, and whether promotions can be stacked.

We do not rely on the client to decide whether a coupon is valid or whether two offers can be combined. The server evaluates the promotion engine against the order being created. It determines:

  1. Which promotions are eligible.
  2. Which promotion combination produces the permitted result.
  3. How each discount is allocated across order lines.
  4. Whether redemption limits remain available.

This also matters under concurrent checkout activity. If a promotion has limited redemptions, the server needs to reserve or consume that eligibility in a controlled transaction. A client-side check can only be an estimate because another order may use the remaining promotion availability first.

Client trust is a security boundary

Client trust is not improved by hiding fields in the interface, disabling buttons, or obfuscating JavaScript. Those controls can improve usability, but they do not protect a pricing rule.

Anyone able to submit an order request can inspect, replay, modify, or generate requests. This is normal for public client applications. We design the API accordingly:

  • Authenticate the actor where pricing depends on identity.
  • Authorize access to products, locations, and customer-specific prices.
  • Recalculate every monetary amount on the server.
  • Validate stock and promotion eligibility during order creation.
  • Use idempotency keys so a retried request does not create duplicate orders or duplicate charges.
  • Record the price inputs, applied rules, and resulting totals needed to explain the order later.

The goal is not to distrust customers personally. It is to place financial authority in a system we control, can update consistently, and can audit.

POS systems follow the same rule

A POS terminal is still a client. It may be operated by staff rather than a consumer, but it can be offline, outdated, misconfigured, or integrated with local hardware that has its own constraints.

For an online POS, we use the same server-side pricing API as other channels. The POS sends scanned items and customer context, and the server returns an authorized quote or creates the order with calculated totals.

Offline POS operation needs an explicit trade-off. If sales must continue without connectivity, the device may need a locally stored price book and promotion subset. We treat that as a temporary operational authority with safeguards, not as proof that the local total is permanently correct.

A practical offline design includes:

  • Versioned price books with effective dates
  • Clear limits on which promotions can work offline
  • Local audit records for every sale and override
  • Sync-time reconciliation with the server
  • A defined policy for conflicts, changed prices, refunds, and inventory updates

The server remains the long-term record of the order and its financial state, even when a POS has to make a temporary local decision.

Quote first, then create the order

We often separate price preview from order creation. A quote endpoint lets a client present a server-calculated result before payment. The client can then submit the selected items, customer context, promotion codes, quote identifier, and payment token to create the order.

A quote should have a limited validity period and enough context to detect changes. The server must still revalidate at creation time, especially for inventory, promotion usage, tax, and time-sensitive pricing. A quote is useful for clarity; it is not a reason to skip final validation.

Once an order is accepted, we persist a pricing snapshot. This normally includes the unit prices used, discounts, tax results, currency, rounding method where relevant, and identifiers for applied pricing or promotion rules. Later catalog changes should not rewrite the historical meaning of an already accepted order.

The design rule

The client owns the shopping experience: it displays prices, collects choices, and explains the result. The server owns financial decisions: it resolves pricing, applies tiered pricing, evaluates the promotion engine, validates eligibility, and records the final total.

That boundary keeps web, mobile, integrations, and POS channels aligned. More importantly, it makes the price of an order a result of controlled business rules rather than a claim made by the device submitting the request.