Bills Payment

Table of Contents

  1. Overview
  2. Getting Started
  3. Authentication
  4. Integration Flow
  5. Step-by-Step Implementation
  6. Field Reference
  7. Amount Types
  8. Response Codes
  9. Test Data
  10. Examples
  11. Best Practices
  12. API Summary

Overview

The QuickTeller Bills Payment API enables merchants to integrate bill payment functionality into their applications. This service allows you to process payments for various services including:

  • Cable TV subscriptions (DSTV, GOtv, Startimes)
  • Utility bills (Electricity, Water)
  • Insurance payments
  • Tax payments
  • Airline tickets
  • Donations
  • Internet services
  • And many more...

How It Works

When a user pays for a service on your platform:

  1. You identify the category, biller, and payment item to obtain a PaymentCode.
  2. You validate the customer's account identifier (when required by the biller).
  3. You submit the payment details to the Bills Payment API.
  4. Interswitch charges your virtual card for the equivalent amount.
  5. Value is delivered to the customer's service account.
  6. You earn commission on each successful transaction.

Getting Started

Prerequisites

Before integrating, ensure you have:

  • Terminal ID for authentication
  • Access credentials (Client ID and Secret)

Terminal ID and Channel Codes

Every TerminalId is composed of the following parts, with no separators:

{ChannelId}{TerminalOwnerCode}{RandomDigits}
  • ChannelId identifies the channel through which the transaction originates.
  • TerminalOwnerCode is the terminal owner's assigned code.
  • RandomDigits is an optional numeric suffix. When this part is used, 0001 is the preferred value.

For example, 5IIP0001 consists of channel ID 5 (Bank API), terminal owner code IIP, and suffix 0001.

Channel IDChannel
1ATM
2POS
3Web
4MOB
5Bank API
6PCPOS
7Location

Base URLs

EnvironmentBase URL
QA/Testinghttps://qa.interswitchng.com/quicktellerservice/api/v5
Productionhttps://orion.interswitchng.com/quicktellerservice/api/v5

Amount Convention

All monetary amounts are in the minor unit of the currency (kobo for NGN).

  • 10000 = ₦100.00
  • 500000 = ₦5,000.00
  • Multiply Naira by 100 to get kobo.

Architecture Flow


Authentication

All API requests require a Bearer token. Generate your access token using the OAuth 2.0 client credentials flow.

Token Generation

Endpoint: POST https://passport-sandbox.interswitchng.com/passport/oauth/token

Request:

curl --location 'https://passport-sandbox.interswitchng.com/passport/oauth/token' \
--header 'Authorization: Basic <BASE64_ENCODED_CREDENTIALS>' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'scope=profile' \
--data-urlencode 'grant_type=client_credentials'

Headers:

HeaderValueDescription
AuthorizationBasic <credentials>Base64 encoded client_id:client_secret
Content-Typeapplication/x-www-form-urlencodedRequest format

Response:

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
    "token_type": "bearer",
    "expires_in": 43199,
    "scope": "profile",
    "merchant_code": "MX52988",
    "merchant-wallet-actions": ["settle", "transact", "reverse"],
    "terminalId": "3PBL0001",
    "env": "TEST",
    "client_name": "QTSVA UAT",
    "email": "[email protected]"
}

Important: Tokens expire after approximately 12 hours (expires_in value in seconds). Implement token refresh logic in your application.


Integration Flow

The complete bill payment flow involves these steps:

Step 1: Get Categories
    │
    ▼
Step 2: Fetch Billers by Category
    │
    ▼
Step 3: Fetch Payment Items (required — source of PaymentCode)
    │
    ▼
Step 4: Validate Customer (when required by the biller)
    │
    ▼
Step 5: Make Payment
    │
    ▼
Step 6: Query Transaction Status (recommended)

Step-by-Step Implementation

Step 1: Get Categories

Purpose: Retrieve the complete list of service categories to understand what types of payments are available and identify which category contains your target service.

Description: This initial step provides a high-level overview of all supported payment categories (e.g., Utility Bills, Cable TV, Insurance, Donations). Each category includes a unique ID, name, and description to help you organize and present payment options in your application.

Endpoint: GET /services/categories

Request:

curl 'https://qa.interswitchng.com/quicktellerservice/api/v5/services/categories' \
-H 'Authorization: Bearer <TOKEN>' \
-H 'terminalId: <YOUR_TERMINAL_ID>' \
-H 'Content-Type: application/json' \
-X GET

Response:

{
    "BillerCategories": [
        {
            "Id": 1,
            "Name": "Utility Bills",
            "Description": "Pay your utility bills here",
            "Billers": []
        },
        {
            "Id": 2,
            "Name": "Cable TV Bills",
            "Description": "Pay for your cable TV subscriptions here",
            "Billers": []
        },
        {
            "Id": 3,
            "Name": "State Payments",
            "Description": "Pay state taxes",
            "Billers": []
        },
        {
            "Id": 7,
            "Name": "Donations",
            "Description": "Donate to a worthy cause",
            "Billers": []
        },
        {
            "Id": 8,
            "Name": "Phone Bills",
            "Description": "Pay all post paid phone bills",
            "Billers": []
        },
        {
            "Id": 9,
            "Name": "Subscriptions",
            "Description": "Pay for your other subscriptions (like ISP) here",
            "Billers": []
        },
        {
            "Id": 12,
            "Name": "Tax Payments",
            "Description": "Tax Payments",
            "Billers": []
        },
        {
            "Id": 13,
            "Name": "Insurance/Smart",
            "Description": "Insurance Payments",
            "Billers": []
        },
        {
            "Id": 15,
            "Name": "Airlines",
            "Description": "Airlines",
            "Billers": []
        },
        {
            "Id": 19,
            "Name": "Microfinance",
            "Description": "Microfinance",
            "Billers": []
        }
    ],
    "ResponseCode": "90000",
    "ResponseCodeGrouping": "SUCCESSFUL"
}

Key Points:

  • The Id field is used to fetch billers in Step 2.
  • The Billers array is empty in this endpoint; use it for display purposes only.
  • Categories may vary based on your terminal configuration.

Step 2: Fetch Billers by Category

Purpose: Retrieve all available billers (service providers) within a specific category along with their configuration details. Alternatively, you can get all billers available to the terminal.

Description: This step returns detailed information about each biller in the selected category, including customer input field labels, currency information, and amount handling rules. Use this data to dynamically build your payment forms.

Endpoints:

  • GET /services?categoryId={categoryId}
  • GET /services

Parameters:

ParameterTypeRequiredDescription
categoryIdIntegerYesThe category ID from Step 1

Request:

Get billers by category:

curl 'https://qa.interswitchng.com/quicktellerservice/api/v5/services?categoryId=1' \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-H "TerminalID: <YOUR_TERMINAL_ID>" \
-X GET

Get all billers:

curl 'https://qa.interswitchng.com/quicktellerservice/api/v5/services' \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-H "TerminalID: <YOUR_TERMINAL_ID>" \
-X GET

Response:

{
    "BillerList": {
        "Count": 49,
        "Category": [
            {
                "Id": 1,
                "Name": "Utility Bills",
                "Description": "Pay your utility bills here",
                "Billers": [
                    {
                        "Type": "PHV",
                        "Id": 17589,
                        "PayDirectProductId": 4588,
                        "PayDirectInstitutionId": 0,
                        "Name": "Abuja Disco BuyPower",
                        "ShortName": "AbjBuyPwr",
                        "Narration": "Abuja Disco Buy Power Prepaid",
                        "CustomerField1": "CustomerRef",
                        "CustomerField2": "CustomerRef",
                        "LogoUrl": "q.gif",
                        "Surcharge": "0",
                        "CurrencyCode": "566",
                        "CurrencySymbol": "NGN",
                        "QuickTellerSiteUrlName": "abjbuypwr",
                        "NetworkId": "buypower-uat",
                        "ProductCode": "051760201",
                        "PageFlowInfo": {
                            "Elements": [],
                            "FinishButtonName": "Finish",
                            "StartPage": "DoPayment.aspx",
                            "UsesPaymentItems": true,
                            "PerformInquiry": true,
                            "AllowRetry": true
                        },
                        "CategoryId": 1,
                        "CategoryName": "Utility Bills",
                        "AmountType": 2
                    },
                    {
                        "Id": 16921,
                        "Name": "ABUJA ELECTRIC PREPAID",
                        "ShortName": "AEPD",
                        "Narration": "ABUJA ELECTRIC PREPAID",
                        "CustomerField1": "ACCOUNT NO",
                        "LogoUrl": "q.gif",
                        "Surcharge": "0",
                        "CurrencyCode": "566",
                        "CurrencySymbol": "NGN",
                        "QuickTellerSiteUrlName": "aedc",
                        "NetworkId": "6280510421",
                        "ProductCode": "6280515222",
                        "CategoryId": 1,
                        "CategoryName": "Utility Bills",
                        "AmountType": 0
                    }
                ]
            }
        ]
    },
    "ResponseCode": "90000",
    "ResponseCodeGrouping": "SUCCESSFUL"
}

Implementation Notes:

  1. Extract Biller ID: The Id field is critical for subsequent API calls.
  2. Customer Fields: Use CustomerField1 (and CustomerField2 if present) to label your input forms.
  3. Amount Handling: Check AmountType to determine how to handle payment amounts (see Amount Types).
  4. Logo Display: Use MediumImageId, SmallImageId, or LargeImageId for biller logos. LogoUrl is legacy and should not be relied on.
  5. Currency: CurrencyCode 566 = NGN, 840 = USD, 404 = KES.
  6. Payment Codes: ProductCode, NetworkId, and PayDirectProductId are internal fields. Do not use them as PaymentCode. The correct PaymentCode is obtained from payment items in Step 3.

Step 3: Fetch Payment Items

Purpose: Retrieve the payment items (packages or products) offered by a biller. This step is required because every bill payment must use a valid PaymentCode, and payment codes are obtained here.

Description: Payment items represent the actual products or services a customer can pay for. Each item exposes a unique PaymentCode that must be used in the validation and payment requests. Without a payment item's PaymentCode, a bill payment cannot be processed.

Endpoint: GET /services/options?serviceid={billerId}

Parameters:

ParameterTypeRequiredDescription
serviceidIntegerYesThe biller ID from Step 2

Request:

curl 'https://qa.interswitchng.com/quicktellerservice/api/v5/services/options?serviceid=17589' \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-H "TerminalID: <YOUR_TERMINAL_ID>" \
-X GET

Response:

{
    "PaymentItems": [
        {
            "Id": "01",
            "Name": "Abuja Disco Buypower Prepaid",
            "BillerName": "Abuja Disco Buy Power Prepaid",
            "ConsumerIdField": "CustomerRef",
            "Code": "01",
            "BillerType": "PHV",
            "ItemFee": "0",
            "Amount": "0",
            "BillerId": "17589",
            "BillerCategoryId": "1",
            "CurrencyCode": "566",
            "CurrencySymbol": "NGN",
            "ItemCurrencyCode": "566",
            "ItemCurrencySymbol": "NGN",
            "Children": [],
            "IsAmountFixed": true,
            "SortOrder": 0,
            "PictureId": 0,
            "PaymentCode": "051758901",
            "AmountType": 2,
            "PaydirectItemCode": "051760201"
        }
    ],
    "ResponseCode": "90000",
    "ResponseCodeGrouping": "SUCCESSFUL"
}

Key Fields:

FieldDescription
PaymentCodeRequired for customer validation and payment. This is the value you send in Step 4 and Step 5.
AmountThe fixed or suggested amount for this item
IsAmountFixedIf true, the amount cannot be changed
ItemFeeAdditional fee for this specific item

Step 4: Validate Customer

Purpose: Verify that the customer ID (e.g., meter number, smart card number, policy number) is valid and retrieve the customer's details including the exact amount due.

Description: This step is crucial for billers that require it. It confirms the customer exists with the biller and returns important information such as the customer's name and the exact amount to be paid (especially for postpaid bills). Validation should always be performed when the biller's configuration requires it.

Endpoint: POST /Transactions/validatecustomers

Request:

curl 'https://qa.interswitchng.com/quicktellerservice/api/v5/Transactions/validatecustomers' \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-H "TerminalID: <YOUR_TERMINAL_ID>" \
-d '{
    "customers": [
        {
            "PaymentCode": "051758901",
            "CustomerId": "12345678910",
            "WithDetails": true
        }
    ],
    "TerminalId": "3PBL0001"
}' \
-X POST

Request Body Fields:

FieldTypeRequiredDescription
customersArrayYesArray of customer objects to validate
customers[].PaymentCodeStringYesPayment code from Step 3
customers[].CustomerIdStringYesThe customer's ID (from user input)
customers[].AmountStringConditionalAmount in minor currency units. Required when the biller applies a customer-borne fee
customers[].WithDetailsboolNoAdd this if you want more customer details
TerminalIdStringYesYour terminal ID

Response:

{
    "Customers": [
        {
            "TerminalId": "3PBL0001",
            "BillerId": 0,
            "PaymentCode": "051758901",
            "CustomerId": "12345678910",
            "ResponseCode": "90000",
            "FullName": "Chukwuma Ciroma",
            "Amount": 35000,
            "AmountType": 2,
            "AmountTypeDescription": "Biller requires minimum amount: NGN 350.00.",
            "Surcharge": 0
        }
    ],
    "ResponseCode": "90000",
    "ResponseCodeGrouping": "SUCCESSFUL"
}

With details:

{
    "Customers": [
        {
            "TerminalId": "3PBL0001",
            "BillerId": 0,
            "PaymentCode": "051758901",
            "CustomerId": "12345678910",
            "WithDetails": "true",
            "ResponseCode": "90000",
            "FullName": "Chukwuma Ciroma",
            "Address": "012 Fake Cresent, Fake City, Fake State",
            "DateOfBirth": "08-06-2026",
            "Lastname": "Chukwuma",
            "Othernames": "Ciroma",
            "Title": "",
            "SecConsumerId": "",
            "PriConsumerId": "12345678910",
            "Amount": 35000,
            "AmountType": 2,
            "AmountTypeDescription": "Biller requires minimum amount: NGN 350.00.",
            "Surcharge": 0,
            "AdditionalInfoParameters": {
                "MinAmount": "0",
                "AmountDue": "0",
                "ConvenientFee": "0.00",
                "CustomerFee": "0.00",
                "Message": "Surcharge is total sum of Convenient Fee + Customer Borne Fee"
            },
            "AdditionalInfo": "Tariff:0;Outstanding:0.0;DebtRepayment:0.0;MinVendAmount:350.0;MaxVendAmount:1.0E7"
        }
    ],
    "ResponseCode": "90000",
    "ResponseCodeGrouping": "SUCCESSFUL"
}

Response Fields:

FieldDescription
ResponseCode90000 = Valid customer, others = Invalid
FullNameCustomer's registered name (if available)
AmountExact amount due (in kobo) for postpaid bills
AmountTypeConfirms how to handle the amount
SurchargeAdditional fee to be added

Important:

  • If ResponseCode is not 90000, do not proceed with payment.
  • Use the returned Amount for postpaid bills (AmountType 3).
  • Always display the FullName to the user for confirmation.

Billers with Customer-Borne Fees

For billers that apply a customer-borne fee, include Amount in the validation request. The API uses the amount to calculate and return the applicable fees.

Request:

{
    "customers": [
        {
            "PaymentCode": "053434101",
            "CustomerId": "08168284169",
            "Amount": "10000",
            "WithDetails": true
        }
    ],
    "TerminalId": "5IIP0001"
}

Response:

{
    "Customers": [
        {
            "TerminalId": "5IIP0001",
            "BillerId": 0,
            "PaymentCode": "053434101",
            "CustomerId": "08168284169",
            "WithDetails": "true",
            "ResponseCode": "90000",
            "FullName": "Azeezat Animashaun Adebola",
            "Address": "",
            "DateOfBirth": "04-09-2026",
            "Email": "",
            "Lastname": "Azeezat",
            "Othernames": "Animashaun Adebola",
            "Title": "",
            "PriConsumerId": "08168284169",
            "Amount": 0,
            "AmountType": 0,
            "AmountTypeDescription": "AnyAmount",
            "Surcharge": 20000,
            "AdditionalInfoParameters": {
                "MinAmount": "0",
                "AmountDue": "0",
                "ConvenientFee": "100.00",
                "CustomerFee": "100.00",
                "Message": "Surcharge is total sum of Convenient Fee + Customer Borne Fee"
            },
            "AdditionalInfo": "Fee:100.00"
        }
    ],
    "ResponseCode": "90000",
    "ResponseCodeGrouping": "SUCCESSFUL"
}

In this example, AdditionalInfo contains Fee:100.00, which represents a customer-borne fee of 100.00 in the transaction currency. The integrating terminal is expected to collect this fee from the end customer because it will be debited from the terminal's financial instrument or otherwise accounted for during transaction processing.

AdditionalInfoParameters.CustomerFee provides the customer-borne fee in a structured field, while Surcharge represents the total of the convenience fee and customer-borne fee.


Step 5: Make Payment

Purpose: Process the actual payment transaction after customer validation (when required).

Description: This is the final step where you submit the payment details to complete the transaction. Upon success, the customer's account is credited with the service (e.g., electricity token, DSTV subscription), and your virtual card is debited for the transaction amount.

Endpoint: POST /Transactions

Request:

curl 'https://qa.interswitchng.com/quicktellerservice/api/v5/transactions' \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-H "TerminalID: <YOUR_TERMINAL_ID>" \
-d '{
    "paymentCode": "051758901",
    "customerId": "12345678910",
    "customerMobile": "2348124888436",
    "customerEmail": "[email protected]",
    "amount": "500000",
    "requestReference": "122200898163"
}' \
-X POST

Request Body Fields:

FieldTypeRequiredDescription
paymentCodeStringYesPayment code from Step 3
customerIdStringYesValidated customer ID
customerMobileStringYesCustomer's mobile number
customerEmailStringNoCustomer's email address
customerNameStringNoCustomer's name
amountStringYesAmount in kobo (e.g., "500000" for ₦5,000)
additionalInfoStringNoSemicolon-delimited additional request information in Key:Value format
requestReferenceStringYesUnique reference for this transaction (max 20 chars)

Passing Additional Information

Use AdditionalInfo to send optional transaction metadata as semicolon-delimited Key:Value pairs. For example, a bank teller channel can pass teller and branch details:

{
    "CustomerId": "12345678910",
    "PaymentCode": "52005",
    "Amount": "250000",
    "CustomerEmail": "[email protected]",
    "CustomerMobile": "07032040511",
    "CustomerName": "John Doe",
    "AdditionalInfo": "Teller Name:James Etim;Branch Name:VI;Branch Code:044;Teller ID:0494243",
    "RequestReference": "{{RequestReference2}}"
}

Separate entries with semicolons (;) and separate each key from its value with a colon (:). The keys and values should contain the information required for your integration or reporting flow.

Response — Pin-based bill payment:

{
    "TransactionRef": "PBL|Web|3PBL0001|AbjBuyPw|080626111212|7WJYMPTTJD",
    "RechargePIN": "03659524516800036346",
    "PhcnTokenDetails": "Pin:03659524516800036346;Receipt Number:284845;Units:55.9;Invoice Number:105709159;Meter Number:1001279455;Tax:N279.07;Total:N4000.0;Token:03659524516800036346;Arrears Balance:N0.0;Address:012 Fake Cresent, Fake City, Fake State;Tariff:0;Outstanding:0.0;DebtRepayment:0.0;MinVendAmount:350.0;MaxVendAmount:1.0E7",
    "ApprovedAmount": "3154364",
    "MiscData": "Pin:03659524516800036346;Receipt Number:284845;Units:55.9;Invoice Number:105709159;Meter Number:1001279455;Tax:N279.07;Total:N4000.0;Token:03659524516800036346;Arrears Balance:N0.0;Address:012 Fake Cresent, Fake City, Fake State;Tariff:0;Outstanding:0.0;DebtRepayment:0.0;MinVendAmount:350.0;MaxVendAmount:1.0E7",
    "AdditionalInfo": {
        "Pin": "03659524516800036346",
        "Receipt Number": "284845",
        "Units": "55.9",
        "Invoice Number": "105709159",
        "Meter Number": "1001279455",
        "Tax": "N279.07",
        "Total": "N4000.0",
        "Token": "03659524516800036346",
        "Arrears Balance": "N0.0",
        "Address": "012 Fake Cresent, Fake City, Fake State",
        "Tariff": "0",
        "Outstanding": "0.0",
        "DebtRepayment": "0.0",
        "MinVendAmount": "350.0",
        "MaxVendAmount": "1.0E7"
    },
    "ResponseCode": "90000",
    "ResponseDescription": "Success",
    "ResponseCodeGrouping": "SUCCESSFUL"
}

Response — Non-pin-based bill payment:

{
    "TransactionRef": "PBL|Web|3PBL0001|CGM|080626111324|M4UWNM9HJN",
    "ApprovedAmount": "3154364",
    "AdditionalInfo": {},
    "ResponseCode": "90000",
    "ResponseDescription": "Success",
    "ResponseCodeGrouping": "SUCCESSFUL"
}

Response Fields:

FieldDescription
TransactionRefUnique transaction reference (store this!)
ApprovedAmountAmount that was processed
AdditionalInfoBill-specific info (tokens, units, etc.)
RechargePINToken or PIN delivered to the customer
ResponseCode90000 = Success
ResponseDescriptionHuman-readable status

Transaction Flow:

  1. Your app submits payment request.
  2. API validates the request.
  3. Your virtual card is debited.
  4. Biller receives the payment.
  5. Value is delivered to customer.
  6. You receive commission.

Step 6: Query Transaction Status

Purpose: Check the status of a previously submitted transaction.

Description: Use this endpoint to verify transaction status, especially useful for handling timeouts or when you need to confirm a transaction's final state. This is recommended for production implementations.

Endpoint: GET /Transactions?requestRef={requestReference}

Parameters:

ParameterTypeRequiredDescription
requestRefStringYesThe request reference from Step 5

Request:

curl 'https://qa.interswitchng.com/quicktellerservice/api/v5/Transactions?requestRef=122200898163' \
-H "Authorization: Bearer <TOKEN>" \
-H "Content-Type: application/json" \
-H "TerminalID: <YOUR_TERMINAL_ID>" \
-X GET

Response:

{
    "BillPayment": {
        "biller": "MCN",
        "customerId1": "000000001",
        "customerId2": null,
        "paymentTypeName": "Family",
        "paymentTypeCode": "COFAMW4",
        "billerId": "104"
    },
    "amount": "2000",
    "currencyCode": "566",
    "customer": "000000001",
    "customerEmail": "[email protected]",
    "customerMobile": "08065186175",
    "paymentDate": "7/18/2016 8:53:39 AM",
    "requestReference": "119420151169",
    "serviceCode": "COFAMW4",
    "serviceName": "Family",
    "serviceProviderId": "104",
    "status": "Completed",
    "surcharge": "100",
    "transactionRef": "FTH|Web|3FTH0001|MCN|180716085339|00000002",
    "transactionResponseCode": "90000",
    "transactionSet": "BillPayment"
}

Status Values:

StatusDescription
CompletedTransaction successful
PendingTransaction is processing
FailedTransaction failed
ReversedTransaction was reversed

Field Reference

Category Fields

FieldTypeDescription
IdIntegerUnique identifier for the category
NameStringCategory name
DescriptionStringBrief description of the category
BillersArrayList of billers (empty in categories endpoint)

Biller Fields

Core Fields (Required)

FieldTypeDescription
IdIntegerUnique biller identifier
NameStringFull biller name
ShortNameStringAbbreviated biller code
NarrationStringService description
CustomerField1StringLabel for primary customer input field
CustomerField2StringLabel for secondary input (if required)
CurrencyCodeStringISO currency code (566=NGN, 840=USD, 404=KES)
CurrencySymbolStringCurrency symbol (NGN, USD, KES)
AmountTypeIntegerHow amount is determined (0-5)

Media Fields

FieldDescription
SmallImageIdUUID for small logo (thumbnails)
MediumImageIdUUID for medium logo (standard display)
LargeImageIdUUID for large logo (detail views)

Internal Fields (Ignore)

These fields are for internal use and should not be used in your implementation:

  • Type, PayDirectProductId, PayDirectInstitutionId
  • QuickTellerSiteUrlName, NetworkId, ProductCode
  • PageFlowInfo, RiskCategoryId, Surcharge
  • CustomSectionUrl, CustomMessageUrl, LogoUrl

Important: ProductCode is not a PaymentCode. The PaymentCode used in validation and payment requests must be obtained from the payment items endpoint in Step 3.

Payment Item Fields

FieldTypeDescription
IdStringItem identifier
NameStringItem name
BillerNameStringAssociated biller name
ConsumerIdFieldStringLabel for the customer ID field
PaymentCodeStringRequired value to use in validation and payment requests
AmountStringFixed or suggested amount in kobo
IsAmountFixedBooleanIf true, the amount cannot be changed
ItemFeeStringAdditional fee for this specific item
AmountTypeIntegerHow amount is determined for this item

Validation Request Fields

FieldTypeRequiredDescription
customersArrayYesArray of customer objects to validate
customers[].PaymentCodeStringYesPayment code from Step 3
customers[].CustomerIdStringYesThe customer's ID (from user input)
customers[].AmountStringConditionalAmount in minor currency units. Required when the biller applies a customer-borne fee
customers[].WithDetailsBooleanNoRequest additional customer details
TerminalIdStringYesYour terminal ID

Payment Request Fields

FieldTypeRequiredDescription
paymentCodeStringYesPayment code from Step 3
customerIdStringYesCustomer ID
customerMobileStringYesCustomer's mobile number
customerEmailStringNoCustomer's email address
customerNameStringNoCustomer's name
amountStringYesAmount in kobo (e.g., "500000" for ₦5,000)
additionalInfoStringNoSemicolon-delimited additional request information in Key:Value format
requestReferenceStringYesUnique reference for this transaction (max 20 chars)

Query Response Fields

FieldDescription
BillPaymentBiller and payment type details
amountTransaction amount
currencyCodeTransaction currency code
customerCustomer ID
customerEmailCustomer email
customerMobileCustomer mobile number
paymentDateTransaction date
requestReferenceRequest reference
serviceCodePayment code
serviceNameService name
serviceProviderIdBiller ID
statusTransaction status
surchargeSurcharge applied
transactionRefUnique transaction reference
transactionResponseCodeResponse code
transactionSetTransaction category

Amount Types

The AmountType field determines how to handle payment amounts:

ValueNameDescription
0Any Amount (None)No specific amount type is defined or required
1Minimum AmountRepresents the minimum allowable amount. The value must be greater than or equal to this amount.
2Greater Than Minimum AmountRepresents an amount that must be strictly greater than the minimum. The value must exceed this amount.
3Maximum AmountRepresents the maximum allowable amount. The value must be less than or equal to this amount.
4Less Than Maximum AmountRepresents an amount that must be strictly less than the maximum. The value must be below this amount.
5Exact AmountRepresents an exact amount that must be matched precisely.

Response Codes

For a complete list of response codes, their meanings, and resolutions, see the Response Codes documentation.

Common response codes for bill payments include:

CodeDescription
90000Transaction/Request successful
90001Invalid customer ID
90002Customer not found
90003Invalid amount
90004Insufficient funds
90005Duplicate transaction
90006Service unavailable
90007Invalid payment code
90008Validation failed

Test Data

Use the following test credentials during development:

Test Credentials

FieldValue
Terminal ID3PBL0001
Client IDIKIAF9CC50BE5C702A3A1859FE8175439EC9910AB7CC
Client SecretTk52bmVRZGNNcXF1RzVMUkRiUk44VWh0Rg
Request Reference Prefix1453
Initiating Entity CodePBL

Test Virtual Card

FieldValue
Card Number6280511000000095
Expiry12/2026
CVV000
PIN0000

Test Billers

ServicePayment CodeCustomer IDCategory
DAARSAT Communications1131001890003338Cable TV Bills (2)
Abuja Disco Buypower Prepaid (PIN)05175890112345678910Utility Bills (1)
Church Of God Mission International520052348169901895Donations (7)

Test Scenarios

Scenario 1: Successful DSTV Payment

Payment Code: 48001
Customer ID: 0000000001
Amount: 500000 (₦5,000)
Expected: Success (90000)

Scenario 2: Invalid Customer

Payment Code: 48001
Customer ID: 9999999999
Expected: Customer not found error

Scenario 3: Electricity Token Purchase

Payment Code: 051758901
Customer ID: 12345678910
Amount: 100000 (₦1,000)
Expected: Success with token in RechargePIN field and AdditionalInfo

Examples

Example 1: Complete DSTV Subscription Flow

// Step 1: Get categories (fetch once and cache)
const categories = await fetchCategories();
const cableCategory = categories.find(c => c.Name === "Cable TV Bills");

// Step 2: Get DSTV biller
const billers = await fetchBillersByCategory(cableCategory.Id);
const dstv = billers.find(b => b.Name === "Multichoice Limited");

// Step 3: Get payment items (bouquet options) — REQUIRED for PaymentCode
const items = await fetchPaymentItems(dstv.Id);
const compactPlus = items.find(i => i.Name === "Compact Plus");

// Step 4: Validate customer
const validation = await validateCustomer({
    paymentCode: compactPlus.PaymentCode,
    customerId: "0000000001"
});

if (validation.ResponseCode !== "90000") {
    throw new Error("Invalid customer");
}

// Step 5: Make payment
const payment = await makePayment({
    paymentCode: compactPlus.PaymentCode,
    customerId: "0000000001",
    customerMobile: "2348012345678",
    customerEmail: "[email protected]",
    amount: compactPlus.Amount,
    requestReference: generateReference() // e.g., "14531678901234"
});

console.log("Transaction Ref:", payment.TransactionRef);
console.log("Status:", payment.ResponseDescription);

Example 2: Electricity Token Purchase

// Steps 1-3: Get AEDC biller and payment item (as shown above)

// Step 4: Validate meter number
const validation = await validateCustomer({
    paymentCode: "051758901",
    customerId: "12345678910"
});

// Step 5: Purchase token
const payment = await makePayment({
    paymentCode: "051758901",
    customerId: "12345678910",
    customerMobile: "2348123456789",
    amount: "100000", // ₦1,000
    requestReference: "14539876543210"
});

// Display token to customer
console.log("Token:", payment.RechargePIN);
console.log("Units:", payment.AdditionalInfo.Units);

Best Practices

  1. Always fetch payment items: Every bill payment requires a PaymentCode obtained from the payment items endpoint. Do not use ProductCode or NetworkId as payment codes.
  2. Validate when required: Perform customer validation for billers that require it before processing payment.
  3. Unique References: Generate a unique requestReference for each transaction.
  4. Handle Timeouts: If payment times out, use Step 6 to check status before retrying.
  5. Store References: Save TransactionRef and requestReference for reconciliation.
  6. Display tokens (RechargePIN): For electricity and similar services, display the token clearly.
  7. Error Handling: Show user-friendly error messages based on response codes.
  8. Idempotency: Implement retry logic with the same reference for failed requests only after confirming the transaction status.
  9. Security: Never expose your client credentials in frontend code.

API Summary

EndpointMethodDescription
/services/categoriesGETGet all categories
/services?categoryId={id}GETGet billers in category
/services/options?serviceid={id}GETGet payment items
/Transactions/validatecustomersPOSTValidate customer ID
/TransactionsPOSTMake payment
/Transactions?requestRef={ref}GETQuery transaction status

Support

For technical support or integration assistance:


Document Version: 2.0
Last Updated: 2026-09-29
API Version: v5


Did this page help you?