π I Built FormPesa β Turn Any Form Into an M-PESA Payment Gateway
If youβve ever tried integrating M-PESA API into a product, you already know the pain:
- Daraja setup friction
- Callback handling headaches
- Broken UX flows
- Forms and payments living separately
So I built something to fix that:
π FormPesa
π https://formpesa.wasmer.app/
π‘ The Problem
Most devs donβt struggle with code β they struggle with integration overhead.
Even though M-PESA supports core flows like sending money, paying bills, and STK push transactions , turning that into a clean product experience is still messy.
You end up writing:
- Custom checkout logic
- Payment validation layers
- Callback processors
- Retry logic
Over and over again.
β‘ The Idea
What if a form was the payment flow?
Thatβs the core idea behind FormPesa.
π§ How FormPesa Works (Under the Hood)
Letβs break it down like a dev.
π§Ύ 1. Form Definition (Frontend)
Instead of building a checkout, you define a form:
<form id="formpesa-form">
<input type="text" name="name" placeholder="Your Name" required />
<input type="tel" name="phone" placeholder="07XXXXXXXX" required />
<input type="email" name="email" placeholder="Email" />
<input type="hidden" name="amount" value="500" />
<button type="submit">Pay with M-PESA</button>
</form>
This is intentionally simple.
π‘ 2. Submit β Payment Trigger
When the user submits:
document.getElementById("formpesa-form").addEventListener("submit", async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const res = await fetch("https://formpesa.wasmer.app/api/pay", {
method: "POST",
body: JSON.stringify(Object.fromEntries(formData)),
headers: {
"Content-Type": "application/json"
}
});
const data = await res.json();
console.log("Payment initiated:", data);
});
π This replaces:
- Daraja authentication
- STK push request building
- Request signing
π² 3. STK Push (Abstracted)
Normally, you'd manually call Safaricomβs API:
{
"BusinessShortCode": "174379",
"Password": "...",
"Timestamp": "...",
"TransactionType": "CustomerPayBillOnline",
"Amount": "500",
"PartyA": "2547XXXXXXXX",
"PartyB": "174379",
"PhoneNumber": "2547XXXXXXXX",
"CallBackURL": "https://yourdomain.com/callback"
}
With FormPesa:
π You donβt touch this at all.
It handles:
- Token generation
- Encoding password
- STK push dispatch
π 4. Callback Handling (The Real Pain Point)
This is where most integrations break.
Typical raw callback:
{
"Body": {
"stkCallback": {
"ResultCode": 0,
"ResultDesc": "Success",
"CallbackMetadata": {
"Item": [
{ "Name": "Amount", "Value": 500 },
{ "Name": "MpesaReceiptNumber", "Value": "ABC123XYZ" },
{ "Name": "PhoneNumber", "Value": 2547XXXXXXXX }
]
}
}
}
}
FormPesa processes this and gives you something usable:
{
"status": "completed",
"amount": 500,
"phone": "2547XXXXXXXX",
"receipt": "ABC123XYZ",
"form_data": {
"name": "John Doe",
"email": "john@example.com"
}
}
π 5. Shareable Payment Link
Instead of embedding code, you can just share:
https://formpesa.wasmer.app/f/abc123
That link:
- Renders the form
- Handles submission
- Triggers payment
- Confirms success
Top comments (0)