Initiate STK Push

Initiate STK Push

The STK Push endpoint allows you to initiate mobile payments by sending a payment request directly to your customer's phone. This is the primary method for collecting payments through the Lipia Online API.

Endpoint

POST /payments/stk-push

Request Headers

Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

Request Body

{
  "phone_number": "0712345678",
  "amount": 100,
  "external_reference": "order_123",
  "callback_url": "https://your-domain.com/callback",
  "metadata": {
    "order_id": "12345",
    "customer_name": "John Doe"
  }
}

Request Parameters

ParameterTypeRequiredDescription
phone_numberStringCustomer's phone number in (e.g., 0712345678 , 0112345678, 254712345678)
amountNumberPayment amount in Kenyan Shillings (minimum 1)
external_referenceString/NumberYour reference ID for tracking this payment
callback_urlStringURL to receive payment confirmation updates via callback webhook
metadataObjectOptional additional data to store with the payment (key-value pairs)

Phone Number Format

Phone numbers must be a valid kenyan safaricom number:

  • 2547... , 2541... , +2547... , +2541... , 07... ,01... (correct)

Amount Validation

  • Must be a positive integer

Success Response

{
  "success": true,
  "status": "success",
  "message": "STK push initiated successfully",
  "customerMessage": "STK push initiated successfully",
  "data": {
    "TransactionReference": "64f1a2b3c4d5e6f7g8h9i0j1",
    "ResponseCode": 0,
    "ResponseDescription": "Success. Request accepted for processing"
  },
  "timestamp": "2024-01-15T10:30:00.000Z"
}

Response Fields

FieldTypeDescription
TransactionReferenceStringUnique identifier for tracking this payment
ResponseCodeNumberM-Pesa response code (0 = success)
ResponseDescriptionStringHuman-readable response description

Error Responses

Validation Error

{
  "success": false,
  "status": "error",
  "message": "Validation failed",
  "customerMessage": "Please check your input and try again",
  "error": {
    "code": "VALIDATION_ERROR",
    "field": "phone_number",
    "location": "body",
    "value": "0712345678",
    "expected": "valid safaricom kenyan number"
  },
  "timestamp": "2024-01-15T10:30:00.000Z"
}

Other errors

{
  "success": false,
  "status": "error",
  "message": "error message",
  "customerMessage": "the error message for display",
  "error": {
    "code": "ERROR_CODE",
    ... OTHER DEBUG ERROR DETAILS
  },
  "timestamp": "2024-01-15T10:30:00.000Z"
}

Code Examples

JavaScript/Node.js

const initiatePayment = async (paymentData) => {
  try {
    const response = await fetch('https://lipia-api.kreativelabske.com/api/v2/payments/stk-push', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${process.env.LIPIA_API_KEY}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(paymentData)
    });
 
    const result = await response.json();
    
    if (result.success) {
      console.log('Payment initiated:', result.data.TransactionReference);
      return result.data;
    } else {
      console.error('Payment failed:', result.message);
      throw new Error(result.customerMessage);
    }
  } catch (error) {
    console.error('Error initiating payment:', error);
    throw error;
  }
};
 
// Usage
const paymentData = {
  phone_number: '254712345678',
  amount: 100,
  external_reference: 'order_123', //optional
  callback_url: 'https://your-domain.com/callback', //optional
  metadata: {
    order_id: '12345',
    customer_name: 'John Doe'
  }  //optional
};
 
initiatePayment(paymentData);

Python

import requests
import os
 
def initiate_payment(payment_data):
    api_key = os.getenv('LIPIA_API_KEY')
    base_url = os.getenv('LIPIA_BASE_URL')
    
    headers = {
        'Authorization': f'Bearer {api_key}',
        'Content-Type': 'application/json'
    }
    
    try:
        response = requests.post(
            f'{base_url}/payments/stk-push',
            headers=headers,
            json=payment_data
        )
        
        result = response.json()
        
        if result['success']:
            print(f"Payment initiated: {result['data']['TransactionReference']}")
            return result['data']
        else:
            print(f"Payment failed: {result['message']}")
            raise Exception(result['customerMessage'])
            
    except requests.exceptions.RequestException as e:
        print(f"Error initiating payment: {e}")
        raise
 
# Usage
payment_data = {
    'phone_number': '254712345678',
    'amount': 100,
    'external_reference': 'order_123',
    'callback_url': 'https://your-domain.com/callback',
    'metadata': {
        'order_id': '12345',
        'customer_name': 'John Doe'
    }
}
 
initiate_payment(payment_data)

PHP

<?php
function initiatePayment($paymentData) {
    $apiKey = $_ENV['LIPIA_API_KEY'];
    $baseUrl = $_ENV['LIPIA_BASE_URL'];
    
    $headers = [
        'Authorization: Bearer ' . $apiKey,
        'Content-Type: application/json'
    ];
    
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $baseUrl . '/payments/stk-push');
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($paymentData));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    
    $response = curl_exec($ch);
    $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    
    if ($httpCode === 200) {
        $result = json_decode($response, true);
        
        if ($result['success']) {
            echo "Payment initiated: " . $result['data']['TransactionReference'];
            return $result['data'];
        } else {
            echo "Payment failed: " . $result['message'];
            throw new Exception($result['customerMessage']);
        }
    } else {
        throw new Exception("HTTP Error: " . $httpCode);
    }
}
 
// Usage
$paymentData = [
    'phone_number' => '254712345678',
    'amount' => 100,
    'external_reference' => 'order_123',
    'callback_url' => 'https://your-domain.com/callback',
    'metadata' => [
        'order_id' => '12345',
        'customer_name' => 'John Doe'
    ]
];
 
initiatePayment($paymentData);
?>

Testing

Test Phone Numbers

Use test phone numbers registered in your apps dashboard for test apps:

Best Practices

1. Error Handling

Always implement proper error handling:

try {
  const result = await initiatePayment(paymentData);
  // Handle success
} catch (error) {
  if (error.code === 'VALIDATION_ERROR') {
    // Show user-friendly validation message
  } else if (error.code === 'PAYMENT_ERROR') {
    // Handle payment-specific errors
  } else {
    // Handle other errors
  }
}

Next Steps

After initiating a payment:

  1. Store the TransactionReference for status checking
  2. Set up webhook handling to receive real-time updates
  3. Implement status polling as a fallback
  4. Handle all payment states (pending, success, failed)

Continue to Transaction Status to learn how to check payment status.