Skip to content

PRD: Points Earning

ModuleLoyalty (EXT-01)PRD IDPRD-PTS-001
StatusPlannedOwnerLoyalty squad
Date2026-03-23Versionv0.1
Packages@nx/sale · @nx/coreURDPTS

TL;DR

Lets a merchant turn every paid, customer-linked order into loyalty points automatically - the customer's balance grows the moment payment completes, using a per-merchant conversion rate. Each award is recorded once per order as an auditable point transaction, so repeat customers see their points accrue without any manual entry and merchants get a verifiable point ledger.

1. Context & Problem

The Loyalty module turns completed purchases into points so repeat customers return more often. Before points earning, paid orders left no loyalty trace: there was no point ledger, no balance accrual, and no link from an order to the points it generated. Customers could not be rewarded for spend, and merchants had no foundation on which to build redemption, tiers, or rewards.

Points earning is the first slice (feature PTS, phase P1): a customer-linked order that completes payment adds points to that customer's balance automatically, once, using a per-merchant conversion rate. It builds on the existing customer identity (which holds the balance) and the order/payment flow (which signals completion), and is the prerequisite for every later loyalty capability.

2. Goals & Non-Goals

Goals

  • Award points automatically when a customer-linked order completes payment, with no manual entry.
  • Make the award idempotent per order so a redelivered or duplicate payment event never double-awards.
  • Read the conversion rate from per-merchant configuration, falling back to a default when unset.
  • Record each award as a PointTransaction (type EARN) and atomically increment the customer's point balance.
  • Expose a read-only point-transaction listing API (find / findById / findOne / count), merchant-scoped.

Non-Goals

  • Redemption, tiers, and the rewards catalog - owned by feature RDM (phase P2, URD-RDM).
  • Cross-merchant / franchise-wide point pooling (URD Non-Goal).
  • Marketing message delivery on earn (owned by Marketing).
  • Awarding points to anonymous sales (constraint C-01 - only identified customers accrue).

3. Success Metrics

MetricTarget / signal
Coverage100% of paid, customer-linked orders award points (where the rate is positive)
IdempotencyZero double-awards - at most one EARN transaction per order
AccuracypointBalance equals the sum of the customer's EARN transactions
LatencyAward completes within the payment-confirmation path without delaying order completion

4. Personas & Use Cases

PersonaGoal in this feature
CustomerEarn points automatically on purchases and see the balance grow
OwnerSet the earning rate and trust that every paid order accrues correctly
ManagerView member point activity and balances

Core scenarios: a customer-linked order completes payment → the system reads the merchant's conversion rate → computes points from the order total → records one EARN transaction and increments the customer balance, once per order.

5. User Stories

  • As a customer, I want points added to my balance automatically when my order is paid, so that I am rewarded for spending without doing anything.
  • As an owner, I want the earning rate configured per merchant, so that I control how spend converts to points.
  • As an owner, I want a duplicate payment event to never double-award, so that the point ledger stays trustworthy.
  • As a manager, I want to list a customer's point transactions, so that I can review how their balance was earned.

6. Functional Requirements

#RequirementURD ref
FR-1Award points when a customer-linked order completes payment; the same logic runs on either payment-completion path (order-payment and check-payment)URD-PTS-001
FR-2Earning is idempotent per order - short-circuit if the order has already been awardedURD-PTS-002
FR-3Conversion rate read from per-merchant configuration (POINT_CONVERSION_RATE); fall back to a default when unsetURD-PTS-003
FR-4points = floor(orderTotal / conversionRate); a non-positive rate or zero points is skipped (logged, no award)URD-PTS-001
FR-5Within one transaction: insert a PointTransaction (type EARN, with points, conversionRate, saleOrderId) and increment the customer's pointBalance; roll back on errorURD-PTS-001..002
FR-6Read-only point-transaction listing API (find / findById / findOne / count), merchant-scopedURD-PTS-001

Full requirement text and acceptance criteria live in the Loyalty URD. This PRD references them rather than restating them.

7. Non-Functional Requirements

AreaRequirement
Data integrityThe PointTransaction insert and the pointBalance increment are written in one transaction - no balance change without a matching ledger entry
IdempotencyAt most one EARN award per order, enforced by an existence check on saleOrderId
Tenancy & authzAll operations scoped per merchant (x-merchant-id); the listing API is gated by sale permissions (JWT / Basic auth)
PrecisionMonetary / point math uses float(value, 4); points are floored to whole units
PerformanceAward runs inside the payment-confirmation path without delaying order completion
i18nUser-facing labels/statuses are bilingual ({ en, vi })

8. UX & Flows

Points earning has no dedicated UI - it runs server-side on payment completion. The earned balance surfaces in the customer profile (apps/client); point transactions are available through the read-only listing API.

9. Data & Domain

EntityRole
PointTransactionAppend-only ledger entry - type EARN, points, conversionRate, saleOrderId, merchant-scoped
Customer.pointBalanceThe running balance incremented atomically with each award
Configuration (POINT_CONVERSION_RATE)Per-merchant rate row (group SYSTEM, principal = merchant, status ACTIVATED); default when absent

Conceptual only - full schema and invariants in the developer customer-points docs and the sale domain model.

10. Dependencies & Assumptions

Depends on

  • Customer identity (Customer) - holds the pointBalance the award increments.
  • Orders / payment (Orders) - order completion on the payment path is the earning trigger.
  • Per-merchant configuration (@nx/core) - supplies POINT_CONVERSION_RATE.

Assumptions

  • The order is linked to an identified customer (anonymous sales earn nothing, C-01).
  • A conversion rate is configured, or the default applies.
  • Payment-completion webhooks fire reliably for each paid order.

11. Risks & Open Questions

Risk / questionMitigation / status
Duplicate / redelivered payment events could double-awardIdempotent per order - existence check on saleOrderId before awarding (C-02)
Award and balance increment could diverge on partial failureBoth written in one transaction; rolled back together on error
Misconfigured (non-positive) rate could award garbageNon-positive rate or zero points is skipped and logged, never awarded
Refund / cancellation after earnOpen: define the clawback / reversal path for earned points

12. Release Plan & Launch Criteria

AspectPlan
PhaseP1 (foundation) - see URD feature catalog
RolloutAll merchants; no feature flag
MigrationNone (new PointTransaction entity; conversion rate from configuration)
Launch criteriaPaid customer-linked order awards points once; balance matches EARN ledger; duplicate event does not double-award; rate read per merchant
MonitoringAward volume per merchant, double-award detection (orders with >1 EARN), balance-vs-ledger consistency checks

13. FAQ

When exactly are points awarded? When a customer-linked order completes payment - the same logic runs whether confirmation arrives via the order-payment or the check-payment path.

Can the same order award points twice? No - earning is idempotent per order. If an EARN transaction already exists for the order, the award is skipped.

Where does the earning rate come from? From the per-merchant POINT_CONVERSION_RATE configuration; a default applies when none is set.

How are points calculated? points = floor(orderTotal / conversionRate). A non-positive rate or a zero result is skipped (logged, no award).

Do anonymous sales earn points? No - only identified customers accrue (constraint C-01).

Can I redeem points yet? Not in this increment. Redemption, tiers, and the rewards catalog are the separate RDM feature (phase P2, URD-RDM).

References

Proprietary and Confidential. Unauthorized copying, distribution, or use of this software is strictly prohibited.