Migrate from ExtPay
Step-by-step guide to switch from ExtPay to crxpay.
Why migrate?
| Feature | ExtPay | crxpay |
|---|---|---|
| Offline subscription cache | No — status disappears on network failure | Yes — HMAC-signed local cache |
| MV3 native | Partial — CORS workarounds needed | Yes — built for MV3 from day one |
| Subscription state machine | Ad-hoc string checks | XState FSM — invalid states impossible |
| Payment processor | ExtPay's Stripe account | Your own Stripe account (Stripe Connect) |
| Entitlements | No — boolean paid/unpaid | Yes — hasEntitlement('pro'), hasEntitlement('ai') |
| Platform fee | 5% (always, from dollar one) | 0% under $2,500 lifetime, then 2.5% — half the rate |
| Analytics dashboard | Basic | MRR, churn, trial conversion, per-customer timeline |
| Test mode | Limited | Full test/live mode with separate Stripe keys |
Migration steps
Step 1: Create a crxpay account
- Sign up at crxpay dashboard
- Register your extension (same Chrome Extension ID)
- Connect your Stripe account
- Create products, prices, and entitlements matching your ExtPay setup
Step 2: Install the crxpay SDK
npm uninstall extpay
npm install @crxpay/sdk
Step 3: Replace ExtPay code
Before (ExtPay):
// background.js
import ExtPay from 'extpay';
const extpay = ExtPay('your-extension-id');
extpay.startBackground();
extpay.onPaid.addListener((user) => {
console.log('User paid!');
});
After (crxpay):
// background.js
import { CrxPay } from '@crxpay/sdk/background';
const client = CrxPay.configure({
apiKey: 'crxpay_pub_YOUR_KEY',
});
client.onPaid.addListener((subscription) => {
console.log('User paid!', subscription.status);
});
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
if (msg.type?.startsWith('CRXPAY_')) {
client.handleMessage(msg, sender).then(sendResponse);
return true;
}
});
Step 4: Replace subscription checks
Before (ExtPay):
const user = await extpay.getUser();
if (user.paid) {
enablePremium();
}
After (crxpay):
import { CrxPay } from '@crxpay/sdk';
const result = await CrxPay.getSubscription();
if (result.ok && result.data.hasEntitlement('pro')) {
enablePremium();
}
Step 5: Replace payment triggers
Before (ExtPay):
extpay.openPaymentPage();
After (crxpay):
await CrxPay.openCheckout('price_YOUR_PRICE_ID');
// or for the default offering:
await CrxPay.openCheckout();
Step 6: Replace login flow
Before (ExtPay):
extpay.openLoginPage();
After (crxpay):
// crxpay uses email identification instead of a login page
await CrxPay.identify('user@example.com');
Step 7: Update manifest.json
{
"permissions": [
"storage",
+ "alarms"
],
"host_permissions": [
- "https://extensionpay.com/*"
+ "https://api.crxpay.io/*"
]
}
Step 8: Handle existing paid users
Existing ExtPay users won't automatically appear in crxpay. You have two options:
Option A: Grant entitlements manually Use the crxpay dashboard to manually grant entitlements to existing paid users via Customers > Grant Entitlement.
Option B: Parallel check during transition Run both ExtPay and crxpay checks during a transition period:
// Transition period — check both systems
const extpayUser = await extpay.getUser();
const crxpayResult = await CrxPay.getSubscription();
const hasPaidAccess =
extpayUser.paid ||
(crxpayResult.ok && crxpayResult.data.hasEntitlement('pro'));
if (hasPaidAccess) {
enablePremium();
}
API equivalents
| ExtPay | crxpay | Notes |
|---|---|---|
ExtPay('id') | CrxPay.configure({ apiKey }) | Config in background only |
extpay.startBackground() | Handled by configure() | Automatic |
extpay.getUser() | CrxPay.getSubscription() | Returns typed Result<Subscription> |
user.paid | subscription.hasEntitlement('pro') | More granular access control |
extpay.openPaymentPage() | CrxPay.openCheckout() | Opens Stripe Checkout |
extpay.openLoginPage() | CrxPay.identify(email) | Programmatic, no redirect |
extpay.onPaid | CrxPay.onPaid | Same event pattern |
| N/A | CrxPay.getOfferings() | Dynamic pricing from dashboard |
| N/A | CrxPay.setAttributes() | Customer segmentation |
| N/A | CrxPay.openBillingPortal() | Stripe self-serve portal |
| N/A | CrxPay.syncSubscription() | Force refresh after checkout |
Was this page helpful?
Your feedback shapes what we document next.