PesaBridge
Todos los análisis
EngineeringJun 2026 · 11 min de lectura

Prompt-to-pay, explained: STK push done right

How a merchant push turns into a PIN prompt on the customer's phone — and what it takes to make it instant, safe and idempotent.

T
The PesaBridge team · Engineering

Prompt-to-pay is the flow every mobile money customer knows in their hands without ever thinking about it. A merchant asks for money, a prompt appears on the customer's phone, they enter a PIN, and it is done. It is the most-used payment pattern in the markets that adopted mobile money early, and it feels effortless. That effortlessness is the product of a surprising amount of engineering working hard to be invisible. Get any of it wrong and the magic becomes a double-charge, a stuck payment, or a merchant who shipped goods for money that never settled.

This is what is actually happening behind the prompt, and what it takes to make it instant, safe and impossible to double-charge.

Two ways the prompt arrives

There are two mechanisms behind "a prompt appears," and they matter because they have different reach and different failure modes.

The classic mechanism is SIM Application Toolkit (STK) — code that runs on the SIM card itself, which is why it can push a secure PIN prompt to a basic feature phone with no app installed. STK is how the original mobile money systems reached everyone: the menu and the PIN entry are driven from the SIM, over the network, on hardware that has nothing else on it. The modern mechanism is an app push — a notification to a smartphone app that opens a confirmation screen. Both end in the same place: the customer authorises, and a charge moves through its lifecycle. A platform worth building supports both, because the customer base spans both.

The charge is a state machine

The single most important idea in prompt-to-pay is that a charge is not an event — it is a state machine with an explicit, well-defined set of states and transitions. Treating it as a fire-and-forget call is the root of most production incidents. The states are roughly:

  • Pending — the merchant has requested a charge; the prompt has been pushed; the system is waiting for the customer.
  • Authorised — the customer entered their PIN and confirmed; the system has checked balance and limits and is committing the move.
  • Completed — the ledger has posted balanced entries; the money has moved; the merchant can ship.
  • Declined / Expired / Failed — the customer refused, the prompt timed out, or a check failed; no money moved, and the charge is closed cleanly.

Every charge is in exactly one of these states at any moment, and it can only move along defined transitions. This is not bureaucracy — it is what makes the flow reasoned-about and recoverable. When something goes wrong, the question is always "what state is this charge in, and what transition stalled?" — a question with an answer, rather than a shrug.

How the merchant learns the outcome

The merchant's system needs to know whether the charge completed — to print a receipt, release goods, update an order. There are two ways to find out, and the difference between them is the difference between a sluggish integration and a crisp one.

  1. Polling. The merchant asks the status endpoint, repeatedly, "is it done yet?" It works, but it is wasteful and laggy: poll too often and you hammer the API; poll too rarely and the customer is standing at the till waiting.
  2. Webhooks. The platform pushes the merchant a signed callback the instant the charge changes state. The merchant learns within moments, makes no wasted calls, and gets a clean event to act on. This is the right primitive, and a well-built platform makes it the default with polling as a fallback.

What makes it trustworthy

Three properties turn a working demo into a system a merchant can build a business on.

Idempotency — the double-charge killer

Networks retry. If a merchant's "create charge" request times out and they resend it, you must not create a second charge. Every charge-creation request carries an idempotency key; the platform records it, and a repeat with the same key returns the original charge rather than starting a new one. The same discipline protects the confirmation step. The guarantee the customer feels — "I was only charged once" — is this property, enforced at the point money moves, not a hopeful check in the app.

Signed webhooks

A webhook is an unsolicited HTTP call arriving at the merchant's server claiming a payment completed. Why should they believe it? Because every callback is signed — an HMAC-SHA256 signature computed over the payload with a shared secret only the platform and merchant know. The merchant recomputes the signature and rejects anything that does not match, which makes forged "you've been paid" callbacks useless to an attacker. And because networks fail, webhooks are retried with backoff until acknowledged, so a momentary outage at the merchant does not lose the event — it is redelivered until their server returns success.

Expiry

An unconfirmed charge cannot linger forever. If the customer walks away without entering their PIN, the charge must expire cleanly rather than sitting as an open liability that might mysteriously complete an hour later. A bounded, predictable expiry window is what keeps the merchant's view of the world consistent with the customer's.

The edge cases that separate real systems from demos

The happy path — push, confirm, complete — is the easy quarter of the work. The interesting failures live in the gaps:

  • The customer confirms, but the network drops the acknowledgement. The charge is completed on the platform; the merchant never heard. The webhook retry exists for exactly this. The merchant's handler must be idempotent on its side too, so a redelivered "completed" event does not ship the goods twice.
  • Two confirmations race in. A retried confirmation must not double-post. The idempotency key and the state machine together ensure the second confirmation observes "already completed" instead of moving money again.
  • The charge expires at the same moment the customer confirms. The state machine must define a single winner — the transition is atomic, so the charge is either authorised or expired, never both, and the customer is told clearly which.
  • Reconciliation. At the end of the day, every completed charge must correspond to a balanced set of ledger entries and a settlement record. Because the charge lifecycle and the ledger are tied together — completion is the posting — this reconciles by construction rather than by investigation.

A worked timeline

Here is what happens, second by second, when a merchant charges a customer 1,250 for groceries. Timings are typical rather than guaranteed.

TimeWhat happensCharge state
0.0 sCashier enters the customer's number and amount; the till sends a create-charge request with an idempotency keyPending
0.3 sThe platform validates the merchant and customer and pushes the promptPending
2–20 sThe customer reads the merchant name and amount and enters the PINPending
+0.2 sThe core checks PIN, balance and limits, and posts balanced entriesCompleted
+0.5 sA signed webhook reaches the merchant; the customer and merchant receive confirmationsCompleted
LaterThe merchant's balance settles to the bank on its scheduleCompleted, settled

The customer's reading and typing dominate the time. Everything the platform does takes well under a second, which is why the experience feels instant when it works, and why the rare slow step is noticed immediately.

Building the merchant's side correctly

Half of a reliable prompt-to-pay integration lives in the merchant's system. A good webhook handler:

  • Verifies the signature before trusting anything in the payload.
  • Is idempotent: a second delivery of the same event changes nothing.
  • Responds quickly and does heavy work afterwards, so the platform does not time out and retry unnecessarily.
  • Matches on the charge reference, never on amount and phone number alone.
  • Falls back to the status endpoint if no event arrives within the expected window, rather than assuming failure.
  • Logs every event it receives, so disputes can be answered from records.

Choosing the expiry window

Expiry is a trade-off. Too short, and customers who fumble for their phone see the charge die before they confirm. Too long, and merchants wait with goods on the counter, and stale prompts arrive after the customer has left. Most operators settle on one to two minutes for in-person payments, longer for remote requests such as an invoice sent to a customer at home. Whatever the window, show it: a cashier who knows the prompt lasts a minute knows when to try again.

Details customers notice

Small choices on the prompt screen decide whether customers trust it:

  • Show the merchant's registered name, not an internal code, so customers know who is asking.
  • Show the amount and the reference clearly before the PIN field.
  • Offer a clear decline option, so a customer who did not expect the prompt can refuse it rather than ignore it.
  • Confirm in words and by message: a receipt with the merchant, amount and reference, on screen and by SMS.
  • Warn about unexpected prompts. Fraudsters send prompts hoping a customer approves by reflex; a short line such as "Only approve if you are paying this merchant" reduces that risk.

One lifecycle, every surface

The final piece of doing this right is that prompt-to-pay should not be a special case wired separately into the app, the till and the API. It should be one charge lifecycle that every surface drives: a customer scanning a merchant QR, a merchant pushing a charge from a POS, and a partner calling the developer API all create the same kind of charge, moving through the same states, posting to the same ledger, emitting the same signed webhooks. Build it once, correctly, and every channel inherits the guarantees.

Prompt-to-pay, QR or till number?

Prompt-to-pay is one of three common ways to collect from a customer. Each fits a different situation:

MethodWho starts itWorks on basic phonesBest for
Prompt-to-payMerchantYes, via SIM-based promptsTills and checkouts where the merchant knows the amount
QR codeCustomer scansNo, needs a smartphone appShops with many smartphone customers; static codes for small traders
Till or Pay Bill numberCustomer enters itYes, over USSDAny merchant; the universal fallback

Merchants do best when they can accept all three, and the platform treats them as the same kind of payment arriving by different routes.

Fraud around prompts

Because a prompt asks the customer to approve a payment, it attracts social engineering. The common pattern is a fraudster who triggers prompts and then calls the customer, claiming to be from the provider, and talks them into approving. Defences work at several levels: show the requesting merchant's verified name prominently; limit how many prompts a merchant can send to the same number in a short time; watch for merchants whose prompts are mostly declined or reported; and tell customers, repeatedly, that the provider will never ask them to approve a payment over the phone. Merchant onboarding matters too, since a verified merchant with a traceable identity is a poor tool for fraud.

Measuring prompt-to-pay

MetricWhy it matters
Approval rateThe share of prompts customers approve; a sudden fall points to delivery problems or confusion
Time to approveHow long customers take; long times suggest unclear screens or slow delivery
Expiry ratePrompts that die unanswered; tune the expiry window and cashier guidance
Webhook success on first tryHow reliably merchants receive outcomes without retries
Reported unexpected promptsAn early signal of fraud attempts

Beyond the counter: remote requests

The same mechanism works when the customer is not standing at the till. A school can send fee requests to parents, a utility can request payment of a bill, a delivery rider can request payment on arrival. Remote requests need a few adjustments: a longer expiry, a clear description of what is being paid for, the requester's verified name, and a reference the customer can recognise from an invoice or message. The state machine, signing and idempotency stay exactly the same, which is why a single charge lifecycle serves the till, the invoice and the API alike.

Testing a prompt-to-pay integration

Before a merchant goes live, run these cases in the sandbox:

  • approve, decline and let a prompt expire, and check that the merchant's system shows the right result for each;
  • send the same create-charge request twice with the same idempotency key and confirm only one charge exists;
  • make the merchant's webhook endpoint fail for a few minutes and confirm events arrive once it recovers;
  • send a webhook with a wrong signature and confirm the merchant rejects it;
  • try a charge above the customer's limit and confirm it is refused cleanly;
  • check that refunds link back to the original charge.

An integration that passes these tests will behave well on its busiest day.

Frequently asked questions

What if the customer has no data connection?

SIM-based prompts work over the mobile network without data. For app-based prompts, customers without data can still pay by entering the merchant's till number over USSD.

Can a merchant charge a customer without their approval?

No. The charge only completes when the customer authorises it with their PIN. A merchant can request; only the customer can approve.

How are refunds handled?

A refund is a new transaction from the merchant back to the customer, linked to the original charge. The original stays in the history, so both sides can see what was paid and what was returned.

What should a cashier do if the customer says they approved but the till shows nothing?

Wait for the result rather than taking the payment twice. The till should query the charge status using its reference. If the charge completed, the result will show within moments; if it expired, the cashier can safely send a new prompt. Never accept a screenshot of a confirmation as proof of payment.

Is prompt-to-pay safe for large amounts?

The customer's tier limits apply exactly as they do for any other payment, and the PIN step is the same. For large business payments, merchants often combine a prompt with an invoice reference so both sides can match the payment to a document.

Done this way, prompt-to-pay feels instant and is impossible to double-charge — not because nothing ever fails, but because every failure has a defined state, a retry, and a path back to consistency. That is what PesaBridge ships, on the apps and the developer API alike, against a single charge lifecycle.

Monederos de valor almacenado Red de agentes Pagos a comercios USSD API de desarrolladores Solicitud de pago Niveles de KYC Reversos Distribución de flotante Liquidación Webhooks firmados Marca blanca Monederos de valor almacenado Red de agentes Pagos a comercios USSD API de desarrolladores Solicitud de pago Niveles de KYC Reversos Distribución de flotante Liquidación Webhooks firmados Marca blanca

¿Listo para lanzar tu monedero?

Solicita una demo y montaremos tu marca, país y rieles — y te guiaremos por las apps, el panel y la API.

¿Prefieres hablar? Llama al +254 746 883809