How to Automate M-Pesa Payments (STK Push) in Your Platform in Under 5 Minutes

Step-by-step guide to adding M-Pesa STK Push to your app in under 5 minutes with PalPluss — no Daraja setup. cURL, Node.js and webhook code included.

By
PalPluss Engineering
Published
Reading time
5 min

The short answer

  • STK Push (Lipa na M-Pesa Online / M-Pesa Express) sends a PIN prompt to the customer's phone so they can pay without typing a paybill or till number.
  • With PalPluss, automating STK Push is one authenticated POST to /v1/payments/stk plus a webhook handler — typically under 5 minutes once your account is set up.
  • STK Push on PalPluss costs a flat KES 3 for any amount of KES 10 or more; KES 1–9 is free.

STK Push — also called Lipa na M-Pesa Online or M-Pesa Express — is how most Kenyan apps collect money. Your server asks M-Pesa to show a payment prompt on the customer's phone; the customer enters their PIN; you receive the result. No paybill number to copy, no typos in the account number, no screenshots of confirmation messages.

Wiring STK Push directly to Safaricom Daraja involves OAuth tokens, a Base64 password built from your shortcode, passkey and timestamp, a public callback URL and status polling. This guide skips all of that and gets a working STK Push into your platform with PalPluss in five steps.

What you need

  • A PalPluss account — register free. New accounts get KES 15 in free tokens.
  • A Paybill or Till added as a payment channel in the console, set as default.
  • A server that can make HTTPS requests and receive one webhook.

Step 1: Create an API key (30 seconds)

In the console open API Keys and create a key. Copy it immediately — it is shown once. Store it as an environment variable; never ship it to a browser or mobile app.

.env
PALPLUSS_API_KEY=pk_live_xxxxxxxxxxxxxxxxxxxxxxxx

Step 2: Fund your service wallet (1 minute)

PalPluss fees are paid from a pre-funded service wallet, deducted when each request is accepted. Top it up by STK Push from the console. At KES 3 per STK Push, KES 300 covers 100 payment requests.

Step 3: Send your first STK Push (1 minute)

terminal
curl -X POST https://api.palpluss.com/v1/payments/stk \
  -u "$PALPLUSS_API_KEY:" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 500,
    "phone": "0712345678",
    "accountReference": "ORDER-1042",
    "transactionDesc": "Order 1042",
    "callbackUrl": "https://yourapp.com/webhooks/palpluss"
  }'

The phone receives the M-Pesa prompt within seconds. The API answers immediately with a PENDING transaction — save its transactionId against your order:

response.json
{
  "success": true,
  "data": {
    "transactionId": "fa98a577-95ea-4a8f-8467-1fbe74f5d6f4",
    "type": "STK",
    "status": "PENDING",
    "amount": 500,
    "currency": "KES",
    "phone": "254712345678",
    "accountReference": "ORDER-1042",
    "transactionFee": 3
  }
}

Phone numbers are accepted as 07XXXXXXXX, 01XXXXXXXX, +254XXXXXXXXX or 254XXXXXXXXX. Keep accountReference to 12 characters and transactionDesc to 13 — M-Pesa shows them on the statement and the PIN prompt.

Step 4: Do it from your code (1 minute)

Install the official SDK, or call the REST endpoint from any language — it is plain HTTPS with Basic auth.

checkout.ts
import { PalPluss } from "@palpluss/sdk";

const palpluss = new PalPluss({ apiKey: process.env.PALPLUSS_API_KEY });

export async function collect(orderId: string, phone: string, amount: number) {
  const stk = await palpluss.stkPush({
    amount,
    phone,
    accountReference: orderId,
    transactionDesc: "Checkout",
    callbackUrl: "https://yourapp.com/webhooks/palpluss",
  });
  await saveTransactionId(orderId, stk.transactionId);
  return stk.status; // "PENDING"
}
checkout.py
import os, requests

resp = requests.post(
    "https://api.palpluss.com/v1/payments/stk",
    auth=(os.environ["PALPLUSS_API_KEY"], ""),
    json={"amount": 500, "phone": "0712345678",
          "accountReference": "ORDER-1042", "transactionDesc": "Order 1042",
          "callbackUrl": "https://yourapp.com/webhooks/palpluss"},
    timeout=30,
)
resp.raise_for_status()
print(resp.json()["data"]["transactionId"])

Step 5: Confirm payment with a webhook (1 minute)

When the customer pays, cancels or the prompt times out, PalPluss sends a POST to your callbackUrl. Fulfil the order only when status is SUCCESS, and store the mpesa_receipt for reconciliation.

webhook.ts
app.post("/webhooks/palpluss", async (req, res) => {
  const { transaction } = req.body;

  if (transaction.status === "SUCCESS") {
    // Idempotent: safe if the same callback arrives twice.
    await fulfilOrderOnce(transaction.external_reference, transaction.mpesa_receipt);
  }

  res.sendStatus(200); // acknowledge quickly
});

Production checklist

  • Never trust the client for the amount — compute it on your server from the order.
  • Make the webhook idempotent — key fulfilment on the transaction ID.
  • Handle rate limits — the API allows up to 2 STK requests per phone number per minute (429 STK_RATE_LIMITED) and 60 requests per minute per API key. Show the customer a friendly “try again in a moment” message.
  • Watch your success rate — accounts whose recent STK success rate drops below 30% are paused for 30 minutes (429 TRANSACTION_HEALTH_BLOCKED). Validate phone numbers and avoid sending prompts nobody asked for.
  • Keep the service wallet funded — requests fail with 402 INSUFFICIENT_SERVICE_BALANCE when it runs dry.

PalPluss vs wiring Daraja STK Push yourself

TaskDaraja directPalPluss
OAuth token fetch and refreshYouHandled
Password = Base64(shortcode + passkey + timestamp)YouHandled
Public callback endpoint for SafaricomYouHandled — you get a clean webhook
Normalising phone formatsYouHandled
Transaction records and dashboardYouIncluded
Abuse and duplicate-prompt protectionYouIncluded
Work you own when integrating STK Push

Frequently asked questions

What is M-Pesa STK Push?

STK Push (Lipa na M-Pesa Online or M-Pesa Express) is a Safaricom feature that sends a payment prompt to a customer's phone. The customer enters their M-Pesa PIN to approve, and the merchant receives the result automatically.

How fast can I integrate STK Push?

With PalPluss, once your account and payment channel are set up, the integration is one POST request to /v1/payments/stk and a webhook handler — typically under 5 minutes.

How much does STK Push cost on PalPluss?

A flat KES 3 per STK Push for amounts of KES 10 and above, regardless of size. Amounts of KES 1–9 are free. There are no monthly or setup fees.

Do I need a Paybill or Till number?

Yes. Each STK Push is routed through a payment channel — a Paybill or Till — that you add in the PalPluss console. That is the number the customer pays to.

How do I know an STK Push payment succeeded?

PalPluss sends a webhook to your callbackUrl with status SUCCESS and the M-Pesa receipt. You can also fetch GET /v1/transactions/{id}.

Build on PalPluss

M-Pesa collections and payouts in one API. KES 15 in free tokens to start.