EP Ecommerce M-Pesa
EP Ecommerce M-Pesa takes M-Pesa payments in Kenya for EP Ecommerce. Your customer enters their number at the checkout, a prompt appears on their phone, and they approve it with their PIN.
Published by ElmsPark Studio.
Overview
Section titled “Overview”- STK push through Safaricom Daraja. No card details, no redirect, no app to install.
- The number is checked before anything is sent. A mistyped number is the commonest reason an M-Pesa payment fails, and its failure mode is the worst kind: no prompt arrives at all, so the customer stares at a phone that never buzzes and decides your shop is broken. The checkout validates the number, shows it back masked, and refuses to send until it looks like a real Safaricom number.
- Waiting is a proper screen, not a spinner. The customer is told what to do, on which number, and how long they have.
- The tab can close. The order settles on your server regardless, and the receipt email is the real confirmation.
- Nothing sits pending forever. An approval that never comes closes cleanly as expired, with a reason, instead of leaving an order stuck.
- Reasons the customer can act on: not enough money, wrong PIN, timed out, cancelled on the handset.
- Every success is confirmed with Safaricom before the order completes. See Security below, because this one matters.
Requirements
Section titled “Requirements”- PageMotor 0.11 or later
- EP Ecommerce (base plugin)
- EP Suite base class
- A Safaricom Daraja account with a short code, consumer key, consumer secret and pass key
- Prices in KES. M-Pesa settles in Kenyan shillings only.
Installation
Section titled “Installation”- Install EP Ecommerce first.
ep-ecommerce-mpesa.zipcomes with an EP Suite licence, supplied directly by ElmsPark (see EP Suite plugins); after install it updates through your site’s Updates screen.- Upload via Plugins → Manage Plugins. Activate.
Setting up M-Pesa
Section titled “Setting up M-Pesa”Step 1 — Get your Daraja credentials
Section titled “Step 1 — Get your Daraja credentials”In the Daraja portal, create an app and take the consumer key and consumer secret. From your Lipa na M-Pesa setup you also need the business short code and the pass key.
Step 2 — Configure the plugin
Section titled “Step 2 — Configure the plugin”Open Plugins → EP Ecommerce M-Pesa → Settings.
- Accept M-Pesa payments: leave this on No until the rest is filled in.
- Mode: start on Sandbox, which uses Safaricom’s test environment and takes no real money.
- Fill in the short code, consumer key, consumer secret and pass key. The three secrets are stored encrypted.
- Seconds to wait for approval: 120 is a sensible default. Below 60 will fail people who are simply slow to find their phone.
Step 3 — Register the callback URL
Section titled “Step 3 — Register the callback URL”The settings screen shows your callback URL:
https://yoursite.com/ep-payment-webhook.json?provider=mpesaRegister it in the Daraja portal.
Keep the .json on the end. Without it, PageMotor redirects the request to a trailing slash and the payment notification body is lost on the way.
Step 4 — Optional, but worth doing
Section titled “Step 4 — Optional, but worth doing”Safaricom callback IP addresses, one per line. The M-Pesa callback carries no signature, so Safaricom identify themselves by address alone. Leaving this empty is still safe, because every success is confirmed with Safaricom before an order completes, but filling it in turns a forged callback away before your site even asks. Take the current list from the Daraja portal rather than from a blog post.
How the payment flow works
Section titled “How the payment flow works”- Your customer enters their M-Pesa number at the checkout and presses Purchase.
- The number is validated and shown back masked, so a wrong one is caught here rather than by silence.
- The plugin asks Daraja to push a prompt to that handset. The amount comes from the product record on your site.
- The checkout switches to the waiting screen: what to do, the masked number, and a countdown.
- Your customer approves on their phone.
- Safaricom calls your site back. The plugin asks Safaricom to confirm it, and only then completes the order.
- The receipt email arrives.
If the approval never comes, the order closes as expired when the window passes and says why.
Amounts
Section titled “Amounts”Daraja accepts whole shillings only. A price of KES 1,500.50 is sent as 1,501. Prices are easiest to keep in whole shillings.
Keeping orders in step
Section titled “Keeping orders in step”M-Pesa callbacks go missing routinely. EP Ecommerce ships a reconciliation action that asks Safaricom what really happened to any order still waiting past its window, then completes or closes it.
Run it on a schedule through EP Cron, or call it yourself:
EP_Ecommerce / reconcile-ordersIt is safe to run twice, and safe to run while a callback is arriving. It cannot complete an order that is already complete.
If it cannot reach Daraja at all, it leaves the order alone and tries again later rather than closing something that may well have been paid.
Security
Section titled “Security”The M-Pesa callback is not signed. Every other payment provider signs its notifications with a shared secret, so your site can prove the message came from them. Safaricom do not. They identify themselves by the address the request came from, and nothing else.
So this plugin treats the callback as a hint rather than as proof. The callback says which order to look at, and the plugin then asks Safaricom directly what actually happened, through the STK Push Query API. Only an answer from Safaricom completes an order.
That means a forged callback claiming a payment succeeded costs your site one query and nothing else. It also means that if Safaricom cannot be reached, nothing completes: the plugin fails closed rather than guessing.
The optional IP list in the settings adds a second layer by turning away anything that did not come from Safaricom before the query is even made.
Alongside that:
- Secrets are encrypted at rest and read back as
__saved__over the API. - The amount charged always comes from the product record on your site.
- The number is re-validated on the server. Nothing the browser sends is trusted.
Troubleshooting
Section titled “Troubleshooting””No prompt arrives on the phone”
Section titled “”No prompt arrives on the phone””Almost always the number. The checkout should have caught it, so check the number shown on the waiting screen against the customer’s actual M-Pesa number. Safaricom mobile numbers start 07 or 01.
”The prompt arrives but the order stays waiting”
Section titled “”The prompt arrives but the order stays waiting””The callback did not reach your site. Check System Status for the last callback received and any rejections, then run reconcile-orders, which will settle it from Safaricom’s own answer.
”Orders expire even though customers say they paid”
Section titled “”Orders expire even though customers say they paid””Check the approval window. Anything under 60 seconds is tight for someone who has to find their phone, unlock it and type a PIN.
”The callback is rejected”
Section titled “”The callback is rejected””If you filled in the IP list, Safaricom may be calling from an address not on it. The current list comes from the Daraja portal. Clearing the field is safe: confirmation with Safaricom still protects the order.
”Sandbox works, live does not”
Section titled “”Sandbox works, live does not””Sandbox and live have entirely separate credentials and short codes. Check the Mode setting matches the credentials you pasted.
Changelog
Section titled “Changelog”- Settings language menu. The language menu in this plugin’s settings now lists only the languages it is actually translated into, plus English, so you can no longer pick a language that changes nothing.
- Once an M-Pesa payment is approved, the page moves on as it is set up to. Where the checkout says where to go after payment (an invoice’s pay link goes back to the invoice, which then shows it paid), the page now goes there a moment after “Payment received” appears. It used to stay on the waiting screen.
- “Send it again” only reuses your own order. Someone else could previously point another customer’s open M-Pesa order at their own phone, so the real customer’s payment no longer matched and their order expired even though they had paid.
- A repeated payment confirmation can no longer look like a new payment.
- If Paystack takes a Kenyan shilling product, the M-Pesa checkout now sends the buyer to Paystack instead of reloading the page.
- M-Pesa donations now ask for the amount the donor chose. A pay-what-you-like donation paid with M-Pesa used to send the product’s list price to the donor’s phone. It now sends the amount they typed. “Send it again” asks for the same amount as the first request. Needs EP Ecommerce 0.1.43.
- A discount code typed at checkout is now refused with a message when M-Pesa takes the payment, instead of being ignored and charging the full price.
First release. STK push through Safaricom Daraja, with number validation before sending, a real waiting state, confirmation with Safaricom before any order completes, reconciliation for missed callbacks, and clean expiry with a reason.
Feedback and corrections
Section titled “Feedback and corrections”For a quick question about this plugin, EP Support inside your admin is the fastest option. The chat widget sits on every EP plugin settings page and knows which one you’re on, with starter questions and links preloaded for that exact screen.
For anything bigger, a bug report, a feature request, or a “how do I…” that needs a real reply, open a ticket at help.elmspark.com. A real person, helped by AI, writes the reply. Usually within a few hours. Tickets don’t disappear into the void.