4. Replace Stripe Calls
Swap your direct Stripe SDK calls with BillStack API equivalents
Overview
This is the core migration step. You'll replace Stripe SDK calls in your SaaS application with BillStack REST API calls. Each section below shows the before (Stripe SDK) and after (BillStack API) code side by side.
Set up your base configuration first:
// config.ts
const BILLSTACK_URL = process.env.BILLSTACK_URL; // e.g., https://your-billstack.com
const TEAM_ID = process.env.BILLSTACK_TEAM_ID;
const PROJECT_ID = process.env.BILLSTACK_PROJECT_ID;
const API_KEY = process.env.BILLSTACK_API_KEY; // bs_...
const BASE = `${BILLSTACK_URL}/api/billstack/teams/${TEAM_ID}/projects/${PROJECT_ID}`;
async function billstack(path: string, options?: RequestInit) {
const res = await fetch(`${BASE}${path}`, {
...options,
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json',
...options?.headers,
},
});
if (!res.ok) throw new Error(`BillStack error: ${res.status}`);
return res.json();
}Create a Customer
Before — Stripe SDK:
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);
const customer = await stripe.customers.create({
email: 'jane@example.com',
name: 'Jane Doe',
metadata: { plan: 'pro' },
});
// customer.id = "cus_xxx"After — BillStack API:
const { customer } = await billstack('/customers', {
method: 'POST',
body: JSON.stringify({
email: 'jane@example.com',
name: 'Jane Doe',
metadata: { plan: 'pro' },
}),
});
// customer.id = "pc_xxx" (BillStack ID)
// customer.stripeCustomerId = "cus_xxx" (original Stripe ID)List Customers
Before — Stripe SDK:
const customers = await stripe.customers.list({ limit: 50 });After — BillStack API:
const { customers } = await billstack('/customers?limit=50&offset=0');Update a Customer
Before — Stripe SDK:
await stripe.customers.update('cus_xxx', {
name: 'Jane Smith',
metadata: { plan: 'enterprise' },
});After — BillStack API:
const { customer } = await billstack('/customers/pc_xxx', {
method: 'PATCH',
body: JSON.stringify({
name: 'Jane Smith',
metadata: { plan: 'enterprise' },
}),
});Create a Product
Before — Stripe SDK:
const product = await stripe.products.create({
name: 'Pro Plan',
description: 'Full access to all features',
});After — BillStack API:
const { product } = await billstack('/products', {
method: 'POST',
body: JSON.stringify({
name: 'Pro Plan',
description: 'Full access to all features',
}),
});
// product.id = "pp_xxx"Create a Price
Before — Stripe SDK:
const price = await stripe.prices.create({
product: 'prod_xxx',
unit_amount: 2999,
currency: 'usd',
recurring: { interval: 'month' },
});After — BillStack API:
const { price } = await billstack('/prices', {
method: 'POST',
body: JSON.stringify({
productId: 'pp_xxx', // BillStack product ID
unitAmount: 2999,
currency: 'usd',
type: 'recurring',
interval: 'month',
intervalCount: 1,
}),
});
// price.id = "pri_xxx"Create a Checkout Session
Before — Stripe SDK:
const session = await stripe.checkout.sessions.create({
customer: 'cus_xxx',
line_items: [{ price: 'price_xxx', quantity: 1 }],
mode: 'subscription',
success_url: 'https://myapp.com/success',
cancel_url: 'https://myapp.com/cancel',
});
// Redirect to session.urlAfter — BillStack API:
const { sessionId, url } = await billstack('/checkout', {
method: 'POST',
body: JSON.stringify({
customerId: 'pc_xxx', // BillStack customer ID
priceId: 'pri_xxx', // BillStack price ID
quantity: 1,
successUrl: 'https://myapp.com/success',
cancelUrl: 'https://myapp.com/cancel',
mode: 'subscription',
trialDays: 14, // optional
}),
});
// Redirect to urlNote that BillStack uses its own IDs (pc_, pri_) — not raw Stripe IDs. BillStack resolves them to Stripe IDs internally.
Cancel a Subscription
Before — Stripe SDK:
// Cancel at period end
await stripe.subscriptions.update('sub_xxx', {
cancel_at_period_end: true,
});
// Cancel immediately
await stripe.subscriptions.cancel('sub_xxx');After — BillStack API:
// Cancel at period end
await billstack('/subscriptions/ps_xxx', {
method: 'PATCH',
body: JSON.stringify({ cancelAtPeriodEnd: true }),
});
// Cancel immediately
await billstack('/subscriptions/ps_xxx', {
method: 'DELETE',
});Read Analytics
No Stripe SDK equivalent — this is new functionality:
const analytics = await billstack('/analytics?growthDays=30');
// {
// mrr: 12500.00,
// customerCount: 450,
// churnRate: 3.2,
// subscriptionBreakdown: { active: 420, trialing: 20, past_due: 10 },
// customerGrowth: [...],
// webhookStats: { total: 1200, processed: 1198, failed: 2 },
// referralStats: { totalCodes: 50, totalReferrals: 120, converted: 95 }
// }ID Mapping Reference
| Resource | Stripe ID | BillStack ID |
|---|---|---|
| Customer | cus_xxx | pc_xxx |
| Product | prod_xxx | pp_xxx |
| Price | price_xxx | pri_xxx |
| Subscription | sub_xxx | ps_xxx |
Each BillStack resource stores a reference to its Stripe ID (e.g., customer.stripeCustomerId). You can use this during migration to map between old and new IDs.
Next Step
With your code updated, configure webhooks so BillStack receives Stripe events.