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 Coworking Space Booking System
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Build a Coworking Space Booking System

Build a coworking booking system by managing resource calendars (desks and meeting rooms with capacity and hourly/daily rates), requiring Paystack payment before issuing an access code or QR pass, and offering both day passes (one-time payment) and monthly memberships (Paystack Plans). Webhook confirmation triggers the access credential generation.

In this article

  1. 1. Resource Booking and Payment
  2. 2. Monthly Memberships and Access Control
  3. 3. Learn More

Resource Booking and Payment

Tables: resources (name, type, capacity, hourly_rate, daily_rate, amenities[]), bookings (member_id, resource_id, start_time, end_time, duration_hours, total_amount, status, access_code, paystack_ref), memberships (member_id, plan_type, status, meeting_room_hours_remaining, paystack_subscription_code).

async function bookResource(memberEmail, memberId, resourceId, startTime, endTime) {
  var resource = await db.resources.findById(resourceId);
  var hours = Math.ceil((new Date(endTime) - new Date(startTime)) / (1000 * 60 * 60));

  // Check availability
  var conflicts = await db.bookings.findConflicts(resourceId, startTime, endTime);
  if (conflicts.length > 0) throw new Error('Resource not available for selected time');

  // Check if member has a monthly bundle with remaining hours
  var membership = await db.memberships.findActiveByMember(memberId);
  if (membership && resource.type === 'meeting_room' && membership.meeting_room_hours_remaining >= hours) {
    // Deduct from bundle — no payment needed
    await db.memberships.decrement(memberId, 'meeting_room_hours_remaining', hours);
    return await createBookingWithAccess(memberId, resourceId, startTime, endTime, hours, 0, null);
  }

  var amount = resource.type === 'hot_desk' ?
    (hours >= 8 ? resource.daily_rate : hours * resource.hourly_rate) :
    hours * resource.hourly_rate;

  var reference = 'CWK-' + resourceId + '-' + Date.now();
  var booking = await db.bookings.create({
    member_id: memberId, resource_id: resourceId, start_time: startTime, end_time: endTime,
    duration_hours: hours, total_amount: amount, status: 'pending_payment', paystack_ref: reference,
  });

  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, currency: 'NGN', reference,
      metadata: { booking_id: booking.id, resource_id: resourceId, start_time: startTime },
    }),
  });
  return (await res.json()).data.authorization_url;
}

// Webhook: confirm booking and issue access
if (event.event === 'charge.success') {
  var meta = event.data.metadata;
  if (!meta.booking_id) return res.sendStatus(200);

  var accessCode = Math.random().toString(36).slice(2, 8).toUpperCase(); // 6-char code
  await db.bookings.update(meta.booking_id, { status: 'confirmed', access_code: accessCode });
  await sendAccessCode(meta.booking_id, accessCode, meta.start_time);
}

Monthly Memberships and Access Control

// Monthly membership subscription
async function subscribeToMembership(memberEmail, memberId, planName) {
  var PLANS = {
    hot_desk_monthly: { price: 2500000, meeting_room_hours: 0, paystack_plan_code: process.env.HOT_DESK_PLAN },
    pro_monthly:      { price: 4500000, meeting_room_hours: 10, paystack_plan_code: process.env.PRO_PLAN },
  };
  var plan = PLANS[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: memberEmail, amount: plan.price,
      plan: plan.paystack_plan_code,
      metadata: { member_id: memberId, plan_name: planName, meeting_room_hours: plan.meeting_room_hours },
    }),
  });
  return (await res.json()).data.authorization_url;
}

if (event.event === 'subscription.create') {
  var meta = event.data.metadata;
  await db.memberships.upsert({
    member_id: meta.member_id,
    plan_type: meta.plan_name,
    status: 'active',
    meeting_room_hours_remaining: meta.meeting_room_hours,
    paystack_subscription_code: event.data.subscription_code,
  });
}

// Renew meeting room hours each billing cycle
if (event.event === 'invoice.create' && event.data.status === 'success') {
  var code = event.data.subscription.subscription_code;
  var membership = await db.memberships.findBySubscription(code);
  if (membership.plan_type === 'pro_monthly') {
    await db.memberships.update(membership.id, { meeting_room_hours_remaining: 10 }); // reset monthly
  }
}

// Validate access at the door (QR scan or code entry)
async function validateEntry(accessCode, resourceId) {
  var booking = await db.bookings.findByAccessCode(accessCode);
  if (!booking || booking.resource_id !== parseInt(resourceId)) throw { status: 403, message: 'Invalid access code.' };
  var now = new Date();
  var start = new Date(booking.start_time);
  var end = new Date(booking.end_time);
  if (now < new Date(start.getTime() - 15 * 60000)) throw { status: 403, message: 'Too early — entry allowed 15 min before booking.' };
  if (now > end) throw { status: 403, message: 'Booking has expired.' };
  return { valid: true, member_id: booking.member_id, resource_name: booking.resource.name };
}

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 hotel booking and deposit system for a similar resource booking pattern with deposit collection and balance at checkout.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Separate resource types: hot desks (first-come-first-served) vs meeting rooms (reserved slots).
  • ✓Generate an access QR code or PIN only after the Paystack charge.success webhook fires.
  • ✓Offer day passes (one-time) and monthly memberships (Paystack Plans) for different use cases.
  • ✓Meeting room bundles (10 hours/month) reduce friction for regular users — track against their monthly allowance.
  • ✓Monitor resource utilization rates — if a desk is occupied less than 50% of the time, it may not be worth reserving.

Frequently Asked Questions

How do I handle last-minute cancellations for meeting rooms?
Define a cancellation policy: full refund if cancelled 2+ hours before start; 50% refund if 1-2 hours before; no refund if less than 1 hour. Process refunds via the Paystack Refund API with the original reference. For monthly members, return the hours to their meeting_room_hours_remaining instead of a cash refund.
Can I support multiple coworking locations under one platform?
Yes. Add a locations table and link resources to locations. Members browse by location, then book resources. Monthly memberships can be location-specific (home location) or multi-location (access to all branches). For corporate clients who want dedicated desks across multiple offices, create a corporate plan with higher pricing and multi-location access.
How do I handle walk-in members who have not pre-booked?
Build a kiosk or front-desk interface where staff can initiate a booking on behalf of a walk-in and send a Paystack payment link to their phone. Alternatively, accept cash for walk-ins and manually mark the booking as "walk_in_cash" in the system. Track walk-in volume — if it exceeds 20% of occupancy, it is worth adding a day-pass QR code that members can buy via PayStack without talking to staff.

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

Paystack Transfers to M-PESA Wallets Explained

A practical guide to sending money to M-Pesa wallets through Paystack Transfers. Covers recipient creation, transfer initiation, webhook handling, OTP requirements, transfer limits, error recovery, and reconciliation patterns for Kenyan developers.

Read more

Testing Split Payment Logic Safely

Split payment bugs are expensive — wrong commission rates or incorrect subaccount amounts mean vendors get overpaid or underpaid. This guide covers unit testing commission math, integration testing with Paystack test mode subaccounts, and verifying the full flow end-to-end without touching production.

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.