Limits and guarantees¶
A points programme is a promise to your customers, paid for out of your margin and held open for months. Loyalty is sold on keeping that promise exactly: a point is awarded once, taken back once, spent at the price the checkout quoted, and never expired behind anybody's back. This page is where each of those is written out, including where it stops. Read it before you switch earning on, because switching it on is the moment your store starts to owe something.
At a glance¶
| If you are asking | The short answer |
|---|---|
| Does switching earning on award points for past orders? | No. Only an order placed after you pressed Start awarding points can earn, whatever status it reaches later. More |
| Does a refund take the points back? | Only if you tick the statuses that mean a refund. Nothing is ticked out of the box, and the screen warns in red while it is empty. More |
| Can a balance go below zero? | Yes. A refunded order takes back points already spent, and two orders placed at once can spend the same points. Nothing stops it; a negative balance blocks spending until it is earned back. More |
| Can I run Loyalty beside OpenCart's own Reward Points total? | Earning, yes. Spending, no: while core's total is on, Loyalty's checkout line and cart control stay away. More |
| Can points pay for shipping? | No. They come off the goods, before shipping and tax, and never take an order below zero. More |
| Will a customer lose points without warning? | No. Nothing expires until a warning email has gone and its notice period has run out. A store whose mail is not working expires nothing. More |
| Do points I added by hand expire? | No. Only points Loyalty itself awarded can expire. More |
| What does OpenCart 4.1.0.4 cost me? | Expiry, the warning email, the birthday bonus and the repair of failed writes run only from a crontab line of your own. Earning and spending work. More |
| Does uninstalling delete my customers' points? | No. It removes Loyalty's event rows and scheduled task and nothing else. More |
| Can another extension award points? | Yes, through one model call, and nothing checks who is calling. More |
| Can my CRM or accounting system read customers' points? | Yes, once you switch the API on: every movement of points and every customer's balance, with when it expires. Points are never given a money value, and nothing can be changed through it. More |
What a balance is¶
- A balance is OpenCart's own figure. It is the live sum of the customer's rows in OpenCart's reward table, the same figure core's customer screen and checkout use. Loyalty writes every movement into that table and never keeps a second copy of anybody's balance, so a copy can never disagree with it.
- Points you add or remove by hand count at once. A change on a customer's Reward Points tab is part of the same sum.
- A customer has one balance across every store. Rates, limits and the expiry window are set per store; the balance is not split by store.
- Points are points, never money. Loyalty stores and shows points. What they buy is your exchange rate applied at the moment of spending; what they cost your business is accounting, which Loyalty does not do for you. See what the store owes.
What earns, and when¶
- No order earns until you press Start awarding points. Installing awards nothing, and setting a rate awards nothing. The button records the moment, and only an order placed after it can earn. An order is judged by when it was placed, not when it completed, so the orders already in your system never earn, whatever status they reach later.
- An order earns once, the first time it reaches a status you ticked. No status is ticked out of the box. Reaching an awarding status again awards nothing more, and changing the list never backdates anything.
- The base is the goods. Either the product lines before tax or including it, as you choose. Shipping, handling and fees are never counted. A coupon and points spent reduce the base; store credit and a gift voucher do not, because the customer had already paid for those. An order total Loyalty cannot classify is treated as a discount, the direction that costs you less, and named in the log.
- Rounded down, once, over the whole order. Not per line, and never up. An order too small to earn a whole point writes nothing.
- The rate is a percentage, and above 100 is ordinary. 100 is one point per unit of your store's default currency; 200 is two.
- A customer group can have its own rate. The group is the one the order was placed in, not the one the customer is in today. A group left blank earns the store's rate, never nothing.
- A product's own Reward Points figure wins, if you leave that on. A line whose product has a figure on the product form's Reward Points tab earns exactly that figure instead of the percentage. Zero means not set, so there is no way to say that one product earns nothing while the rest earn.
- An award is never recalculated. Editing an order after it earned changes nothing, and the award records the base and rate it used, so you can trace a figure months later.
- Guests earn nothing. Points belong to an account.
- The welcome bonus is paid once per account, for ever. It is paid for an account made on the register page, at the checkout's register step, or by you in the admin, and an account is paid once whichever way it came. An account waiting for approval is paid all the same. Raising the figure pays the accounts created from then on; nothing is backfilled. It does not wait for Start awarding points: it is paid whenever the programme's Status is on and the figure is above zero.
- The birthday bonus needs a field you create. OpenCart has no birthday field, so you choose one of your own Date custom fields at the Account location. A customer who left it empty, or typed something that is not a real date, is simply not celebrated. It is paid once a year, on or after the birthday, by the hourly scheduled pass, and only for a birthday that fell on or after the day earning was switched on: switching anything on never pays for birthdays already gone. A 29 February birthday counts from 1 March in other years.
- Stopping earning stops new awards and takes nothing back. Pressing Start again restarts the clock from that moment.
What is taken back¶
- Only on the statuses you tick, and none is ticked out of the box. Canceled, Refunded and Chargeback are the ones to tick. While awarding statuses are ticked and none of these is, the screen warns in red, and saves anyway, because a store may decide points once given stay given. A status ticked in both lists is refused.
- All of an order's award, or none of it. OpenCart has no partial refund at the order level, so a reversal takes back exactly what the order was awarded, whatever the rate says today. To take back part, adjust the customer's Reward Points tab by hand.
- Points already spent are taken back too, and the balance goes below zero. A customer who earned 500 points, spent them, and then had the order refunded is at −500. The points page tells them which order put them there, and they cannot spend again until they are back above zero.
- Once per order, for ever. A reversed order never earns again, whatever statuses it passes through afterwards.
- An award that has already expired is not taken back. Expiry took it once; taking it again would charge the customer twice.
- Reversal keeps working after you stop earning, for the orders that earned while it was on. It does not survive switching the programme's Status off: with it off, an order reaching a reversing status takes nothing back, and switching it on again does not reach back to that order.
- OpenCart's own Remove Reward button is never blocked. It deletes the order's reward rows, and the balance is right at once because it is core's own sum. Loyalty keeps its matching row as history and counts it under Needing attention on its screen.
Spending points at checkout¶
- Loyalty or OpenCart's own Reward Points total, not both. While core's Reward Points order total is switched on, Loyalty's line at checkout and its control on the cart stay away, so a customer is never shown two Reward Points lines that disagree. Earning carries on. A fresh OpenCart installation ships with core's total switched on. Both of Loyalty's admin screens say so and link to it; neither switches it off for you, and uninstalling Loyalty does not switch it back on.
- The exchange rate is one number per store. It says how many points buy one unit of your store's default currency, the same for every customer group. An order in another currency shows the discount in that currency, the way OpenCart shows any discount. The cart's control moves in steps of that rate, one unit of currency at a time.
- An order keeps the rate it was created at. Changing the rate while a customer is at checkout never changes what their order already quoted, and the admin order editor keeps the points and the rate the order was placed with.
- Points come off the goods and nothing else. The discount is spread over every product line in proportion to its value, and each line's percentage taxes come down with its share, the way a coupon's do. Shipping is never paid with points. Most of an order points may pay caps the share, measured on the goods before shipping and tax, and however it is set an order reaches zero and never passes it.
- Any product can be paid for with points. A product's own Reward Points figure is never read when spending.
- Below the minimum balance the control does not appear at all. So does a guest's cart, and so does a cart whose customer has nothing to spend.
- Points leave the balance when OpenCart confirms the order, not when the customer moves the slider. An order that is never paid for is never deducted. An order cancelled or voided after it was confirmed gives the points back as a movement of its own.
- Two orders at once can spend the same points. A customer with two tabs open can choose the same points in both. The second order is deducted in full rather than flagged as fraud, the balance goes below zero, and the log says so. No setting prevents it.
Expiry¶
Expiry is off until you switch it on, and when it is on:
- Nothing expires without a warning email first, and the warning has no switch. An email that fails to send records nothing, so that customer cannot expire and is warned again on the next pass. A store whose mail is not working expires nothing at all. That is the rule working, not a fault.
- The notice is at least as long as you set, never shorter. The date the email names is never sooner than the warning period after it was sent, and a warning delayed by a backlog pushes the date later rather than shortening anybody's notice.
- Only points Loyalty awarded can expire. Points added by hand on a customer's Reward Points tab, or earned before Loyalty was installed, never do, however old they are. So a balance can be part expirable and part not. The email and the points page name the part that will go, never the balance, and say in one further line that the rest stays.
- Only a points movement counts as activity. Earning, spending, a reversal or points another extension awarded through Loyalty restart the clock, and clear a pending warning at once. Signing in, browsing, an order that earned nothing and a change made by hand on the Reward Points tab do not.
- The window runs from 12 to 120 months, and the warning from 14 to 365 days. A figure outside either is saved as the nearest one inside it.
- Switching expiry on expires nobody straight away. The first pass after you switch it on only warns: it emails the customers whose window has already run out, and expires nobody, so nothing can expire retroactively. Expect those emails within the hour of saving, on a store whose scheduled pass runs.
- Switching it off stops further expiry and brings back nothing. Expired points do not come back.
- The warning goes whether or not the customer subscribed to your newsletter, and has no unsubscribe link. It is a notice about their own balance; an opt-out would quietly mean that customer's points never expire. Its words are shipped, not configurable, and no copy goes to the store.
- A backlog takes several passes. Each hourly pass warns at most 50 customers per store, and expires at most 50. Birthdays and the repair of failed writes are capped the same way, 50 per store per pass.
- A customer's expiry follows their own store's settings. One balance, one clock, set by the store their account belongs to.
The hourly scheduled pass¶
Loyalty registers one task on OpenCart's own scheduler, hourly, under Extensions → Cron Jobs. It repairs failed writes, pays birthday bonuses and runs expiry. Earning, spending and reversal do not depend on it: they happen when the order changes status.
- It only runs when your store's cron runs. If nothing on your host calls
OpenCart's
cron.php, none of those three things happens. Nothing is lost: birthdays and expiry catch up when it next runs, without paying twice. - On OpenCart 4.1.0.4 it runs only from a crontab line of your own.
That release's
cron.phpstops inside OpenCart's own code before any extension is reached (see requirements).php extension/loyalty/loyalty.phpruns the same pass instead; see install. Without it, nothing expires, nobody is warned, no birthday bonus is paid and a failed write stays unfinished. Earning and spending work normally, and the Loyalty screen says so on such a store. - A failed write is repaired, never duplicated. Loyalty writes its own row first and OpenCart's reward row second. If a request dies in between, the customer's balance does not move, and the pass writes OpenCart's row an hour or more later, adopting one that was already written rather than adding a second. Until then it is counted under Needing attention.
- It adds one index to a table Loyalty does not own. OpenCart's reward table ships with no index on the customer, so core's own cart page reads the whole table for every signed-in shopper. The pass adds that index once, only if no index on the customer is there already, and nothing ever removes it. If your host refuses the change, the pass logs it and carries on.
- Its own route cannot be called from a browser. A web request to it is
answered Not found. OpenCart's scheduler is another matter: its
cron.phpsits at the store's root, and anyone who requests it starts whatever task is due, this pass included. That costs nothing, because the pass runs only when it is due, holds a lease while it runs, and does each thing once.
Other extensions awarding points¶
Any installed extension can award or deduct points through one call, described on the award seam. Review Requests uses it to pay for an approved review.
- Each movement happens once. A caller supplies a key, and a second call with the same key writes nothing, even when two requests race.
- Nothing checks who is calling beyond the code running in your store. Any installed extension may award or deduct points, the way any installed extension may already write to OpenCart's own tables.
- A store with the programme switched off refuses every award, from any caller. A store earning nothing on orders still accepts them.
Uninstalling and updating¶
- Uninstalling removes Loyalty's event rows and its scheduled task, and nothing else. No table, no row and no copy of your settings is deleted, because OpenCart performs an update as an uninstall followed by an install, and what is at stake is every customer's points history.
- Your customers' points stay in OpenCart's own reward table after uninstalling, and remain their balance. Uninstalling does not switch OpenCart's own Reward Points total back on, so the points cannot be spent until you do.
- An update keeps every setting, including whether the programme and earning are on. See the changelog for the one exception.
The API, and what it can read¶
Off until you switch it on, under API on the default store's settings, and
while it is off every route answers as though this extension had no API at all.
Two read resources: movement is one entry in the points ledger — an earn, a
spend, a reversal, an expiry — with where it came from and whether it still
counts, and balance is one customer's points as OpenCart holds them and when
the expirable part goes. The API is generated from what the
extension actually answers.
- It only ever reads. Nothing can award, spend, reverse or expire points through it; awarding stays with the one call other extensions use, inside your store.
- Points are not money. Balances and movements are whole numbers of points, with no currency and no value attached. The one amount is the order total an award was worked out from.
- A balance is OpenCart's own figure, so it includes points you added by hand and points from before you installed Loyalty, exactly as the customer's points page does.
- "Changed since" works for movements, from this version on. Every change to a movement — posted, voided, orphaned, erased — moves its modified date. Movements from before you updated to 1.1.0 carry no date until they next change, so an integration starts with one full walk. Balances have no such date, because OpenCart's own reward rows have none; walk them.
- The expiry date is a forecast until the warning goes out, worked out the way the customer's points page works it out. A date in the past means the scheduled pass has not run, and nothing expires without it.
- An erased customer's movements stay, and name nobody. Their customer is emptied and the state reads erased, as below. A movement another extension awarded keeps the key that extension wrote for it.
- The credential is OpenCart's own API user, and it is not scoped. One is enough to read every customer's points on every store, and every other extension's API and core's own order API besides. Restrict it by IP address under System → Users → API, and treat it the way you would treat an admin login.
- Nothing records that anybody read anything. No read log, no per-credential audit trail, no rate limiting.
Personal data¶
What Loyalty holds about a person, column by column, is on what this holds about a person.
- Deleting a customer erases their Loyalty history in the same request, whether you delete them under Customers › Customers or OpenCart's GDPR erasure does. Their movements stay, with the customer emptied and marked erased, so the ledger still explains the orders it came from and stops counting toward anything; their summary row goes. The keys Loyalty itself made from the customer's account number — for a welcome bonus, a birthday, an expiry — are rewritten to name the movement instead, so an emptied row no longer points back to the account by number. A key another extension wrote through the award call is kept as that extension wrote it.
- The remove-everything screen does to every customer at once what deleting one does. The Remove everything held about a person screen linked from Loyalty's settings counts what Loyalty holds, and pressing it deletes every customer's summary row and empties the customer from every movement, marking it erased. It reports what the database says changed, so pressing it a second time reports that nothing was left to remove.
- It leaves OpenCart's own reward rows alone, on purpose. Those rows are every customer's balance, on the account page, at checkout and on the customer screen, and deleting them would take points away from every customer rather than erase anything. So balances stand after the purge, but the points earned before it can no longer expire: expiry counts only movements that are not erased. One person's reward rows go when that customer is deleted, and that is OpenCart's own erasure rather than Loyalty's.
- On OpenCart 4.1, up to 4.1.0.3, OpenCart's own GDPR erasure stops partway. It deletes the account and fails before the rest, while marking the request complete. Loyalty's erasure runs before that point, so Loyalty's own rows are handled; OpenCart's reward rows for that customer are left behind by core. See what core's own GDPR feature does.
Language¶
Loyalty talks to your customers on the points page in their account, on the cart and checkout, and in the expiry warning email, so what language those are in is a question about your shoppers before it is one about your admin. Both halves are answered, and counted, on the shared language promise page.
- Every word of ours is translated as far as somebody has read it and no further, and the rest is English. A string a named reviewer has signed off is served in the language asked for; a string nobody has read is served in English rather than blank; and a string whose English has since been reworded goes back to English until it is read again.
- The line written into OpenCart's reward table is written once, when the points move. Core's table has no language column, so what core's own Reward Points tab shows for a movement is fixed at that moment: the order, welcome and birthday lines in your store's default language, and Points expired in English. Loyalty's own points page labels its own rows again in the customer's language each time it is shown. See reference.
- The expiry email goes in the customer's own language where Loyalty ships one, and in English otherwise.
Where this page stops¶
Behaviour that is not built yet is not described here as though it were. There are no tiers, no referral bonus, and no points for anything but orders, sign-ups and birthdays unless another extension awards them. There is no accounting valuation of what the points you owe cost your business. If you are evaluating Loyalty against a requirement you do not find on this page or on the overview, assume it is not there and ask rather than inferring it.