crxpaydocs

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

PermissionRequired?Why
storageYesThe SDK persists the HMAC-signed subscription cache in chrome.storage.local. Without this permission, the SDK can't survive a service worker restart.
alarmsYesWe schedule the periodic refresh via chrome.alarms. setInterval does not survive MV3 service worker termination — alarms is the only safe replacement.
notificationsOptionalOnly if you call chrome.notifications.create() yourself — the SDK does not.
identityOptionalIf 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

Was this page helpful?

Your feedback shapes what we document next.