Billing & Subscriptions

Membership billing without a subscriptions platform: proration, pauses, and renewals

Not every recurring-revenue business fits Stripe Billing's assumptions cleanly. Here's how we handled tiered memberships, mid-cycle changes, and pauses for a subscription rental library on plain payment-gateway primitives.

By Team WebSync · · 4 min read

Stack of membership cards with a recurring billing cycle icon and calendar markers

Off-the-shelf subscription billing tools handle the common case well: one plan, one price, upgrade or cancel any time. Real membership businesses rarely stay that simple for long - tiered packages, items on loan that need to be returned before a plan can downgrade, pauses for a family going on holiday, referral credits that offset a renewal. Once the rules stop being generic, you're often better off building billing logic on top of a payment gateway's raw primitives than fighting a subscriptions platform's opinions.

We built exactly that for a subscription toy-and-book rental library: catalogued products, tiered membership packages, and a referral engine, where the billing rules were specific enough to the business that no off-the-shelf platform matched them without workarounds of its own.

1. Model membership state, not just payment status

A subscription isn't just "paid" or "not paid" - it has a tier, a renewal date, a set of items currently on loan, and a history of pauses and referral credits. We modelled membership as its own state machine (ACTIVE, PAUSED, PAST_DUE, CANCELLED) independent of any single payment gateway's subscription object, so business logic like "can this member downgrade" checks our own rules against our own data, not a third party's schema.

Let the payment gateway own charging money. Let your own database own what membership actually means for this business.

2. Proration is arithmetic, not magic

Mid-cycle tier changes need a fair adjustment, and it's simpler to compute than it looks: take the unused portion of the current cycle (days remaining divided by cycle length, times the current tier's price) as a credit, and charge the difference against the new tier's prorated cost for the same remaining period. We calculate this server-side at the moment of the change and show the exact number before confirming - members trust proration a lot more when they can see the maths, not just a total.

function prorate(currentTierPrice, newTierPrice, daysRemaining, cycleDays) {
  const unusedCredit = (daysRemaining / cycleDays) * currentTierPrice;
  const newTierCost = (daysRemaining / cycleDays) * newTierPrice;
  return Math.max(0, newTierCost - unusedCredit); // charge now, or 0 if downgrading
}

3. Pauses need a real hold state, not a cancel-and-rejoin

Cancelling a membership and asking a member to resubscribe later loses their tenure, their referral history, and often the member entirely - the friction of signing up again is real. We built an explicit PAUSED state instead: billing stops, outstanding rentals are handled by their own return-by rules independent of billing, and resuming reactivates the same membership record with the same history intact, on the next cycle boundary rather than an arbitrary resume date.

4. Keep the payment gateway as a dumb charging engine

  1. Store a reusable payment method token against the member, not a recurring "subscription" object owned by the gateway.
  2. Run a scheduled job that charges the correct amount - full price, prorated, or referral-adjusted - against that token on each member's actual renewal date.
  3. Handle a failed charge with your own dunning logic: a short grace period, a retry, then a move to PAST_DUE - not whatever default the gateway happens to apply.
  4. Reconcile nightly: every ACTIVE membership should have a matching successful charge in the expected window, and any gap gets flagged before a member notices.

5. Referral credits are a ledger, not a coupon code

A referral programme that just discounts the next invoice breaks the moment credits need to roll over, split across two referred members, or get reversed after a refund. We track referral credit as its own ledger - each grant and each redemption a separate row - so a renewal simply asks "what's this member's current credit balance" instead of trying to remember which coupon applies.

A subscriptions platform is optimised for the subscriptions its designers imagined. The moment your billing rules are specific to your business, that's a sign to own the state machine yourself.

How do you handle mid-cycle tier changes and pauses in custom membership billing?

Model membership as its own state machine - active, paused, past-due, cancelled - independent of the payment gateway, and compute proration server-side as unused-cycle credit against the new tier's cost. Give pauses a real hold state that preserves tenure and history, and reconcile charges against expected renewals nightly.

Share this guideLinkedInXWhatsAppFacebook
All guides

Want this built for you?

Book a free consult - we'll scope it and give you a fixed price.