The $0/month stack for paid browser extensions.
A complete launch kit: an MV3 extension with offline licensing, a Cloudflare Worker that turns Paddle payments into license keys, and a checkout site. No servers. No database bill. No sales-tax filings.
One-time purchase · lifetime updates · unlimited products · 14-day refund
Team or building for clients? Agency, $399 one-time →
no license stored
- Basic mode
- Advanced mode
- Export data
- Unlimited items
Edge cases: tampering & outages
Live demo: real ES256 keys signed and verified in your browser with a keypair generated for this page. Nothing leaves the tab.
Your browser doesn't support WebCrypto, so the live demo is off.
Three things no other extension kit gives you together
Other boilerplates say "connect Stripe, add Firebase, host a backend" and leave you the hosting bill, the uptime and the VAT.
- Paddle checkoutBuyer pays; Paddle handles the tax
- Cloudflare WorkerWebhook in, ES256 key signed
- License keyEmailed and stored in the extension
- Your extensionVerifies offline with the public key
$0/month, honestly
GitHub Pages hosts the site. Cloudflare Workers + KV run licensing free for roughly a thousand daily-active paying users, then $5/month. Paddle only takes a cut of sales. Your fixed cost is a domain (~$12/year).
Merchant of record built in
Paddle is legally the seller: it collects VAT, GST and sales tax worldwide, issues invoices and handles refunds and chargebacks. With Stripe those are yours. You never register for or file sales tax. (Income tax on what you earn is still yours.)
Pro works when your server doesn't
License keys are ES256-signed JWTs verified inside the extension with an embedded public key. No "ask the server if the user is Pro" — and no one-star reviews when a backend hiccups.
What's inside
One private repository, five packages, one config file. Everything reads from paidextension.config.json. Real code, straight from the repo:
TypeScript MV3 extension
- Popup, settings, upgrade flow, sample content script
licensing/module: offline key verification, device seats, trials, checkout polling, offline grace- Config-driven feature gates —
hasFeature('exportData') - Firefox manifest variant, Edge-ready packaging
- Vitest suite, ESLint, strict TypeScript, webpack
// Entitlement derives from the signed license, never a stored flag.
export async function isPro(): Promise<boolean> {
return (await getEntitlementStatus()).tier !== 'free';
}
export async function hasFeature(feature: string): Promise<boolean> {
const { tier } = await getEntitlementStatus();
return featuresForTier(tier).has(feature);
}
// verify.ts — offline ES256 check with the embedded PUBLIC key
const pubKey = await crypto.subtle.importKey('jwk', LICENSE_PUBLIC_JWK,
{ name: 'ECDSA', namedCurve: 'P-256' }, false, ['verify']);
return crypto.subtle.verify({ name: 'ECDSA', hash: 'SHA-256' },
pubKey, signature, data);
Cloudflare Worker licensing backend
- Verifies Paddle webhooks, issues signed keys for monthly, yearly and lifetime plans
- Seat registry in KV, short-lived install-bound entitlement tokens
- Revokes on refund, chargeback and cancellation
- Abuse-limited free trials, per-route rate limits
- License-key emails via Resend, idempotent on retries
- 95+ tests, no framework, deploys with
wrangler deploy
const claims = await verifyLicenseKey(body.key, env.LICENSE_PRIVATE_JWK);
if (!claims) return json({ error: 'invalid_key' }, 400, cors);
// 'unknown' = couldn't confirm; never lock a payer out over our outage.
const status = await licenseStatus(claims.sub, env);
if (status === 'cancelled' || status === 'expired') {
return json({ error: 'subscription_inactive', status }, 403, cors);
}
const seats = await readSeats(claims.sub, env);
const known = seats.installs.some((i) => i.id === installId);
if (!known && seats.installs.length >= MAX_SEATS) {
return json({ error: 'seat_limit',
seats: { used: seats.installs.length, max: MAX_SEATS } }, 409, cors);
}
const ttl = now + ENTITLEMENT_TTL_SECONDS;
const token = await signEntitlement(claims, installId, ttl, env);
Static checkout & customer site
- Landing, Paddle overlay checkout, license-key page, device management
- Privacy, terms, support pages with refund policy
- Strict per-page CSP, HTML validation, link and CSP checkers
- Deploys to GitHub Pages on push
// The extension creates the Paddle transaction server-side (Worker
// /checkout) and opens this page with ?txn=<id>&env=<sandbox|production>
// This page only renders Paddle's overlay for that transaction.
var config = window.SITE_CONFIG || {};
var PINNED_TOKENS = config.paddleTokens || {};
var TXN_PATTERN = /^txn_[a-z0-9]{1,64}$/;
var params = new URLSearchParams(window.location.search);
var txnId = params.get('txn');
var env = params.get('env') || 'production';
var token = Object.prototype.hasOwnProperty.call(PINNED_TOKENS, env)
? PINNED_TOKENS[env] : '';
Agent-native
- Step-by-step skills for Devin, Claude Code, Cursor and Codex: setup, publish, test
AGENTS.mdwith the invariants an agent must not break- Hand the repo to your agent: "rename to X, set prices, deploy, submit"
---
name: setup
description: Take a fresh clone of the kit to a working paid extension —
rebrand, signing keypair, Paddle sandbox, Worker deploy, GitHub Pages,
and a verified trial → purchase → activate loop.
---
# Setup
Ask the user for the product facts once: name, description, company,
support email, site origin, seats per license, trial days.
Never paste a secret into a tracked file — `wrangler secret put` only.
## 1. Rebrand · 2. Keypair · 3. Paddle sandbox
## 4. Worker · 5. Site + domain · 6. Prove the loop
Rebrand, validate, release
npm run initrewrites the entire repo for your product from a few answersvalidate:configkeeps manifests, Worker and site consistent;generate:listingdrafts store copy and a privacy policy- CI on every PR; tag-triggered release builds store zips with production config
- Pages deploy for the site, Wrangler deploy for the Worker
# Rebrand the kit from the sample "Acme Extension" to your product.
npm run init # interactive
npm run init -- --from answers.json # non-interactive (agents, CI)
npm run validate:config && npm test # manifests, Worker, site agree
# release.yml — push tag v1.2.3 → store zips + GitHub release
on: { push: { tags: ['v*'] } }
steps:
- run: npm run validate:config
- run: npm run lint && npm run typecheck && npm test
- name: Build with production payment config
run: npm run build && npm run check:manifest && npm run package
Try npm run init: answer a few questions and watch the config, manifest and popup change
More answers
Pro unlocks advanced mode, export and unlimited items.
Try one before you buy
Marker, a highlighter extension built from the kit
- Free tier, 7-day trial, and Pro unlocked by a Paddle sandbox checkout (card 4242 4242 4242 4242)
- Server-verified sharing, and revocation when you cancel
- Product code is public — about 40 lines of it touch licensing
How it compares
Against extension boilerplates and hosted payment SDKs, including what each leaves you per month at your numbers.
| PaidExtension | Typical boilerplate (ExtensionFast, extFast, TurboStarter…) |
Hosted payment SDKs (ExtensionPay, crxpay…) |
|
|---|---|---|---|
| You keep, per month on $1,160 of sales | $0 | $0 | $0 |
| Fees, infrastructure & tax, per month | |||
| Sales tax / VAT handled | Yes — Paddle MoR | No — Stripe, you file | No — Stripe, you file |
| Works when backend is down | Yes — offline ES256 keys | No | No |
| Device seat limits | Yes, self-serve release | No | No |
| Refund / chargeback revocation | Automatic | DIY | Yes |
| Own the code, no vendor | Yes | Yes | No — hosted dependency |
| AI-agent setup/publish/test skills | Yes | No | No |
Assumptions behind the monthly numbers
Illustrative, from published rates at the time of writing: Paddle 5% + 50¢ per sale (tax included); Stripe 2.9% + 30¢; Stripe Tax 0.5% per transaction; Cloudflare Workers free tier 100k requests/day, then $5/month. Competitor details as published on their sites; check current terms before deciding.
Pricing
Pay once, build as many extensions as you like. The tier only sets how many developers may use the kit.
Solo
$149$99
one-time, one developer
$50 off at launch, for the first 50 customers
- One developer, any number of products
- Personal or company projects
Agency
$399
one-time, unlimited developers
- Any number of developers and contractors at one company
- Build and hand over products for clients
- Pay on Polar
- Connect GitHub in the customer portal
- Accept the invite, usually within a minute
- Clone and run
npm run init
- Unlimited end products, commercial use
- Lifetime updates
- Setup, licensing and publishing guides, plus agent skills
- Support via GitHub Issues and email
14-day refund on both tiers, no questions asked: email [email protected]. Payments are handled by Polar (merchant of record); you'll get a proper invoice.
Questions
What exactly do I receive?
Access to a private GitHub repository containing the full source: extension/, worker/, site/, scripts/, docs/, .agents/skills/ and the GitHub Actions workflows. After purchase you connect your GitHub account in the Polar customer portal and the repository invitation arrives within a minute; on Agency, share it with your developers as the license allows. Updates arrive as commits.
Do I need to know Cloudflare or Paddle already?
No. The setup guide (and the setup agent skill) walks through creating the Paddle prices and webhook, deploying the Worker and pointing your domain at GitHub Pages. Budget an afternoon for a first run, mostly in the Paddle and Cloudflare dashboards. Paddle's production approval takes a few days; the sandbox covers everything until then.
Can I use React / WXT / Plasmo?
The extension scaffold is plain TypeScript + webpack so there's nothing to fight. The licensing/ module has no UI dependencies and drops into any MV3 project; the Worker and site are independent of the extension framework.
What does the license allow?
Unlimited commercial end products, forever, including all updates. The tier sets who may work with the kit's files: Solo is one developer; Agency is any number of developers at one company, including work delivered to clients. You may not redistribute the kit itself or use it to build a competing kit. Full text on the license page.
How is this "agent-native"?
The repo ships an AGENTS.md plus three skills in .agents/skills/ — setup, publish and test — written as checklists an AI coding agent can execute. They encode the things that usually go wrong: service-worker constraints, CSP rules, Paddle sandbox quirks, store-review requirements.