Build a gym membership app by creating Paystack Plans (monthly, quarterly, annual tiers), subscribing members via the transaction initialize endpoint with a plan attached, and gating check-in access on active membership status. Listen for subscription lifecycle webhooks to suspend access when payments fail and restore it when they succeed.
Tables: plans (name, duration, price, paystack_plan_code), members (name, email, phone, joined_at), memberships (member_id, plan_id, status, starts_at, expires_at, paystack_subscription_code), checkins (member_id, checked_in_at, method).
Create Paystack Plans once per tier (do this via dashboard or API on first setup):
async function createPlan(name, amountInKobo, interval) {
var res = await fetch('https://api.paystack.co/plan', {
method: 'POST',
headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ name, amount: amountInKobo, interval, currency: 'NGN' }),
});
var data = await res.json();
return data.data.plan_code; // store this in your plans table
}
// Run once:
// createPlan('Monthly Membership', 1500000, 'monthly') // NGN 15,000
// createPlan('Quarterly Membership', 4000000, 'quarterly') // NGN 40,000
When a member signs up, initialize a transaction with the plan attached. After the first payment, Paystack creates a subscription automatically and charges the card monthly.
async function signupMember(memberId, planId) {
var member = await db.members.findById(memberId);
var plan = await db.plans.findById(planId);
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: member.email,
amount: plan.price,
plan: plan.paystack_plan_code,
metadata: { member_id: memberId, plan_id: planId },
}),
});
var data = await res.json();
return data.data.authorization_url;
}
// Webhook handler
if (event.event === 'subscription.create') {
var meta = event.data.metadata;
var expiresAt = new Date();
expiresAt.setMonth(expiresAt.getMonth() + 1);
await db.memberships.create({
member_id: meta.member_id,
plan_id: meta.plan_id,
status: 'active',
paystack_subscription_code: event.data.subscription_code,
expires_at: expiresAt,
});
}
if (event.event === 'invoice.payment_failed') {
var code = event.data.subscription.subscription_code;
// Give 3-day grace period before suspending
await db.memberships.update(code, { status: 'grace_period', grace_until: addDays(new Date(), 3) });
}
if (event.event === 'subscription.not_renew' || event.event === 'subscription.disable') {
var code = event.data.subscription_code;
await db.memberships.update(code, { status: 'suspended' });
}
// Check-in access gate
async function canCheckin(memberId) {
var membership = await db.memberships.findActive(memberId);
if (!membership) return false;
if (membership.status === 'active' || membership.status === 'grace_period') return true;
return false;
}
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 sports club subscription system for a similar recurring billing pattern applied to clubs and leagues.
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