BillStack Docs

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.url

After — 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 url

Note 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

ResourceStripe IDBillStack ID
Customercus_xxxpc_xxx
Productprod_xxxpp_xxx
Priceprice_xxxpri_xxx
Subscriptionsub_xxxps_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.

On this page