Back to Blog
Kenya
4 min read

M-Pesa API Integration: Building Secure Local Payment Rails with Node.js

A step-by-step guide to connecting Safaricom's Daraja API with Node.js for real-time transaction verification in e-commerce.

M-Pesa API Integration: Building Secure Local Payment Rails with Node.js

For any developer building e-commerce in Kenya, the Safaricom Daraja API is the most important tool in their kit. It is the bridge between a customer’s phone and your server. However, many developers struggle with making this integration "Production Ready"—often resulting in security vulnerabilities or missing transaction data. In this guide, I will break down how I build high-performance M-Pesa payment rails using Node.js.

The Daraja Architecture

The M-Pesa ecosystem revolves around several API products, but the most relevant for e-commerce is the **LNM (Lipa Na M-Pesa) Online** or **STK Push**. The flow is simple on the surface: your server asks Safaricom to prompt the user, the user pays, and Safaricom notifies your server.

The technical complexity lies in the authentication and the callback handling. Safaricom uses an OAuth 2.0 flow. Your Node.js application must first request an Access Token using your Consumer Key and Consumer Secret. This token is typically valid for one hour, so I always implement a caching layer (often using Redis) to store the token and avoid unnecessary API calls for every transaction.

Implementing the STK Push

Once you have your token, you can initiate the STK Push request. This involves sending a POST request to the LNM endpoint with a specific payload including your Business Short Code, Password (a base64 encoded string of your Shortcode, Passkey, and Timestamp), and the customer's phone number.

Using Node.js with a library like Axios makes this process clean. However, the real work happens in the callback. You must provide a publicly accessible URL where Safaricom can send the results of the transaction. This is where environment consistency becomes critical. In my projects, I use Docker and DigitalOcean to ensure that the environment where the callback is received is stable and secure.

The Callback: Ensuring Data Integrity

The most common failure in M-Pesa integrations is the "Missing Callback." If Safaricom sends a payment notification and your server is down, or if the response is lost, you end up with a paid order that is marked as "Pending" in your system. This is a nightmare for customer service.

To solve this, I build resilient callback handlers. When a JSON payload hits the endpoint, the first thing my Node.js service does is log the raw data and then immediately respond to Safaricom with a `200 OK`. This acknowledges receipt before any heavy processing begins. We then parse the `ResultCode`. A code of `0` means success; anything else means the transaction failed (e.g., the user cancelled or had insufficient funds).

For high-volume stores, I often use a task queue like **Temporal** or a simple background worker. This ensures that even if our main server is busy, the payment confirmation logic is retried until it succeeds. This level of reliability is what separates a "side project" from a professional e-commerce system.

Security First: Hardening the Rails

Handling money requires a "Security First" mindset. You should never trust the phone number or the amount sent in the callback without verification. A sophisticated attacker could try to "spoof" a callback to your server.

To prevent this, I implement several layers of protection: 1. **IP Whitelisting**: Configure your Nginx reverse proxy to only allow traffic from Safaricom’s official IP ranges on your callback endpoint. 2. **Transaction Query**: If you are unsure about a callback, your system should proactively use the `LNM Query` API to check the status of that specific `CheckoutRequestID`. 3. **Data Encryption**: Ensure all communication is over HTTPS with TLS 1.3.

This rigorous approach to security is a hallmark of my technical capabilities. We don't just "make it work"; we "make it secure."

Integration with Modern Frameworks

Integrating these payment rails into a Next.js 15 frontend is where the magic happens. Using Server Actions or a dedicated API route, you can trigger the STK Push and then use a WebSocket or a polling mechanism to give the user instant feedback on the screen. The moment they hit "Finish" on their phone, the website should say "Order Successful."

This seamless experience is what drives sales. It removes the need for customers to "Contact us on WhatsApp with the code," a workflow that kills scalability. By automating the entire flow, you turn your website into a high-conversion machine.

Conclusion

Building secure local payment rails for Kenyan e-commerce is about more than just reading API documentation. It's about engineering a system that handles network failure, prevents fraud, and provides a world-class user experience. Node.js is a fantastic tool for this because of its non-blocking I/O and vast ecosystem of libraries.

If you're looking to build a high-performance storefront with M-Pesa at its core, explore my Services or see how I've handled similar large-scale data integrations in my Case Studies.

WhatsApp
Get in touch