Chrome extension payments

How do I monetize a Chrome extension without Stripe?

The problem“I want to monetize my Chrome extension, but popular platforms like Stripe and Lemon Squeezy aren't available in my region." (a developer in Ukraine, Chromium Extensions group, August 2025)”

To monetize a Chrome extension without Stripe, you need two things: a checkout that accepts you as a seller, and a way for the extension to know who paid. ExtensionPay, the usual shortcut, connects your own Stripe account, so it only works where Stripe does. Without Stripe, sell through a merchant of record, unlock paid features from a webhook to a small backend you run, or open a company in a Stripe country.

Reviewed by Panche Isajeski, founder of AgentaOS, 19+ years in payments, featured on Indie Hackers. Checked . General information, not financial, tax or legal advice.

The reason

Why doesn't ExtensionPay work in my country?

ExtensionPay's setup starts with "Sign up for ExtensionPay and connect a Stripe account", and it says "Funds are paid directly to your Stripe account" (ExtensionPay). It charges 5% per transaction, and its PayPal option is listed as "Coming Soon". So ExtensionPay needs a Stripe account, and Stripe needs you in one of its countries.

Your countryStripe accountExtensionPay (needs Stripe)Lemon Squeezy bank payoutPaddleAgentaOS payout
N. MacedoniaNot listedNoListedNot on unsupported listYes, EUR or USD
SerbiaNot listedNoListedNot on unsupported listYes, EUR or USD
PakistanNot listedNoListedNot on unsupported listYes, USD
UkraineNot listedNoNot listed (PayPal payouts only)5 regions restrictedYes, EUR or USD

Sources: Stripe global availability · Lemon Squeezy supported countries · Paddle supported countries (Crimea, Donetsk, Kherson, Luhansk and Zaporizhzhia restricted) · AgentaOS payouts: N. Macedonia, Serbia, Pakistan, Ukraine. "Listed" means the seller-country page includes it; each company still reviews your account.

Getting paid

Does Chrome Web Store allow external payments?

Yes, and there is no Google option left. Google deprecated Chrome Web Store payments, and existing items stopped charging money on 1 February 2021; Google told paying developers to move to another payment processor (Chrome Web Store payments deprecation). So Google takes no cut of your sales. The program policies set the rules: "If your Product requires the user to pay to obtain basic functionality, you must make that clear in the description", and "You must clearly identify that you, not Google, are the seller" (Chrome Web Store program policies). With a merchant of record, the checkout and invoice name the merchant of record as the seller; say so on your listing.

Your options

Which route fits my extension?

ExtensionPayCompany abroad + StripeLemon Squeezy or PaddleAgentaOS
Needs your own Stripe accountYesYesNoNo
Works as an individual in N. Macedonia, Serbia, Pakistan, UkraineNoNo, you need the companyCheck their country pages (table above)Yes
You run a backendNoYesYes, or their license keysYes, a small backend or serverless function
How the extension knows who paid`extpay.getUser()` returns `user.paid`Your Stripe webhookTheir webhooks or license keysWebhook to your backend
License keysNoBuild your ownSee their docsNo, you track paid users yourself
Who handles VAT for buyers abroadYouYouThey do, as merchant of recordAgentaOS, as the official seller
Fee per card sale5%, plus Stripe's own feesStripe's rateSee their pricing pages4.5% + €0.50; 6.0% + €0.50 for cards issued outside the EEA

Fees: ExtensionPay · AgentaOS pricing docs. We do not quote other companies' fees here; open their pricing pages.

Honestly: if Stripe takes you, ExtensionPay is the fastest start because you need no backend. If Lemon Squeezy or Paddle accepts your country and you want license keys, use them. AgentaOS fits if you want a merchant of record that pays out to your own bank in 86 countries, accepts individuals, and you are fine running a small backend.

Step by step

How do I set it up, step by step?

  1. Check your country first. Open Stripe's list. If you are on it, ExtensionPay works and you can stop here. If not, skip every tool that says "connect your Stripe account".
  2. Pick the model. One-time unlock, monthly or yearly subscription, or a subscription with a free trial. In AgentaOS, a trial can be 1 to 730 days and the buyer pays nothing until it ends (subscriptions docs).
  3. Know who the user is. Paid status needs an identity: an email sign-in or Google sign-in in your extension, with a session token your backend can check. Without this, no route except license keys can unlock the right person.
  4. Run a small backend. A serverless function plus a database table with `user_id` and `paid` is enough. It needs three routes: `/me` (is this user paid?), `/upgrade` (create a checkout) and `/webhooks` (receive payment events).
  5. Create the product in AgentaOS. Make a free account, create the plan, copy the API key, add your `/webhooks` URL and copy the signing secret from Settings, Developers, Webhooks (webhooks docs).
  6. Wire Upgrade to checkout. The Upgrade button calls `/upgrade`. Your backend creates a checkout with the user's ID in `metadata` and returns the checkout URL; the extension opens it in a new tab. When the webhook arrives, your backend verifies the signature and sets `paid`. The extension calls `/me` and unlocks.
  7. Disclose, test, go live. Say in your Chrome Web Store description which features are paid and who the seller is. Pay once with a test key, then switch to the live key.
Answer

What does the code look like?

A generic sketch. The AgentaOS calls (`checkouts.create`, `webhooks.verify`, `metadata`) follow the documented paywall demo and Express webhook guide; `/me`, `/upgrade` and your database are yours.

// extension: popup.js
const API = 'https://api.your-extension.com';

async function authHeaders() {
  const { token } = await chrome.storage.local.get('token'); // from your sign-in
  return { Authorization: `Bearer ${token}` };
}

async function isPaid() {
  const res = await fetch(`${API}/me`, { headers: await authHeaders() });
  return (await res.json()).paid === true;
}

async function upgrade() {
  const res = await fetch(`${API}/upgrade`, { method: 'POST', headers: await authHeaders() });
  const { url } = await res.json();
  chrome.tabs.create({ url }); // AgentaOS checkout opens in a new tab
}

// backend: server.js (Node + Express)
import express from 'express';
import { AgentaOS, WebhookVerificationError } from '@agentaos/pay';

const agentaos = new AgentaOS(process.env.AGENTAOS_API_KEY);
const app = express();

// Webhook first, with the raw body: verification fails on parsed JSON.
app.post('/webhooks', express.raw({ type: 'application/json' }), async (req, res) => {
  let event;
  try {
    event = agentaos.webhooks.verify(
      req.body,
      req.headers['x-agentaos-signature'],
      process.env.AGENTAOS_WEBHOOK_SECRET,
    );
  } catch (err) {
    if (err instanceof WebhookVerificationError) return res.sendStatus(400);
    throw err;
  }
  if (event.type === 'checkout.session.completed') {
    const userId = event.data.metadata.customerId; // metadata.customerId, see the paywall demo
    if (userId) await db.users.update(userId, { paid: true });
  }
  // also handle subscription.updated / subscription.canceled to lock users again
  res.sendStatus(200);
});

app.use(express.json());

app.post('/upgrade', requireUser, async (req, res) => {
  const checkout = await agentaos.checkouts.create({
    linkId: process.env.PRO_PLAN_LINK_ID,
    buyerEmail: req.user.email,
    metadata: { customerId: req.user.id },
    successUrl: 'https://your-extension.com/thanks',
    cancelUrl: 'https://your-extension.com/pricing',
  });
  res.json({ url: checkout.checkoutUrl });
});

app.get('/me', requireUser, async (req, res) => {
  res.json({ paid: (await db.users.get(req.user.id)).paid === true });
});

Webhooks are signed with HMAC-SHA256 in the `X-AgentaOS-Signature` header, and failed deliveries are retried up to 3 attempts in total (webhooks docs). Do not hand signed-in users the plan's shared payment link: it carries no `metadata`, so your webhook cannot tell who paid.

Example: a €5 monthly plan, paid by card

EEA cardCard issued outside the EEA
Buyer pays€5.00€5.00
AgentaOS card fee4.5% + €0.50 = −€0.736.0% + €0.50 = −€0.80
After the card fee€4.27€4.20

Rates from AgentaOS pricing docs. The fixed €0.50 weighs more on small prices; a yearly plan pays it once. Try your own numbers in the payout calculator.

Before you start

Checklist: before you charge for your extension

  • Country checked on Stripe's list before trying ExtensionPay
  • Lemon Squeezy and Paddle country pages checked too
  • Model chosen: one-time, monthly, yearly or trial
  • Users sign in, so you know who paid
  • Backend with `/me`, `/upgrade` and `/webhooks`
  • Webhook signature verified on the raw body
  • User ID sent in checkout `metadata`, not a shared link
  • Cancellations lock the user again
  • Paid features and the seller named in the Chrome Web Store description
  • A EUR or USD account at your bank
  • One test payment before going live
What AgentaOS cannot do

When is AgentaOS the wrong choice?

  • Stripe takes you. ExtensionPay needs no backend. Use it.
  • You want license keys. AgentaOS has none; you track paid users in your own database. Dodo Payments' own guide shows license keys with activation limits (Dodo guide).
  • You cannot run any backend. Someone must store who paid. If that is a blocker, a license-key product fits better.
  • You want tips or donations. Our Terms do not allow donations or sponsorships (Terms).
  • You need all the cash on day one. New accounts start with up to 15% of each sale set aside for 120 days (Terms, section 9.2).
Where AgentaOS fits

Where does AgentaOS fit?

If Stripe will not take you and your extension is software people pay for once or monthly, AgentaOS is the official seller of each sale. Buyers pay by card, AgentaOS handles VAT and invoices, and you get paid to your own bank. It pays out to 86 countries, accepts individuals, and needs no company abroad. You keep the paywall logic in your own backend.

Change how you sell. Grow revenue 4×.

Answer 3 questions and get a free audit of your price and checkout, from the founder. Free, in 2 working days.

Get my free audit
Questions

Related questions

Does ExtensionPay need a Stripe account?

Yes. ExtensionPay's setup says to connect a Stripe account, and payments go directly to that account. If Stripe does not accept sellers in your country, ExtensionPay cannot pay you. Its PayPal option is listed as "Coming Soon".

Does Google take a cut of Chrome extension sales?

No. Google deprecated Chrome Web Store payments, and existing items stopped charging on 1 February 2021. You use your own payment provider. Chrome Web Store policy asks you to disclose paid features and say that you, not Google, are the seller.

Can I sell a Chrome extension subscription from N. Macedonia, Pakistan or Ukraine?

Yes, without Stripe. Use a merchant of record that accepts your country and a backend that unlocks paying users. AgentaOS pays out to all three, in EUR or USD, and USD only for Pakistan. Check Lemon Squeezy's and Paddle's country pages too.

Do I need a backend to charge for a Chrome extension?

With ExtensionPay, no. Without it, yes: something must store who paid. A serverless function and one database table are enough: the payment webhook sets a paid flag, and the extension asks your backend for it.

Does AgentaOS give license keys for extensions?

No. AgentaOS sends a signed webhook when someone pays, renews or cancels, and your backend stores who is paid. If you need license keys that the extension validates itself, use a provider that offers them.

Free pricing review

Know what to charge before you launch

Paste your product or store link. Pricing Intelligence tells you what to charge and how to package it. Free, no card.

€0 account, no cardSell as an individual or a company