Seamless Mobile Money Subscriptions: Integrating ClickPesa with Relet
A deep dive into how we built a robust USSD push payment flow for platform subscriptions using ClickPesa's API.

Seamless Mobile Money Subscriptions: Integrating ClickPesa with Relet
In the Tanzanian market, mobile money is king. For a SaaS platform like Relet, offering seamless subscription payments via M-Pesa, Tigo Pesa, Airtel Money, and Halotel is not just a feature—it's a necessity.
We recently overhauled our billing system to support automated platform subscriptions using ClickPesa. Here's a look at how we implemented a frictionless USSD push experience and robust callback handling.
The Challenge: Asynchronous by Nature
Unlike credit card payments which are often instantaneous, mobile money transactions are asynchronous. You initiate a request, the user receives a USSD prompt on their phone, enters their PIN, and then—seconds or minutes later—the transaction completes.
Our goal was to make this feel as instant and reliable as a card swipe.
Step 1: The User Experience (Frontend)
We built a custom PaymentForm component that prioritizes trust and ease of use. Instead of blindly initiating a charge, we implemented a "Preview" step.
1. Intelligent Input
As the user types their phone number, we auto-detect the carrier (Vodacom, Tigo, etc.) based on the prefix. This provides immediate visual feedback.
2. Account Verification (The "Preview")
Before we ever ask for money, we hit ClickPesa's API to verify the account.
- User Action: Enters 10-digit phone number.
- System Action: Fetches account details.
- Result: We display the Account Name (e.g., "Juma Hamisi") and the Provider to the user.
This confirmation step is crucial. It assures the user that the prompt will go to the right phone and that we have the correct details.
// Simplified logic from our PaymentForm
const handlePreviewAutomatic = async (phone: string) => {
const result = await onPreview(phone)
// We show the user their name and provider before they click "Pay"
setPreviewData(result)
setShowPreview(true)
}3. The USSD Push
Once confirmed, the user clicks "Pay". This triggers the payment intent, sending a USSD prompt directly to their device. We then show a waiting state, instructing them to check their phone.
Step 2: The Backend Magic (Webhooks)
The real heavy lifting happens in our webhook handler, clickpesa-callback.ts. Since we can't keep the user's browser open forever, we rely entirely on server-to-server communication to confirm payments.
Handling the Callback
We set up a secure endpoint to listen for events from ClickPesa. The payload gives us everything we need: status, transaction ID, and metadata we passed during initiation.
// Our callback handler structure
export async function handleClickPesaCallback(payload: CallbackPayloadInput) {
const { eventType, data } = parsePayload(payload)
if (eventType === 'PAYMENT RECEIVED') {
return handlePaymentReceivedEvent(data)
}
// ... handle failures
}Closing the Loop
When a PAYMENT RECEIVED event hits our server, we perform a sequence of atomic operations:
- Verify Order: We look up the pending payment record using the
orderReference. - Update Status: Mark the payment as
successand record thetransactionId. - Provision Subscription:
- If it's a new subscription, we create the customer and subscription in our billing engine (Rebill) and link it to the user's organization.
- If it's an upgrade, we calculate proration and update the existing plan immediately.
By decoupling the payment confirmation from the user's browser session, we ensure that even if the user closes the tab or loses internet connection after entering their PIN, their subscription is still activated correctly.
Key Takeaways
- Verify First: Always validate the mobile money number before initiating a charge. It reduces failed transactions and builds user trust.
- Webhooks are Truth: Never rely on the client-side to confirm a payment. Always wait for the secure server callback.
- Metadata is Your Friend: We pass context (like
organizationIdandplanId) in the payment metadata, so the webhook knows exactly what to do without needing complex state lookups.
Integrating ClickPesa has allowed us to offer a native, familiar payment experience to our Tanzanian customers, significantly reducing friction during onboarding.