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

# M-Pesa Integration

> Centralized vs direct paybill, settlement destinations, Daraja credentials, STK Push, C2B, and reconciliation.

<a
  href="https://fyberpay.com/embed/docs-chat"
  target="_blank"
  rel="noopener noreferrer"
  aria-label="Ask FyberPay docs chat"
  style={{
position: 'fixed',
bottom: '24px',
right: '24px',
width: '56px',
height: '56px',
borderRadius: '9999px',
background: 'linear-gradient(135deg, #6366f1 0%, #7c3aed 100%)',
boxShadow: '0 4px 16px rgba(99, 102, 241, 0.35)',
color: 'white',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 9999,
textDecoration: 'none',
transition: 'transform 150ms ease',
}}
>
  <svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
    <path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z" />
  </svg>
</a>

## Overview

Every FyberPay ISP collects M-Pesa payments from subscribers; the question is whether the funds land in **FyberPay's shared paybill** or in your **own Daraja shortcode**. The default and easiest path is centralized; direct is for ISPs that already operate their own Daraja account.

| Mode                      | Funds collected on                  | Daraja credentials                                | Transaction fee                                                      | Settlement to ISP                                                                                                                                      |
| ------------------------- | ----------------------------------- | ------------------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Centralized** (default) | FyberPay's M-Pesa Paybill `4026145` | Not required                                      | None from FyberPay; the actual M-Pesa B2B fee is deducted per payout | Per your settlement mode (batch to a threshold, instant, or Disburse now) to the ISP's configured destination (M-Pesa Paybill, Till, or bank-via-Tuma) |
| **Direct**                | Your own Paybill or Till            | Required (Consumer Key, Consumer Secret, Passkey) | None on FyberPay's side; Safaricom charges still apply               | Funds reach your Safaricom account immediately, no FyberPay settlement step                                                                            |

<Info>
  ISPs typically start on centralized because it requires no Daraja paperwork. You can switch to direct any time by entering valid Daraja credentials at **Settings → Payment Gateways → M-Pesa**.
</Info>

## Centralized paybill

Centralized mode is the default for every new ISP. Subscribers pay into FyberPay's Paybill (`4026145`) using **their own phone number** as the account number (a short canonical code works as a fallback). FyberPay matches each payment to the right subscriber automatically and settles collections to the ISP in full per the ISP's settlement mode, minus only the M-Pesa B2B fee on each payout.

### Subscriber payment instructions

Each subscriber gets a short canonical account code (e.g. `ACM00422`) and, optionally, can pay using their phone number as the account alias. SMS and invoice notifications surface both, but the phone alias is the most reliable for typing on a phone keypad. To pay:

```
Lipa Na M-Pesa → Paybill → 4026145
Account number: 0712345678   (subscriber's phone number)
                or  ACM00422  (canonical short code)
Amount: KES 2,500
```

### Settlement destination

Centralized requires the ISP to declare where payouts should land. Configure at **Settings → Billing → Settlement**, along with the settlement mode (batch or instant) and payout threshold:

* **M-Pesa Paybill** – Safaricom paybill number (e.g. `123456`) plus account name.
* **M-Pesa Till (Buy Goods)** – till number (e.g. `5332138`). Single-line, no account-number suffix.
* **Bank via Tuma** – Tuma payout to a Kenyan bank account. Tuma fees apply per disbursement; see Settings → Payment Gateways → Tuma for current rates.

If no destination is configured, disbursements queue with a `MissingSettlementPaybill` error and the platform admin sees them flagged on the disbursements dashboard.

### Transaction fee

FyberPay deducts **no transaction fee** on centralized payments. Collections settle in full; the only deduction is the actual M-Pesa B2B fee on each payout, shown as a separate line on every disbursement. ISPs in direct mode collect straight into their own paybill with no settlement step at all.

## Direct mode

Direct mode is for ISPs that want subscribers to pay into their own Paybill or Till. You provide Daraja credentials; FyberPay drives STK Push, Bill Manager, and C2B reconciliation against your shortcode.

### Prerequisites

Before configuring direct mode in FyberPay, you need:

1. A **Safaricom Daraja developer account** at [developer.safaricom.co.ke](https://developer.safaricom.co.ke)
2. A registered **Paybill** or **Till** number
3. An approved **Daraja app** with M-Pesa Express (Lipa Na M-Pesa) enabled
4. The **Passkey** for your shortcode (provided by Safaricom after go-live)

<Warning>
  Start with the Daraja **sandbox** environment to test your integration before switching to production. Sandbox credentials use test phone numbers and do not process real money.
</Warning>

## Setting Up Daraja Credentials

<Steps>
  <Step title="Create a Daraja app">
    Log in to [developer.safaricom.co.ke](https://developer.safaricom.co.ke) and create a new app. Enable the following APIs:

    * M-Pesa Express (Lipa Na M-Pesa)
    * C2B (Customer to Business)

    Copy the **Consumer Key** and **Consumer Secret** from your app dashboard.
  </Step>

  <Step title="Enter credentials in FyberPay">
    Navigate to **Settings > Payment Gateways > M-Pesa** and fill in:

    * **Consumer Key**: from your Daraja app
    * **Consumer Secret**: from your Daraja app
    * **Shortcode**: your Paybill or Till number
    * **Passkey**: provided by Safaricom for STK Push
    * **Environment**: `sandbox` for testing, `production` for live payments
  </Step>

  <Step title="Save and verify">
    Click **Save**. FyberPay validates your credentials by requesting an OAuth token from the Daraja API. If the credentials are invalid, you will see an error message.
  </Step>

  <Step title="Send a test STK Push">
    Use the **Send Test STK Push** button to trigger a payment prompt to your phone. In sandbox mode, this uses Safaricom's test numbers.
  </Step>
</Steps>

<Info>
  FyberPay encrypts your Daraja credentials at rest using AES-256 encryption. Credentials are never stored in plaintext and are only decrypted when making API calls.
</Info>

## STK Push Configuration

STK Push (also called Lipa Na M-Pesa Online) sends a payment prompt directly to the subscriber's phone. The flow works like this:

1. FyberPay sends an STK Push request to Daraja with the subscriber's phone number and amount
2. The subscriber receives a popup on their phone asking them to enter their M-Pesa PIN
3. Safaricom processes the payment and sends a callback to FyberPay
4. FyberPay matches the payment to the invoice, marks it as paid, and extends the subscription

### Callback URL

FyberPay automatically configures the callback URL for STK Push responses. The URL follows this pattern:

```
https://api.fyberpay.com/v1/payments/webhooks/mpesa/stk-callback
```

<Warning>
  The callback URL must be publicly accessible over HTTPS. If you are running FyberPay behind a firewall, ensure port 443 is open for inbound requests from Safaricom's IP ranges.
</Warning>

### STK Push Polling

If the callback is delayed or missed, FyberPay polls the Daraja STK Query API to check the transaction status. The poller runs automatically and handles these result codes:

| Result Code | Meaning                 | FyberPay Action            |
| ----------- | ----------------------- | -------------------------- |
| 0           | Success                 | Mark payment as successful |
| 1032        | Cancelled by user       | Mark payment as cancelled  |
| 1019        | Transaction in progress | Retry poll later           |
| 1025        | Transaction timed out   | Retry poll later           |
| 1037        | Insufficient balance    | Retry poll later           |
| Other       | Failed                  | Mark payment as failed     |

## C2B Validation

C2B (Customer to Business) handles payments that subscribers initiate themselves through M-Pesa, such as:

* Paying via the M-Pesa app using your Paybill number
* Sending money via USSD (`*334#`)
* Paying at an M-Pesa agent

FyberPay registers validation and confirmation URLs with Safaricom:

* **Validation URL**: FyberPay checks the account reference to determine if the payment can be accepted
* **Confirmation URL**: After payment, FyberPay matches the transaction to a subscriber and invoice

### Account Reference Format

Subscribers must use their account reference when paying via C2B. The reference format is:

```
{org-slug}-{invoice-number}
```

For example, a subscriber of "Acme Internet" paying invoice ACM-0042 would use:

```
Account Number: acme-ACM-0042
```

<Tip>
  Include the account reference format in your subscriber onboarding SMS and on the subscriber portal. This reduces mismatched payments.
</Tip>

## Bill Manager

Bill Manager pushes structured invoices to Safaricom, which then sends SMS notifications to subscribers with a direct payment option.

### How Bill Manager Works

1. FyberPay generates an invoice for a subscription renewal
2. The `invoice.created` event triggers the Bill Manager sync listener
3. FyberPay pushes the invoice to Safaricom's Bill Manager API
4. The subscriber receives an SMS: "You have a bill of KES X from \[Your ISP]. Pay via M-Pesa."
5. When the subscriber pays, Safaricom sends a callback to FyberPay
6. FyberPay acknowledges the payment (triggers an e-receipt to the subscriber)

### Enabling Bill Manager

Navigate to **Settings > Payment Gateways > M-Pesa** and enable the **Bill Manager** toggle. Bill Manager uses the same Daraja credentials as STK Push but requires your shortcode to be enrolled in the Bill Manager program.

<Info>
  Bill Manager enrollment is done through Safaricom. Contact your Safaricom relationship manager to register your Paybill number for Bill Manager. FyberPay handles the API integration once your shortcode is enrolled.
</Info>

### Bulk Invoice Push

Bill Manager supports pushing up to 1,000 invoices per batch. FyberPay handles batching automatically. When the bulk invoice generation job runs (for subscriptions expiring within 3 days), all generated invoices are queued for Bill Manager push.

## Payment Reconciliation

FyberPay automatically reconciles payments from all M-Pesa channels:

<AccordionGroup>
  <Accordion title="STK Push payments">
    Matched automatically via the `CheckoutRequestID` returned by Daraja. Every STK Push request is tracked and matched to the originating invoice.
  </Accordion>

  <Accordion title="C2B payments">
    Matched via the account reference in the C2B callback. FyberPay parses the reference to extract the org slug and invoice number, then allocates the payment.
  </Accordion>

  <Accordion title="Bill Manager payments">
    Matched via the `accountReference` field in the Bill Manager payment notification. FyberPay looks up the org and invoice, creates a payment log, and sends an acknowledgment to Safaricom (which triggers the customer e-receipt).
  </Accordion>

  <Accordion title="Unmatched payments">
    If a payment cannot be matched to a subscriber or invoice (for example, the subscriber used an incorrect account reference), FyberPay logs it as an unmatched payment. You can view and manually allocate unmatched payments from **Billing > Unmatched Payments**.
  </Accordion>
</AccordionGroup>

### What Happens After Payment

When a payment is successfully matched and processed:

1. The invoice is marked as **paid** with the M-Pesa receipt number
2. The subscription is extended by the plan's billing cycle days
3. If the subscriber was in a dunning state (retrying, walled garden, or suspended), dunning is reset
4. RADIUS credentials are restored to the subscriber's plan group (if they were in walled garden)
5. The subscriber receives a payment confirmation via SMS and email

## Troubleshooting

### Common Daraja Errors

<AccordionGroup>
  <Accordion title="Invalid shortcode (error 174)">
    Your shortcode is not registered for M-Pesa Express on Daraja. Verify at [developer.safaricom.co.ke](https://developer.safaricom.co.ke) that your app has the correct shortcode associated and that M-Pesa Express is enabled.
  </Accordion>

  <Accordion title="Request timeout">
    The Daraja API did not respond within the timeout window. This is usually a temporary Safaricom outage. FyberPay uses circuit breakers to handle this gracefully. The payment will be retried automatically if it was part of a dunning cycle.
  </Accordion>

  <Accordion title="Insufficient balance (result code 1)">
    The subscriber does not have enough M-Pesa balance to complete the transaction. FyberPay marks this as a failed payment. The dunning pipeline will retry based on your configured intervals.
  </Accordion>

  <Accordion title="OAuth token failure">
    FyberPay could not obtain an access token from Daraja. Verify that your Consumer Key and Consumer Secret are correct. Tokens are cached for approximately 5 minutes and refreshed automatically.
  </Accordion>

  <Accordion title="Wrong credentials (invalid consumer key/secret)">
    Double-check that you are using credentials from the correct Daraja app and environment. Sandbox and production use different credentials.
  </Accordion>

  <Accordion title="Callback not received">
    If STK Push callbacks are not arriving, verify that your server's HTTPS endpoint is publicly reachable. FyberPay falls back to STK Query polling, so payments will still be captured, but with a slight delay.
  </Accordion>
</AccordionGroup>

### Testing in Sandbox

Safaricom provides test credentials and phone numbers for sandbox testing:

| Parameter         | Sandbox Value |
| ----------------- | ------------- |
| Test phone number | 254708374149  |
| Shortcode         | 174379        |
| Environment       | `sandbox`     |

<Warning>
  Remember to switch the environment from `sandbox` to `production` before going live. Sandbox payments are simulated and do not move real money.
</Warning>

## Next Steps

<CardGroup cols={2}>
  <Card title="Billing Setup" icon="file-invoice-dollar" href="/guides/billing-setup">
    Configure invoicing, tax settings, and billing cycles.
  </Card>

  <Card title="Dunning Automation" icon="bell" href="/guides/dunning-automation">
    Automate payment retries and service restriction for overdue accounts.
  </Card>
</CardGroup>
