# M-Pesa Express (STK Push) Setup Guide

## Overview

This POS system uses **M-Pesa Daraja API** with **M-Pesa Express (STK Push)** for mobile money payments. M-Pesa Express allows customers to pay directly from their M-Pesa account via a push notification on their phone.

## What is M-Pesa Express (STK Push)?

**M-Pesa Express** (also known as **STK Push** or **Lipa na M-Pesa Online**) is a payment service that:
- Sends a payment request directly to the customer's phone
- Allows customers to complete payment without entering merchant details
- Provides instant payment confirmation
- Works with both PayBill and Till numbers

## Current Implementation

### ✅ What We're Using

1. **STK Push Initiation**: `/mpesa/stkpush/v1/processrequest`
   - Initiates payment request to customer's phone
   - Uses `CustomerPayBillOnline` transaction type (for PayBill numbers)

2. **STK Push Query**: `/mpesa/stkpushquery/v1/query`
   - Checks payment status
   - Used for polling payment completion

3. **Callback Handler**: `/api/payments/mpesa/callback`
   - Receives payment confirmation from Safaricom
   - Updates payment status automatically

## Required Environment Variables

Add these to your `server/.env` file:

```env
# M-Pesa Daraja API Configuration
MPESA_BASE_URL=https://sandbox.safaricom.co.ke    # Sandbox URL (change to https://api.safaricom.co.ke for production)
MPESA_CONSUMER_KEY=your_consumer_key              # From Safaricom Developer Portal
MPESA_CONSUMER_SECRET=your_consumer_secret        # From Safaricom Developer Portal
MPESA_PASSKEY=your_passkey                        # STK Push passkey from Daraja dashboard
MPESA_SHORTCODE=your_business_shortcode           # Your PayBill or Till number (without prefix)
MPESA_CALLBACK_URL=https://yourdomain.com/api/payments/mpesa/callback  # Public URL for callbacks
MPESA_TIMEOUT_URL=https://yourdomain.com/api/payments/mpesa/callback   # Public URL for timeout callbacks
MPESA_ACCOUNT_REFERENCE=XYZ_POS                   # Account reference prefix
MPESA_TRANSACTION_DESC=Payment for goods          # Default transaction description
MPESA_TIMEOUT_SECONDS=60                          # STK Push timeout (default: 60 seconds)

# API Base URL (for callback URLs if not fully specified)
API_BASE_URL=https://yourdomain.com               # Your server's public URL
```

## Setup Steps

### 1. Register on Safaricom Developer Portal

1. Visit: https://developer.safaricom.co.ke/
2. Create an account and log in
3. Go to "My Apps" and create a new app
4. Note down your `Consumer Key` and `Consumer Secret`

### 2. Get STK Push Credentials

1. In your app, go to "Staging" or "Production" environment
2. Get your:
   - **Shortcode**: Your PayBill or Till number (without prefix like 174379)
   - **Passkey**: STK Push passkey (used to generate password)
   - **Consumer Key**: From app credentials
   - **Consumer Secret**: From app credentials

### 3. Configure Callback URL

Your callback URL must be:
- **Publicly accessible** (not localhost in production)
- **HTTPS** (required for production)
- **Accessible by Safaricom servers**

**For Development (using ngrok or similar):**
```bash
# Install ngrok
npm install -g ngrok

# Start your server
npm run dev

# In another terminal, expose your local server
ngrok http 4000

# Use the ngrok URL in your .env:
MPESA_CALLBACK_URL=https://your-ngrok-url.ngrok.io/api/payments/mpesa/callback
MPESA_TIMEOUT_URL=https://your-ngrok-url.ngrok.io/api/payments/mpesa/callback
```

**For Production:**
```env
MPESA_CALLBACK_URL=https://yourdomain.com/api/payments/mpesa/callback
MPESA_TIMEOUT_URL=https://yourdomain.com/api/payments/mpesa/callback
```

### 4. Transaction Types

**Current Configuration:**
- `CustomerPayBillOnline` - For PayBill numbers (e.g., 174379)
  - Customer enters business number manually
  - More common for retail businesses

**Alternative (if using Till number):**
- `CustomerBuyGoodsOnline` - For Till numbers (e.g., 174379)
  - Direct payment to merchant
  - Faster checkout

To switch to Till number, update `server/modules/payments/services/mpesa.js`:
```javascript
TransactionType: 'CustomerBuyGoodsOnline', // Instead of 'CustomerPayBillOnline'
```

## Testing

### Sandbox Testing

1. Use sandbox URL: `https://sandbox.safaricom.co.ke`
2. Test phone numbers (from Safaricom documentation):
   - Format: `2547XXXXXXXX` (e.g., `254712345678`)
3. Use test credentials from Safaricom Developer Portal

### Production Testing

1. Change `MPESA_BASE_URL` to: `https://api.safaricom.co.ke`
2. Use real Safaricom credentials
3. Test with real M-Pesa accounts

## Payment Flow

1. **Customer initiates payment** in POS
   - Enters phone number (e.g., `0704108976`)
   - System normalizes to `254704108976`

2. **STK Push initiated** (`initiateSTKPush`)
   - System sends request to Safaricom
   - Customer receives push notification

3. **Customer completes payment** on phone
   - Enters M-Pesa PIN
   - Payment is processed

4. **System receives callback** (`handleMPesaCallback`)
   - Safaricom sends confirmation
   - Payment status updated to `SUCCESS`
   - Payment record updated with transaction code

5. **Status query** (if callback delayed)
   - Frontend polls payment status
   - System queries Safaricom for updates

## Troubleshooting

### Error: "M-Pesa STK Push not configured"

**Cause:** Missing required environment variables

**Solution:**
- Check that `MPESA_SHORTCODE` and `MPESA_PASSKEY` are set in `.env`
- Restart server after adding variables

### Error: "Failed to get M-Pesa access token"

**Cause:** Invalid consumer key/secret or network issue

**Solution:**
- Verify `MPESA_CONSUMER_KEY` and `MPESA_CONSUMER_SECRET`
- Check `MPESA_BASE_URL` is correct (sandbox vs production)
- Ensure server can reach Safaricom API

### Payment stays PENDING

**Causes:**
1. Customer didn't complete payment
2. Callback URL not accessible
3. Network timeout

**Solutions:**
1. Use manual confirmation (Manager approval)
2. Check callback URL is publicly accessible
3. Increase `MPESA_TIMEOUT_SECONDS` if needed
4. Query payment status manually

### Callback not received

**Causes:**
- Callback URL not publicly accessible
- Firewall blocking Safaricom IPs
- SSL certificate issues (production requires HTTPS)

**Solutions:**
- Use ngrok for development
- Ensure HTTPS in production
- Whitelist Safaricom IP ranges if using firewall

## API Endpoints

### Initiate STK Push
```
POST /api/payments
Body: {
  "sale_id": 123,
  "provider": "MOBILE_MONEY",
  "amount": 100,
  "mpesa_phone_number": "0704108976"
}
```

### Query STK Push Status
```
GET /api/payments/mpesa/query/:checkoutRequestID
```

### Manual M-Pesa Confirmation
```
POST /api/payments/mpesa/manual-confirm
Body: {
  "payment_id": 456,
  "mpesa_transaction_code": "RFT7X8Z9K0"
}
```

### Callback (Handled automatically by Safaricom)
```
POST /api/payments/mpesa/callback
```

## Security Notes

1. **Never commit `.env` file** - Contains sensitive credentials
2. **Use HTTPS in production** - Required by Safaricom
3. **Validate callbacks** - Verify they're from Safaricom (IP whitelisting recommended)
4. **Store credentials securely** - Use environment variables, not hardcoded values
5. **Rotate credentials** - Regularly update consumer key/secret

## Additional Resources

- [Safaricom Daraja API Documentation](https://developer.safaricom.co.ke/docs)
- [STK Push API Reference](https://developer.safaricom.co.ke/docs#stk-push-api)
- [Test Credentials](https://developer.safaricom.co.ke/test_credentials)
- [Daraja Status Page](https://developer.safaricom.co.ke/api-status)

