> ## Documentation Index
> Fetch the complete documentation index at: https://docs.centipidbilling.com/llms.txt
> Use this file to discover all available pages before exploring further.

# PalmPesa

> Configure and operate PalmPesa, the Tanzanian mobile-money collection gateway.

# Collect payments with PalmPesa

PalmPesa (Eagle Star Technology) is a Tanzanian mobile-money collection gateway. Centipid Billing pushes a payment request to the payer's wallet, the payer authorizes it on their handset, and the subscriber is provisioned once PalmPesa confirms the order.

PalmPesa is offered to ISPs in **Tanzania (TZ)**, alongside ClickPesa, AzamPay, PawaPay, PesaPal, Relworx, ZenoPay, PayPal, and DPO. Provider API documentation is published at [documentation.palmpesa.co.tz](https://documentation.palmpesa.co.tz).

## Before you start

1. Complete PalmPesa merchant onboarding in an account owned by the ISP, and have the account verified.
2. Collect the **API token** from the PalmPesa dashboard.
3. Confirm the settlement account and the currency PalmPesa will settle in.
4. Confirm your Centipid Billing host is reachable from the public internet, so PalmPesa can deliver callbacks.

## Configure the gateway

<Steps>
  <Step title="Open Settings → Payments → PalmPesa">
    Select PalmPesa from the payment marketplace to open its configuration form.
  </Step>

  <Step title="Enter the API token">
    **API token** (required) is issued on your PalmPesa dashboard once the account is verified. It is stored as a secret and is not echoed back into the form after saving.
  </Step>

  <Step title="Leave the API host blank unless PalmPesa told you otherwise">
    **API host** (optional) overrides the endpoint Centipid Billing calls. Leave it empty to use the default PalmPesa endpoint. Only set it if PalmPesa has issued your account a different host.
  </Step>

  <Step title="Save and switch">
    Saving validates the form; it does not prove the token works. Switching to PalmPesa requires the API token to be present and may require an OTP confirmation.
  </Step>

  <Step title="Run a controlled test payment">
    Pay a small amount from the **captive portal**. Verify the wallet prompt arrives, the payment lands in the ledger with method **PalmPesa**, and the subscriber's package is applied. See the limitation below before testing from the customer portal.
  </Step>
</Steps>

<Note>
  The callback URL is sent to PalmPesa on every payment request, so there is nothing to register in the PalmPesa dashboard. Settings → Payments still shows it under **Callback URLs** (`https://<your-host>/api/palmpesa/callback`) so you can confirm the address PalmPesa will call, and check it is not blocked by a firewall or WAF rule.
</Note>

## Credentials

| Field     | Required | Notes                                                                                                               |
| --------- | -------- | ------------------------------------------------------------------------------------------------------------------- |
| API token | Yes      | Secret. Issued on the PalmPesa dashboard after account verification. The only field required to switch to PalmPesa. |
| API host  | No       | Falls back to the platform default endpoint. Enter a full URL, for example `https://palmpesa.drmlelwa.co.tz`.       |

## What the payer must provide

PalmPesa rejects a payment request that carries no email address, so Centipid Billing requires one before the request is sent.

* **Email address (required).** The **captive portal** asks the payer for an email when your account is on PalmPesa. The **customer portal does not ask** — it uses the email on the subscriber's profile, so a subscriber with no email saved cannot pay from the portal. In either case, if no valid email is available checkout stops with *"A valid email address is required for PalmPesa payments"* before any request reaches PalmPesa. Add an email to the subscriber's profile before directing them to the customer portal.
* **Tanzanian phone number (required).** Accepted as `07…`, `06…`, or with the `255` country code; the number is normalized before it is sent.
* **Amount.** Rounded to a whole number before the request is sent.

Because authorization happens on the payer's handset rather than in a hosted checkout page, no PalmPesa domain needs to be added to the captive walled garden for the payer's browser.

<Warning>
  **Customer-portal top-ups and invoice payments report a false failure on PalmPesa.** The wallet prompt is sent and the payment completes normally, but the portal shows *"Unable to start the payment. Please try again."* because it cannot read PalmPesa's reference in the shape it expects.

  Direct PalmPesa payers to the **captive portal** until this is resolved. If a subscriber has already hit the error, **do not tell them to retry** — check the ledger first, because the original payment may have gone through, and a retry raises a second wallet prompt.
</Warning>

## How a payment completes

<Steps>
  <Step title="Request">
    Centipid Billing calls `POST /api/palmpesa/initiate`. PalmPesa answers with an `order_id`, which becomes the local transaction reference that everything afterwards reconciles against.
  </Step>

  <Step title="Authorization">
    The payer approves the request on their phone.
  </Step>

  <Step title="Confirmation">
    A background job polls `POST /api/order-status` every 5 seconds, for up to 40 attempts (about 3 minutes 20 seconds), until the order reaches a terminal state. **This poller — not the callback — is what completes the payment**, so a payment still completes when the payer closes the captive browser mid-checkout.
  </Step>

  <Step title="Provisioning">
    On `COMPLETED`, a payment is recorded with method **PalmPesa** and the subscriber's package is applied asynchronously. On `FAILED`, the failure reason is stored against the transaction and nothing is credited.
  </Step>
</Steps>

<Warning>
  PalmPesa's callback carries no signature, HMAC, or shared secret, so it cannot prove it came from PalmPesa. Centipid Billing treats it as a hint only: nothing is credited from the callback body. Every credit is made against a status read from PalmPesa itself, so a spoofed "completed" callback cannot provision a subscriber.
</Warning>

A failed request is deliberately **not** retried automatically. PalmPesa documents no idempotency on the request reference, so an automatic retry after a lost response could raise a second order and prompt the payer twice. The payer retries by choice instead.

## Troubleshooting

| Symptom                                                                                       | What it means                                                                                        | What to do                                                                                                                |
| --------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| *"PalmPesa API token not configured"*                                                         | No API token is saved.                                                                               | Enter the API token in Settings → Payments → PalmPesa.                                                                    |
| *"PalmPesa authentication failed. Check the API token"*                                       | PalmPesa rejected the token (HTTP 401).                                                              | Re-copy the token from the PalmPesa dashboard. Confirm the account is verified and the token has not been rotated.        |
| *"A valid email address is required for PalmPesa payments"*                                   | Checkout had no valid email for the payer.                                                           | Ask the payer for an email at checkout, or set one on the subscriber's profile.                                           |
| *"A valid Tanzanian phone number is required for PalmPesa payments"*                          | The number could not be normalized to a Tanzanian MSISDN.                                            | Re-enter the number as `07…`, `06…`, or `2557…`.                                                                          |
| *"Could not reach PalmPesa. Please try again"*                                                | The request could not reach the PalmPesa endpoint.                                                   | Check outbound connectivity from the application host, and any custom **API host** value.                                 |
| Customer portal shows *"Unable to start the payment"* but the payer still got a wallet prompt | The known customer-portal limitation above. The push succeeded; only the portal's response is wrong. | Check the ledger before advising anything. Do not tell the payer to retry. Use the captive portal for PalmPesa payments.  |
| Payer paid, but the transaction sits pending                                                  | The order never reached a terminal state within the polling window.                                  | Confirm the payment on the PalmPesa dashboard, then raise it with support with the order id and the payer's phone number. |

For symptoms that are not specific to PalmPesa, see [Payment troubleshooting](/billing/payment-gateways/troubleshooting). For every other gateway's fields, see the [credential reference](/billing/payment-gateways/credentials-reference).
