Skip to content

Loyalty ledger integrity

Loyalty is a projection of the payment ledger. It must never decide whether a payment succeeds, and caller-provided receipt details are not authoritative.

Accrual boundary

  • Accrual accepts only merchant-owned paid or completed ledger transactions linked to a paid, non-cancelled order and an identified customer.
  • Amounts must match the order exactly at cent precision.
  • core_transactions.settled_at is captured once when a transaction first becomes paid or completed. Later metadata updates cannot move a payment into a newly-created program or rule.
  • (transaction_id, program_id) is the exact-once key. Deferred database triggers run after the payment and order writes agree, while the recovery endpoint can safely replay the same transaction.
  • Rule thresholds, periods, and reward values must be positive at both the API and database boundaries.
  • A program has exactly one earning period across all active rules. Program and rule creation or updates are one atomic database statement; an invalid final rule set rolls back as a unit. Legacy mixed-period or empty programs are paused and recorded in loyalty_configuration_issues for explicit repair.
  • Settlement snapshots customer identity, amount, time, the program period anchor, and rule economics in loyalty_accrual_bindings before accrual is attempted. Retries use only that immutable payload after rechecking that the live payment remains eligible, so later payment metadata or program changes cannot redirect or resize the entitlement.
  • Period boundaries are derived from the immutable program anchor and period length, independent of processing order. Every resolved accrual event persists its own period_days, so a historical retry cannot rewrite the current period with an older configuration version. Events created by migration revisions that predate settlement bindings are recorded in loyalty_accrual_event_issues when no binding, matching progress window, or uniform historical rule set can prove their period. Those events and any prior rewards remain auditable but are excluded from allocation until an operator reconstructs the missing period; the migration never guesses their economics.
  • loyalty_reward_allocations is the deterministic projection of the immutable event stream. A replay orders settlements by (transaction_at, transaction_id), carries the residual spend forward, and evaluates each settlement using that settlement's bound rule snapshot. Opposite-order and concurrent retries therefore converge on the same allocation slots, rewards, and residual balance even when rules changed between payments.

Reward claims and consumption

The current order and POS contracts do not provide an authoritative merchant-side operation that consumes a loyalty reward or applies its economic value. Direct /redeem therefore fails closed with 409; it must not mark a reward redeemed merely because a customer tapped a button.

Customers may reserve an idempotent presentation claim through /claim. A claim and its audit row prove customer intent only: the reward remains unredeemed and no discount, cashback, or points value is considered consumed. Completing economic redemption requires a future merchant-authorized consumption contract tied atomically to the order/payment adjustment and its audit trail.

Refunds and reversals

Full refunds, reversals, chargebacks, voids, failures after settlement, and cancellations reverse the accrual event idempotently. Reconciliation uses the entire loyalty period, because several payments can jointly fund a reward even when that reward is attributed to the last payment.

The period's allocation projection is rebuilt after the reversed payment is removed. Rewards that no longer correspond to a deterministic allocation slot are reconciled as follows:

  • unredeemed rewards are invalidated and excluded from reads and redemption;
  • redeemed rewards cannot be erased, so an idempotent quarantined compensation record is created for operations to resolve or waive;
  • the reversal audit records the progress before and after and the affected reward counts.

Partial refunds are not guessed from order status. Until the ledger provides an authoritative refunded amount, they create a terminal reconciliation record for explicit handling.

Recovery

Trigger failures are durable and do not abort payment settlement. The production one-minute scheduler claims due failures with FOR UPDATE SKIP LOCKED, leases each item for five minutes, and retries accrual or reversal independently. Accrual failures retain their settlement binding even if the current program becomes inactive, and late settlements are assigned to their deterministic historical period. Retries use bounded exponential delay and become terminal after five failed sweeps. Terminal rows and quarantined redeemed-reward compensation are operational work, not silently discarded errors.