How to Migrate from Stripe Billing to Kill Bill in 2026
The Stripe Billing Cost Problem
Stripe is excellent as a payment processor. At 2.9% + $0.30 per transaction, it's competitive. The cost problem compounds when you add Stripe Billing for subscription management.
Stripe Billing fees (2026):
- Starter: 0.5% of recurring revenue
- Scale: 0.8% of recurring revenue
- Plus the base payment processing fee (2.9% + $0.30)
For a SaaS business doing $500,000 ARR:
- Stripe Billing fee: $2,500-4,000/year (0.5-0.8%)
- Payment processing: ~$14,500/year (2.9%)
- Total: $17,000+/year
Stripe Billing's percentage fee is separate from payment processing. At $1M ARR, that's $5,000-8,000/year just for subscription management — before payment processing.
Kill Bill (Apache 2.0 license) is the most mature open source billing and subscription management platform. Self-hosted, no percentage fees, 10+ years of production use. You still pay Stripe (or another processor) for payment processing, but you eliminate the subscription management layer fee.
Important caveat: This migration is technically complex. Billing systems are critical infrastructure. Budget 2-8 weeks of engineering time for a production migration. This guide covers the approach and key decisions, not a copy-paste solution.
What Kill Bill Provides
Kill Bill is not a Stripe replacement — it doesn't process payments. It's a subscription management and billing engine that sits between your application and payment processors.
Kill Bill handles:
- Subscription lifecycle (create, upgrade, downgrade, cancel, pause)
- Billing plan changes and proration
- Invoice generation
- Payment orchestration (sends payment requests to your processor)
- Dunning (retry logic for failed payments)
- Credit management
- Tax calculation integration
- Revenue reporting
Payment processors Kill Bill works with:
- Stripe (Kill Bill sends payment requests to Stripe, eliminating Stripe Billing fee)
- Adyen
- Braintree
- PayPal
- Many others via plugins
The key: you use Kill Bill for subscription logic, Stripe just processes the individual payments. You pay Stripe's 2.9% processing fee but not the 0.5-0.8% Billing fee.
Before You Start: Is Kill Bill Right for You?
Kill Bill is a serious engineering commitment. It's appropriate when:
- You're processing $200K+ ARR and the percentage fee savings justify the engineering cost
- You need billing customization that Stripe Billing doesn't support
- Your billing model is complex (usage-based, tiered, hybrid)
- You require full control of billing data (financial services, compliance requirements)
Consider staying with Stripe Billing if:
- You're under $200K ARR (engineering cost outweighs savings)
- Your team doesn't have Java/backend experience
- You need features like Stripe Revenue Recovery (ML-based dunning)
- Simplicity is a higher priority than cost optimization
Other self-hosted alternatives to evaluate:
- Lago (GitHub: 7K+ stars, MIT): More modern codebase, easier setup than Kill Bill. Good for usage-based billing.
- BillaBear (Stripe Billing-specific alternative): PHP, simpler setup
- Medusa.js (for eCommerce): Better if you're in commerce rather than pure SaaS
Architecture Overview
Your Application
│
▼
Kill Bill API
├── Subscription Engine (plans, entitlements, proration)
├── Invoice Engine (billing calculation)
├── Payment Engine (orchestrates payment processors)
└── Plugin System
│
▼
Kill Bill Stripe Plugin
│
▼
Stripe API (payment processing only)
Your application talks to Kill Bill. Kill Bill handles billing logic. Kill Bill's Stripe plugin handles payment collection. Stripe's role is reduced to payment processing only.
Step 1: Set Up Kill Bill Infrastructure
Kill Bill runs on Java (JVM). Self-hosting options:
Docker Compose (Development/Testing)
services:
killbill:
image: killbill/killbill:latest
ports:
- "8080:8080"
environment:
KB_org.killbill.dao.url: jdbc:mysql://db:3306/killbill
KB_org.killbill.dao.user: root
KB_org.killbill.dao.password: killbill
KB_org.killbill.billing.osgi.bundles.jruby.version: LATEST
depends_on:
- db
kaui:
image: killbill/kaui:latest
ports:
- "9090:8080"
environment:
KAUI_CONFIG_DAO_URL: jdbc:mysql://db:3306/kaui
KAUI_CONFIG_DAO_USER: root
KAUI_CONFIG_DAO_PASSWORD: killbill
KAUI_KILLBILL_URL: http://killbill:8080
depends_on:
- db
db:
image: killbill/mariadb:latest
volumes:
- db_data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: killbill
KAUI is Kill Bill's admin UI. It lets you manage accounts, subscriptions, and invoices through a web interface.
Production Deployment
For production, Kill Bill requires:
- 4GB RAM minimum (8GB recommended)
- MySQL or PostgreSQL database
- Persistent storage for the database
- Load balancer for horizontal scaling (Kill Bill is stateless)
Kill Bill is horizontally scalable — run multiple Kill Bill instances behind a load balancer for high availability.
Verify Installation
# Test Kill Bill API is running
curl -u admin:password http://localhost:8080/1.0/kb/test/clock
Step 2: Install the Stripe Plugin
Kill Bill uses plugins for payment processor integration. Install the Stripe plugin:
# Via Kill Bill admin API
curl -v \
-X POST \
-u admin:password \
-H "X-Killbill-ApiKey: bob" \
-H "X-Killbill-ApiSecret: lazar" \
-H "Content-Type: application/json" \
-H "X-Killbill-CreatedBy: admin" \
"http://127.0.0.1:8080/1.0/kb/plugins/installPlugin" \
-d '{
"pluginKey": "stripe",
"pluginType": "PAYMENT",
"pluginVersion": "LATEST"
}'
Configure the Stripe plugin with your API keys:
curl -v \
-X POST \
-u admin:password \
-H "X-Killbill-ApiKey: bob" \
-H "X-Killbill-ApiSecret: lazar" \
-H "Content-Type: application/json" \
-H "X-Killbill-CreatedBy: admin" \
"http://127.0.0.1:8080/1.0/kb/tenants/uploadPluginConfig/killbill-stripe" \
-d "org.killbill.billing.plugin.stripe.apiKey=sk_live_your_stripe_secret_key"
Step 3: Create Your Catalog (Billing Plans)
Kill Bill's billing plans are defined in an XML catalog. This is the core configuration that determines what your customers can purchase.
<?xml version="1.0" encoding="UTF-8"?>
<catalog xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="CatalogSchema.xsd"
standaloneCatalog="false">
<effectiveDate>2026-01-01T00:00:00+00:00</effectiveDate>
<catalogName>MyProductCatalog</catalogName>
<currencies>
<currency>USD</currency>
<currency>EUR</currency>
</currencies>
<products>
<product name="Starter">
<category>BASE</category>
</product>
<product name="Professional">
<category>BASE</category>
</product>
</products>
<rules>
<changePolicy>
<changePolicyCase>
<policy>END_OF_TERM</policy>
</changePolicyCase>
</changePolicy>
<cancelPolicy>
<cancelPolicyCase>
<policy>END_OF_TERM</policy>
</cancelPolicyCase>
</cancelPolicy>
</rules>
<plans>
<plan name="starter-monthly">
<product>Starter</product>
<initialPhases/>
<finalPhase type="EVERGREEN">
<duration>
<unit>UNLIMITED</unit>
</duration>
<recurring>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
<price>
<currency>USD</currency>
<value>29.00</value>
</price>
</recurringPrice>
</recurring>
</finalPhase>
</plan>
<plan name="professional-monthly">
<product>Professional</product>
<initialPhases>
<phase type="TRIAL">
<duration>
<unit>DAYS</unit>
<number>14</number>
</duration>
<fixed>
<fixedPrice/>
</fixed>
</phase>
</initialPhases>
<finalPhase type="EVERGREEN">
<duration>
<unit>UNLIMITED</unit>
</duration>
<recurring>
<billingPeriod>MONTHLY</billingPeriod>
<recurringPrice>
<price>
<currency>USD</currency>
<value>99.00</value>
</price>
</recurringPrice>
</recurring>
</finalPhase>
</plan>
</plans>
<priceLists>
<defaultPriceList name="DEFAULT">
<plans>
<plan>starter-monthly</plan>
<plan>professional-monthly</plan>
</plans>
</defaultPriceList>
</priceLists>
</catalog>
Upload this catalog via the Kill Bill API:
curl -v \
-X POST \
-u admin:password \
-H "X-Killbill-ApiKey: bob" \
-H "X-Killbill-ApiSecret: lazar" \
-H "Content-Type: application/xml" \
-H "X-Killbill-CreatedBy: admin" \
"http://127.0.0.1:8080/1.0/kb/catalog/xml" \
-d @catalog.xml
Step 4: Migrate Customer Data
The migration order matters:
1. Export Stripe Billing Data
# List all Stripe customers
stripe customers list --limit 100 > customers.json
# List all subscriptions
stripe subscriptions list --limit 100 --status=active > subscriptions.json
# List payment methods
stripe payment_methods list --limit 100 > payment_methods.json
2. Create Kill Bill Accounts
For each Stripe customer, create a Kill Bill account:
curl -v \
-X POST \
-u admin:password \
-H "Content-Type: application/json" \
-H "X-Killbill-ApiKey: bob" \
-H "X-Killbill-ApiSecret: lazar" \
-H "X-Killbill-CreatedBy: migration" \
"http://127.0.0.1:8080/1.0/kb/accounts" \
-d '{
"name": "Customer Name",
"email": "customer@example.com",
"currency": "USD",
"externalKey": "stripe_customer_id_here"
}'
Use the Stripe customer ID as externalKey — this lets you cross-reference records during migration.
3. Migrate Payment Methods
Stripe's Stripe.js payment tokens can be reused in Kill Bill via the Stripe plugin. The plugin stores a reference to the Stripe payment method — the actual card data stays in Stripe's vault.
# Add payment method to Kill Bill account (references existing Stripe payment method)
curl -v \
-X POST \
-u admin:password \
-H "Content-Type: application/json" \
-H "X-Killbill-ApiKey: bob" \
-H "X-Killbill-ApiSecret: lazar" \
-H "X-Killbill-CreatedBy: migration" \
"http://127.0.0.1:8080/1.0/kb/accounts/{accountId}/paymentMethods" \
-d '{
"pluginName": "killbill-stripe",
"pluginInfo": {
"properties": [{
"key": "token",
"value": "stripe_payment_method_id_here"
}]
}
}'
No card re-capture required — existing Stripe payment methods transfer to Kill Bill's Stripe plugin seamlessly.
4. Create Subscriptions in Kill Bill
For each active Stripe subscription, create a corresponding Kill Bill subscription:
curl -v \
-X POST \
-u admin:password \
-H "Content-Type: application/json" \
-H "X-Killbill-ApiKey: bob" \
-H "X-Killbill-ApiSecret: lazar" \
-H "X-Killbill-CreatedBy: migration" \
"http://127.0.0.1:8080/1.0/kb/subscriptions" \
-d '{
"accountId": "kill-bill-account-id",
"planName": "professional-monthly",
"billCycleDayLocal": 15
}'
Set billCycleDayLocal to match the customer's existing Stripe billing day to minimize billing disruption.
Step 5: Update Your Application Integration
Your application needs to interact with Kill Bill instead of Stripe Billing:
Subscription Creation (Sign-up Flow)
Before (Stripe Billing):
const subscription = await stripe.subscriptions.create({
customer: customerId,
items: [{ price: 'price_pro_monthly' }],
trial_period_days: 14,
});
After (Kill Bill API):
const response = await fetch(`${KILLBILL_URL}/1.0/kb/subscriptions`, {
method: 'POST',
headers: {
'Authorization': 'Basic ' + btoa('admin:password'),
'X-Killbill-ApiKey': KB_API_KEY,
'X-Killbill-ApiSecret': KB_API_SECRET,
'Content-Type': 'application/json',
'X-Killbill-CreatedBy': 'api',
},
body: JSON.stringify({
accountId: killBillAccountId,
planName: 'professional-monthly',
}),
});
Subscription Status Checks
Before (Stripe Billing):
const subscription = await stripe.subscriptions.retrieve(subscriptionId);
const isActive = subscription.status === 'active';
After (Kill Bill):
const response = await fetch(`${KILLBILL_URL}/1.0/kb/subscriptions/${subscriptionId}`, {
headers: killBillHeaders,
});
const subscription = await response.json();
const isActive = subscription.state === 'ACTIVE';
Webhook Events
Kill Bill fires events that your application listens to, similar to Stripe webhooks:
// Kill Bill event types
ACCOUNT_CREATION
SUBSCRIPTION_CREATION
SUBSCRIPTION_CANCEL
INVOICE_CREATION
PAYMENT_SUCCESS
PAYMENT_FAILED
Configure Kill Bill webhooks (notifications) in the admin UI or API.
Step 6: Cancel Stripe Billing Subscriptions
Once Kill Bill is handling billing and your application is fully integrated:
- Stop creating new subscriptions in Stripe Billing
- As each customer's Stripe subscription renews, migrate it to Kill Bill
- Cancel the Stripe subscription after the period covered (avoid double billing)
- Once all subscriptions are in Kill Bill, downgrade your Stripe Billing plan
Important: Keep Stripe as your payment processor. You're only removing Stripe Billing (the subscription management layer). Stripe remains for payment processing.
Cost Savings Analysis
$500K ARR SaaS (Monthly Subscriptions)
| Fee | Stripe Billing | Kill Bill |
|---|---|---|
| Payment processing (2.9%) | $14,500 | $14,500 |
| Subscription management (0.5%) | $2,500 | $0 |
| Self-hosting (Hetzner CPX31) | $0 | $120 |
| Annual Total | $17,000 | $14,620 |
| Savings | $2,380/year |
$2M ARR SaaS
| Fee | Stripe Billing | Kill Bill |
|---|---|---|
| Payment processing (2.9%) | $58,000 | $58,000 |
| Subscription management (0.5%) | $10,000 | $0 |
| Self-hosting (Hetzner CCX13) | $0 | $420 |
| Annual Total | $68,000 | $58,420 |
| Savings | $9,580/year |
The engineering cost of migration is typically $20,000-50,000 in developer time. Break-even at $500K ARR happens within 8-20 years. Break-even at $2M ARR happens in 2-5 years.
This is why the migration calculation matters: Kill Bill makes most financial sense at $2M+ ARR, or for teams with compliance requirements that Stripe Billing can't meet.
Find More Billing Alternatives
Browse all Stripe Billing alternatives on OSSAlt — compare Kill Bill, Lago, BillaBear, and every other open source billing platform with deployment guides and use case comparisons.