Decide which system owns each amount
A cart page can display a convincing total while the final request sends a different one. The customer's browser is under the customer's control, including hidden fields, local storage, scripts and network requests. The OWASP Business Logic Security guidance gives a direct example: syntactically valid product and coupon inputs do not make a client-supplied price trustworthy. The shop should accept a product identifier, requested quantity and coupon code as requests, then derive unit price, eligibility, delivery charge and final payable amount from its own current rules. Staff need a written answer for who may change those rules and when they take effect. Otherwise a technically correct recalculation can still apply a price the business did not intend.
Keep the sources of truth separate. A catalogue price is an offer for a product under stated conditions. A promotion is a rule with validity, product scope and redemption limits. A delivery fee depends on destination and chosen service. A payment provider reports what it processed, but it should not decide which product was sold or whether a discount was allowed. Make an explicit order calculation record that names the rule version, currency, quantities, adjustments and resulting amount. This record lets staff explain a dispute without trying to reconstruct the old storefront screen from today's changed catalogue. Treat this as a design recommendation, not a claim that any particular payment provider implements it for you.
Reprice before offering payment
The server should re-read the selected product and quantity, confirm that each item is still saleable, apply the business's current approved price, and then evaluate every discount as a rule rather than as a supplied amount. It should determine shipping from a selected service and a validated address, using the shop's rate table. An unsupported combination should stop with a clear explanation: for example, the delivery method no longer covers the destination, or a coupon does not apply to an item. The customer can then review the revised amount before authorizing payment. Silently charging a new total because a price changed mid-session creates a different customer problem, even if the server arithmetic is sound.
Suppose a buyer places two units in the cart and pastes a promotional code. The browser may show a subtotal, a reduction and a delivery fee. Those display values are a preview, not a payment instruction. The server evaluates the item count, the code's permitted products and usage conditions, and the selected delivery service. It then returns a single proposed order quote with its currency and expiry or change policy. This hypothetical scenario illustrates a sequence, not a claim about any existing shop's prices or conversion rate. If stock, promotion or delivery eligibility changes before payment, invalidate the old quote and ask for confirmation of the new one.
Bind the approved quote to the payment request
When the customer confirms the final review, record the approved server-side quote and create a payment request for that exact amount and currency. The OWASP payment gateway guidance calls for backend cart validation and server-side recalculation before an order is created. Associate the shop order identifier with the provider's payment identifier so later messages can be matched to one decision. Do not let a later browser request replace the approved amount, choose a different currency or add a new discount while reusing the same payment link. If the cart changes, generate a new quote and a new payment request under the shop's defined cancellation and retry rules.
Avoid treating the return page as proof of settlement. A browser can close, revisit or manipulate a return URL. A provider callback can be delayed or repeated. The shop must verify the provider's reported payment state through the documented trusted channel, then compare the provider amount and currency with the stored approved quote. A match is a necessary check, but the shop also needs to distinguish authorized, captured, failed and refunded states as its provider defines them. The payment-pending fulfilment decision has its own operational policy; here the narrow question is whether the amount and identity of a completed payment agree with the order that was approved.
Handle changes and mismatches deliberately
Several ordinary events can create a mismatch without fraud. A staff member may change a catalogue price during an open cart, a coupon may expire, or a delivery address may move outside a zone. Define when a quote is frozen and what expires it. If the server calculates a different total before payment, show the changed line and reason, then require a fresh confirmation. If the payment provider reports a different amount after payment, hold the order for investigation rather than rewriting the shop order to fit the provider response. Preserve both original records and the event sequence. Staff can then determine whether the difference came from a stale quote, currency handling, a provider setting or a genuine manipulation attempt.
Make a separate rule for rounding. Decimal display, stored minor units and tax or delivery allocation can differ unless the team specifies calculation order and rounding points. Record the currency alongside every amount; a bare number cannot tell staff whether the payment was in Hong Kong dollars or another currency. Test boundary cases with approved examples before launch: a discount larger than an eligible line, a zero delivery fee, mixed eligible and ineligible products, and a changed address after quote confirmation. Expected results should be agreed with finance and operations, then encoded in tests. No invented universal tax or rounding rule should be smuggled into the application merely because a developer chose a default library.
Reconcile and test the full transaction
A useful acceptance test changes the browser request while keeping the visible page plausible. Submit an altered unit price, a changed shipping amount, an ineligible coupon, a negative or excessive quantity and a currency switch. The server should reject or recalculate each case according to its approved rules, with no provider request at an unauthorized amount. Follow a legitimate order through quote, payment initiation, provider confirmation and order ledger entry. Compare identifiers, currencies and amounts at each step. This tests the trust boundary directly; a screen recording of a normal checkout cannot establish it. Repeat after changes to catalogue pricing, promotions, delivery rules or provider integration.
For day-to-day operations, provide staff with an exception view that shows the stored approved quote, payment reference, provider-reported amount and current order state. Restrict who can resolve a mismatch and require a reason for the outcome. Reconciliation should surface paid-but-unmatched transactions as well as orders marked paid without a verified corresponding provider record. These checks do not guarantee that every commercial rule is fair or legally correct; they give the business a traceable way to show what it approved and what was actually processed. If the shop cannot reconstruct that comparison for a sample order, the next development task is to fix the event and amount records before adding more promotional complexity.
Make price changes reviewable
Define the people and records behind price changes as carefully as the code. A price list update should carry an effective time, the products affected and an approver appropriate to the business. A promotion should record its eligibility rules and the reason it ended or was withdrawn. Keep the old rule version long enough to explain orders placed while it was active, subject to the company's retention policy. These records are especially useful when customer service receives a screenshot of an older price. Staff can distinguish a valid earlier quote, an expired preview and a calculation defect, rather than offering an ad hoc adjustment. The engineering team should expose the relevant rule version in an internal order view, while keeping sensitive configuration and staff actions behind appropriate access controls.
Run one joint review with a developer, an operations owner and the person responsible for money reconciliation. Give each a different question. The developer traces where each amount is calculated and whether the browser can override it. Operations checks whether the rule reflects what was promised to the customer. Finance checks whether the approved amount, provider amount and ledger amount can be matched without interpretation. Record disagreements as test cases before adding new discount features. A checkout that is technically resistant to tampering can still fail if delivery exceptions or currency handling are undefined. Conversely, a beautifully written promotion can still lose money if the server accepts its discount amount from a client request. The shared record closes that gap between business policy and implementation.
Keep customer-facing and internal amounts in agreement without exposing internal rules. A confirmation page should state the items, quantities, delivery choice, adjustments, currency and payable total in ordinary language. The internal record should add the rule identifiers and payment references needed for reconciliation. If a promotion changes after the customer has confirmed, the system needs an explicit choice: honor a still-valid committed quote under the shop's policy, or ask for renewed consent to a new quote. The choice depends on the shop's published terms and operational capacity; the software should not silently invent it. Walk through this case with customer service as well as engineering, because the first sign of a defect may be a customer asking why the statement differs from the checkout screen.
Before you proceed
- Are all prices, discounts, delivery fees and currency derived or verified on the server?
- Does the approved quote match the payment request and verified provider result?
- Do mismatches stop for investigation with both original records preserved?


