Points and earn rules
The points engine is an append-only ledger. Every change to a member's balance writes a row to wp_oloyalty_ledger with the running balance_after. The members table's points_balance is a denormalized cache of the latest ledger row.
Ledger entry types
Five types via the type column:
earn- new points awardedredeem- points spent on a rewardadjust- manual admin adjustmentexpire- points removed by expiry sweepreverse- earn reversed on refund or cancellationtransfer- member-to-member transfer
Each row links back to its source: source (text key like order, review, birthday, referral) and source_id (the WC order ID, comment ID, etc.). The unique (source, source_id, type, member_id) combination is checked before writes to keep the ledger idempotent.
Built-in earn actions
- Order placed - hooks
woocommerce_order_status_completedandwoocommerce_order_status_processing - Product review - hooks
comment_post; caps one bonus per product per member - Birthday - via the
OLoyalty_Birthdaysdaily scheduler - Signup - on
user_registerfor new accounts
Earn rule shape
A rule lives in wp_oloyalty_earn_rules with:
action- the trigger key (order_placed,product_review, etc.)points- base award (used when no formula)formula- JSON expression, for example points per dollar of subtotalmultipliers- tier multipliers and campaign multiplierscaps- per-day, per-month, lifetime capsexclusions- product or category exclusions
OLoyalty_Earn_Rules::evaluate($action, $context, $member) returns the awarded points for the given action and context.
Refund reversal
On woocommerce_order_status_refunded or woocommerce_order_status_cancelled, OLoyalty_Ledger::reverse_for_source('order', $order_id) writes a reverse row that nets out the original earn. The points.reverse_on_refund setting accepts full, partial, or none.
Expiry
Earn entries can carry an expires_at date. A scheduled sweep walks rows where expires_at < now() and writes expire entries to neutralize them.

