Two billing models in one product, and neither one pretending the other doesn't exist
A content platform sold both by subscription and as a lifetime deal, where a redeemed code and a monthly plan resolve to the same entitlement, with no parallel systems.
How the work was scoped
- Industry
- Media & Publishing
- Duration
- 18 weeks
- Cooperation model
- Time and materials
Client name withheld under NDA. Engagement details are shown to the extent our agreement permits.
One entitlement, two sources
A redeemed code and an active subscription resolve to the same answer about what an account can do.
Entitlement is resolved once from whichever source granted it, so the rest of the application never asks how an account paid. A redeemed lifetime code and an active Stripe subscription both write into the same entitlement record with the same shape, which is what stops the codebase filling up with the branch that eventually gets one of the two cases wrong.
- One entitlement record, whatever granted it
- Code and subscription write the same shape
- No branch anywhere asking how an account paid
The resolved answer to what this workspace can do, each capability traced back to the source that granted it: a Tier 2 lifetime code and a monthly add-on, both writing the same three-field shape into one record.
Redemption with an audit trail
A Tier 2 code going in, with every earlier attempt on this workspace listed below it (redeemed, expired, already used) and the audit record of code, account, timestamp and campaign.
Codes are single-use and every redemption is recorded, so a disputed code is settleable.
Codes are single-use and redemption is atomic, so two people submitting the same code at the same moment resolve to one winner and one clear refusal, never two entitlements. Every redemption records the code, the account, the timestamp and the source campaign. That's what makes a disputed code an answerable question instead of a judgment call.
- Atomic single-use redemption; concurrent attempts resolve cleanly
- Code, account, timestamp and campaign recorded on every redemption
- A disputed code is settled by the record, not by judgment
Billing without a special case
Lifetime and subscription records sit in one view; support doesn't need to know which they're looking at.
Support sees one billing view listing both kinds of account, with the source shown as a field on the same screen. Nobody has to know in advance whether they're looking at a lifetime holder or a subscriber, which was the point: the deal brought in thousands of accounts at once, and a support flow with a special case would have failed on volume alone.
- One billing view; source is a field, not a separate screen
- Support never needs to know which kind first
- Designed for the volume the deal was about to deliver
Support's one billing view: lifetime and subscription accounts in the same list with the source as a field, and one workspace's code that never renews and add-on that bills monthly, in the same ledger and totalled together.
Writing against Tier 2: tables and embeds stay locked, the side panel shows each capability check and names the code that decides it, and the SEO checks come from the add-on.
Gated on capability, not plan name
The editor checks the entitlement, so pricing changes never reach into the product.
The editor and every other gated surface check a capability (can this account publish, can it exceed the document limit), never a plan name. That means pricing can be renamed, repackaged or restructured without an edit reaching into product code, and it's the reason the tier reshuffle after the launch took an afternoon instead of a sprint.
- Gates ask for a capability, never a plan name
- Pricing can be restructured without touching product code
- The post-launch tier reshuffle cost an afternoon
Built for the spike
A lifetime deal is a load event with a date on it, and the launch was planned as one.
A lifetime deal is a load event with a date printed on it, so it was planned as one. The redemption path was load-tested at several times the expected peak, signup and redemption were separated so a failure in one couldn't block the other, and the database had its indexes built for the specific queries launch day would hammer, not for the steady state.
- Redemption path load-tested at several times expected peak
- Signup and redemption separated so one can't block the other
- Indexes built for launch-day queries, not the steady state
The launch desk for week one: no downtime, zero codes redeemed twice, signups and redemptions on separate paths drawn to shape with no scale, and the indexes built for launch-day queries.
What we were brought in to do
The content platform launched on a lifetime-deal marketplace and kept selling subscriptions afterward. We built the platform so both kinds of customer resolve to one entitlement model instead of two.
A content platform launching on a lifetime-deal marketplace while continuing to sell subscriptions. That combination brings thousands of accounts in two weeks and then leaves two permanently different kinds of customer in the same product. The engagement was scoped around getting the entitlement model right before the launch date, while it could still be done cleanly.
Full-Stack Engineering
Where the old way broke
- 01
A lifetime deal brings thousands of accounts in two weeks, and if the code redemption path is bolted on beside subscriptions the two disagree immediately: a lifetime customer gets billed, or a lapsed subscriber keeps access because the entitlement lives in two places.
The obvious implementation, a code redemption path beside the subscription check, produces two sources of truth for the same question, and they disagree within two weeks. The specific failures are both bad: a lifetime customer who gets billed, and a lapsed subscriber who keeps access because one of the two checks still returns true.
One entitlement resolved from whichever source granted it. A redeemed code and an active subscription produce the same answer to 'what can this account do', and the billing views show both without treating either as the exception.
What we built together
Made entitlement a single resolved value, not a check against two systems
Resolving entitlement once was settled before the launch date was set, because retrofitting it with thousands of accounts already redeemed isn't feasible.
Built code redemption as a first-class flow with its own audit trail
Redemption is atomic and single-use, and records the code, account, timestamp and campaign, so a disputed code is settled by the record.
Kept lifetime and subscription billing visible side by side in one view
One billing view lists both kinds of account with the source as a field, so support never needs to know which they're looking at first.
Built the article tooling against the entitlement, never against a plan name
The editor and every gated surface check a capability, never a plan name, which is why the post-launch tier reshuffle took an afternoon.
Planned for the launch spike, because a lifetime deal is a load event with a date on it
The launch was planned as a load event: the redemption path was tested well above expected peak, and signup was separated from redemption.
Phase by phase
Phase 1: One Entitlement
Whatever Granted It
Resolved what an account can do to a single value, so a lifetime code and a monthly plan are two sources of one answer instead of two competing checks.
- Entitlement Model
- Source Resolution
- Feature Gates
Phase 2: Redemption
A Code Is A Transaction
Built redemption as a real flow with validation, single use, and an audit trail, because a code redeemed twice is a support conversation nobody can settle without one.
- Redeem Flow
- Single-Use Enforcement
- Redemption Audit
Phase 3: Billing Side By Side
Neither One Is The Exception
Showed lifetime and subscription billing in the same surface, so support doesn't have to know which kind of customer they're looking at before they can help.
- Unified Billing View
- Lifetime Records
- Subscription Records
Phase 4: The Editorial Side
Articles Against The Entitlement
Built the article authoring and management tooling against the resolved entitlement instead of a plan name, so pricing can change without touching the editor.
- Article Authoring
- CKEditor Integration
- Content Management
Seats: the fifth seat is the last one the code covers, so adding a writer offers both routes out, a Tier 3 code or a monthly seat, either of which raises the same limit.
Operational results after launch
1
Entitlement systems
0
Codes redeemed twice
1
Billing views to learn
No downtime
Launch-week signups absorbed
Entitlement systems and billing views are counts. Codes redeemed twice is zero, guaranteed by atomic redemption, not merely observed. Launch-week signups absorbed with no downtime is a statement about the launch itself: the redemption path handled the deal's volume without a degraded window.
Client name withheld under NDA. Figures are approximate, drawn from the engagement’s own reporting.
About our collaboration
We resolved entitlement to a single value before the marketplace launch date, not after it, which is the only reason the launch spike was uneventful. Redemption, billing views and the editorial tooling were all built against that one value.
Time and materials over eighteen weeks, with the launch spike treated as a deliverable in its own right, planned for instead of merely monitored. The redemption path was load-tested at several times the expected peak, and signup and redemption were separated so a failure in one couldn't block the other on the day.
What we'd carry into the next one
- 01
Two billing models checked separately will disagree within two weeks of launch.
Two checks for one question is the failure: they disagree within two weeks, and both directions of the disagreement are commercially bad.
- 02
Entitlement resolved once is what lets pricing change without touching the product.
Resolving entitlement once is what makes pricing changeable. With capability gates, repackaging is a billing change, not a product change.
- 03
A code with no audit trail is a support dispute you can't settle either way.
An unaudited code can't be settled in either direction, which turns a five-pound support question into a judgment call nobody can defend.
- 04
A lifetime deal is a load event with a date on it. Plan the spike like a launch, because it is one.
The deal has a date, so the traffic is predictable, which makes it the rare load event you can plan for precisely instead of simply absorbing.
One workspace, four conditions
A code and a subscription are two sources of one answer.
A copy studio’s workspace resolved with only its Tier 2 lifetime code, then with a monthly add-on beside it, then when a sixth writer meets the seat limit, and finally when the same code is submitted again. Each capability shows the source that grants it. Switch tabs, or use the arrow keys once one is focused.
The lifetime code is the only source. It writes its grants into the workspace's one entitlement record, and every screen reads that record. What the code doesn't grant stays locked, and each lock names what would open it.
Sources
GM-T2-8KQD-47XR
—
Entitlement recordwrites: 1
Northgate Copy Studio
The resolution
- Load every source for ws_7Hq2Nd
- Tier 2 code: 4 grants
- No subscription on this workspace
- One entitlement resolved
What this workspace can do, and which source grants it
- Publish articlesarticles.publish = true Tier 2 code
- Review steparticles.review = true Tier 2 code
- Documentsdocuments.limit = 250 Tier 2 code
- Seatsseats.limit = 5 Tier 2 code
- Scheduled publishingpublish.schedule = falseNeeds the scheduling add-on
- Headline & SEO checksseo.checks = falseNeeds the scheduling add-on
- Tablesblocks.tables = falseNeeds a Tier 3 code
- Embedsblocks.embeds = falseNeeds a Tier 3 code
4 granted, 4 locked, from one source, in one record.
Why it cannot disagree with billing: the gates ask for a capability and billing lists the sources that granted it, and both read the same record. Step timing here is illustrative.
From whatever granted it to one answer about what an account can do
Entitlement is resolved once. The rest of the application never asks how an account paid, which is what keeps the branch that eventually gets one of the two cases wrong out of the codebase.
- 01 · SourceLifetime code or Stripe subscriptionTwo ways to pay, and both write the same entitlement shape. Neither is treated as the exception.
- 02 · IntakeSignup and redemption, apartSeparated so a failure in one can't block the other; the redemption path was load-tested at several times the expected peak.
- 03 · EngineAtomic single-use redemptionTwo people submitting the same code at the same moment resolve to one winner and one clear refusal, never two entitlements.
- 04 · StateEntitlement and audit in PostgreSQLOne entitlement record per account, and every redemption stores the code, account, timestamp and campaign. Indexes built for launch-day queries.
- 05 · DeliveryCapability gates in Next.jsThe editor and every gated surface ask for a capability, never a plan name, so pricing can change without touching product code.
What a lifetime deal can go wrong on
Code abuse, lost access & billing drift
A code redeems once, and every attempt is on record
Redemption is atomic and single-use, so concurrent submissions of one code resolve to one winner and one clear refusal. Each redemption records the code, the account, the timestamp and the source campaign, so a disputed code is settled by the record, not by judgment.
No second check to disagree
Access comes from one entitlement resolved from whichever source granted it. No subscription check runs beside the code path, so a lifetime customer isn't billed and a lapsed subscriber doesn't keep access because one of two checks still returns true.
Billing shows the same sources the app reads
Support has one billing view listing lifetime and subscription accounts together, with the source as a field. What billing shows and what the gates allow come from the same record, so neither can drift from the other.
Launching a lifetime deal next to your subscriptions? Scope your build in 3 minutes.
Scope your buildNearby engagements
Web PlatformsA writing desk where the manuscript never leaves the machine
A desktop notebook built on Electron, where documents live on disk, the editor is a real rich-text surface, and the AI assistant reads only what the writer hands it.
Book Publishing · 14 weeks
AI & AutomationLong text becomes narrated audio, and the job tells you the truth while it runs
A production tool that pulls text out of a source, narrates it, and reports honestly on a job that takes minutes, without pretending to be instant.
Media & Publishing · 12 weeks
Web PlatformsThree kinds of publication, two comment streams, and one queue that keeps them civil
A market-news publication with announcements, reporting and commentary as separate content types, member profiles, and a moderation queue that can hold a comment while a person decides.
Media & Publishing · 14 weeks
Let's talk
Running a large platform, shaping a first MVP, or getting a product ready for a funding round? Tell us where you are. We'll shape the process around it, and stay with you after launch.














