This page shows how to perform card payments using xpresspay's APIs

Xpresspay allows you charge local (card's issued in Nigeria) and International cards using our APIs. When charging cards with Xpresspay you have to take into account authentication models this is primarily how the user who is meant to pay authenticates the transaction e.g. using a one time pin (OTP), a card internet PIN (i-Pin) or an address verification system (AVS).

Xpresspay automatically determines the authentication model to be used by the card and sends a response requiring you pass the needed parameter for that authentication model.

The guide below would show you how to charge cards on Xpresspay using our APIs.

Step 1: Collect the card details from the customer

You can use a custom form to collect the card details from the customer including extra information needed, see a sample card payment request details to collect from the customer.

{
   "publicKey": "XPPUBK-ead4d14d9ded04aer5d5b63a0a06d2f-X",
   "cardNumber": "5438898014560229",
   "currency": "NGN",
   "country": "NG",
   "cvv": "789",
   "amount": "300",
   "expiryYear": "19",
   "expiryMonth": "09",
   "email": "[email protected]",
   "ip": "103.238.105.185",
   "transactionId": "MXX-ASC-4578",
   "deviceFingerPrint": "69e6b7f0sb72037aa8428b70fbe03986c",
   "phoneNumber": "07063762229",
   "firstName": "Abdussamad",
   "lastName": "Olaiya",     
   "billingZip": "07205",
   "billingCity": "Hillside",
   "billingAddress": "470 Mundet PI",
   "billingState": "NJ",
   "billingCountry": "US",
   "meta": [{"metaName": "ticketId", "metaValue": "123949494DC"}],
   "redirectUrl": "https://yourdomain.com/paymentnotification",
   "deviceFingerPrint": "69e6b7f0b72037aa8428b70fbe03986c"
 }

Parameter Description

ParameterRequiredDescription
publicKeytrueThis is a unique key generated for each merchant created on xpresspay's dashboard.
cardNumbertrueThis is the number on the cardholders card. E.g. 5399 6701 2349 0229.
cvvtrueCard security code. This is 3/4 digit code at the back of the customers card, used for web payments.
expiryMonthtrueTwo-digit number representing the card's expiration month.
expiryYeartrueTwo- digit number representing the card's expiration year.
currencyfalseThis is the specified currency to charge the card in. defaults to NGN
countryfalseThis is the pair country for the transaction with respect to the currency. defaults to NG
amounttrueThis is the amount to be charged from card it is passed as - (“amount”:10).
emailtrueThis is the email address of the customer.
phoneNumberfalseThis is the phone number of the customer.
firstNamefalseThis is the first name of the card holder or the customer.
lastNamefalseThis is the last name of the card 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
billingZipfalse
billingCityfalseThis is the address of the city
billingAddressfalseThis is the billing address.
billingStatefalseThis is the state to be billed.
billingCountryfalseThis is the country to be billed.
paymentTypetrue
value equals CARD
The is the type of payment. The value for this will be CARD
metafalseSet of key-value pairs that you can attach to an object. This can be useful for storing additional information about the object in a structured format.
redirectUrlfalseThis is a url you provide, we redirect to it after the customer completes payment. This occurs when the transaction is 3DSecure
deviceFingerPrintfalseThis is the fingerprint for the device being used. It can be generated using a library on whatever platform is being used.

Step 2: Encrypt the card details

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

Step 3: Initiate Payment

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

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

Sample Request:

Copy the curl request and make a request in your terminal to see how it works!

curl --request POST \
  --url https://api.xpresspayonline.com/v1/payments \
  --header 'content-type: application/json' \
  --data '{"publicKey": "XPPUBK-ead4d14d9ded04aer5d5b63a0a06d2f-X","request": "uuQtURIuaK/8uZfEkyi2mydo3CgaGXiXJ3DmP3gdQ8JMylsiFSnSCvgThxp1+HGPAuSf/kHfiCmW62oFdyBpWiUAn32d6CLWfkiw/Yanpj3gKmpIDfVgEic6cCX4AC7cwl2hgOFSENS0/JlfyzwAO7LOzUnQmiMzQ5WA2D60GT6lmI7ELdoJXklJBvbiYNX6WGTpe9jzzCAPqIm9Fc/CDYrujNeDmL4bMCZs00ctMNZNbOGwubPiju8qrUtIM3ya7zPTvmyK1dKfXJwPomb1+GBzrplsqpOQAvwa8mtIrrOKunnGHMA/eTM1lWaJTyuNc6158t74MBoIGkYHSILJZWXCjpgr3E2j5o2ta+EyCFvonuRUu02YmmQ/FHiExcvg06E02+JftoCyfJGQ/RdlwNwdip8WdO1mDZ9+W46k664k+exBYSeTbj/+QZtPD6uAHA50vK01k0AtV28UFxL/Qx/IszNLhDTkVx2Thwa3qKOmLTSONI3Cpg==", "paymentType": "CARD"}'
{
  "publicKey": "XPPUBK-ead4d14d9ded04aer5d5b63a0a06d2f-X",
   "request":"uuQtURIuaK/8uZfEkyi2mydo3CgaGXiXJ3DmP3gdQ8JMylsiFSnSCvgThxp1+HGPAuSf/kHfiCmW62oFdyBpWiUAn32d6CLWfkiw/Yanpj3gKmpIDfVgEic6cCX4AC7cwl2hgOFSENS0/JlfyzwAO7LOzUnQmiMzQ5WA2D60GT6lmI7ELdoJXklJBvbiYNX6WGTpe9jzzCAPqIm9Fc/CDYrujNeDmL4bMCZs00ctMNZNbOGwubPiju8qrUtIM3ya7zPTvmyK1dKfXJwPomb1+GBzrplsqpOQAvwa8mtIrrOKunnGHMA/eTM1lWaJTyuNc6158t74MBoIGkYHSILJZWXCjpgr3E2j5o2ta+EyCFvonuRUu02YmmQ/FHiExcvg06E02+JftoCyfJGQ/RdlwNwdip8WdO1mDZ9+W46k664k+exBYSeTbj/+QZtPD6uAHA50vK01k0AtV28UFxL/Qx/IszNLhDTkVx2Thwa3qKOmLTSONI3Cpg==", 
  "paymentType": "CARD"
}
  • publicKey : This is the encrypted request parameters.
  • request : This is the encrypted request parameters.
  • paymentType : must always be passed as CARD

When you initiate the payment you would get a response based on the card that was sent to Xpresspay, below is the response you would get for each card type and what you need to do next.

Step 4: Authenticate Payment

Authenticate card payment Xpresspay allows you charge local (card's issued in Nigeria) and International cards using our APIs. When charging cards with Xpresspay you have to take into account authentication models this is primarily how the user who is meant to pay authenticates the transaction e.g. using a one time pin (OTP), a card internet PIN (i-Pin) or an address verification system (AVS).

Xpresspay automatically determines the authentication model to be used by the card and sends a response requiring you pass the needed parameter for that authentication model.
When you initiate the payment you would get a response based on the card that was sent to Xpresspay, we explain the response you would get for each card type and what you need to do next.

Using a Local Mastercard/verve i.e. card issued in Nigeria

When using a local mastercard/Verve card, we suggest that you charge the card using the customers PIN, the suggested auth is returned after initiating payment, you would get a response that looks like this:

{
    "data": {
        "payment": {
            "status": "SUCCESSFUL",
            "statusMessage": "AUTH_SUGGESTION",
            "transactionId": "MXX-ASC-4578",
            "suggestedAuthentication": "PIN"
        }
    }
}

When you get this response you are to a new request to /validate endpoint. See an example of the new request to send to the validate endpoint.

See how to do that below.

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

{
   "publicKey": "XPPUBK-ead4d14d9ded04aer5d5b63a0a06d2f-X",
   "suggestedAuthentication": "PIN",
   "pin": "3310",
   "transactionId": "MXX-ASC-4578",
   "paymentType": "CARD"
}

Using AVS (Address verification system) to charge an international card.

When using an international card that uses the AVS system, we detect this automatically and suggest using the AVS authentication model. The suggested authentication model is returned after initiating payment with the card details, see a sample response below.

{
    "data": {
        "payment": {
            "status": "SUCCESSFUL",
            "statusMessage": "AUTH_SUGGESTION",
            "transactionId": "MXX-ASC-4578",
            "suggestedAuthentication": "NOAUTH_INTERNATIONAL"
        }
    }
}
{
    "data": {
        "payment": {
            "status": "SUCCESSFUL",
            "statusMessage": "AUTH_SUGGESTION",
            "transactionId": "MXX-ASC-4578",
            "suggestedAuthentication": "AVS_VBVSECURECODE"
        }
    }
}

When you get this response you are to send a request to /authenticate endpoint adding the cards billing address details to your payload again. See an example of the new request to send to the validate the payment.

See how to do that below.

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

{
   "publicKey": "XPPUBK-ead4d14d9ded04aer5d5b63a0a06d2f-X",
   "suggestedAuthentication": "pin",
   "transactionId": "MXX-ASC-4578",
   "billingCity": "Hillside",
   "billingAddress": "470 Mundet PI",
   "billingState": "NJ",
   "billingCountry": "US",
   "paymentType": "CARD"
}

The billing details of the card include, billingzip, billingcity, billingaddress, billingstate, billingcountry.

  • billingZip: This is the zip code or postal card registered with the card, customers can easily find this on their bank statement.

  • billingCity: This is the city registered with the card, it makes up part of the address the customer used for their card. Customers can easily find this on their bank statement.

  • billingAddress: This is the house/building address registered with the card. Customers can easily find this on their bank statement.

  • billingState: This is the state registered with the card. Customers can easily find this on their bank statement.

  • billingCountry: This is the country registered with the card. Customers can easily find this on their bank statement.

Handling AVS_VBVSECURECODE & 3DSecure Transactions

When the suggested auth is AVS_VBVSECURECODE it means the payment requires that the billing address of the card is sent, and after the Initiate payment step the validation step would happen using 3DSecure authentication.

What you need to do after receiving the initial payment response is load the authUrl returned in an iFrame and allow the customer validate the transaction, once that is completed we would call your redirectUrl and append the response as query parameters.

📘

3Dsecure Transactions.

Billing address details are not required for strictly 3DSecure transactions.

Final response to expect from the authenticate payment call

{
   "data": {
      "payment": {
         "uniqueKey": "34ac423f07",
         "authenticatePaymentResponseCode": "02",
         "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": "Approved. 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:34.504",
         "deletedAt": null,
         "customerId": "56151",
         "accountId": "509",
         "customerCanDoSubsequentNoAuth": "false",
         "providerReference": "FLW-MOCK-81788caa66bcc4b0dc0b8106dac9689e"
      }
   }
}
https://xpresspayonline.com/newregistration?response=%7B%22name%22%3A%22opop%22%2C%22data%22%3A%7B%22status%22%3A%22success%22%2C%22message%22%3A%22V-COMP%22%2C%22data%22%3A%7B%22id%22%3A301782%2C%22txRef%22%3A%22xpresspayonline-checkout-1541080175862%22%2C%22orderRef%22%3A%22URF_1541080202653_2603435%22%2C%22flwRef%22%3A%22FLWACHMOCK-1541080203844%22%2C%22redirectUrl%22%3A%22https%3A%2F%2Frave-webhook.herokuapp.com%2Fnewregistration%22%2C%22device_fingerprint%22%3A%22532b4e9fa7695279392f4780b9868b9b%22%2C%22settlement_token%22%3Anull%2C%22cycle%22%3A%22one-time%22%2C%22amount%22%3A60%2C%22charged_amount%22%3A60%2C%22appfee%22%3A0.9%2C%22merchantfee%22%3A0%2C%22merchantbearsfee%22%3A1%2C%22chargeResponseCode%22%3A%2200%22%2C%22raveRef%22%3A%22RV31541080202194E1A956BA48%22%2C%22chargeResponseMessage%22%3A%22Approved.+Successful.%22%2C%22authModelUsed%22%3A%22AUTH%22%2C%22currency%22%3A%22NGN%22%2C%22IP%22%3A%22197.149.95.62%22%2C%22narration%22%3A%22Synergy+Group%22%2C%22status%22%3A%22successful%22%2C%22modalauditid%22%3A%2276d43165b4e49f5ce5e71736298e109d%22%2C%22vbvrespmessage%22%3A%22N%2FA%22%2C%22authurl%22%3A%22NO-URL%22%2C%22vbvrespcode%22%3A%22N%2FA%22%2C%22acctvalrespmsg%22%3Anull%2C%22acctvalrespcode%22%3Anull%2C%22paymentType%22%3A%22account%22%2C%22paymentPlan%22%3Anull%2C%22paymentPage%22%3Anull%2C%22paymentId%22%3A%22478%22%2C%22fraud_status%22%3A%22ok%22%2C%22charge_type%22%3A%22normal%22%2C%22is_live%22%3A0%2C%22createdAt%22%3A%222018-11-01T13%3A50%3A02.000Z%22%2C%22updatedAt%22%3A%222018-11-01T13%3A50%3A03.000Z%22%2C%22deletedAt%22%3Anull%2C%22customerId%22%3A58159%2C%22AccountId%22%3A134%2C%22customer%22%3A%7B%22id%22%3A58159%2C%22phone%22%3A%22N%2FA%22%2C%22fullName%22%3A%22Anonymous+customer%22%2C%22customertoken%22%3Anull%2C%22email%22%3A%22cchizie26%40gmail.com%22%2C%22createdAt%22%3A%222018-10-24T16%3A58%3A21.000Z%22%2C%22updatedAt%22%3A%222018-10-24T16%3A58%3A21.000Z%22%2C%22deletedAt%22%3Anull%2C%22AccountId%22%3A134%7D%2C%22validateInstructions%22%3A%7B%22valparams%22%3A%5B%5D%2C%22instruction%22%3A%22%22%7D%2C%22validateInstruction%22%3A%22Please+dial+*901*4*1%23+to+get+your+OTP.+Enter+the+OTP+gotten+in+the+field+below%22%7D%7D%2C%22tx%22%3A%7B%22id%22%3A301782%2C%22txRef%22%3A%22rave-checkout-1541080175862%22%2C%22orderRef%22%3A%22URF_1541080202653_2603435%22%2C%22flwRef%22%3A%22FLWACHMOCK-1541080203844%22%2C%22redirectUrl%22%3A%22https%3A%2F%2Frave-webhook.herokuapp.com%2Fnewregistration%22%2C%22device_fingerprint%22%3A%22532b4e9fa7695279392f4780b9868b9b%22%2C%22settlement_token%22%3Anull%2C%22cycle%22%3A%22one-time%22%2C%22amount%22%3A60%2C%22charged_amount%22%3A60%2C%22appfee%22%3A0.9%2C%22merchantfee%22%3A0%2C%22merchantbearsfee%22%3A1%2C%22chargeResponseCode%22%3A%2200%22%2C%22raveRef%22%3A%22RV31541080202194E1A956BA48%22%2C%22chargeResponseMessage%22%3A%22Approved.+Successful.%22%2C%22authModelUsed%22%3A%22AUTH%22%2C%22currency%22%3A%22NGN%22%2C%22IP%22%3A%22197.149.95.62%22%2C%22narration%22%3A%22Synergy+Group%22%2C%22status%22%3A%22successful%22%2C%22modalauditid%22%3A%2276d43165b4e49f5ce5e71736298e109d%22%2C%22vbvrespmessage%22%3A%22N%2FA%22%2C%22authurl%22%3A%22NO-URL%22%2C%22vbvrespcode%22%3A%22N%2FA%22%2C%22acctvalrespmsg%22%3Anull%2C%22acctvalrespcode%22%3Anull%2C%22paymentType%22%3A%22account%22%2C%22paymentPlan%22%3Anull%2C%22paymentPage%22%3Anull%2C%22paymentId%22%3A%22478%22%2C%22fraud_status%22%3A%22ok%22%2C%22charge_type%22%3A%22normal%22%2C%22is_live%22%3A0%2C%22createdAt%22%3A%222018-11-01T13%3A50%3A02.000Z%22%2C%22updatedAt%22%3A%222018-11-01T13%3A50%3A03.000Z%22%2C%22deletedAt%22%3Anull%2C%22customerId%22%3A58159%2C%22AccountId%22%3A134%2C%22customer%22%3A%7B%22id%22%3A58159%2C%22phone%22%3A%22N%2FA%22%2C%22fullName%22%3A%22Anonymous+customer%22%2C%22customertoken%22%3Anull%2C%22email%22%3A%22cchizie26%40gmail.com%22%2C%22createdAt%22%3A%222018-10-24T16%3A58%3A21.000Z%22%2C%22updatedAt%22%3A%222018-10-24T16%3A58%3A21.000Z%22%2C%22deletedAt%22%3Anull%2C%22AccountId%22%3A134%7D%2C%22validateInstructions%22%3A%7B%22valparams%22%3A%5B%5D%2C%22instruction%22%3A%22%22%7D%2C%22validateInstruction%22%3A%22Please+dial+*901*4*1%23+to+get+your+OTP.+Enter+the+OTP+gotten+in+the+field+below%22%7D%2C%22success%22%3Atrue%7D

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.authenticatePaymentResponseMessage: This is the response message and it can be shown to the customer to show what needs to be done next.

  • data.payment.authModelUsed: This shows you the authentication model used for the transaction, it can also help you decide internally what steps to take after the payment is initiated, e.g. if the value is PIN the customer would be required to submit their otp based on the message returned in authenticatePaymentResponseMessage or if the value is VBVSECURECODE you would be required to load the authurl returned in the response in an iframe.

  • data.payment.authurl: This is used for authenticating the customer in a VBVSECURECODE transaction, you need to load it in an iFrame if returned to you

  • 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.

Step 5: Validate Payment

After authenticating the payment you would need to validate the transaction, essentially the customer is required to validate that he/she is the customer with the correct permissions to carry out the payment.

When validating transactions you need to take into account the authModelUsed and the authenticatePaymentResponseMessage returned.

Scenario 1:

When

authModelUsed: PIN you would be asked to validate the transaction by asking the customer for the OTP sent to their registered(with the bank account) mobile number.

authenticatePaymentResponseMessage: You need to show this to the customer, it would come with the instructions needed to complete validation.

Scenario 2:

When

authModelUsed: VBVSECURECODE or XPSECURECODE you would be asked to validate by loading the authurl returned in an iframe, the customer would see a page with their bank's branding asking them to validate the transaction, once this is completed we would call your redirect_url and append the response as query parameters. NB: When validating a transaction with VBVSECURECODE or XPSECURECODE as the auth model there would be no use of the /v1/payments/validate endpoint.

To Validate a transaction, see how below:

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

Sample Request:

{
    "publicKey": "XPPUBK-ead4d14d9ded04aer5d5b63a0a06d2f-X",
    "transactionReference": "MXX-ASC-4578",
    "otp": "123456",
    "paymentType": "CARD"
}
  • publicKey: This is your merchant public key.

  • transactionReference: This is the transaction id sent in the Initiate payment request.

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

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

{
    "data": {
        "payment": {
            "paymentResponseCode": "000",
            "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": "erty-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"
        }
    }
}

Once you get this response you need to the last step to complete the payment; Payment verification.

Step 6: Requery Payment

After making a card payment successfully, you need to re-query 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.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-ASC-4578", "paymentType": "CARD"}'
<?php

$result = array();

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

$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-ASC-4578", publicKey = "XPPUBK-e6db11d1f8a6208de8cb2f94e293450e-X", paymentType="CARD"};
            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 re-query a completed payment see sample response below:

{
    "status": null,
    "data": {
        "payment": {
            "paymentResponseCode": "000",
            "paymentResponseMessage": "successful",
            "uniqueKey": "6ce13c57aa",
            "transactionId": "null-MXX1-QR-4578",
            "deviceFingerPrint": "69e6b7f0sb72037aa8428b70fbe03986c",
            "cycle": "one-time",
            "amount": "300",
            "currency": "NGN",
            "chargedAmount": "300",
            "appFee": "4.2",
            "merchantFee": "0",
            "merchantBearsFee": "1",
            "chargeCode": "00",
            "chargeMessage": "QR GENERATED. PENDING VALIDATION",
            "authModel": "MVISA-QR",
            "ip": "103.238.105.185",
            "narration": "Xpress Payments",
            "status": "successful",
            "vbvCode": "N/A",
            "vbvMessage": "N/A",
            "authUrl": "NO-URL",
            "accountCode": "00",
            "accountMessage": "successful",
            "paymentType": "mvisa-qr",
            "paymentId": "118",
            "fraudStatus": "ok",
            "chargeType": "normal",
            "createdDay": "1",
            "createdDayName": "MONDAY",
            "createdWeek": "5",
            "createdMonth": "0",
            "createdMonthName": "JANUARY",
            "createdQuarter": "1",
            "createdYear": "2019",
            "createdYearIsLeap": "false",
            "createdDayIsPublicHoliday": "0",
            "createdHour": "11",
            "createdMinute": "8",
            "createdPmAm": "am",
            "created": null,
            "customerId": "79997",
            "customerPhone": "090882232",
            "customerNetworkProvider": "UNKNOWN PROVIDER",
            "customerName": "Ade Test",
            "customerEmail": "[email protected]",
            "customerEmailProvider": "COMPANY EMAIL",
            "customerCreated": "2019-01-28T10:48:51.000Z",
            "accountId": "509",
            "accountBusinessName": "Xpress Payments",
            "accountContactPerson": "Taofeek Gidado",
            "accountCountry": "NG",
            "accountBearsFeeAtTransactionTime": "1",
            "accountParent": "1",
            "accountVpcMerchant": "N/A",
            "accountAlias": null,
            "accountIsLiveApproved": "0",
            "orderReference": "RV46FDC384C30FE0",
            "paymentPlan": null,
            "paymentPage": null,
            "accountNumber": null,
            "bankCode": null,
            "firstName": null,
            "lastName": null,
            "accountIsBlackListed": null,
            "accountToken": null,
            "createdAt": "2019-01-28T11:35:32.61",
            "updatedAt": "2019-01-28T13:03:16.752"
        }
    }
}