Version 5.3.1
October 15, 2026
Fixed
- A birthday reward missed by the sweep across New Year is now paid within the grace period,
against last year: a 30 December birthday swept on 2 January was skipped as next December's.
Rewards::lastBirthday()returns the most recent occurrence on or before a date. (#2)
Version 5.3.0
October 15, 2026
Added
- Remind after issue on coupon rules (Pro): reminders a list of days after a coupon was
issued (
30, 60, 180, 365), while it is still unused — including for coupons that never expire. A new system message, When a Pointz coupon is still unused, carries them. A sweep that has missed several of a code's reminders sends one. --remind-afteronpointz/import/coupons, counted from each row'sissueddate. Offsets that have already passed are dropped.- At most once per order on threshold rules: one order pays one reward, however far past the threshold it takes the balance, and the rest stays on the balance.
- Spend the whole balance on threshold rules, which resets the balance to zero for the reward.
- Once per product on review rules: one payment per customer per reviewed product, rather than one per review.
CouponEvent::$reminder, saying which reminderCoupons::EVENT_BEFORE_REMINDis for.Coupons::getCouponCodeError(),Coupons::reminderSchedule(),Rewards::hasFiredForOrder().
Changed
pointz/import/couponsno longer refuses a discount because of codes that can't be used: codes Commerce says are used up, and codes the file lists as used or past their expiry. It reports how many it left behind.pointz/import/revertlifts the single-use limit the import put on codes that had none.
Fixed
- Someone else's Pointz code, or an expired one, stayed on a cart silently while taking nothing off. It is now removed with Commerce's "Coupon removed: Coupon not valid." notice, as a used code is.
- A coupon reminder that failed to send was marked sent and never retried. It is now retried on the next sweep.
- The coupon emails threw under Craft's Twig sandbox (
enableTwigSandbox). The properties and getters they read are now marked#[AllowedInSandbox]. - Rule links on the rules index went to a 404:
Rule::getCpEditUrl()returned a path rather than a control panel URL.
Version 5.2.0
October 9, 2026
Added
- GraphQL, in Lite:
pointzBalance,pointzCreditBalance,pointzPendingBalance,pointzAccount,pointzLedger,pointzExpiring,pointzEarnFor,pointzQuoteandpointzWillEarn, pluspointzRedeemandpointzRemoveRedemptionmutations that mirror thepointz/cart/*actions. Each set has its own schema component. Every field answers for the signed-in customer only: no field takes a user, and a cart is only read or changed when it belongs to the signed-in user. Craft's GraphQL result cache is switched off for any query that names a Pointz field, because that cache isn't keyed by visitor. The mutations need the session's CSRF token, sent in a POST. Lifecycle::getExpiringLots(), which lists one customer's expiring lots, each with its own expiry date.
Fixed
craft.pointz.expiring()could miss the customer when more than 500 accounts had value expiring in the window. It now filters by customer in the query.
Version 5.1.1
October 3, 2026
Added
pointz/import/balances, which loads opening balances from a CSV file: one lot per row, each with its own expiry date, of a new Imported ledger kind. Rows already imported are skipped, by theirreferencecolumn or by file and line, and imported points cross no threshold rule.pointz/import/coupons(Pro), which takes over coupon codes another system already issued in Commerce. Pointz records each code's owner and expiry, enforces them at checkout, sends its reminder, and deletes the old per-customer discount once its last code is used or expired. A discount with codes the file does not list is refused whole.pointz/import/revert, which takes back what is left of an imported balance and hands unused coupons back to Commerce.Importservice (Plugin::getInstance()->getImport()) andCoupons::clearMemo().- Pay people without an account, an option on custom-event rules (Pro). An event for an email address with no account creates an inactive one, as Commerce does for a guest checkout, and pays it — so a newsletter signup can earn a welcome coupon that is waiting when the subscriber registers. One reward per address; the signup bonus doesn't fire for these accounts.
Changed
- A Formie submission from someone without an account now passes their email to
awardEvent(), so a rule with Pay people without an account on can pay them.
Version 5.1.0
October 3, 2026
Added
- Earning rules for things that are not an order (Pro). Each pays a customer once per
occurrence, and conditions, windows, caps and expiry apply as on any rule:
- An approved review, through Stars. Options require written text, or require the reviewed entry to be a product the customer bought (or related to one). The reviewer is matched to a customer by email.
- A customer's birthday, read from a Date or plain text user field and paid by
pointz/sweep/run. A missed night is caught up for six days, and 29 February falls on the 28th. - Reaching a points balance. By default the threshold is spent on the reward, so "every 500 points becomes €5" is one rule; with spending off it is a once-ever milestone. Tiers are written with precedence and stop after this one.
- A custom event, fired by handle:
formie:<form>for a Formie submission,dispatch:<list>for a Dispatch signup from the site, or anything a module passes toRewards::awardEvent(). A handle may end in*.
- Coupons as a reward (Pro). Any rule but an order can issue a single-use Commerce coupon code, a percentage or a fixed amount, to one customer, with an optional expiry, an optional email when it is issued and an optional reminder before it expires. Pointz uses one Commerce discount per rule rather than one per customer. A code only discounts its owner's order and only until it expires, and the sweep deletes expired codes from Commerce and removes discounts nothing issues against any more. Changing a rule's value starts a new discount, so codes already issued keep their value.
- The signup bonus can award store credit or a coupon (Pro).
- Two system messages, When Pointz issues a coupon and When a Pointz coupon is about to expire, edited under Utilities → System Messages.
- A customer's coupons on their balance page, with a Revoke button.
craft.pointz.coupons(), and aneventargument oncraft.pointz.activeRules().pointz/sweep/birthdaysandpointz/sweep/coupons;pointz/sweep/runnow does both.Rewards::EVENT_BEFORE_REWARD,Coupons::EVENT_AFTER_ISSUE,Coupons::EVENT_BEFORE_REMIND, andLedger::EVENT_AFTER_COMMIT, raised after a positive movement once the account's lock is released, for handlers that need to move value themselves.- An Exchanged ledger kind for points a threshold spends.
Version 5.0.1
October 3, 2026
Security
- A guest could spend a registered customer's points and store credit. Commerce makes the owner of an email the customer of a guest cart that types it in, and redemption only looked at the cart's customer — so a guest who knew a customer's email could see their balances in the quote and spend them at checkout. Redemption now needs that customer to be signed in, records who applied it, and only ever spends from the account of the person who asked. A quote shows anyone else zeroes.
- Points or credit already applied to carts are cleared by the update, because there is no telling who applied them; customers apply them again.
Fixed
- The Liability dashboard widget's settings were a fatal error on any install with more than one
store: they called a
Viewmethod Craft doesn't have. pointz/accounts/recalculateprinted blank progress lines — a curly ellipsis straight after$donemade PHP read it as part of the variable's name.
Added
Redemption::spenderId(), and an optional$userIdonsetIntent().tests/integration/security.php: 11 checks, over HTTP as a guest and as the signed-in customer.- PHPStan and ECS configuration, with
composer phpstan,check-csandfix-cs.