McTaba Labs logo
Software EngineeringThe WhiteboardProjectsEventsMasterclassesWebinarsLearn
Book Call

500+ developers already in the community

Get answers, find accountability partners, and stay ahead

Join Discord →
McTaba Labs logo

Educating 1 million students in tech by 2030.

Programs

  • HighSchool Graduates Launchpad
  • AI & Business Automation
  • Corporate Training
  • McTaba Story & Mission
  • Full-Stack Curriculum
  • Course Pricing & Plans
  • Frequently Asked Questions

Resources

  • Join Discord Community
  • Tech Events in Nairobi
  • Masterclasses
  • Free Resources
  • Learn
  • Campus
  • Africa Tech Talent Report
  • Partner With Us
  • Start a Project
  • Our Work

Contact Us

  • 124 Manyani East Road, Lavington, Nairobi
  • +254727795603
  • Book a Discovery Call

Office Hours:

Mon-Fri: 9am - 2pm EAT

Sat: 10am - 12pm EAT

© 2027 McTaba Labs - A product of Edtech Industries. All rights reserved.

Privacy PolicyTerms of ServiceRefund Policy
  1. Learn
  2. Paystack for African Developers
  3. Build a Streaming Service Billing Backend
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Build a Streaming Service Billing Backend

Build a streaming billing backend using Paystack Plans (one per tier), subscribing users via the transaction initialize endpoint, gating content access on active subscription status in your database, and listening to subscription webhooks (subscription.create, invoice.payment_failed, subscription.disable) to grant or revoke access automatically.

In this article

  1. 1. Subscription Plans and Content Access
  2. 2. Subscription Lifecycle via Webhooks
  3. 3. Learn More

Subscription Plans and Content Access

Tables: subscription_plans (name, price, quality, concurrent_streams, paystack_plan_code), user_subscriptions (user_id, plan_id, status, paystack_subscription_code, current_period_start, current_period_end), content (title, min_plan_tier, video_url_sd, video_url_hd, video_url_4k).

var PLANS = {
  basic:    { tier: 1, quality: 'SD',  streams: 1, price: 120000 }, // NGN 1,200/month
  standard: { tier: 2, quality: 'HD',  streams: 2, price: 250000 }, // NGN 2,500/month
  premium:  { tier: 3, quality: '4K',  streams: 4, price: 450000 }, // NGN 4,500/month
};

async function subscribeUser(userId, userEmail, planName) {
  var plan = await db.subscriptionPlans.findByName(planName);

  var res = await fetch('https://api.paystack.co/transaction/initialize', {
    method: 'POST',
    headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      email: userEmail, amount: plan.price, plan: plan.paystack_plan_code,
      metadata: { user_id: userId, plan_id: plan.id },
    }),
  });
  return (await res.json()).data.authorization_url;
}

// Content access gate
async function getStreamUrl(userId, contentId) {
  var sub = await db.userSubscriptions.findActiveByUser(userId);
  if (!sub) throw { status: 403, message: 'Subscribe to watch this content.' };

  var content = await db.content.findById(contentId);
  var userPlan = PLANS[sub.plan_name];

  if (userPlan.tier < content.min_plan_tier) {
    throw { status: 403, message: 'Upgrade to ' + content.required_plan + ' to watch this.' };
  }

  // Return appropriate quality URL based on plan
  if (userPlan.quality === '4K') return content.video_url_4k;
  if (userPlan.quality === 'HD') return content.video_url_hd;
  return content.video_url_sd;
}

Subscription Lifecycle via Webhooks

if (event.event === 'subscription.create') {
  var meta = event.data.metadata;
  await db.userSubscriptions.upsert({
    user_id: meta.user_id,
    plan_id: meta.plan_id,
    paystack_subscription_code: event.data.subscription_code,
    status: 'active',
    current_period_end: new Date(event.data.next_payment_date),
  });
}

if (event.event === 'invoice.create' && event.data.status === 'success') {
  // Subscription renewed — extend period
  var code = event.data.subscription.subscription_code;
  await db.userSubscriptions.update({ code }, {
    status: 'active',
    current_period_end: new Date(event.data.next_payment_date),
  });
}

if (event.event === 'invoice.payment_failed') {
  // Grace period — do not cut access immediately
  var code = event.data.subscription.subscription_code;
  await db.userSubscriptions.update({ code }, { status: 'grace_period' });
  await sendPaymentFailedEmail(code);
}

if (event.event === 'subscription.not_renew' || event.event === 'subscription.disable') {
  var code = event.data.subscription_code;
  await db.userSubscriptions.update({ code }, { status: 'cancelled' });
  await sendCancelledEmail(code);
}

// Plan upgrade: cancel old, start new
async function upgradePlan(userId, userEmail, newPlanName) {
  var currentSub = await db.userSubscriptions.findActiveByUser(userId);
  if (currentSub) {
    // Disable old subscription
    await fetch('https://api.paystack.co/subscription/disable', {
      method: 'POST',
      headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY, 'Content-Type': 'application/json' },
      body: JSON.stringify({ code: currentSub.paystack_subscription_code, token: userEmail }),
    });
  }
  return await subscribeUser(userId, userEmail, newPlanName);
}

Get weekly developer tips

Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.

No spam. Unsubscribe anytime.

Learn More

See build a podcast membership platform for a simpler single-tier subscription access model.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Create one Paystack Plan per subscription tier (Basic, Standard, Premium) — do not create plans per user.
  • ✓Gate content quality (SD vs HD vs 4K) and concurrent streams based on the user's active plan tier.
  • ✓Handle invoice.payment_failed with a 3-day grace period before downgrading access.
  • ✓Support plan upgrades: cancel the old subscription and start a new one at the higher tier.
  • ✓Use Paystack customer objects to associate all subscriptions with one customer profile.

Frequently Asked Questions

How do I handle concurrent stream limits?
Track active streams per user in a Redis cache with session tokens. When a user starts streaming, create a session with a 30-second heartbeat. If the user's active session count exceeds their plan limit, block new stream starts with a "Too many devices" message. Reset session count when all streams expire or the user logs out. Heartbeats prevent stale sessions from blocking legitimate streams.
How do I let users cancel and still keep access until the end of the billing period?
Call Paystack's disable subscription endpoint (cancels future renewals but does not immediately terminate). On your end, keep the subscription status as "active" until current_period_end. When that date passes without a renewal, set status to "cancelled". The user keeps access for the rest of the period they paid for.
Should I offer a free trial?
Yes if conversion is your primary metric. Paystack Plans support free trial periods via the invoice_limit parameter. Alternatively, create a 7-day free plan that auto-converts to a paid plan. For the free trial to work with card-on-file, the user must enter their card upfront — this reduces trial sign-up volume but increases conversion rate to paid.

Learn Paystack Integration

KES 2,999

The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.

Start Paystack Course

Not ready? Create Free Account

Keep reading

Things You Can Build with Paystack: Project Guides

A complete map of real-world projects you can build using the Paystack API. From online store checkouts and subscription platforms to savings apps and solar pay-as-you-go systems, each category links to step-by-step build guides with working code.

Read more

Build a Parking Payment App

A build guide for a parking payment app where drivers scan a QR code on entry, pay for their session via Paystack (card or M-Pesa), and the system validates payment before the barrier lifts for exit. Covers time-based pricing, session management, and overstay handling.

Read more

Build a Referral Payout System

A build guide for a referral payout system where users share referral links, earn commissions when referrals convert, and withdraw their balance via Paystack Transfers to their bank account or M-Pesa wallet.

Read more

Build a Farmers Market Ordering Platform

A build guide for a farmers market ordering platform where customers order fresh produce from multiple farmers in one cart, the platform collects the combined payment via Paystack, and each farmer receives their share via Paystack Split Payments or Transfers. Covers inventory management, order routing to individual farmers, and delivery coordination.

Read more

CBK Payment Service Provider Rules Developers Should Know

A practical guide to the Central Bank of Kenya regulations that affect developers building payment products. Covers PSP licensing, what requires a license versus what does not, AML and KYC requirements, transaction limits, record-keeping, and what you should verify with legal counsel.

Read more

Delayed Settlement Patterns for Marketplaces

Delayed settlement holds a vendor's funds until a condition is met — delivery confirmed, dispute window closed, or event completed. This guide covers how to implement delayed settlement using Paystack manual settlement, hold timers in your database, and conditional payout release.

Read more

Explore related topics

M-Pesa & African Stack

M-Pesa Daraja API tutorials, USSD development, and WhatsApp Business API guides. The African Stack that global bootcamps do not teach.

Learn to Code in Kenya

Everything a Kenyan needs to know before writing their first line of code. Career paths, real costs, hardware requirements, and honest answers about what it takes.

Learning to Code

Roadmaps, language guides, and practical advice for learning to code and launching a programming career.

Breaking Into Tech

Practical, no-nonsense guides for anyone looking to start or change their career in tech. From choosing your first language to acing interviews.

The African Stack: Skills for This Market

The skills that make Kenyan developers hard to replace: M-Pesa, USSD, WhatsApp, fintech, and building for the African market.

Bonaventure Ogeto

Bonaventure Ogeto

Founder, McTaba Labs

Bonaventure is the founder of McTaba Labs, a coding bootcamp in Nairobi, Kenya. He has spent years building products for the African market and mentoring the next generation of developers across the continent.