Using Bank Accounts

This page shows you how to make payment using bank accounts on Xpresspay.

Xpresspay allows you charge local bank accounts in Nigeria, your customer's can input their account number, an OTP is sent to their phone and the charge is authorised.

The guide below would show you how to charge accounts on xpresspay using our APIs.

Pre-requisites for accepting account payments on xpresspay.

For all account payments you would need to implement three steps to the transactions, Initiate payment , Validate payment, Re-query completed payment.

Step 1: Collect the bank details from the customer

You can use a custom form to collect the bank account details from the customer including extra information needed, see a sample of the bank account details to collect from the customer.

{
   "publicKey": "XPPUBK-ead4d14d9ded04aer5d5b63a0a06d2f-X",
   "accountNumber": "0690000031",
   "currency": "NGN",
   "country": "NG",
   "bankCode": "044", // get the bank code from the bank list endpoint.
   "amount": "300",
   "email": "[email protected]",
   "ip": "103.238.105.185",
   "transactionId": "MXX1-AabasSC-4578",
   "deviceFingerPrint": "69e6b7f0sb72037aa8428b70fbe03986c",
   "redirectUrl": "https://localhost/recievepayment",
   "phoneNumber": "07063762229",
   "firstName": "Abdussamad",
   "lastName": "Olaiya",
   "bvn": "222333224455",
   "dateOfBirth": "09101989" //customer Date of birth this is required for UBA bank account payment.
}
ParameterRequiredDescription
publicKeytrueThis is a unique key generated for each button created on Xpresspay’s dashboard. It starts with a prefix XPPUBK and ends with suffix X
bankCodetrueThis represents the bank to be debited. To get a list of banks in Nigeria, call the List of Banks API
accountNumbertrueThis is the account number of the customer associated with a valid bank account.
currencyfalse(Defaults to NGN) This is the specified currency to charge the account in.
countryfalse(Defaults to NG) This is the pair country for the transaction with respect to the currency
amounttrueThis is the amount to be charged from the account it is passed as - (“amount”:10).
emailtrueThis is the email address of the customer.
dateOfBirthfalse(Required for Zenith, UBA bank) This is required for Zenith, UBA bank account payments, you are required to collect the customer's date of birth and pass it in this format DDMMYYYY.
phoneNumberfalseThis is the phone number of the customer.
firstNamefalseThis is the first name of the account holder or the customer.
lastNamefalseThis is the last name of the account holder or the customer.
ipfalseIP - Internet Protocol. This represents the current IP address of the customer carrying out the transaction.
transactionIdtrueThis is the unique reference, unique to the particular transaction being carried out. It is generated by the merchant for every transaction.
paymentTypetrue(Expected value: ACCOUNT)This specifies that the payment method being used is for account payments
bvnfalse(Required for UBA account payment option) This is the customer's bvn number.
deviceFingerprintfalseThis is the fingerprint for the device being used. It can be generated using a library on whatever platform is being used.
redirectUrlfalse(Required for GTB & First bank account payments.)This is a url you provide, we redirect to it after the customer completes payment and append the response to it as query parameters.

Step 2: Encrypted String

To see how to encrypt using any of our encryption function copy the sample request below and visit the Xpresspay Encryption section.

Step 3: Initiate your payment.

After encryption, the next step is to initiate your payment using the encrypted string by sending a request to the /v1/payments endpoint. See how to do that below.

Live: https://api.xpresspayonline.com/v1/payments
Sandbox: https://xpresspayonlineapisandbox.xpresspayments.com/v1/payments

Sample Request:

curl --request POST \
  --url https://api.xpresspayonline.com/v1/payments \
    --data '{"publicKey": "XPPUBK-ead4d14d9ded04aer5d5b63a0a06d2f-X", "request": "uuQtURIuaK/8uZfEkyi2mydo3CgaGXiXJ3DmP3gdQ8JMylsiFSnSCvgThxp1+HGPAuSf/kHfiCmhiX3DsFx7Ydk/MHJn86J2aqr7UZsc4KDWab5xZn1LAlZQG4wLF9yBxBx38ehQIzlRM+tgf+VEgw17tV+LQdr9ZQtlvOIBOu/AP6FNaQO26hZ2XRx2y/La4LKqJSqEau4zNZVmiU8rjXOtefLe+DAaCBpGB0iCyWVlwo6YK9xNo+aNrWvhMghb6J7kVLtNmJpkPxR4hMXL4NOhNNviX7aAsnyRkP0XZcATNSMFuiJ0tFbeABfAzoXdZoRwsKXWlFxG3kKEBLszdXvCPih0IzlvnOCxrBKfLbF5OemYVl/MpA3XGRL+y2yVX6taGjaCxmFk+mXD48EpYCvTxt5d5DnbJrW2Bw+dEU4b64YTpMOWbahfBj/QH70YC6DIayr9clitcyiIRo2jew==", "alg": "3DES-24", "paymentType": "ACCOUNT"}'
JSON
{
   "publicKey": "XPPUBK-ead4d14d9ded04aer5d5b63a0a06d2f-X",
   "request": "uuQtURIuaK/8uZfEkyi2mydo3CgaGXiXJ3DmP3gdQ8JMylsiFSnSCvgThxp1+HGPAuSf/kHfiCmhiX3DsFx7Ydk/MHJn86J2aqr7UZsc4KDWab5xZn1LAlZQG4wLF9yBxBx38ehQIzlRM+tgf+VEgw17tV+LQdr9ZQtlvOIBOu/AP6FNaQO26hZ2XRx2y/La4LKqJSqEau4zNZVmiU8rjXOtefLe+DAaCBpGB0iCyWVlwo6YK9xNo+aNrWvhMghb6J7kVLtNmJpkPxR4hMXL4NOhNNviX7aAsnyRkP0XZcATNSMFuiJ0tFbeABfAzoXdZoRwsKXWlFxG3kKEBLszdXvCPih0IzlvnOCxrBKfLbF5OemYVl/MpA3XGRL+y2yVX6taGjaCxmFk+mXD48EpYCvTxt5d5DnbJrW2Bw+dEU4b64YTpMOWbahfBj/QH70YC6DIayr9clitcyiIRo2jew==",
   "paymentType": "ACCOUNT"
}

request: This is the encrypted request parameters.

  • publicKey: This is your merchant public key.

  • paymentType: must always be passed as ACCOUNT

When you initiate the payment you would get a response that looks like responses below:

{
    "data": {
        "payment": {
            "uniqueKey": "7c93cb23e6",
            "authenticatePaymentResponseCode": "02",
            "authenticatePaymentResponseMessage": "Pending OTP validation",
            "suggestedAuthentication": null,
            "transactionId": "MXX1-AabasSC-4578",
            "redirectUrl": "https://localhost/recievepayment",
            "deviceFingerPrint": "N/A",
            "settlementToken": null,
            "cycle": "one-time",
            "amount": "300",
            "chargedAmount": "300",
            "appFee": "4.2",
            "merchantFee": "0",
            "merchantBearsFee": "1",
            "authModelUsed": "AUTH",
            "currency": "NGN",
            "ip": "::ffff:10.63.243.194",
            "narration": "Xpress Payments",
            "status": "success-pending-validation",
            "vbvResponseMessage": "N/A",
            "authUrl": "NO-URL",
            "vbvResponseCode": "N/A",
            "accountValidationResponseMessage": null,
            "accountValidationResponseCode": null,
            "paymentType": "account",
            "paymentId": "2",
            "fraudStatus": "ok",
            "chargeType": "normal",
            "isLive": "0",
            "createdAt": "2018-10-17T18:21:35.182",
            "updatedAt": "2018-10-17T18:21:45.003",
            "deletedAt": null,
            "customerId": "56863",
            "customerPhoneNumber": null,
            "customerEmail": "[email protected]",
            "customerFullName": "Anonymous customer",
            "accountId": "509",
            "customerCanDoSubsequentNoAuth": null,
            "providerReference": "ACHG-1539796901003",
            "validationType": null,
            "validationInstruction": "Please validate with the OTP sent to your mobile or email"
        }
    }
}

Some of the important responses you need to check are broken down below:

  • data.payment.authenticatePaymentResponseCode: This is the response code of the transaction, it typically tells you when a transaction is successful with a response code 00 or when the transaction requires validation 02.

  • data.payment.validateInstruction: This object contains the instructions you are meant to show to the customer so they know the next step to take, it typically tells them how to validate the transaction.

  • data.payment.paymentType: This shows you the payment instrument used i.e. if the customer used a card, account or ussd to complete the payment.

Collecting account payments using GTB & First Bank account payment.

When using GTB or First Bank for account payment collection, you would need to redirect the user to a new window for them to log in to their internet banking profile and complete the transaction.

After the transaction is completed we would redirect back to the redirectUrl you provided in the initial request. Below is a sample of the initial request to send for GTB account payments.

📘

When using the GTB account payment option the amount passed needs to be greater than NGN 100

{
   "publicKey": "XPPUBK-ead4d14d9ded04aer5d5b63a0a06d2f-X",
   "accountNumber": "0690000031",
   "currency": "NGN",
   "country": "NG",
   "bankCode": "044", // get the bank code from the bank list endpoint.
   "amount": "300",
   "email": "[email protected]",
   "ip": "103.238.105.185",
   "transactionId": "MXX1-AabasSC-4578",
   "deviceFingerPrint": "69e6b7f0sb72037aa8428b70fbe03986c",
   "redirectUrl": "https://localhost/recievepayment",
   "phoneNumber": "07063762229",
   "firstName": "Abdussamad",
   "lastName": "Olaiya",
   "bvn": "222333224455",
   "dateOfBirth": "09101989" //customer Date of birth this is required for UBA bank account payment.
}

When using GTB or First Bank account payment the validate payment step happens on the Internet banking page of the customer.

After the initial charge request, the response would contain an authUrl, this is a link to redirect the customer too so they can complete their payment.

Step 4: Validate Account Payment

After initiating the payment you would need to validate the transaction, validation is like authentication, essentially the customer is required to validate that he is the customer with the correct permissions to carry out the payment.

To Validate a transaction, see how below:

https://api.xpresspayonline.com/v1/payments/validate

Sample Request:

curl --request POST \
  --url https://api.xpresspayonline.com/v1/payments \
    --data '{"publicKey": "XPPUBK-ead4d14d9ded04aer5d5b63a0a06d2f-X", "transactionId": "MXX1-AabasSC-4578","otp": "12345","paymentType" : "ACCOUNT"}'
JSON
{
    "publicKey": "XPPUBK-ead4d14d9ded04aer5d5b63a0a06d2f-X",
    "transactionId": "MXX1-AabasSC-4578",
    "otp": "12345",
    "paymentType" : "ACCOUNT"
}
  • publicKey: This is your merchant public key.

  • transactionId: This is the unique reference generated by merchant.

  • otp: This is the one time pin inputted by the customer.

When you validate a payment you would get a response that looks like response below:

{
    "data": {
        "payment": {
            "paymentResponseCode": "00",
            "paymentResponseMessage": "successful",
            "uniqueKey": "34ac423f07",
            "authenticatePaymentResponseCode": "00",
            "authenticatePaymentResponseMessage": "Please enter the OTP sent to your mobile number 080****** and email te**@xpresspayonline**.com",
            "suggestedAuthentication": null,
            "transactionReference": "MXX-ASC-4578",
            "orderReference": "URF_1539600452892_8524035",
            "redirectUrl": "N/A",
            "deviceFingerPrint": "69e6b7f0sb72037aa8428b70fbe03986c",
            "settlementToken": null,
            "cycle": "one-time",
            "amount": "300",
            "chargedAmount": "300",
            "appFee": "4.2",
            "merchantFee": "0",
            "merchantBearsFee": "1",
            "authModelUsed": "PIN",
            "currency": "NGN",
            "ip": "::ffff:10.30.69.116",
            "narration": "CARD Transaction ",
            "vbvResponseMessage": "successful",
            "authUrl": "N/A",
            "vbvResponseCode": "00",
            "accountValidationResponseMessage": null,
            "accountValidationResponseCode": null,
            "paymentType": "card",
            "paymentId": "861",
            "fraudStatus": "ok",
            "chargeType": "normal",
            "isLive": "0",
            "createdAt": "2018-10-15T11:47:18.447",
            "updatedAt": "2018-10-15T11:47:40.416",
            "deletedAt": null,
            "customerId": "56151",
            "accountId": "509"
        }
    }
}
{
    "status": "FAILED",
    "error": "CONFLICT",
    "message": "Payment with this transactionId exists: transactionId"
}
{
    "status": "FAILED",
    "error": "INPUT",
    "message": "otp is required"
}

Step 5: Re-query Payment

After charging an account successfully, you need to verify that the payment was successful with Xpresspay before giving value to your customer on your website.

Although the Xpresspay inline already verifies the payment from the client side, we strongly recommend you still do a server side verification to be double sure no foul play occurred during the payment flow.

Below are the important things to check for when validating the payment:

Verify the transaction reference.

Verify the data.payment.paymentResponseCode of the transaction to be 000.

Verify the currency to be the expected currency

Most importantly validate the amount paid to be equal to or at least greater than the amount of the value to be given.

Below is sample code of how to implement server side validation in different programming languages

curl --request POST \
  --url https://api.xpresspayonline.com/v1/payments/query \
  --header 'content-type: application/json' \
  --data '{"publicKey": "XPPUBK-ead4d14d9ded04aer5d5b63a0a06d2f-X", "transactionId": "MXX-AabasSC-4578", "paymentType": "ACCOUNT"}'
<?php

$result = array();

$postdata =  array(
  'publicKey' => 'XPPUBK-ead4d14d9ded04aer5d5b63a0a06d2f-X',
  'transactionId' => 'MXX-AabasSC-4578',
  'paymentType' => 'ACCOUNT'
  );

$ch = curl_init();
curl_setopt($ch, CURLOPT_URL,"https://api.xpresspayonline.com/v1/payments/query");
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS,json_encode($postdata));  //Post Fields
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

$headers = [
  'Content-Type: application/json',
];

curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$request = curl_exec ($ch);
$err = curl_error($ch);

if($err){
    // there was an error contacting xpresspayonline
  die('Curl returned error: ' . $err);
}


curl_close ($ch);

$result = json_decode($request, true);

if('null' != $result->error){
  // there was an error from the API
  die('API returned error: ' . $result->message);
}

if('000' == $result->data->payment->paymentResponseCode){
  // transaction was successful...
  // please check other things like whether you already gave value for this ref
  // If the amount and currency matches the expected amount and currency etc.
  // if the email matches the customer who owns the product etc
  // Give value
}
//Endpoint to verify transaction
    private final String REQUERY_ENDPOINT = "https://api.xpresspayonline.com/v1/payments/query";

    /**
     *
     * Method to
     *
     * @param paymententity - <b>paymententity - set as a constant with default value as 1</b>
     * @param transactionId - <b>transactionId - is the unique payment reference generated by the merchant.</b>
     * @param publicKey - <b>publicKey - is the merchant public key</b>
     * @param paymentType - <b>PaymentType - is the transaction payment type (CARD/ACCOUNT/QR/USSD/WALLET)</b>
     * @return
     * @throws UnirestException
     */
    public JSONObject verify(String transactionId, String publicKey, double amount, String paymentType) throws UnirestException, Exception {

        // This packages the payload
        JSONObject data = new JSONObject();
        data.put("transactionId", transactionId);
        data.put("publicKey", publicKey)
        data.put("paymentType", paymentType)

        // end of payload

        // This sends the request to server with payload
        HttpResponse<JsonNode> response = Unirest.post(REQUERY_ENDPOINT)
                .header("Content-Type", "application/json")
                .body(data)
                .asJson();

        // This get the response from payload
        JsonNode jsonNode = response.getBody();

        // This get the json object from payload
        JSONObject responseObject = jsonNode.getObject();

        // check of no object is returned
        if(responseObject == null)
            throw new Exception("No response from server");

        // This get status from returned payload
        String status = responseObject.optString("status", null);
        String error = responseObject.optString("error", null);

        // this ensures that status is not null
        if(status == null)
            throw new Exception("Transaction status unknown");

        // This confirms the transaction exist on xpresspayonline
        if(!"null".equalsIgnoreCase(error)){

            String message = responseObject.optString("message", null);

            throw new Exception(message);
        }

        data = responseObject.getJSONObject("data");

        // This get the amount stored on server
        double actualAmount = data.getDouble("amount");

        // This validates that the amount stored on client is same returned
        if(actualAmount != amount)
            throw new Exception("Amount does not match");


        // now you can give value for payment.

    }
var data = new {transactionId = "MXX-AabasSC-4578", publicKey = "XPPUBK-e6db11d1f8a6208de8cb2f94e293450e-X", paymentType="ACCOUNT"};
            var client = new HttpClient();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            var responseMessage = client.PostAsJsonAsync("https://api.xpresspayonline.com/v1/payments/query", data).Result;
            var responseStr = responseMessage.Content.ReadAsStringAsync().Result;
            var response = Newtonsoft.Json.JsonConvert.DeserializeObject<ResponseData>(responseStr);
            if (response.data.payment.paymentResponseCode == "000" && response.data.amount == amount)
            {

              System.Console.WriteLine("Payment Successful then give value");

            }

When you successfully verify a completed payment see sample response below:

{
    "data": {
        "payment": {
            "uniqueKey": "7c93cb23e6",
            "transactionId": "MXX1-AabasSC-4578",
            "deviceFingerPrint": "N/A",
            "cycle": "one-time",
            "amount": "300",
            "currency": "NGN",
            "chargedAmount": "300",
            "appFee": "4.2",
            "merchantFee": "0",
            "merchantBearsFee": "1",
            "chargeCode": "02",
            "chargeMessage": "Pending OTP validation",
            "authModel": "AUTH",
            "ip": "::ffff:10.63.243.194",
            "narration": "Xpress Payments",
            "status": "failed",
            "vbvCode": "RR",
            "vbvMessage": "Failed to retrieve Card",
            "authUrl": "NO-URL",
            "accountCode": null,
            "accountMessage": null,
            "paymentType": "account",
            "paymentId": "2",
            "fraudStatus": "ok",
            "chargeType": "normal",
            "createdDay": "3",
            "createdDayName": "WEDNESDAY",
            "createdWeek": "42",
            "createdMonth": "9",
            "createdMonthName": "OCTOBER",
            "createdQuarter": "4",
            "createdYear": "2018",
            "createdYearIsLeap": "false",
            "createdDayIsPublicHoliday": "0",
            "createdHour": "17",
            "createdMinute": "21",
            "createdPmAm": "pm",
            "created": null,
            "customerId": "56863",
            "customerPhone": null,
            "customerNetworkProvider": "N/A",
            "customerName": "Anonymous customer",
            "customerEmail": "[email protected]",
            "customerEmailProvider": "COMPANY EMAIL",
            "customerCreated": "2018-10-17T17:21:39.000Z",
            "accountId": "509",
            "accountBusinessName": "Xpress Payments",
            "accountContactPerson": "Taofeek Gidado",
            "accountCountry": "NG",
            "accountBearsFeeAtTransactionTime": "1",
            "accountParent": "1",
            "accountVpcMerchant": "N/A",
            "accountAlias": null,
            "accountIsLiveApproved": "0",
            "orderReference": "URF_1539796900107_2389535",
            "paymentPlan": null,
            "paymentPage": null,
            "accountNumber": "0690000031",
            "bankCode": "044",
            "firstName": "NO-NAME",
            "lastName": "NO-LNAME",
            "accountIsBlackListed": "0",
            "accountToken": "xps-t0e471ec537e94ec2c-k3n-mock",
            "createdAt": "2018-10-17T18:21:35.183",
            "updatedAt": "2018-10-17T18:26:47.439"
        }
    }
}