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 Sports Club Subscription System
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Build a Sports Club Subscription System

Build a sports club subscription system by creating Paystack Plans for each membership tier (junior/senior/family), subscribing members via the transaction initialize endpoint, issuing a QR membership card on subscription.create, and gating facility access and session bookings behind active subscription status. Handle grace periods on invoice.payment_failed before suspending access.

In this article

  1. 1. Membership Tiers and Enrollment
  2. 2. Facility Access and Training Session Booking
  3. 3. Learn More

Membership Tiers and Enrollment

Tables: membership_plans (name, tier, monthly_fee, annual_registration_fee, max_dependants, paystack_plan_code), members (name, email, phone, dob, photo_url, membership_number, plan_id, paystack_subscription_code, status, card_qr_code, access_suspended_at), training_sessions (coach_id, name, sport, day_of_week, time, max_participants, location), session_bookings (member_id, session_id, date, status).

var QRCode = require('qrcode');

async function enrollMember(memberEmail, memberData, planName) {
  var plan = await db.membershipPlans.findByName(planName);

  // Collect annual registration fee first (one-time)
  var regReference = 'REG-' + Date.now();
  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: memberEmail,
      amount: plan.annual_registration_fee,
      currency: 'NGN',
      reference: regReference,
      // After registration fee, redirect to monthly plan setup
      callback_url: 'https://yourclub.com/enroll/subscription?email=' + memberEmail + '&plan=' + planName,
      metadata: { step: 'registration', email: memberEmail, plan_name: planName, ...memberData },
    }),
  });
  return (await res.json()).data.authorization_url;
}

// After registration fee paid, set up monthly subscription
if (event.event === 'charge.success' && event.data.metadata.step === 'registration') {
  var meta = event.data.metadata;
  var memberNumber = 'CLB-' + new Date().getFullYear() + '-' + Math.floor(Math.random() * 9000 + 1000);
  var member = await db.members.create({
    ...meta, membership_number: memberNumber, plan_id: meta.plan_id, status: 'pending_subscription',
  });
  // Redirect to Paystack subscription setup is handled via callback_url
}

// Monthly subscription created
if (event.event === 'subscription.create') {
  var meta = event.data.metadata;
  var member = await db.members.findByEmail(meta.email);

  var qrData = JSON.stringify({ member_id: member.id, membership_number: member.membership_number });
  var qrCode = await QRCode.toDataURL(qrData);

  await db.members.update(member.id, {
    paystack_subscription_code: event.data.subscription_code,
    status: 'active',
    card_qr_code: qrCode,
  });
  await sendMembershipCard(member.email, member.name, member.membership_number, qrCode);
}

Facility Access and Training Session Booking

// Scan QR at facility entrance
async function validateMemberAccess(qrData) {
  var data = JSON.parse(qrData);
  var member = await db.members.findById(data.member_id);
  if (!member || member.membership_number !== data.membership_number) {
    throw { status: 403, message: 'Invalid membership card.' };
  }
  if (member.status !== 'active') {
    throw { status: 403, message: 'Membership ' + member.status + '. Please pay outstanding dues.' };
  }
  await db.accessLog.create({ member_id: member.id, accessed_at: new Date() });
  return { valid: true, name: member.name, plan: member.plan_name };
}

// Book a training session
async function bookSession(memberId, sessionId, date) {
  var member = await db.members.findById(memberId);
  if (member.status !== 'active') throw new Error('Active membership required to book sessions');

  var session = await db.trainingSessions.findById(sessionId);
  var booked = await db.sessionBookings.countBySessionAndDate(sessionId, date);
  if (booked >= session.max_participants) throw new Error('Session is full');

  await db.sessionBookings.create({ member_id: memberId, session_id: sessionId, date, status: 'booked' });
  await sendBookingConfirmation(member.phone, session.name, date, session.time, session.location);
}

// Subscription payment failed — grace period
if (event.event === 'invoice.payment_failed') {
  var code = event.data.subscription.subscription_code;
  var member = await db.members.findBySubscription(code);
  await db.members.update(member.id, { status: 'grace_period', access_suspended_at: addDays(new Date(), 5) });
  await sendSMS(member.phone, 'Club subscription payment failed. Access continues for 5 days. Pay now to avoid suspension.');
}

if (event.event === 'subscription.disable') {
  var code = event.data.subscription_code;
  var member = await db.members.findBySubscription(code);
  if (member) await db.members.update(member.id, { status: 'lapsed' });
}

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 gym membership app for a simpler single-tier membership model without session booking or family plans.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Create one Paystack Plan per membership tier — link members to the correct plan at enrollment.
  • ✓Issue a QR membership card on subscription.create that is scanned at the facility entrance.
  • ✓Suspend facility access after a 5-day grace period on invoice.payment_failed.
  • ✓Support annual registration fees (one-time) in addition to monthly subscription dues.
  • ✓Track session attendance per member to identify at-risk members before they churn.

Frequently Asked Questions

How do I handle family memberships where multiple people share one plan?
Add a dependants table linked to a primary member. The primary member subscribes to the Family plan; dependants are added for free up to the plan limit (e.g., max 4 family members). Each dependant gets their own QR card linked to the primary member's subscription. When the primary member's subscription is suspended, all dependant access is suspended simultaneously.
Can I sell guest day passes alongside memberships?
Yes. Build a guest pass purchase flow as a one-time Paystack charge (not a Plan). On charge.success, generate a time-limited QR code valid for the day only (set an expires_at field on the access pass). At the facility scanner, check both member QR codes and guest pass QR codes. Guest passes help visitors experience the club before committing to membership.
How do I manage tournament registration fees separately from monthly dues?
Tournament registrations are one-time charges, not subscription renewals. Create a tournaments table with registration_fee and deadline fields. Require active membership to register (gate behind status: "active" check). Process tournament fees as separate Paystack transactions — this keeps tournament revenue clearly separated from membership dues in your reporting.

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 Veterinary Clinic Booking and Payment System

A build guide for a veterinary clinic booking and payment system where pet owners book appointments online, pay a deposit via Paystack to confirm the slot, and pay the balance at the clinic after the consultation. Includes pet health records, vaccination schedules, and automated reminders.

Read more

Building an M-Pesa Fallback When Paystack Is Unavailable

When your payment gateway goes down, your customers still need to pay. This guide covers how to build a fallback from Paystack to direct Daraja STK Push with health checks, circuit breakers, switching logic, and reconciliation across both providers.

Read more

Percentage vs Flat Split Configuration on Paystack

A detailed comparison of percentage and flat split configurations on Paystack. Covers how each model works, worked margin calculations across different transaction sizes, when to use percentage vs flat, implementing per-transaction overrides, and building hybrid models that combine both approaches.

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.