# 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.

- URL: https://palpluss.com/blog/automate-mpesa-stk-push-payments
- Published: 2026-11-03 · Updated: 2026-11-03
- Author: PalPluss Engineering (Palpluss)

## Summary
- 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](https://palpluss.com/products) in five steps.

## What you need

- A PalPluss account — [register free](https://console.palpluss.com/register). 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](https://console.palpluss.com) 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.

```bash
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)

```bash
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:

```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.

```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"
}
```

```python
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.

```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
});
```

> **No webhook yet?:** You can poll `GET /v1/transactions/{id}` until the status leaves `PENDING`. Webhooks are faster and cheaper on your server, so switch to them before going live.

## 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

*Work you own when integrating STK Push*

| Task | Daraja direct | PalPluss |
| --- | --- | --- |
| OAuth token fetch and refresh | You | Handled |
| Password = Base64(shortcode + passkey + timestamp) | You | Handled |
| Public callback endpoint for Safaricom | You | Handled — you get a clean webhook |
| Normalising phone formats | You | Handled |
| Transaction records and dashboard | You | Included |
| Abuse and duplicate-prompt protection | You | Included |

> **Start collecting today:** [Create your PalPluss account](https://console.palpluss.com/register), then keep the [STK Push API reference](https://docs.palpluss.com/api-reference/stk/initiate) open while you build.

## FAQ

**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}.
