Skip to content
PEWES ENERGY

Engineering

Subscriptions we built instead of bought

8 min readPEWES ENERGY

Three PEWES ENERGY cans standing together

There is a subscription on this site. You choose any mix of the thirty eight cans, choose the day of the month the box should land, and pay ten percent less than the shelf price every time. You can pause it or cancel it from your account in one click, with no retention screen in the way.

None of that is unusual as a description. What is unusual is that it was built rather than bought, and the reason why is the most useful thing in this article for anybody planning recurring revenue on WooCommerce.

The plugin question

There is a mature subscriptions plugin for WooCommerce. It is competent software and for a large number of shops it is the correct answer. We did not use it, and the reason was not price and not pride.

A subscriptions plugin is built around a subscription as a product. You sell a thing, on a schedule, and the plugin manages renewals of that thing. Our requirement inverted it: the subscription is not a product, it is a standing instruction about products. The customer builds a basket of any drinks in any quantities and says when it should arrive. The basket can change. The date can change. The contents are not a plan, they are a cart with a calendar attached.

You can force that shape into a plugin designed for the other shape. Everybody who has tried knows what it costs: a layer of custom code that fights the plugin’s own admin screens, its own emails, its own status machine, and its own opinions about what a renewal is. You end up maintaining both the plugin and the thing that disagrees with it.

The honest question is not whether the plugin can do it. It is which of the two you would rather debug at eleven at night.

How it is put together

The design splits responsibility along a line that is worth stealing.

Stripe owns the money. The recurring charge is a Stripe subscription. Stripe decides when to bill, retries a failed card on its own schedule, handles the strong customer authentication that European cards require, and keeps the payment method. None of that is reimplemented, because none of it should be.

WooCommerce owns the commerce. Every cycle, a webhook from Stripe arrives and creates a real WooCommerce order with the right lines, the right prices and the right customer. From that moment it is an ordinary order: it appears in the order list, it produces an invoice, it can be refunded, it feeds the reports, and the warehouse sees it exactly like every other order.

The storefront owns the experience. Building the basket, choosing the date, pausing, resuming, changing the contents, cancelling. All of it on the site, none of it in a plugin’s template.

That split is the whole architecture. Each system does the thing it is genuinely best at, and the seams between them are two webhooks and a scheduled job.

The part that is harder than it sounds

Card payments are asynchronous and that fact leaks into everything.

On this site an order is created in a pending state before payment is attempted. The storefront collects the card with Stripe, and a webhook marks the order paid when Stripe confirms it. That sounds like an implementation detail. It is actually the difference between a shop that stays correct and one that quietly loses orders.

The alternative, marking an order paid because the browser came back to a success page, fails in every direction. The customer closes the tab before the redirect. The bank sends them through an authentication step that takes two minutes. The payment is refused after the page already said thank you. The payment succeeds and the browser never returns at all.

Listening to the payment processor rather than to the browser costs a webhook endpoint, a signature check and a state machine. It is perhaps a day of work and it removes an entire class of support ticket.

Cancellation as a design input

Most subscription software is built to protect the second month. The discount lands on the first order. The cancel control is three screens deep. The billing date is chosen by the company.

This one was built the other way round, and the interesting part is how much that changes upstream. If cancelling is one click in the account, then the discount cannot be an introductory rate that expires, because a customer who notices will leave. If the customer picks the billing date, the fulfilment side has to cope with charges spread across the month instead of a single batch. If pausing is free, the system needs a paused state that is distinct from cancelled and that resumes cleanly.

None of those are technical decisions. They are commercial decisions that become technical requirements, and deciding them after the build is how a project doubles.

What this generalises to

The same machinery, with the product swapped, is a large part of what recurring revenue looks like on WooCommerce.

A box of anything. Coffee, wine, pet food, cosmetics, supplements. The only change is the catalogue.

A membership. The recurring charge is the same; what it grants is access rather than a delivery. A paid membership is a subscription plus a rule that checks whether the account is current, and the interesting work moves to what happens when it lapses.

A retainer. An agency or a maintenance contract billed monthly, with an invoice the client’s finance department will accept. WooCommerce is already good at invoices, and Stripe is already good at collecting.

A service plan with tiers. This is where it gets interesting, because tiers and upgrades are the same problem as the reseller discount levels on this site: an account carries a level, the level changes what the cart charges, and something has to move an account between levels at the right moment.

The questions that decide the estimate

Four answers separate a two week subscription feature from a two month one.

  • Is the subscription a fixed product or an editable basket? Fixed is dramatically cheaper and a plugin may genuinely be enough.
  • Who chooses the billing date, you or the customer? Customer choice spreads billing across the month and changes fulfilment.
  • What happens on a failed payment? Retry for how long, tell the customer what, pause or cancel at the end. This is called dunning and it is a feature, not an afterthought.
  • Can the customer change the contents mid cycle, and does that reprice immediately or next time? This one question has ruined more subscription projects than any other.

The part worth paying for

It is not the recurring charge. Stripe does that and it is close to free.

It is that every renewal lands as a normal order in the same system as everything else, that the price it charges is the price the site promised, and that a customer who wants out is out in one click without anybody writing a retention email. Those three properties are what a subscription built into the shop gives you and a subscription bolted onto it does not.

What the business actually sees

The customer facing half of a subscription gets all the attention. The half that decides whether the feature is worth having is the operational one.

Because every renewal lands as an ordinary WooCommerce order, nothing downstream needs to know that subscriptions exist. The warehouse picks it from the same list. The accountant finds it in the same report. The invoice has the same sequential number as every other invoice. A refund works the way refunds work. When a courier integration is added later, it receives subscription orders without anybody writing a line of code for the case.

That is the quiet argument for putting renewals into the commerce system rather than into a parallel one. Every integration you build afterwards gets subscriptions for free, and every report is correct without a second query.

There is one operational consequence worth planning for. Letting customers choose their own billing date spreads renewals across the whole month instead of concentrating them. That is better for cash flow and worse for anybody who batches picking once a week, so the decision belongs to whoever runs fulfilment rather than to whoever writes the code.

Dunning is a feature, not an afterthought

A meaningful share of subscription losses are not people deciding to leave. They are cards that expired, banks that declined a foreign charge, and limits that were reached on the wrong day. The industry calls this involuntary churn and on most subscription businesses it is larger than the voluntary kind.

Handling it is a sequence of decisions that somebody has to make explicitly:

  • How long to retry, and on what schedule. Payment processors have sensible defaults and they are a good starting point.
  • What the customer is told, and when. A first failure is usually worth a quiet message. A final one needs to be unmissable and needs to contain the link that fixes it.
  • What the subscription does while this is happening. Paused is not the same as cancelled, and a customer who updates a card on day six should resume rather than start again.
  • What happens to the order. An order created for a cycle that never got paid has to end somewhere honest rather than sitting pending forever.

None of that is difficult. All of it is invisible until it is missing, and it is the most reliable place to find revenue in an existing subscription business.

The metric that decides the design

Ask what the expected lifetime of a subscriber is, before anything is built.

If the answer is measured in years, the money is in retention mechanics and the system should be generous: easy pausing, easy changes, easy skipping of one delivery. Every one of those is a reason not to cancel outright.

If the answer is measured in months, the money is in acquisition and the system should be simple. Building pausing, skipping and mid cycle editing for a product people keep for four months is engineering that will never earn its cost back.

That is one question, and the answer changes the scope by a factor of three. It is worth having the argument before the estimate rather than after it.

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

Engineering9 min read

A loyalty scheme with no points

No points, no wallet, no balance. Spend is measured two ways at once and the kinder answer wins, and twice a year a real voucher appears with your name on it. Here is why, and what it takes to make it correct.

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