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