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 Grocery Delivery App with Rider Payouts
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Build a Grocery Delivery App with Rider Payouts

Build a grocery delivery app by connecting a product catalog with a Paystack checkout. When the customer pays, the webhook triggers order preparation and rider assignment. After the rider confirms delivery, use the Paystack Transfers API to pay them. Riders are registered as Transfer Recipients (bank account or M-Pesa) for automatic payout.

In this article

  1. 1. Cart Checkout and Payment
  2. 2. Rider Assignment and Payout
  3. 3. Learn More

Cart Checkout and Payment

Tables: products (name, price, category, stock), orders (customer_id, items[], subtotal, delivery_fee, total, delivery_address, status, paystack_ref), riders (name, phone, paystack_recipient_code), deliveries (order_id, rider_id, assigned_at, delivered_at, payout_ref).

async function checkout(customerId, customerEmail, cartItems, deliveryAddress) {
  var subtotal = cartItems.reduce((sum, item) => sum + (item.price * item.qty), 0);
  var deliveryFee = 150000; // NGN 1,500 in kobo
  var total = subtotal + deliveryFee;
  var reference = 'GROC-' + Date.now();

  var order = await db.orders.create({
    customer_id: customerId, items: cartItems, subtotal, delivery_fee: deliveryFee,
    total, delivery_address: deliveryAddress, status: 'pending', 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,
      metadata: { order_id: order.id, delivery_fee: deliveryFee },
    }),
  });
  return (await res.json()).data.authorization_url;
}

Rider Assignment and Payout

// Webhook: confirm payment and start fulfillment
if (event.event === 'charge.success') {
  var orderId = event.data.metadata.order_id;
  await db.orders.update(orderId, { status: 'confirmed' });
  var rider = await assignAvailableRider(orderId); // your assignment logic
  await db.deliveries.create({ order_id: orderId, rider_id: rider.id, assigned_at: new Date() });
  await notifyRider(rider.id, orderId);
  await notifyCustomer(orderId);
}

// Rider confirms delivery — trigger payout
async function confirmDelivery(deliveryId) {
  var delivery = await db.deliveries.findById(deliveryId);
  var order = await db.orders.findById(delivery.order_id);

  await db.deliveries.update(deliveryId, { delivered_at: new Date(), status: 'delivered' });
  await db.orders.update(order.id, { status: 'delivered' });

  // Pay rider via Transfer
  var rider = await db.riders.findById(delivery.rider_id);
  var payoutAmount = order.delivery_fee; // rider gets the delivery fee

  var res = await fetch('https://api.paystack.co/transfer', {
    method: 'POST',
    headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      source: 'balance',
      amount: payoutAmount,
      recipient: rider.paystack_recipient_code,
      reason: 'Delivery payout #' + deliveryId,
      reference: 'RIDER-' + deliveryId + '-' + Date.now(),
    }),
  });
  var transfer = await res.json();
  await db.deliveries.update(deliveryId, { payout_ref: transfer.data.reference });
}

To register a rider as a Transfer Recipient: POST to /transferrecipient with type "mobile_money" (for M-Pesa) or "nuban" (for Nigerian bank), the rider's phone/account number, and bank code. Store the returned recipient_code in the riders table.

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 restaurant ordering and payment app for the same rider payout pattern in a restaurant delivery context.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Pay riders via Paystack Transfers after delivery confirmation, not via cash handover.
  • ✓Riders must be registered as Paystack Transfer Recipients with their bank or M-Pesa details.
  • ✓Handle the charge.success webhook before doing anything — no fulfillment without payment confirmation.
  • ✓Build a rider app or dashboard for status updates: assigned, picked_up, delivered.
  • ✓Batch daily rider payouts using Paystack Bulk Transfers to reduce API calls and transfer fees.

Frequently Asked Questions

What if a transfer to the rider fails?
Listen for the transfer.failed webhook event. Log the failure and alert your operations team. Retry the transfer with corrected details (wrong account number is the most common cause). Have a fallback process for paying riders manually if automated transfer fails repeatedly.
Should I pay riders per delivery or accumulate and pay daily?
Accumulate and pay daily using Paystack Bulk Transfers — it reduces API calls, transfer fees, and operational complexity. Send one transfer per rider at end of day covering all their completed deliveries. Riders generally prefer daily payouts; this schedule is also easier to reconcile.
How do I handle a rider who picks up an order but never delivers?
Add a delivery timeout: if an order stays in "picked_up" status for more than 2 hours, flag it for operations review. Do not pay the rider until delivery is confirmed. If the order is confirmed stolen or abandoned, the rider does not get the payout, and you issue a refund to the customer using the Paystack Refund API.

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 School Fees Portal for a Kenyan School

A complete guide to building a school fees collection system for Kenyan schools with Paystack. Student registration, term-based billing, M-Pesa and card payments, receipt generation, fee balance tracking, bulk reminders, and reconciliation with school accounts.

Read more

Building a Multi-Vendor Store with Paystack

A multi-vendor store lets customers buy products from different sellers in one cart. This guide covers splitting a mixed-cart payment across multiple vendor subaccounts using Paystack Transaction Splits, collecting platform commission, and managing vendor payouts.

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.