Payments API

M-Pesa STK Push (Lipa Na M-Pesa) and Paystack card payments.

Base: /api/v1/payments  |  X-Tenant-ID required

M-Pesa

POST /api/v1/payments/mpesa/stkpush 🔒 Auth Required

Initiate an M-Pesa STK Push prompt to the customer's phone.

Request Body

{ "order_id": "uuid", "phone": "0712345678" }

Response

{ "success": true, "data": { "checkout_request_id": "ws_CO_...", "customer_message": "Please enter your PIN" } }

Possible Errors

CodeHTTPWhen
NOT_FOUND404Order not found
PAYMENT_FAILED400Order already paid
PAYMENT_FAILED500Safaricom API error
POST /api/v1/payments/mpesa/stkquery 🔒 Auth Required

Poll the status of an STK push by its CheckoutRequestID.

Request Body

{ "checkout_request_id": "ws_CO_..." }

Response

{ "success": true, "data": { "ResultCode": 0, "ResultDesc": "The service request is processed successfully." } }
POST /api/v1/payments/mpesa/callback Public

Daraja webhook endpoint. Called automatically by Safaricom on payment completion. Updates order payment_status to "paid" and triggers invoice + notification.

Response

200 OK — { "ResultCode": 0, "ResultDesc": "Accepted" }

Paystack

POST /api/v1/payments/paystack/initiate 🔒 Auth Required

Initialize a Paystack transaction and get a payment URL.

Request Body

{ "order_id": "uuid", "email": "jane@email.com", "amount": 1766 }

Response

{ "success": true, "data": { "authorization_url": "https://checkout.paystack.com/...", "access_code": "...", "reference": "ALO-2024-00001-..." } }

Possible Errors

CodeHTTPWhen
PAYMENT_FAILED500Paystack API error
GET /api/v1/payments/paystack/verify/:ref Public

Verify a Paystack payment by reference. Updates order if not already paid.

Response

{ "success": true, "data": { "reference": "...", "status": "success", "amount": 1766, "currency": "NGN", "paid_at": "2024-01-10T09:00:00Z" } }
POST /api/v1/payments/paystack/webhook Public

Paystack webhook endpoint. Verifies HMAC-SHA512 signature before processing. Updates order on charge.success event.

Response

200 OK

Coupons

POST /api/v1/coupons/validate Public

Validate a coupon code against the cart total. Check expiry, usage limits, and per-user limits.

Request Body

{ "code": "WELCOME10", "cart_total": 1500 }

Response

{ "success": true, "data": { "valid": true, "code": "WELCOME10", "type": "percentage", "value": 10, "discount": 150 } }

Possible Errors

CodeHTTPWhen
COUPON_INVALID422Invalid, inactive, or exceeded
COUPON_EXPIRED422Past expiry date