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 Gym Membership App
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Build a Gym Membership App

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.

In this article

  1. 1. Data Model and Membership Tiers
  2. 2. Subscription Signup and Access Control
  3. 3. Learn More

Data Model and Membership Tiers

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.

Subscription Signup and Access Control

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.

Learn More

See build a sports club subscription system for a similar recurring billing pattern applied to clubs and leagues.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Use Paystack Plans for monthly membership billing — recurring charges happen automatically without your intervention.
  • ✓Gate check-in access on an active membership record, not on payment dates in your own logic.
  • ✓Handle invoice.payment_failed with a grace period (3-5 days) before suspending access — card failures are often temporary.
  • ✓Support membership freeze: pause the subscription on Paystack and extend the expiry date on your end.
  • ✓Offer family plans using split subscriptions or bundle pricing with subaccounts for gym franchise splits.

Frequently Asked Questions

How do I handle membership freeze (when a member travels for a month)?
Call the Paystack Disable Subscription endpoint to pause billing temporarily. On your end, extend the membership expiry_at by the freeze duration. When the member returns, re-enable the subscription via Paystack. Budget for this in your revenue model — freeze periods reduce your recurring revenue but improve retention.
Can members pay monthly via M-Pesa in Kenya?
Paystack supports M-Pesa for one-time transactions in Kenya, but recurring M-Pesa charges (standing orders) require Safaricom Business API. For Kenyan gyms, a common approach is card-on-file for recurring billing and M-Pesa for one-time top-ups or day passes. Alternatively, use M-Pesa standing orders via Daraja directly for automated monthly collection.
How do I issue a partial refund when a member cancels mid-month?
Calculate the unused days as a proportion of the billing period and refund that amount using the Paystack Refund API with the transaction reference and amount. Cancel the subscription simultaneously to stop future charges. Log the cancellation reason for retention analysis.

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 Laundry Pickup and Payment App

A build guide for a laundry pickup and delivery app that uses Paystack to collect payments. Covers order pricing by weight or item count, pickup scheduling, status tracking through the wash cycle, payment collection at order creation or delivery, and customer notifications.

Read more

Build an Affiliate Commission Engine

A build guide for an affiliate commission engine where affiliates share tracking links, earn commissions when their referrals convert (pay via Paystack), and withdraw their balance via bank transfer. Covers click tracking, last-click attribution, commission ledger, and Paystack Transfers payout.

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

Paystack Callback Handling for Kenyan Mobile Money

Paystack webhooks for M-Pesa and Pesalink payments behave differently from card payment webhooks. The timing is different, the pending states are different, and the race conditions between webhooks and redirect callbacks are more pronounced. This article covers how to build a webhook handler that correctly distinguishes payment channels, handles the mobile money pending window, and avoids the double-grant bug.

Read more

Building a Two-Sided Marketplace on Paystack

An end-to-end architecture guide for building a two-sided marketplace on Paystack. Covers seller onboarding with subaccount creation, initializing split transactions at checkout, handling webhooks to confirm splits, managing refunds on split transactions, database schema design, and the operational edge cases that trip up most marketplace builders.

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.