Build · RECURRING · TZS

There is no card on file in Dar es Salaam

How SaaS companies in Tanzania bill monthly on mobile money, where every renewal is a fresh prompt the customer must approve, and how to design for it.

Ultraner team··6 min read

Picture Neema (a composite of founders we talk to), who runs a small accounting tool for pharmacies in Dar es Salaam. TZS 25,000 a month, around two hundred customers, and a billing problem that every SaaS founder in Tanzania eventually meets: on the first of the month, nothing happens automatically.

In Europe or the US, a subscription is a card number stored once and charged forever until someone cancels. Stripe's own billing model is built on exactly that, a saved payment method charged on a schedule 3. In Tanzania most of Neema's customers pay with M-Pesa, Mixx by Yas or Airtel Money, and no mobile money network in Africa lets a merchant store a credential and pull money later. Each payment needs the account holder to approve it on their own phone with their own PIN.

That is not a bug to work around. It is the design constraint, and the SaaS companies that bill well in Tanzania are the ones that accepted it early.

What a mobile money subscription really is

When you create a recurring plan on mobile money through Ultraner, our scheduler does what a card network would do on its own: at each interval it sends the customer a fresh USSD push for the plan amount. The customer approves, the charge succeeds, the period renews. If they do not approve, the plan does not silently renew.

POST /v1/recurring/plans
X-API-Key: uk_live_...

{ "name": "Pharmacy Pro", "mno_provider": "Vodacom", "account_number": "255712345678",
  "amount": 25000, "currency": "TZS", "interval_type": "monthly" }

Note the amount. TZS has no minor unit in practice, so 25000 is TZS 25,000, not 250. Get that wrong in one direction and you undercharge by a factor of a hundred.

The response includes a manage_url. Every plan gets a public page, no account needed, where the subscriber can see their billing history and cancel. Store that URL against the customer in your own database. You will want to put it in every receipt and every "your payment failed" message.

Your webhook then hears the story of the subscription:

  • recurring.activated fires once, on the first successful mobile money charge (there is no separate approval step on mobile money, so the first payment is the activation).
  • recurring.success fires on every successful charge, including the first.
  • recurring.past_due and recurring.failed tell you a renewal did not go through.
  • recurring.cancelled tells you the customer or you ended it.

The capability is described in one place on recurring billing, and the subscriber's side of it is on manage subscriptions.

The three things that change in your product

1. Renewal is a moment, not a background job

Because each renewal needs the customer's thumb, renewal day is a customer touchpoint. Neema learned to treat it like one:

  • She sends an SMS the day before: "Pharmacy Pro renews tomorrow. You will get an M-Pesa prompt for TZS 25,000."
  • The prompt arrives at 10:00, not 03:00, because a prompt that arrives while the owner is asleep times out.
  • The receipt Ultraner issues on success goes to the same number, with the manage link.

The customers who were surprised by a prompt declined it. The ones who were told about it approved it.

2. Past due needs a grace period you designed

On card, a failed renewal is usually an expired card. On mobile money it is usually one of three ordinary things: the phone was off, the wallet was short that morning, or the customer was busy and the prompt expired. Mobile money prompts that go unanswered close as failed after 10 minutes.

None of those mean the customer churned. So decide, before launch:

  • How many days of access continue after recurring.past_due. Five is common, seven is generous.
  • What the in-app banner says during that window. "Your renewal did not go through. Tap to pay now" with a one-tap payment link beats a lockout.
  • When you actually cut access.

If you use Ultraner's entitlements to gate features, check the subscription state at request time rather than caching "is paid" for a month.

3. Cancellation should be painless

Cancelling from the manage page defaults to cancel at period end: the plan stays usable until the current period finishes and can be resumed until then. That is the behaviour customers expect and it reduces the support tickets that start with "I cancelled and you cut me off".

If you cancel programmatically with DELETE /v1/recurring/plans/{id}, the default is immediate. Add ?immediate=false if you mean the gentler version.

Pick the network the customer actually uses

Tanzania is the most crowded mobile money market Ultraner serves. Six brands are in the checkout: M-Pesa (Vodacom), Airtel Money, Mixx by Yas (formerly Tigo Pesa), Halopesa, TTCL T-Pesa and AzamPesa. The Bank of Tanzania regulates them as payment systems 1, and they interoperate for person-to-person transfers, but your subscription still charges one specific wallet. The mno_provider has to match the network the customer's number is on.

Two practical rules:

  • Ask the customer which network they use, and pre-select based on the prefix as a hint only. People keep numbers when they switch networks.
  • If a business customer pays from a shared office phone, ask whose phone gets the prompt. The person who signed up is not always the person who holds the wallet.

The full network list and phone format for Tanzania (255 plus nine digits) are on the Tanzania payments page.

Offer card and PayPal too, for a different reason

Some of Neema's customers are pharmacy chains whose finance team pays everything on a corporate card. For them, the same recurring plan with "channel": "card" hands the schedule to Stripe Billing, which charges automatically each month. PayPal works the same way through PayPal Subscriptions.

That gives you a clean product rule: mobile money for the owner-operator who pays from their phone, card for the company that wants it off their desk. Both show up in one subscriber list and one set of webhooks. Card renewals do not need a prompt, so the past-due rate on those plans is typically lower, which is a reason to offer card, not a reason to push everyone onto it.

Yearly plans are a mobile money feature

Here is the counterintuitive one. On mobile money, every renewal is a chance for a busy customer to miss a prompt. Twelve chances a year on monthly, one chance a year on annual.

Offer an annual plan at a real discount (two months free is the familiar framing) and present it as the convenience option: "Pay once, no monthly prompt." For a TZS 25,000 monthly product, that is TZS 250,000 a year. Not every pharmacy will have that in its wallet on one day, which is why monthly still has to exist, but the ones who can will thank you.

When you outgrow one country

Kenya and Uganda customers will find you. The recurring engine works the same way there, but watch the currency: Ugandan shillings are whole numbers like TZS, while Kenyan shillings carry two decimal places, so KES 2,500 is 250000. Your plan creation code needs to read the currency's decimals, not assume Tanzania's.

Your revenue settles to your own bank account in any country. If you also need to pay commissions or refunds into Tanzanian wallets, wallet payouts are live in Tanzania today. See pricing for what each leg costs.

A launch checklist for Tanzanian SaaS billing

  • Plans created with whole-number TZS amounts.
  • manage_url stored per subscriber and included in every billing message.
  • SMS reminder the day before renewal, prompt timed for working hours.
  • Grace period and in-app banner wired to recurring.past_due.
  • Webhook handler idempotent on transaction_id, signature verified.
  • Annual plan offered as the "no monthly prompt" option.
  • Card channel available for business customers who want automatic charging.

The GSMA's yearly reports keep showing mobile money growing as the way East Africans move money 2. A SaaS business that treats the monthly prompt as part of its product, rather than as a defect in somebody else's, gets to bill the customers who never owned a card in the first place.

Sources

  1. 1National Payment Systems, Bank of Tanzania
  2. 2State of the Industry Report on Mobile Money, GSMA
  3. 3How subscriptions work, Stripe Docs

For AI agents and tools: this article is also available as plain Markdown, every article is in the RSS feed, and the site guide for AI is at llms.txt.

Keep reading