WhatsApp Commerce Plus Paystack Payment Links
You can sell on WhatsApp and collect payment through Paystack by generating a payment link via the Paystack API, then sending that link in a WhatsApp message. The customer taps the link, pays with M-Pesa or card on the Paystack checkout page, and you receive a webhook confirming the payment. No custom checkout page required.
Why WhatsApp Commerce Works in Kenya
WhatsApp is the most used messaging app in Kenya. It is where people coordinate, negotiate, and make buying decisions. Millions of small businesses already sell through WhatsApp by posting product photos in status updates and group chats, then negotiating in DMs.
The problem has always been payment. The conversation happens on WhatsApp, but collecting money requires jumping to another channel. "Send to till 123456." "Pay to paybill 789." "I'll send you M-Pesa, give me your number." Each jump introduces friction. Friction kills conversions.
Paystack payment links solve this by putting a clickable payment URL directly inside the WhatsApp conversation. The customer taps it, lands on a Paystack-hosted checkout page, pays with M-Pesa or card, and comes back to the chat. No new app to install. No till number to remember. No amount to type manually.
This is not a new payment method. It is an existing payment method (Paystack checkout) delivered through the channel where Kenyan customers already spend their time.
What Paystack Payment Links Actually Are
A Paystack payment link is a URL that opens a hosted checkout page. The page shows the amount, accepts payment, and redirects the customer afterward. Think of it as a mini checkout with no website required.
There are two ways to create payment links:
- Dashboard. Go to your Paystack dashboard, click "Payment Pages" or "Payment Links," fill in the details, and copy the generated URL. This works for a handful of products with fixed prices.
- API. Call the Paystack Transaction Initialize endpoint to create a unique payment link per order. This is the approach that scales, because you can generate links on the fly for custom amounts, attach metadata, and track each link back to a specific customer and order.
When we talk about WhatsApp commerce, the API approach is what matters. You want to generate a link for each order, with the correct amount, and send it in the chat. Doing this manually through the dashboard works for the first five orders. After that, you need automation.
The link itself looks something like https://paystack.com/pay/some-reference for dashboard-created links, or you can use the authorization_url returned by the Transaction Initialize endpoint. Both lead to the same Paystack checkout experience.
Creating Payment Links via the Paystack API
The Transaction Initialize endpoint is the foundation. You call it with the customer's email, the amount, and any metadata you want to attach. Paystack returns an authorization_url, which is the payment link you send to the customer.
// Generate a Paystack payment link for a WhatsApp order
async function createPaymentLink(order) {
const response = 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: order.customerEmail,
amount: order.totalCents, // KES 1,500 = 150000
currency: 'KES',
reference: `wa-${order.id}-${Date.now()}`,
callback_url: 'https://yoursite.co.ke/payment-complete',
metadata: {
order_id: order.id,
customer_phone: order.phone,
channel: 'whatsapp',
custom_fields: [
{
display_name: 'Order',
variable_name: 'order_id',
value: order.id,
},
],
},
}),
});
const data = await response.json();
if (!data.status) {
throw new Error(`Paystack error: ${data.message}`);
}
return {
paymentUrl: data.data.authorization_url,
reference: data.data.reference,
accessCode: data.data.access_code,
};
}
A few details worth noting:
- Reference prefix. Using a prefix like
wa-lets you filter WhatsApp orders in your Paystack dashboard and reconciliation reports. When you have orders coming from your website, your app, and WhatsApp, this prefix saves hours. - Metadata. Attach the order ID, customer phone number, and the channel. When the webhook arrives, you can route the confirmation back to the right WhatsApp conversation.
- Callback URL. This is where the customer lands after paying. For WhatsApp commerce, this could be a simple "Thank you" page with order details, or a deep link back to your WhatsApp chat.
- Amount in cents. KES 1,500 is
150000. This trips people up on the first integration. Double-check your amounts.
The authorization_url in the response is the link you send on WhatsApp. It is a one-time-use URL tied to that specific transaction.
Tracking Payments with Webhooks
After you send the link, you need to know when the customer pays. This is where Paystack webhooks come in.
When the customer completes payment, Paystack sends a charge.success event to your webhook URL. The payload includes the reference you set when creating the link, the amount paid, and the metadata you attached.
// Webhook handler for WhatsApp payment confirmations
app.post('/webhooks/paystack', express.json(), async (req, res) => {
// Always return 200 immediately
res.status(200).send('OK');
const hash = crypto
.createHmac('sha512', process.env.PAYSTACK_SECRET_KEY)
.update(JSON.stringify(req.body))
.digest('hex');
if (hash !== req.headers['x-paystack-signature']) {
console.error('Invalid Paystack signature');
return;
}
const event = req.body;
if (event.event === 'charge.success') {
const { reference, amount, currency, metadata } = event.data;
// Check if this is a WhatsApp order
if (reference.startsWith('wa-')) {
const orderId = metadata.order_id;
const customerPhone = metadata.customer_phone;
// Update order status in your database
await updateOrderStatus(orderId, 'paid', {
paystackRef: reference,
amountPaid: amount,
currency,
});
// Send confirmation back to the customer on WhatsApp
await sendWhatsAppMessage(
customerPhone,
`Payment received for order ${orderId}. We are preparing your order now.`
);
}
}
});
The key pattern here is the loop: the customer pays on Paystack, your webhook picks it up, and you send a confirmation back to WhatsApp. The entire transaction starts and ends in the chat, even though the actual payment happened on Paystack's checkout page.
Always verify the webhook signature. Always confirm the amount matches what you expected for that order. These are not optional steps. Without them, someone could forge a webhook and your system would mark an order as paid without receiving money.
WhatsApp Business Catalog Integration
WhatsApp Business lets you create a product catalog inside the app. Customers can browse your products, see prices and descriptions, and tap to inquire. The catalog is free and built into WhatsApp Business.
The limitation: WhatsApp catalogs in Kenya do not have built-in payment. The customer can browse and add items to a cart, but there is no checkout button that processes payment. That is where Paystack payment links fill the gap.
The workflow looks like this:
- The customer browses your WhatsApp Business catalog and selects items.
- They send a cart or message expressing interest ("I want 2 of the blue ones and 1 red").
- You (or your bot) calculate the total and generate a Paystack payment link for that specific order.
- You send the payment link in the chat.
- The customer pays.
- Your webhook confirms the payment and you send a delivery update.
For businesses using WhatsApp Business (the app, not the API), steps 2 through 4 happen manually. You read the message, calculate the total, go to your backend or a simple tool to generate the link, and paste it back.
For businesses using the WhatsApp Business API, you can automate the entire flow. The customer sends a catalog order, your bot parses the items, calculates the total, calls the Paystack API to generate a link, and sends it back. All within seconds.
A few catalog-specific tips:
- Keep catalog prices up to date. If your catalog says KES 500 but the payment link says KES 600, the customer will not trust either number.
- Use catalog item IDs in your metadata. When generating the Paystack link, attach the catalog item IDs so you can track which products were sold through WhatsApp versus your website.
- Handle out-of-stock gracefully. If a customer orders something from the catalog that you no longer have, tell them before generating the payment link. Do not let them pay for something you cannot deliver.
Order Management via WhatsApp
Once payment is confirmed, you still need to fulfill the order. WhatsApp becomes your order management channel, and the Paystack reference ties everything together.
A basic order lifecycle on WhatsApp looks like this:
- Order placed: Customer sends items and details in chat.
- Payment link sent: You generate and send the Paystack link.
- Payment confirmed: Webhook fires, you send a confirmation message.
- Preparing: You update the customer ("Your order is being packed").
- Dispatched: You send a delivery update ("Your rider is on the way, ETA 30 mins").
- Delivered: Customer confirms receipt.
For tracking, store each order in your database with the WhatsApp phone number, the Paystack reference, the items, and the current status. When the customer asks "Where is my order?" you look up their phone number and give them a status update.
If you need to issue a refund, use the Paystack Refunds API with the original transaction reference. Let the customer know in the chat that the refund has been initiated and when to expect it.
One thing to be careful about: WhatsApp has a 24-hour messaging window for Business API users. After 24 hours without a customer message, you can only send pre-approved template messages. This means your post-payment updates need to go out promptly, or you need approved templates for order status messages.
Automating Link Generation
Manual link generation works when you have a few orders per day. Beyond that, you need a system.
The simplest automation is a lightweight backend service that takes an order (items, amounts, customer details) and returns a payment link. Here is a minimal Express endpoint:
// POST /api/whatsapp-order
// Body: { items: [...], customerPhone: '0712345678', customerEmail: 'j@example.com' }
app.post('/api/whatsapp-order', async (req, res) => {
const { items, customerPhone, customerEmail } = req.body;
// Calculate total
const totalKES = items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const totalCents = totalKES * 100;
// Build order summary for the WhatsApp message
const summary = items
.map((item) => `${item.quantity}x ${item.name} (KES ${item.price.toLocaleString()})`)
.join(', ');
// Generate order ID
const orderId = `WA-${Date.now()}`;
// Save order to database
await saveOrder({
id: orderId,
items,
totalKES,
customerPhone,
customerEmail,
status: 'pending_payment',
});
// Create Paystack payment link
const paystackRes = 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: totalCents,
currency: 'KES',
reference: orderId,
metadata: {
order_id: orderId,
customer_phone: customerPhone,
channel: 'whatsapp',
items: items.map((i) => `${i.quantity}x ${i.name}`),
},
}),
});
const paystackData = await paystackRes.json();
if (!paystackData.status) {
return res.status(500).json({ error: paystackData.message });
}
res.json({
orderId,
paymentUrl: paystackData.data.authorization_url,
message: `Your order: ${summary}. Total: KES ${totalKES.toLocaleString()}.\n\nPay here: ${paystackData.data.authorization_url}`,
});
});
This endpoint does three things: calculates the total, saves the order, and generates the payment link. The response includes a ready-to-send WhatsApp message with the link. If you are using the WhatsApp Business API, your bot calls this endpoint and sends the message automatically. If you are running a manual operation, a staff member hits this endpoint from a simple internal tool and pastes the message into WhatsApp.
For businesses processing dozens of WhatsApp orders daily, this small backend is the difference between spending an hour on payment logistics and spending five minutes.
Handling Edge Cases
WhatsApp commerce is messy. Customers change their mind, lose connection, pay partial amounts by accident, or send payment for the wrong order. Here is how to handle the common problems.
- Customer does not pay within the link's lifetime. Paystack transaction links expire. If the customer does not pay, generate a new link when they come back. Do not keep stale orders in "pending" status forever. Run a cleanup job that marks old unpaid orders as expired.
- Customer wants to change the order after receiving the link. Do not edit the existing Paystack transaction. Generate a new link with the updated amount. Tell the customer the old link is no longer valid.
- Customer pays but you do not receive the webhook. This can happen if your server was down briefly. Paystack retries webhooks, but you should also have a verification fallback. Periodically poll the Paystack Verify Transaction endpoint for pending orders to catch any missed webhooks.
- Customer sends money directly to your M-Pesa instead of using the link. This happens. The customer sees a payment link but decides to "just send M-Pesa" to your personal number. Now you have money outside Paystack and no way to reconcile automatically. Make it clear in the chat that payment must go through the link. If they insist on M-Pesa, handle it outside the system and manually mark the order as paid.
- Multiple orders from the same customer. Use unique references for each order. If a customer places three orders in one day, each order gets its own link with its own reference. Do not batch multiple orders into one payment link unless the customer explicitly wants that.
Next Steps
Payment links on WhatsApp are the starting point. They work today with zero custom development if you are willing to generate links from the Paystack dashboard manually. With a small backend, they scale to hundreds of daily orders.
If you want to go further, the WhatsApp Bot That Collects Payment via Paystack guide covers building a full conversational bot that handles product browsing, cart management, and payment all within WhatsApp.
For the broader picture of Paystack in Kenya, including M-Pesa, Pesalink, settlement, and regulatory requirements, start with Paystack in Kenya: M-Pesa, Pesalink, and the Daraja Question.
If you are building payment integrations professionally, the McTaba 26-week bootcamp covers payment systems as part of a full-stack engineering curriculum. You ship real products that accept real money from Kenyan customers.
Key Takeaways
- ✓Paystack payment links are the simplest way to collect payment on WhatsApp. You generate a link, drop it into the chat, and the customer pays on a hosted checkout page.
- ✓The Paystack API lets you create payment links programmatically. This means you can generate a unique link per order, per customer, per conversation without touching the dashboard.
- ✓Payment links support M-Pesa, cards, Pesalink, and other methods available in your Paystack account. The customer chooses their preferred method on the checkout page.
- ✓You track payment completion through Paystack webhooks. When the customer pays, your server receives a charge.success event with the transaction reference you attached to the link.
- ✓WhatsApp Business catalogs let you showcase products, but WhatsApp itself has no built-in payment processing in Kenya. Payment links bridge that gap.
- ✓Automating link generation with a simple backend turns WhatsApp from a chat tool into a lightweight storefront without building a full e-commerce site.
Frequently Asked Questions
- Do I need a website to sell on WhatsApp with Paystack?
- No. The Paystack payment link opens a hosted checkout page that Paystack manages. You do not need your own website or checkout page. You do need a Paystack account with KES enabled and a way to receive webhooks (a simple server or serverless function).
- What payment methods are available on Paystack payment links in Kenya?
- The same methods available in your Paystack account: M-Pesa, cards (Visa/Mastercard), Pesalink, Airtel Money, and bank transfer. The customer chooses their preferred method on the checkout page.
- How long do Paystack payment links last before they expire?
- Transaction-specific links created via the Transaction Initialize endpoint have a limited lifetime. The exact duration depends on Paystack's current settings. If a customer tries to use an expired link, they will see an error. Generate a new link if the original expires.
- Can I send Paystack payment links in WhatsApp groups?
- Technically yes, a payment link is just a URL. But be careful. A payment link created for a specific customer and amount should only be used by that customer. If you share a link in a group, anyone could tap it and pay. For group sales, generate individual links for each buyer.
- How do I handle refunds for WhatsApp orders paid through Paystack?
- Use the Paystack Refunds API with the original transaction reference. Initiate the refund programmatically or through the Paystack dashboard. Notify the customer in the WhatsApp chat that the refund has been processed. The refund timeline depends on the original payment method.
Ready to build real-world apps?
Join the McTaba Labs full-stack marathon (4 months full-time · 6 months part-time). Learn M-Pesa, USSD, and WhatsApp engineering while shipping 8 production apps.
Apply to the McTaba Marathon