experiment · shipped

Coupon Application Engine

A framework-agnostic coupon auto-apply engine, built as a plain TypeScript state machine

the constraint

Coupon auto-application has to run inside browser-extension content scripts on arbitrary store checkouts, so the engine can’t assume Vue, React, or any other reactivity layer. Whatever we built had to work in a content script, in a popup, and in whatever UI framework the extension around it happened to use. The engine is plain TypeScript that works in any JavaScript browser environment

what we built

The base class extends EventEmitter and uses its patterns for everything observable. Application runs as a state machine: a coupon run moves from validating and waking through starting, processing and restoring, to a terminal success or failure, with pause and stop available throughout

  1. unavailable
  2. stop
  3. pause
  4. init
  5. validating
  6. waking
  7. available
  8. created
  9. starting
  10. process
  11. restoring
  12. finishing
  13. fail
  14. success

Fourteen statuses cover the whole lifecycle, which sounds excessive until a run gets interrupted halfway through a checkout that has just reloaded itself

reading a checkout we don't control

This is the interesting part. Checkout pages expose totals and coupon fields in wildly different ways, and no selector strategy survives contact with more than a handful of stores. So the engine doesn’t hardcode any of it: values are read through config-driven data retrievers

  • DOM
  • cookie
  • cache
  • localStorage
  • query
  • request
  1. checkout page
  2. retriever
  3. engine
configuration decides which retriever reads which value

Adding a store then means writing configuration rather than writing code, and a store that changes its checkout is a configuration fix rather than a release. That decision is what made per-store coverage a data problem instead of an engineering one

the public API

Consumers subscribe to engine events and read plain properties off the instance:

on(‘UPDATE:status’)
subscribe to lifecycle changes
cachedCoupons
the applied coupons
total
purchase cost after all discounts
result
best coupon and savings
status
where the run currently is
processIndex
index of the coupon being tried, typically rendered as a progress bar

what survived

The engine shipped as part of our extension systems and is still the basis of the coupon integration module in Coupon Genius. It has run in more than one of our extension projects, and Reactive Coupon Integration is the Vue layer built on top of it — the cost of keeping the engine framework-free, paid once, in a wrapper instead of in the engine

The engine’s lineage goes back to a coupon auto-applier we built for a client in 2019 — per-store config files even then

It is not the newest layer of our extension tooling any more; the things we build now abstract this differently. That is the normal shape of infrastructure that worked: it stopped being the interesting part and became the part underneath

Let's talk

Your contact info is sent!

We will contact you soon!