Skip to main content

Reward Engine

The reward engine is implemented as a pure domain package:

  • packages/reward-engine/src/evaluate.ts

No database or network side effects exist in this package.

Core Functions

evaluateReward(input)

Computes points earned for a transaction.

Input includes:

  • amountMinorUnits
  • rule (conversionRate, thresholds, caps, rounding)
  • campaignMultiplier

Decision flow:

  1. enforce minimum spend (minSpendMinorUnits)
  2. compute base points from dollars (amountMinorUnits / 100)
  3. apply multiplier
  4. apply per-transaction cap (maxPointsPerTransaction)
  5. apply rounding policy (floor|ceil|round)

Output:

{
pointsEarned: number,
appliedRule: RuleConfig,
didMeetMinSpend: boolean
}

checkRedemptionEligibility(balance, cartAmountMinorUnits, rule)

Determines whether redemption is allowed and max redeemable points.

Rules applied:

  • block when balance below minBalanceToRedeem
  • cap redemption by cart percentage (maxRedemptionPercentage)
  • cap by current balance

Rule Config Contract

Rule config schema comes from packages/types/src/reward.ts.

Fields:

  • conversionRate
  • minSpendMinorUnits
  • maxPointsPerTransaction
  • minBalanceToRedeem
  • maxRedemptionPercentage
  • roundingPolicy

Runtime Integration

Engine is used in API ingestion path:

  • apps/api/src/services/ingestion.ts -> reward calculation for POS transactions

When points > 0, wallet credit path is executed via wallet service.

Determinism and Auditability

The pure-function design gives:

  • repeatable outcomes for same inputs,
  • easy unit testing,
  • easier dispute resolution using historical transaction + rule snapshots.

Existing Automated Coverage

packages/reward-engine/src/evaluate.test.ts verifies:

  • min-spend block,
  • basic conversion,
  • multiplier and cap,
  • rounding behavior,
  • redemption eligibility and caps,
  • deterministic behavior.
Written byDhruv Doshi