# Setup payment methods for PNLCS

> **Writer's brief**
> - **Audience:** a reseller ready to take money: M-PESA first, cards and bank transfer as options.
> - **Promise:** "Customers pay by M-PESA, card or bank, and invoices mark themselves paid."
> - **Keywords:** M-PESA STK push billing, Daraja API setup, Paystack hosting billing, accept M-PESA payments online, PNLCS payment gateway.
> - **Call to action:** pay a test invoice with each method.
> - **Split idea:** this guide is long. M-PESA could be its own article, with this page as the overview.

A **payment method** (gateway) is how customers pay their invoices. When a
payment is confirmed, PNLCS marks the invoice **paid** and sets up whatever was
waiting on it, such as a hosting account or a domain.

**You need:** the keys for each method you'll offer, and a panel on an
`https://` address the internet can reach. See [Before you start](00-before-you-start.md#payments).

## Which methods to offer

| Method | Customers pay with | Good for |
|---|---|---|
| **M-PESA** (Safaricom Daraja) | A payment prompt on their phone, or your PayBill/Till | Kenya. Money goes straight to your PayBill or Till |
| **Paystack** | Card, M-Pesa and other mobile money, bank | Kenya, Nigeria, Ghana, South Africa. One account covers several methods |
| **Stripe** | Card | International cards |
| **PayPal** | PayPal balance, card | International customers |
| **Bank Transfer** | Their bank; you confirm by hand | Larger or business payments |

Most Kenyan resellers start with **M-PESA** plus **Paystack** or **Bank Transfer**.

All gateways are under **Setup → Payment Gateways**. Some (such as Paystack)
must first be enabled under **Setup → Modules**.

> 📸 **IMAGE 1: Payment Gateways list**
> - **Show:** Setup → Payment Gateways with M-PESA, Paystack and Bank Transfer active.
> - **Highlight:** the active switches.
> - **Blur:** nothing.
> - **File:** `images/payment-methods/01-gateways-list.png`
> - **Alt text:** "Payment gateways list with M-PESA, Paystack and Bank Transfer active"

> ⚠️ **Test first, every time.** Set each method up with test (sandbox) keys,
> pay a test invoice, and check it turns **Paid**. Then switch to live keys
> and make one small real payment.

---

## M-PESA (Safaricom Daraja)

Customers get an **M-PESA prompt on their phone** (STK push) and enter their
PIN, or pay your **PayBill/Till from the M-PESA menu**. PNLCS matches the
payment to the invoice by itself.

### What you need from Safaricom

1. A **Daraja account** at [developer.safaricom.co.ke](https://developer.safaricom.co.ke).
2. For testing: an **app** on Daraja with **M-PESA Express**, **C2B**,
   **Transaction Status** and **Reversal**. It gives you a **consumer key**
   and **consumer secret**; the sandbox **passkey** is on the portal.
3. For real money: **Go Live** on Daraja with your PayBill or Till. You need a
   **Business Administrator** on the M-PESA org portal. Safaricom gives you
   production keys, and emails the production **passkey**.
4. To check payment status and refund (reverse) payments: an **API operator
   (initiator)** created on the M-PESA org portal with the *Transaction Status
   query ORG API* and *Org Reversals Initiator* roles, and its **security
   credential** generated on Daraja.

> 💡 Going live can take a few days at Safaricom. Start the Go Live request
> early and test in the sandbox meanwhile.

### Set it up in PNLCS

1. Go to **Billing → M-PESA → Settings**, on the **Sandbox** tab first.
2. Fill in:

| Field | What to enter |
|---|---|
| **Merchant account** | PayBill or Till (Buy Goods) |
| **Business short code** | Your PayBill number; for a Till, the store / head-office number used at Go Live. Sandbox: `174379` |
| **Till number** | Till only: the number customers pay to |
| **Consumer key** / **Consumer secret** | From your Daraja app |
| **STK push passkey** | Sandbox: from the portal. Production: from the Go Live email |
| **Initiator name**, plus security credential or **Initiator password** | For status checks and reversals |
| **Offer the prompt on the phone (STK push)** | Tick to offer the phone prompt |
| **Offer paying the paybill/till by hand** | Tick to let customers pay from the M-PESA menu |
| **Offer M-PESA to customers** | Tick to show M-PESA on invoices |

3. **Save settings**, then **Test credentials**. This only checks the keys; no
   money moves.
4. **Register C2B URLs**, so Safaricom tells PNLCS about PayBill/Till payments
   made from the phone menu.

> 📸 **IMAGE 2: M-PESA settings** (GIF suggested: filling in, then Test credentials)
> - **Show:** Billing → M-PESA → Settings, Sandbox tab, filled in.
> - **Highlight:** Business short code, Test credentials, Register C2B URLs.
> - **Blur:** consumer key, consumer secret, passkey, initiator password, security credential, the callback URLs.
> - **File:** `images/payment-methods/02-mpesa-settings.png`
> - **Alt text:** "M-PESA Daraja settings in PNLCS, sandbox tab"

> ⚠️ **Never publish the callback URLs.** They contain a secret. Blur them in
> every screenshot.

### Test it

1. As a customer, open an unpaid invoice, choose M-PESA and enter a phone number.
2. Sandbox test number: `254708374149`. It usually answers "cancelled by user"
   by itself. To complete a payment, use a real Safaricom number added to your
   sandbox app.

> 📸 **IMAGE 3: Paying an invoice with M-PESA**
> - **Show:** the client-area invoice with the M-PESA phone number box, and a phone with the STK prompt.
> - **Blur:** phone numbers.
> - **File:** `images/payment-methods/03-mpesa-invoice.png`
> - **Alt text:** "Customer paying an invoice with an M-PESA prompt on their phone"

### Go live

1. **Production** tab: enter the production keys, passkey and initiator. Save.
2. Switch **Environment in use** to **Production** and tick *"I am switching
   to production: real money will move."*
3. **Test credentials**, then **Register C2B URLs**.

   > ⚠️ In production, registering URLs is a **one-time** call. Changing
   > them later goes through Safaricom's URL management.
4. Open **Health** (under **Billing → M-PESA**) and fix anything it flags.
5. Pay a small real invoice with each option you offer.

### Day to day

- **Billing → M-PESA → Payments**: every payment. One that didn't match an
  invoice (wrong account number) can be applied to the right invoice from here.
- **Reversals**: refund a payment through M-PESA.
- **Look up**: a customer says they paid but nothing shows? Enter the M-PESA
  code to ask Safaricom (needs the initiator).
- Alerts for unmatched payments and failures: **Setup → Notification Channels**.

> ⚠️ PNLCS's scheduled tasks must be running (your provider sets this up). They
> catch payments whose confirmation from Safaricom went missing.

---

## Paystack

1. **Setup → Modules**: enable **Paystack**.
2. In [Paystack](https://dashboard.paystack.com) → **Settings → API Keys &
   Webhooks**, copy the **Public Key** and **Secret Key** (test keys first:
   they start with `pk_test_` / `sk_test_`).
3. **Setup → Payment Gateways → Paystack**: tick **Active** and enter:
   - **Public Key**, **Secret Key**;
   - **Currencies**: the currencies your Paystack account settles in, e.g.
     `KES` or `KES, USD`. Each one other than your shop currency needs a rate
     under **Setup → Currencies**;
   - **Payment Channels**: leave empty for everything switched on in
     Paystack, or limit it, e.g. `card, mobile_money`.
4. Back in Paystack, set the **Webhook URL** to
   `https://<your panel>/gateway/paystack/callback`.
5. Pay a test invoice, then swap in the live keys.

> 📸 **IMAGE 4: Paystack settings**
> - **Show:** Setup → Payment Gateways → Paystack, filled in with test keys.
> - **Blur:** Public Key and Secret Key.
> - **File:** `images/payment-methods/04-paystack.png`
> - **Alt text:** "Paystack gateway settings in PNLCS"

---

## Stripe (cards)

1. In the [Stripe dashboard](https://dashboard.stripe.com), copy the
   **Publishable key** and **Secret key** (test mode first).
2. Add a webhook endpoint `https://<your panel>/gateway/stripe/webhook`, and
   copy its **signing secret**.
3. **Setup → Payment Gateways → Stripe**: paste all three and enable it.

Test card: `4242 4242 4242 4242`, any future date, any CVC.

> ⚠️ Without the **Webhook Signing Secret**, Stripe payments are not confirmed.

## PayPal

1. Create an app on [developer.paypal.com](https://developer.paypal.com) and
   copy its **Client ID** and **Secret** (sandbox first).
2. **Setup → Payment Gateways → PayPal**: enter them, choose sandbox or live,
   enable it.
3. In PayPal, add a webhook to `https://<your panel>/gateway/paypal/webhook`.

---

## Bank Transfer

1. **Setup → Payment Gateways → Bank Transfer**: enable it and enter the
   account holder and your bank details (bank name, account number, branch or
   SWIFT code), plus a note such as "Use your invoice number as the reference".
2. Customers see these details on the invoice. After paying, they can send a
   **payment notification** with the receipt.
3. You check it under **Billing → Payment Notifications** and approve it. The
   invoice is marked paid, and anything waiting on it is set up.

> 📸 **IMAGE 5: Approving a bank payment**
> - **Show:** Billing → Payment Notifications with one notification and its receipt.
> - **Highlight:** the Approve button.
> - **Blur:** customer name, account numbers.
> - **File:** `images/payment-methods/05-payment-notifications.png`
> - **Alt text:** "Approving a customer's bank transfer notification"

---

## Currencies

Set your currencies under **Setup → Currencies**: the default (e.g. KES) and
any others with their rates. Package prices are set per currency
([Creating packages](01-creating-packages.md)).

## Troubleshooting

| Problem | Fix |
|---|---|
| A method doesn't show at checkout | Is it enabled, with every required field filled in? Third-party gateways also need enabling under **Setup → Modules** |
| Customer paid, invoice still unpaid | Card gateways: check the webhook URL and secret. M-PESA: look in **Billing → M-PESA → Payments**, then **Look up** the M-PESA code. Gateway errors are in **Utilities → System Logs → Gateway** |
| M-PESA "Invalid CallBackURL" | The panel must be on a public `https://` address |
| M-PESA works in sandbox but not live | Production needs its own keys, passkey, URL registration and initiator: check each one, then **Health** |
