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.
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;
}
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.
See build a podcast membership platform for a simpler single-tier subscription access model.
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