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 Home Services Booking Marketplace
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Build a Home Services Booking Marketplace

Build a home services marketplace by registering each provider as a Paystack Subaccount, routing service bookings through the subaccount split at payment time, and confirming the booking only after the charge.success webhook fires. The provider receives their share automatically; your platform keeps the service fee. Use Paystack Transfers for bonus payouts or adjustments.

In this article

  1. 1. Provider Onboarding and Service Booking
  2. 2. Job Completion and Provider Ratings
  3. 3. Learn More

Provider Onboarding and Service Booking

Tables: providers (name, phone, email, category, skills[], bio, avg_rating, paystack_subaccount_code, is_verified, is_available), services (provider_id, name, category, base_price, duration_hrs, description), bookings (customer_id, provider_id, service_id, address, scheduled_at, status, total_amount, paystack_ref).

var PLATFORM_FEE_PERCENT = 20; // platform keeps 20%, provider gets 80%

// Onboard a new provider with Paystack Subaccount
async function onboardProvider(providerData) {
  var provider = await db.providers.create({ ...providerData, is_verified: false });

  var res = await fetch('https://api.paystack.co/subaccount', {
    method: 'POST',
    headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      business_name: providerData.name,
      settlement_bank: providerData.bank_code,
      account_number: providerData.account_number,
      percentage_charge: PLATFORM_FEE_PERCENT,
    }),
  });
  var subaccountCode = (await res.json()).data.subaccount_code;
  await db.providers.update(provider.id, { paystack_subaccount_code: subaccountCode });
  return provider;
}

// Customer books a service
async function bookService(customerEmail, customerId, serviceId, address, scheduledAt) {
  var service = await db.services.findById(serviceId);
  var provider = await db.providers.findById(service.provider_id);

  if (!provider.is_available) throw new Error('Provider is not currently available');

  // Travel surcharge based on distance from provider base
  var travelFee = await estimateTravelFee(provider, address);
  var total = service.base_price + travelFee;
  var reference = 'HOME-' + serviceId + '-' + Date.now();

  var booking = await db.bookings.create({
    customer_id: customerId, provider_id: provider.id, service_id: serviceId,
    address, scheduled_at: scheduledAt, status: 'pending_payment',
    total_amount: total, 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: customerEmail, amount: total, currency: 'NGN', reference,
      subaccount: provider.paystack_subaccount_code,
      bearer: 'account', // platform bears transaction fee
      metadata: { booking_id: booking.id, customer_id: customerId, provider_id: provider.id, service: service.name },
    }),
  });
  return (await res.json()).data.authorization_url;
}

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

  await db.bookings.update(meta.booking_id, { status: 'confirmed' });
  var booking = await db.bookings.findById(meta.booking_id);
  var provider = await db.providers.findById(meta.provider_id);
  await sendBookingConfirmation(meta.customer_id, provider.name, booking.scheduled_at, booking.address);
  await sendJobNotification(provider.phone, meta.booking_id, booking.scheduled_at, booking.address);
}

Job Completion and Provider Ratings

// Provider marks job complete
async function completeJob(providerId, bookingId) {
  var booking = await db.bookings.findById(bookingId);
  if (booking.provider_id !== providerId) throw new Error('Not your booking');
  if (booking.status !== 'confirmed') throw new Error('Booking not confirmed');

  await db.bookings.update(bookingId, { status: 'completed', completed_at: new Date() });

  // Send rating request to customer
  var customer = await db.customers.findById(booking.customer_id);
  await sendRatingRequest(customer.email, customer.name, booking.id, providerId);
}

// Customer rates the provider
async function rateProvider(customerId, bookingId, rating, review) {
  if (rating < 1 || rating > 5) throw new Error('Rating must be between 1 and 5');
  var booking = await db.bookings.findById(bookingId);
  if (booking.customer_id !== customerId) throw new Error('Not your booking');

  await db.reviews.create({ booking_id: bookingId, provider_id: booking.provider_id, customer_id: customerId, rating, review, created_at: new Date() });

  // Update provider average rating
  var allRatings = await db.reviews.findByProvider(booking.provider_id);
  var avgRating = allRatings.reduce(function(sum, r) { return sum + r.rating; }, 0) / allRatings.length;
  await db.providers.update(booking.provider_id, { avg_rating: Math.round(avgRating * 10) / 10 });
}

// Customer disputes the job — escalate to admin
async function disputeJob(customerId, bookingId, reason) {
  var booking = await db.bookings.findById(bookingId);
  if (booking.customer_id !== customerId) throw new Error('Not your booking');

  await db.bookings.update(bookingId, { status: 'disputed' });
  await db.disputes.create({ booking_id: bookingId, raised_by: customerId, reason, status: 'open' });
  await notifyAdmin('New dispute on booking #' + bookingId + ': ' + reason);
}

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 tutoring marketplace with split payments for the same provider subaccount split pattern applied to educational services.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Create a Paystack Subaccount per service provider — revenue splitting is automated at payment time.
  • ✓Set platform commission as the bearer's share (15-20%); providers receive the remainder automatically.
  • ✓Confirm bookings only in the charge.success webhook — never before payment clears.
  • ✓Rate providers after job completion — use average rating to rank providers in search results.
  • ✓For multi-person jobs (e.g., cleaning crew of 3), assign a lead provider and split internally.

Frequently Asked Questions

How do I verify that providers are qualified (e.g., licensed electricians)?
Require document upload at onboarding: professional certification, national ID, and a selfie with the document. Run a manual review before setting is_verified: true. For high-risk services (electrical, gas), partner with professional bodies (e.g., ERB in Kenya, COREN in Nigeria) to verify membership numbers via their APIs or manual lookup. Display a "Verified Provider" badge prominently on the profile.
What if the customer is not home when the provider arrives?
Give the provider a 15-minute wait window. If the customer does not answer after 15 minutes, the provider marks the job as "customer_absent." No refund is issued — the provider's time was committed. The provider receives 50% of the service fee as a show-up payment via Paystack Transfers. The customer gets the remaining 50% as a platform credit for a future booking, not cash refund.
How do I handle services that take longer than the estimated duration?
Include an overtime surcharge in your terms: NGN X per additional 30 minutes beyond the booked duration. After the job, the provider submits the actual duration in the completion form. If overtime applies, generate a supplementary Paystack payment link for the extra amount and send it to the customer. The customer pays the overtime charge before rating the provider.

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 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

Building a Kenyan SaaS Subscription Billing System

A complete build guide for SaaS subscription billing in Kenya with Paystack. Covers KES plan creation, handling M-Pesa for initial charges, card-based recurring billing, dunning for failed charges, plan upgrades and downgrades, free trial management, and building a billing page UI.

Read more

Delayed Settlement Patterns for Marketplaces

Delayed settlement holds a vendor's funds until a condition is met — delivery confirmed, dispute window closed, or event completed. This guide covers how to implement delayed settlement using Paystack manual settlement, hold timers in your database, and conditional payout release.

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.