Skip to content
PEWES ENERGY

Engineering

A loyalty scheme with no points

9 min readPEWES ENERGY

Grape Madness 250 ml can, front

There is a loyalty scheme on this site and it has no points, no wallet, no balance and nothing to redeem. Customers do not collect anything and there is no screen where they exchange one currency for another. Twice a year, or four times a year if they buy enough, a voucher appears in their account with their name on it.

That design was chosen deliberately, and the reasons are worth going through, because loyalty is the feature most often bought as a plugin and most often quietly abandoned six months later.

Why not points

A points scheme creates a second currency. Once it exists, somebody has to decide what a point is worth, what it costs to earn, what happens to points on a refund, whether they expire, what the liability on the balance sheet is, and what to tell a customer holding four hundred points on the day the scheme changes.

Every one of those is a real question with a real answer, and together they are a project rather than a feature. Worse, they are a project that mostly produces a number the customer does not understand and cannot spend today.

The alternative is to skip the currency. Measure what somebody actually spent, decide on a schedule whether that earns something, and if it does, put a real voucher in their account with a real value in euro. No conversion rate, no balance, nothing to explain.

The three levels

Spend is measured, a level falls out of it, and the level decides two things: how much the voucher is worth and how often one arrives.

  • Spark begins at two hundred and fifty euro. Five euro off, in March and September.
  • Surge begins at five hundred and one. Fifteen euro off, in March and September.
  • Overdrive begins at one thousand five hundred and one. Fifteen euro off, four times a year, in March, June, September and December.

Notice what the top level actually buys. It is not a larger discount, it is a more frequent one. That is a deliberate choice: frequency rewards the habit rather than the size of one order, and a customer who receives something quarterly thinks about the brand quarterly.

The measurement is the interesting part

Spend is counted two ways at once, and the customer gets whichever is kinder.

The first window is the calendar year to date. The second is the rolling twelve months ending today. The level is taken from the higher of the two.

This solves a problem that annoys people about every annual scheme. A customer who spends heavily in November and December and then reaches January has, under a calendar year rule, nothing. Under a rolling rule they keep what they earned. Under a calendar rule somebody who spends steadily from January gets credit earlier in the year. Running both and taking the better of the two means the scheme is never the reason somebody is disappointed, and it costs one extra database query.

Two more details matter more than they look:

Only paid orders count. An order that is pending payment or has failed contributes nothing. This sounds obvious and it is the single most common defect in loyalty plugins, which frequently count the order at checkout and never look again.

Refunds are subtracted. The query sums the orders, then sums the refunds attached to those orders, and uses the difference. A customer who bought six hundred euro of drinks and returned three hundred of them has spent three hundred. Any scheme that does not do this can be farmed, and it will be.

Sending the same voucher twice is the real risk

A scheduled job that issues money has one catastrophic failure mode: running twice. Server restarts, overlapping cron runs, a manual trigger by somebody checking whether it works. If two runs overlap, every eligible customer receives two vouchers, and there is no polite way to take one back.

The defence here is to make the claim before the work. Before creating a voucher, the job writes a marker against the customer for that specific period, using an operation that succeeds only if no such marker exists. If the write fails, somebody already claimed it and this run moves on. If the voucher creation then fails, the marker is removed so a later run can retry cleanly.

That is the same pattern as the booking lock described elsewhere on this blog, and it is the same pattern used to promote reseller calls. Letting the database enforce uniqueness, rather than checking first and writing second, is the difference between a system that is correct and one that is usually correct.

It also has to finish

The job walks customers in batches of three hundred and stores a cursor after each one. If it is interrupted, the next run picks up where it stopped rather than starting again. On a shop with forty customers that is pointless. On a shop with two hundred thousand it is the difference between a feature that works and one that times out every night and silently issues nothing.

Building it that way from the start costs an hour. Discovering you need it costs a rewrite and an apology.

The birthday voucher

Customers can add their birthday in their account details. On the day, a five euro voucher appears.

Only the day and month are stored, never the year, because the year is not needed for anything and storing an age you do not need is a liability rather than an asset.

The one genuinely fiddly case is the twenty ninth of February. Somebody born on that date has a birthday in three years out of four, and a naive implementation silently never sends them anything. Here, in a year that does not have a twenty ninth, the job issues their voucher on the twenty eighth. It is four lines of code and it is the kind of detail that decides whether a feature feels considered or cheap.

What a voucher actually is

Every voucher these systems produce is an ordinary WooCommerce coupon, and that choice carries most of the safety.

  • It is locked to one email address. Posting the code publicly achieves nothing, because it will not apply for anybody else.
  • It can be used once, by one person. Both limits are set, because they guard different failure modes.
  • It is a fixed amount off the cart, not a percentage, so the cost to the business is known before it is issued.
  • It carries its own provenance. Who owns it, why it was issued and which scheme produced it are stored on the coupon, so every voucher in the system can be traced back to the rule that created it.

Because they are real coupons, they appear in the WooCommerce reports, they work in the cart without special handling, and the finance side of the business sees discounts in the place it already looks for discounts. Nothing about them is bespoke except the rule that decided to create them.

What changes for a different business

The structure transfers almost unchanged. What moves is the definition of the thresholds and the rhythm.

A shop with low order frequency, furniture or equipment, should measure differently. Spend over twelve months is the wrong signal when somebody buys once every three years; the useful trigger is the anniversary of a purchase, or a consumable running out.

A business with high frequency, coffee or groceries, can afford a shorter cycle and smaller amounts, because the voucher needs to arrive while the habit is still warm.

A services business usually wants the level to change the price rather than to produce a voucher, which is exactly what the reseller tiers on this site do. The measurement code is identical; only the consequence differs.

Anybody with a birthday field already collected is a day of work away from the simplest retention mechanic there is, and most of them have never used it.

The questions that decide the build

  • What are you rewarding, size or habit? Amount rewards size, frequency rewards habit. Pick one on purpose.
  • What happens on a refund? If the answer is nothing, the scheme is farmable.
  • Does the reward expire? An expiry raises redemption and annoys people. It is a commercial decision, not a technical one.
  • What does a customer see before they qualify? Progress towards the first level is most of the motivation, and it is free to show.

None of that is hard to build. All of it is hard to change after launch, which is the reason it belongs in the first conversation rather than the third.

What the customer sees

There is a Levels tab in the account. It shows the current level, what was spent in each of the two windows, and how far there is to go before the next one. Progress is drawn against a ceiling so that the bar means something rather than filling instantly.

Showing progress before somebody qualifies is the part that does the work. A customer at one hundred and eighty euro who can see that seventy more reaches the first level has a reason to think about the next order. The same customer, shown nothing until they qualify, has no idea a scheme exists.

That is also why the thresholds are published rather than hidden. A loyalty scheme whose rules are secret cannot motivate anybody, because nobody can act on a rule they cannot read.

Where this sits next to discounts you already run

Most shops already discount. Seasonal sales, a welcome code, a recovery code for an abandoned basket. A loyalty scheme is a fourth thing, and it is worth being clear about how it differs.

A sale discounts everybody, including the people who would have paid full price. A welcome code buys a first order. A recovery code rescues one basket. None of them is aimed at the behaviour that actually makes a shop profitable, which is somebody buying for the fourth time without being prompted.

That is the only thing this scheme pays for. It does not discount the first order, it does not run a sale, and it does not fire at a moment of hesitation. It arrives because somebody has already been a good customer, which is the cheapest revenue a shop has and the one most businesses spend nothing on.

It is also, as a consequence, far cheaper to run than it appears. A voucher that is issued twice a year to customers above a real threshold is a small and predictable cost, and every one of them is attached to spend that has already happened.

The coupons as a ledger

One more property worth naming, because it is a by-product rather than a feature.

Every voucher this system issues carries who owns it, which scheme created it, what it was for and how much it was worth. Loyalty, birthday and referral rewards all use the same shape. That means the full history of everything the business has given away is a single query against the coupon list, broken down by reason.

Most shops cannot answer that question without exporting orders into a spreadsheet. Being able to answer it in a second is not why the system was built this way, but it is the kind of thing that turns out to matter the first time somebody asks whether the scheme pays for itself.

This shop is a working demonstration of what a headless WooCommerce build is capable of, and everything described here is running on it. If you are weighing a build of your own, it was made by Pavels Web Solutions.

Back to the journal

Keep reading.

Engineering8 min read

Paying for customers, not for signups

Every customer here has a referral code, and the reward arrives when the person they sent places their first paid order. The timing is the whole design, and the rest is about fraud.

Read the note

Engineering8 min read

A booking system is a scheduling system

The reseller call booking on this site is, almost exactly, what a consultancy sells and what a hotel sells. Here is what is underneath it, and what actually changes when you charge for the slot.

Read the note

Engineering8 min read

One catalogue, five readers

The same WooCommerce data on this site is read by the shop, a Google Shopping feed, a reseller API, AI assistants and search. One source of truth, and no duplication. That only works if it is decided early.

Read the note