DEV Community

Cover image for Formpesa- a form builder for kenyan creatives β€πŸš€

Formpesa- a form builder for kenyan creatives β€πŸš€

πŸš€ 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>
Enter fullscreen mode Exit fullscreen mode

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);
});
Enter fullscreen mode Exit fullscreen mode

πŸ‘‰ 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"
}
Enter fullscreen mode Exit fullscreen mode

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 }
        ]
      }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

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"
  }
}
Enter fullscreen mode Exit fullscreen mode

πŸ”— 5. Shareable Payment Link

Instead of embedding code, you can just share:

https://formpesa.wasmer.app/f/abc123
Enter fullscreen mode Exit fullscreen mode

That link:

  • Renders the form
  • Handles submission
  • Triggers payment
  • Confirms success

Top comments (0)