Skip to main content

Bill Payments

The bill payment API allows merchants to purchase various bills (airtime, data, TV subscriptions, utility, insurance, education) using their wallet balance. All amounts are specified in kobo (e.g., ₦100 = 10000 kobo).

Overview

iNPAY's bill payment service enables you to offer value-added services to your customers, including:

  • Airtime: Mobile phone top-ups
  • Data: Internet data bundles
  • TV Subscriptions: Cable TV services (DSTV, GOTV, etc.)
  • Utility: Electricity bill payments
  • Insurance: Insurance premium payments
  • Education: WAEC, JAMB, and other educational services

Authentication

Bill payment endpoints use the same authentication as other API endpoints:

  • Header: Authorization: Bearer sk_live_your_secret_key_here
  • Wallet: Must specify fromWallet (wallet PayID ending with .wallet)

Commission Structure

Commission is calculated as a discount (deducted from bill amount before debiting merchant):

Bill TypeCommission
Airtime1.5%
Data1.5%
Electricity0.5%
Cable TV0.5%
Education₦100 flat

Note: Convenience fees (if any) are added to the total charge, but commission percentage is not applied to convenience fees.

Service ID Reference

Below is a comprehensive list of serviceIDs for each category. Important: Always use the exact serviceID as shown below.

Airtime Services

ServiceIDProvider Name
mtnMTN
gloGLO
airtelAirtel
etisalat9mobile (formerly Etisalat)

Data Services

ServiceIDProvider Name
mtn-dataMTN Data
glo-dataGLO Data
airtel-dataAirtel Data
etisalat-data9mobile Data (Note: Use etisalat-data, not 9mobile-data)
glo-sme-dataGLO Data (SME)
smile-directSmile Payment
spectranetSpectranet Internet Data

TV Subscription Services

Common serviceIDs include:

  • dstv - DSTV
  • gotv - GOTV
  • startimes - StarTimes

Note: Use the /services endpoint with categoryIdentifier=tv-subscription to get the complete list.

Electricity/Utility Services

Common serviceIDs include:

  • ikeja-electric - Ikeja Electric (IKEDC)
  • eko-electric - Eko Electric (EKEDC)
  • kano-electric - Kano Electric
  • portharcourt-electric - Port Harcourt Electric
  • jos-electric - Jos Electric
  • ibadan-electric - Ibadan Electric
  • kaduna-electric - Kaduna Electric

Note:

  • Use the /services endpoint with categoryIdentifier=electricity-bill to get the complete list
  • For utility verification, the type parameter is required and must be either "prepaid" or "postpaid"

Education Services

  • waec-result - WAEC Result Checker
  • waec-registration - WAEC Registration
  • jamb - JAMB

Note: Use the /services endpoint with categoryIdentifier=education to get the complete list.

Insurance Services

Note: Use the /services endpoint with categoryIdentifier=insurance to get available insurance services.

Important Notes

  1. 9mobile/Etisalat:

    • For airtime: Use etisalat
    • For data: Use etisalat-data (NOT 9mobile-data)
  2. Variation Amount: When purchasing data or other fixed-price services, the variation_amount field from the variations response represents the actual cost of the service in kobo.

    • VTpass returns variation_amount in naira, but our API converts it to kobo for consistency
    • Use this amount directly in your calculations when purchasing fixed-price services (like data plans)
    • Example: If VTpass returns variation_amount: 100 (₦100), our API returns variation_amount: 10000 (10000 kobo = ₦100)
  3. Dynamic Service Lists: While this reference provides common serviceIDs, always use the /services endpoint to get the most up-to-date list of available services for each category, as VTpass may add or remove services.

Base URL

https://api.inpaycheckout.com/api/v1/developer

Endpoints

Get Bill Categories

Retrieve all available bill payment categories.

GET /api/v1/developer/bills/categories

Headers:

Authorization: Bearer sk_live_your_secret_key_here

Response:

{
"success": true,
"message": "Bill categories retrieved successfully",
"data": {
"categories": [
{
"category": "airtime",
"name": "Airtime VTU",
"identifier": "airtime"
},
{
"category": "data",
"name": "Data Subscription",
"identifier": "data"
}
]
}
}

Get Services for Category

Get all services available for a specific category.

GET /api/v1/developer/bills/services?categoryIdentifier=airtime

Headers:

Authorization: Bearer sk_live_your_secret_key_here

Response:

{
"success": true,
"message": "Bill services retrieved successfully",
"data": {
"services": [
{
"serviceID": "mtn",
"name": "MTN",
"variation": "mtn",
"category": "airtime"
},
{
"serviceID": "glo",
"name": "GLO",
"variation": "glo",
"category": "airtime"
}
]
}
}

Get Variations for Service

Get available variations (plans/bundles) for a specific service.

GET /api/v1/developer/bills/variations?serviceID=mtn-data

Important: For 9mobile data, use serviceID=etisalat-data (NOT 9mobile-data)

Headers:

Authorization: Bearer sk_live_your_secret_key_here

Response:

{
"success": true,
"message": "Bill variations retrieved successfully",
"data": {
"serviceName": "MTN Data",
"serviceID": "mtn-data",
"convenienceFee": 0,
"variations": [
{
"variation_code": "mtn-500mb-100",
"name": "MTN 500MB - ₦100",
"variation_amount": 10000,
"fixedPrice": "Yes"
}
]
}
}

Note: The variation_amount field represents the actual cost of the service/variation in kobo.

  • VTpass returns variation_amount in naira, but our API converts it to kobo for consistency
  • Use this amount directly in your calculations when purchasing fixed-price services (like data plans)
  • Example: If VTpass returns variation_amount: 100 (₦100), our API returns variation_amount: 10000 (10000 kobo = ₦100)

Verify Bill Account

Verify bill account details before purchase (required for TV subscriptions, utility, and JAMB).

POST /api/v1/developer/bills/verify

Headers:

Authorization: Bearer sk_live_your_secret_key_here
Content-Type: application/json

Request Body (TV Subscription):

{
"serviceID": "dstv",
"billersCode": "1234567890",
"phone": "08012345678"
}

Note: For TV subscriptions, the type field is optional and not required.

Request Body (Utility/Electricity - Prepaid):

{
"serviceID": "ikeja-electric",
"billersCode": "12345678901",
"phone": "08012345678",
"type": "prepaid"
}

Request Body (Utility/Electricity - Postpaid):

{
"serviceID": "ikeja-electric",
"billersCode": "12345678901",
"phone": "08012345678",
"type": "postpaid"
}

Request Body (JAMB):

{
"serviceID": "jamb",
"billersCode": "12345678901",
"phone": "08012345678",
"type": "jamb-pin-variation-code"
}

Important Notes about the type field:

  1. For Utility/Electricity Services:

    • The type field is required and must be either "prepaid" or "postpaid"
    • This tells the system whether you're verifying a prepaid meter or a postpaid meter account
    • Example: "type": "prepaid" for prepaid electricity meters
  2. For JAMB Services:

    • The type field is required and should be the variation code obtained from calling getVariationCodes("jamb")
    • First, call the variations endpoint for JAMB to get the available variation codes
    • Then use one of those variation codes as the type value
    • Example: If getVariationCodes("jamb") returns "jamb-pin-2024", use "type": "jamb-pin-2024"
  3. For TV Subscriptions:

    • The type field is optional and not required
    • You can omit it entirely for TV subscription verification

Response (TV):

{
"success": true,
"message": "Bill account verified successfully",
"data": {
"customerName": "John Doe",
"renewalAmount": 2500,
"currentBouquet": "Compact",
"expiryDate": "2024-12-31"
}
}

Response (Utility):

{
"success": true,
"message": "Bill account verified successfully",
"data": {
"customerName": "John Doe",
"customerAddress": "123 Main St",
"meterType": "prepaid"
}
}

Purchase Bill Payment

Purchase a bill payment using your wallet balance.

POST /api/v1/developer/bills/purchase

Headers:

Authorization: Bearer sk_live_your_secret_key_here
Content-Type: application/json

Request Body (Airtime):

{
"fromWallet": "merchant.wallet.1234567890",
"serviceID": "mtn",
"amount": 10000,
"phone": "08012345678",
"narration": "MTN Airtime purchase",
"reference": "unique-ref-123"
}

Request Body (Data):

{
"fromWallet": "merchant.wallet.1234567890",
"serviceID": "mtn-data",
"variation_code": "mtn-500mb-100",
"phone": "08012345678",
"narration": "MTN Data purchase",
"reference": "unique-ref-124"
}

Request Body (TV Subscription - Renewal):

{
"fromWallet": "merchant.wallet.1234567890",
"serviceID": "dstv",
"billersCode": "1234567890",
"phone": "08012345678",
"subscription_type": "renewal",
"amount": 2500,
"quantity": 1,
"narration": "DSTV renewal",
"reference": "unique-ref-125"
}

Request Body (TV Subscription - Change Bouquet):

{
"fromWallet": "merchant.wallet.1234567890",
"serviceID": "dstv",
"billersCode": "1234567890",
"phone": "08012345678",
"subscription_type": "change",
"variation_code": "dstv-compact",
"quantity": 1,
"narration": "DSTV bouquet change",
"reference": "unique-ref-126"
}

Request Body (Utility - Prepaid):

{
"fromWallet": "merchant.wallet.1234567890",
"serviceID": "ikeja-electric",
"billersCode": "12345678901",
"phone": "08012345678",
"variation_code": "prepaid",
"amount": 5000,
"narration": "IKEDC prepaid payment",
"reference": "unique-ref-127"
}

Request Body (Education - WAEC Result Checker):

{
"fromWallet": "merchant.wallet.1234567890",
"serviceID": "waec-result",
"phone": "08012345678",
"variation_code": "waec-result-checker",
"quantity": 1,
"narration": "WAEC result checker",
"reference": "unique-ref-128"
}

Response (Success - Airtime):

{
"success": true,
"message": "Bill payment completed successfully",
"data": {
"transactionId": "iNPAY-abc123def4",
"reference": "unique-ref-123",
"fromWallet": "merchant.wallet.1234567890",
"serviceID": "mtn",
"billType": "airtime",
"amount": 10000,
"phone": "08012345678",
"commission": {
"rate": "1.5%",
"amount": 150,
"merchantDebitAmount": 9850
},
"convenienceFee": 0,
"totalCharge": 10000,
"status": "completed",
"vtpassReference": "vtpass-ref-123"
}
}

Response (Success - Utility/Electricity):

{
"success": true,
"message": "Bill payment completed successfully",
"data": {
"transactionId": "iNPAY-abc123def4",
"reference": "unique-ref-123",
"fromWallet": "merchant.wallet.1234567890",
"serviceID": "ikeja-electric",
"billType": "electricity",
"amount": 5000,
"phone": "08012345678",
"commission": {
"rate": "0.5%",
"amount": 25,
"merchantDebitAmount": 4975
},
"convenienceFee": 0,
"totalCharge": 5000,
"status": "completed",
"vtpassReference": "vtpass-ref-123",
"token": "1234-5678-9012-3456"
}
}

Response (Success - Education/WAEC):

{
"success": true,
"message": "Bill payment completed successfully",
"data": {
"transactionId": "iNPAY-abc123def4",
"reference": "unique-ref-123",
"fromWallet": "merchant.wallet.1234567890",
"serviceID": "waec-result",
"billType": "education",
"amount": 2000,
"phone": "08012345678",
"commission": {
"rate": "₦100 flat",
"amount": 10000,
"merchantDebitAmount": 1900
},
"convenienceFee": 0,
"totalCharge": 2000,
"status": "completed",
"vtpassReference": "vtpass-ref-123",
"cards": "1234567890"
}
}

Response (Failed):

{
"success": false,
"message": "Bill payment failed. Transaction has been recorded for manual review.",
"code": "BILL_PAYMENT_FAILED",
"transactionId": "iNPAY-abc123def4",
"reference": "unique-ref-123",
"amount": 10000,
"status": "failed",
"requiresManualReview": true,
"error": "INSUFFICIENT_BALANCE",
"vtpassCode": "INSUFFICIENT_BALANCE"
}

Note: Bill-type-specific fields are only included when relevant:

  • Utility payments: token (meter token)
  • Insurance: certUrl (certificate URL)
  • Education/WAEC Registration: tokens (registration tokens)
  • Education/WAEC Result Checker: cards (result checker cards)
  • Education/JAMB: pin (JAMB PIN)

Webhooks

Bill Purchase Completed Event

You'll receive a webhook notification when a bill payment is completed.

Airtime/Data Example:

{
"event": "bill.purchase.completed",
"timestamp": "2024-01-01T10:35:00.000Z",
"data": {
"transactionId": "iNPAY-abc123def4",
"reference": "unique-ref-123",
"fromWallet": "merchant.wallet.1234567890",
"serviceID": "mtn",
"billType": "airtime",
"amount": 10000,
"phone": "08012345678",
"commission": {
"rate": "1.5%",
"amount": 150,
"merchantDebitAmount": 9850
},
"convenienceFee": 0,
"totalCharge": 10000,
"status": "completed",
"vtpassReference": "vtpass-ref-123",
"completedAt": "2024-01-01T10:35:00.000Z"
}
}

Utility/Electricity Example (with token):

{
"event": "bill.purchase.completed",
"timestamp": "2024-01-01T10:35:00.000Z",
"data": {
"transactionId": "iNPAY-abc123def4",
"reference": "unique-ref-123",
"fromWallet": "merchant.wallet.1234567890",
"serviceID": "ikeja-electric",
"billType": "electricity",
"amount": 5000,
"phone": "08012345678",
"commission": {
"rate": "0.5%",
"amount": 25,
"merchantDebitAmount": 4975
},
"convenienceFee": 0,
"totalCharge": 5000,
"status": "completed",
"vtpassReference": "vtpass-ref-123",
"token": "1234-5678-9012-3456",
"completedAt": "2024-01-01T10:35:00.000Z"
}
}

Note: Bill-type-specific fields are only included when relevant:

  • Utility payments: token (meter token)
  • Insurance: certUrl (certificate URL)
  • Education/WAEC Registration: tokens (registration tokens)
  • Education/WAEC Result Checker: cards (result checker cards)
  • Education/JAMB: pin (JAMB PIN)

Error Handling

Common Error Codes

CodeDescriptionDetails
WALLET_PAYID_REQUIREDBill payments require wallet PayIDPayID must end with .wallet
AMOUNT_TOO_LOWMinimum bill payment amount not metBelow minimum (₦50 for airtime/data, ₦1,000 for others)
AMOUNT_TOO_HIGHAmount exceeds maximum limitAbove ₦100,000 limit
INSUFFICIENT_WALLET_BALANCEInsufficient wallet balanceWallet balance too low
BILL_PAYMENT_FAILEDBill payment failedVTpass payment failed (transaction recorded for review)
FETCH_CATEGORIES_FAILEDFailed to fetch bill categoriesVTpass API error
FETCH_SERVICES_FAILEDFailed to fetch servicesVTpass API error
FETCH_VARIATIONS_FAILEDFailed to fetch variationsVTpass API error
VERIFY_ACCOUNT_FAILEDFailed to verify accountSmartcard/meter/Profile ID verification failed

Error Response Format

{
"success": false,
"message": "Insufficient wallet balance",
"code": "INSUFFICIENT_WALLET_BALANCE"
}

Code Examples

Node.js with Axios

const axios = require('axios');

class InpayBillPaymentAPI {
constructor(secretKey, baseUrl = 'https://api.inpaycheckout.com') {
this.secretKey = secretKey;
this.baseUrl = baseUrl;
}

async getBillCategories() {
try {
const response = await axios.get(
`${this.baseUrl}/api/v1/developer/bills/categories`,
{
headers: {
'Authorization': `Bearer ${this.secretKey}`
}
}
);

return response.data;
} catch (error) {
console.error('Error getting bill categories:', error.response?.data || error.message);
throw error;
}
}

async getBillServices(categoryIdentifier) {
try {
const response = await axios.get(
`${this.baseUrl}/api/v1/developer/bills/services`,
{
params: {
categoryIdentifier
},
headers: {
'Authorization': `Bearer ${this.secretKey}`
}
}
);

return response.data;
} catch (error) {
console.error('Error getting bill services:', error.response?.data || error.message);
throw error;
}
}

async getBillVariations(serviceID) {
try {
const response = await axios.get(
`${this.baseUrl}/api/v1/developer/bills/variations`,
{
params: {
serviceID
},
headers: {
'Authorization': `Bearer ${this.secretKey}`
}
}
);

return response.data;
} catch (error) {
console.error('Error getting bill variations:', error.response?.data || error.message);
throw error;
}
}

async verifyBillAccount(serviceID, billersCode, phone, type = null) {
try {
const requestBody = {
serviceID,
billersCode,
phone
};

// Add type field if provided (required for utility and JAMB services)
if (type) {
requestBody.type = type;
}

const response = await axios.post(
`${this.baseUrl}/api/v1/developer/bills/verify`,
requestBody,
{
headers: {
'Authorization': `Bearer ${this.secretKey}`,
'Content-Type': 'application/json'
}
}
);

return response.data;
} catch (error) {
console.error('Error verifying bill account:', error.response?.data || error.message);
throw error;
}
}

async purchaseBillPayment(fromWallet, serviceID, options = {}) {
try {
const response = await axios.post(
`${this.baseUrl}/api/v1/developer/bills/purchase`,
{
fromWallet,
serviceID,
amount: options.amount,
variation_code: options.variation_code,
billersCode: options.billersCode,
phone: options.phone,
subscription_type: options.subscription_type,
quantity: options.quantity,
narration: options.narration,
reference: options.reference
},
{
headers: {
'Authorization': `Bearer ${this.secretKey}`,
'Content-Type': 'application/json'
}
}
);

return response.data;
} catch (error) {
console.error('Error purchasing bill payment:', error.response?.data || error.message);
throw error;
}
}
}

// Usage
const api = new InpayBillPaymentAPI('sk_live_your_secret_key_here');

// Get bill categories
const categories = await api.getBillCategories();

// Get services for a category
const services = await api.getBillServices('airtime');

// Get variations for a service
const variations = await api.getBillVariations('mtn-data');

// Verify bill account examples:

// TV Subscription (type is optional, can be omitted)
const tvVerification = await api.verifyBillAccount('dstv', '1234567890', '08012345678');

// Utility/Electricity - Prepaid (type is required)
const prepaidVerification = await api.verifyBillAccount('ikeja-electric', '12345678901', '08012345678', 'prepaid');

// Utility/Electricity - Postpaid (type is required)
const postpaidVerification = await api.verifyBillAccount('ikeja-electric', '12345678901', '08012345678', 'postpaid');

// JAMB (type is required - use variation code from getVariationCodes)
// First, get JAMB variations to find the correct type
const jambVariations = await api.getBillVariations('jamb');
// Then use the variation code as the type
const jambVerification = await api.verifyBillAccount('jamb', '12345678901', '08012345678', 'jamb-pin-2024');

// Purchase airtime (10000 kobo = ₦100.00)
const airtimePurchase = await api.purchaseBillPayment(
'merchant.wallet.1234567890',
'mtn',
{
amount: 10000,
phone: '08012345678',
narration: 'MTN Airtime purchase',
reference: 'unique-ref-123'
}
);

// Purchase data bundle
const dataPurchase = await api.purchaseBillPayment(
'merchant.wallet.1234567890',
'mtn-data',
{
variation_code: 'mtn-500mb-100',
phone: '08012345678',
narration: 'MTN Data purchase',
reference: 'unique-ref-124'
}
);

// Purchase TV subscription renewal
const tvRenewal = await api.purchaseBillPayment(
'merchant.wallet.1234567890',
'dstv',
{
billersCode: '1234567890',
phone: '08012345678',
subscription_type: 'renewal',
amount: 2500,
quantity: 1,
narration: 'DSTV renewal',
reference: 'unique-ref-125'
}
);

Python with Requests

import requests

class InpayBillPaymentAPI:
def __init__(self, secret_key, base_url='https://api.inpaycheckout.com'):
self.secret_key = secret_key
self.base_url = base_url
self.headers = {
'Authorization': f'Bearer {secret_key}',
'Content-Type': 'application/json'
}

def get_bill_categories(self):
"""Get all bill categories"""
url = f"{self.base_url}/api/v1/developer/bills/categories"
response = requests.get(url, headers=self.headers)
response.raise_for_status()
return response.json()

def get_bill_services(self, category_identifier):
"""Get services for a category"""
url = f"{self.base_url}/api/v1/developer/bills/services"
params = {'categoryIdentifier': category_identifier}
response = requests.get(url, headers=self.headers, params=params)
response.raise_for_status()
return response.json()

def get_bill_variations(self, service_id):
"""Get variations for a service"""
url = f"{self.base_url}/api/v1/developer/bills/variations"
params = {'serviceID': service_id}
response = requests.get(url, headers=self.headers, params=params)
response.raise_for_status()
return response.json()

def verify_bill_account(self, service_id, billers_code, phone, type=None):
"""Verify bill account"""
url = f"{self.base_url}/api/v1/developer/bills/verify"
data = {
'serviceID': service_id,
'billersCode': billers_code,
'phone': phone
}

# Add type field if provided (required for utility and JAMB services)
if type:
data['type'] = type

response = requests.post(url, headers=self.headers, json=data)
response.raise_for_status()
return response.json()

def purchase_bill_payment(self, from_wallet, service_id, **options):
"""Purchase bill payment"""
url = f"{self.base_url}/api/v1/developer/bills/purchase"
data = {
'fromWallet': from_wallet,
'serviceID': service_id,
'amount': options.get('amount'),
'variation_code': options.get('variation_code'),
'billersCode': options.get('billersCode'),
'phone': options.get('phone'),
'subscription_type': options.get('subscription_type'),
'quantity': options.get('quantity'),
'narration': options.get('narration'),
'reference': options.get('reference')
}
response = requests.post(url, headers=self.headers, json=data)
response.raise_for_status()
return response.json()

# Usage
api = InpayBillPaymentAPI('sk_live_your_secret_key_here')

# Get bill categories
categories = api.get_bill_categories()

# Get services for a category
services = api.get_bill_services('airtime')

# Get variations for a service
variations = api.get_bill_variations('mtn-data')

# Verify bill account examples:

# TV Subscription (type is optional, can be omitted)
tv_verification = api.verify_bill_account('dstv', '1234567890', '08012345678')

# Utility/Electricity - Prepaid (type is required)
prepaid_verification = api.verify_bill_account('ikeja-electric', '12345678901', '08012345678', 'prepaid')

# Utility/Electricity - Postpaid (type is required)
postpaid_verification = api.verify_bill_account('ikeja-electric', '12345678901', '08012345678', 'postpaid')

# JAMB (type is required - use variation code from getVariationCodes)
# First, get JAMB variations to find the correct type
jamb_variations = api.get_bill_variations('jamb')
# Then use the variation code as the type
jamb_verification = api.verify_bill_account('jamb', '12345678901', '08012345678', 'jamb-pin-2024')

# Purchase airtime (10000 kobo = ₦100.00)
airtime_purchase = api.purchase_bill_payment(
'merchant.wallet.1234567890',
'mtn',
amount=10000,
phone='08012345678',
narration='MTN Airtime purchase',
reference='unique-ref-123'
)

PHP with cURL

<?php
class InpayBillPaymentAPI {
private $secretKey;
private $baseUrl;

public function __construct($secretKey, $baseUrl = 'https://api.inpaycheckout.com') {
$this->secretKey = $secretKey;
$this->baseUrl = $baseUrl;
}

private function makeRequest($method, $endpoint, $data = null) {
$url = $this->baseUrl . $endpoint;
$headers = [
'Authorization: Bearer ' . $this->secretKey,
'Content-Type: application/json'
];

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);

if ($data && in_array($method, ['POST', 'PUT', 'PATCH'])) {
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
}

$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($httpCode >= 400) {
throw new Exception('API Error: ' . $response);
}

return json_decode($response, true);
}

public function getBillCategories() {
return $this->makeRequest('GET', '/api/v1/developer/bills/categories');
}

public function getBillServices($categoryIdentifier) {
$params = http_build_query([
'categoryIdentifier' => $categoryIdentifier
]);

return $this->makeRequest('GET', '/api/v1/developer/bills/services?' . $params);
}

public function getBillVariations($serviceID) {
$params = http_build_query([
'serviceID' => $serviceID
]);

return $this->makeRequest('GET', '/api/v1/developer/bills/variations?' . $params);
}

public function verifyBillAccount($serviceID, $billersCode, $phone, $type = null) {
$data = [
'serviceID' => $serviceID,
'billersCode' => $billersCode,
'phone' => $phone
];

// Add type field if provided (required for utility and JAMB services)
if ($type !== null) {
$data['type'] = $type;
}

return $this->makeRequest('POST', '/api/v1/developer/bills/verify', $data);
}

public function purchaseBillPayment($fromWallet, $serviceID, $options = []) {
$data = [
'fromWallet' => $fromWallet,
'serviceID' => $serviceID,
'amount' => $options['amount'] ?? null,
'variation_code' => $options['variation_code'] ?? null,
'billersCode' => $options['billersCode'] ?? null,
'phone' => $options['phone'] ?? null,
'subscription_type' => $options['subscription_type'] ?? null,
'quantity' => $options['quantity'] ?? null,
'narration' => $options['narration'] ?? null,
'reference' => $options['reference'] ?? null
];

return $this->makeRequest('POST', '/api/v1/developer/bills/purchase', $data);
}
}

// Usage
$api = new InpayBillPaymentAPI('sk_live_your_secret_key_here');

// Get bill categories
$categories = $api->getBillCategories();

// Get services for a category
$services = $api->getBillServices('airtime');

// Get variations for a service
$variations = $api->getBillVariations('mtn-data');

// Verify bill account examples:

// TV Subscription (type is optional, can be omitted)
$tvVerification = $api->verifyBillAccount('dstv', '1234567890', '08012345678');

// Utility/Electricity - Prepaid (type is required)
$prepaidVerification = $api->verifyBillAccount('ikeja-electric', '12345678901', '08012345678', 'prepaid');

// Utility/Electricity - Postpaid (type is required)
$postpaidVerification = $api->verifyBillAccount('ikeja-electric', '12345678901', '08012345678', 'postpaid');

// JAMB (type is required - use variation code from getVariationCodes)
// First, get JAMB variations to find the correct type
$jambVariations = $api->getBillVariations('jamb');
// Then use the variation code as the type
$jambVerification = $api->verifyBillAccount('jamb', '12345678901', '08012345678', 'jamb-pin-2024');

// Purchase airtime (10000 kobo = ₦100.00)
$airtimePurchase = $api->purchaseBillPayment(
'merchant.wallet.1234567890',
'mtn',
[
'amount' => 10000,
'phone' => '08012345678',
'narration' => 'MTN Airtime purchase',
'reference' => 'unique-ref-123'
]
);
?>

Best Practices

  1. Always verify accounts before purchasing TV subscriptions, utility bills, or JAMB services
  2. Include the type field when verifying utility/electricity accounts ("prepaid" or "postpaid") and JAMB services (use variation code from getVariationCodes)
  3. Check wallet balance before initiating bill payments
  4. Handle errors gracefully - failed transactions are recorded for manual review
  5. Use unique references for each transaction to track payments
  6. Store transaction IDs for reconciliation and support purposes
  7. Monitor webhooks to track bill payment status in real-time
  8. Validate amounts - ensure amounts meet minimum requirements (₦50 for airtime/data, ₦1,000 for others)
  9. Respect rate limits - API has rate limits to ensure service stability