Manifest V3 setup
Permissions, host_permissions, service worker config — the full reference, line-by-line.
This page is the canonical reference for what your manifest.json needs in order to use crxpay. Copy the relevant block, then read the explanation below if you want to know why each field is there.
The minimal manifest
{
"manifest_version": 3,
"name": "Your Extension",
"version": "1.0.0",
"description": "What your extension does",
"permissions": [
"storage",
"alarms"
],
"host_permissions": [
"https://api.crxpay.io/*"
],
"background": {
"service_worker": "background.js",
"type": "module"
},
"action": {
"default_popup": "popup.html",
"default_title": "Open"
}
}
That's everything required to use the SDK in popups and the background script. Add a content_scripts block only if you also call the SDK from a content script.
Field-by-field
permissions
| Permission | Required? | Why |
|---|---|---|
storage | Yes | The SDK persists the HMAC-signed subscription cache in chrome.storage.local. Without this permission, the SDK can't survive a service worker restart. |
alarms | Yes | We schedule the periodic refresh via chrome.alarms. setInterval does not survive MV3 service worker termination — alarms is the only safe replacement. |
notifications | Optional | Only if you call chrome.notifications.create() yourself — the SDK does not. |
identity | Optional | If you want Google sign-in to pre-fill CrxPay.identify(email). |
host_permissions
"host_permissions": ["https://api.crxpay.io/*"]
This is the only host permission the SDK needs. api.crxpay.io is where every fetch goes. The SDK never talks directly to Stripe — that's our backend's job — so you do not need to allow *.stripe.com.
background
"background": {
"service_worker": "background.js",
"type": "module"
}
"type": "module" is what allows ES module imports in the background script. The SDK ships ESM, so without this you'll see "Cannot use import statement outside a module" the moment Chrome loads the worker.
If you're using a bundler (Vite, Webpack, esbuild) you'll typically still want "type": "module" — it lets you use top-level import instead of bundler-specific shims.
content_scripts (optional)
Only needed if you import @crxpay/sdk/content:
"content_scripts": [
{
"matches": ["https://*/*"],
"js": ["content.js"],
"run_at": "document_idle"
}
]
run_at: "document_idle" is recommended so your content script doesn't slow page load. Use document_start only if you need to gate something before the page renders.
Content Security Policy
If your extension declares a custom CSP, allow Stripe's checkout iframe so the hosted checkout page renders correctly when opened in a sub-window:
"content_security_policy": {
"extension_pages": "script-src 'self'; object-src 'self'; frame-src https://checkout.stripe.com https://js.stripe.com"
}
Most extensions don't need a custom CSP — Chrome's default for MV3 is already strict and includes Stripe by virtue of allowing self-hosted assets.
Bundler configuration
The SDK works with any bundler that emits ES modules. Here are the configs we test against.
Vite
import { defineConfig } from 'vite';
export default defineConfig({
build: {
rollupOptions: {
input: {
background: 'src/background.ts',
popup: 'src/popup.ts',
content: 'src/content.ts',
},
output: {
entryFileNames: '[name].js',
format: 'es',
},
},
},
});
Webpack 5
module.exports = {
entry: {
background: './src/background.js',
popup: './src/popup.js',
content: './src/content.js',
},
output: {
filename: '[name].js',
path: __dirname + '/dist',
},
experiments: { outputModule: true },
output: { module: true },
};
experiments.outputModule is required so Webpack emits an ESM service worker. Without it, the SDK's tree-shaken ESM imports collapse into UMD and "type": "module" becomes a lie.
esbuild
esbuild src/background.ts \
--bundle --format=esm --target=chrome120 \
--outfile=dist/background.js
Repeat for popup.ts and content.ts. --target=chrome120 matches the floor for MV3 features the SDK uses.
Common errors
Manifest changes for crxpay updates
The SDK has a stable manifest contract — you'll never need to add or remove a permission for an SDK upgrade. If we ever need a new permission (we won't lightly), it ships in a major version bump.
Next
- Stripe Connect setup — connect your Stripe account, create products
- MV3 guide — the conceptual model behind these manifest fields
Was this page helpful?
Your feedback shapes what we document next.