NAV Navbar
shell csharp javascript php

Preface

About

This document contains detailed explanation about how to integrate with APPS API’s Based transactions functionality. This document also contains the details for online transactions.

Intended Audience

This document is for merchants, acquirers and developers who want to integrate with APPS to perform a API’s based Transactions.

Integration scope

The merchant will implement all ecommerce functionality. APPS’ service (Payfast) will be used only for payment processing.

APPS – Go Cashless!

AVANZA PREMIER PAYMENT SERVICES APPS is focused to bring merchants on ecommerce platforms by offering a complete suite of solutions to all types of merchants from building a website, online store, mobile application, social media marketing to securing payment. APPS will enable merchants to sell the products and services online, and receive their payment safely. APPS

Key Features

How Payfast Works

APPS has developed a flagship payment gateway solution called “PayFast” which is targeted towards merchants, schools and other corporate entities to accept payments digitally from customers by using multiple options such as Debit Cards and Account Numbers. It will be integrated directly with the client’s website to allow smooth transaction placements. How It Works

API Based Transaction

All retail stores can integrate with Payfast direct API’s for over the counter payments without redirection. After the payment process is completed, the merchant will get an intimation of the transaction status.

Integration Prerequisites

Merchants will be registered on APPS prior to integration. After merchant sign up for APPS account, following two unique values will be provided to merchant to operate: * MERCHANT_ID * SECURED_KEY These keys are used to get a one-time authentication token, which is used to authenticate payment requests to the “Payfast” payment gateway.

Getting Authentication Token

To initiate the transaction, the merchant needs to generate the Authentication Token from APPS server. For fetching, the token developer needs to call the Authentication token API.

Let’s start with Scenarios

Scenario 1

Consider a typical ecommerce transaction where a customer enters card/account details and tries to pay via PayFast. In this scenario, a temporary transaction token (refer to this section) should be fetched (which will last for short period) and then a tokenized transaction API (refer to this section) will be called to initiate a payment request based on temporary token. In this process, an OTP will be sent to customer mobile number. This token cannot be used for recurring transaction.

Scenario 2

Consider a scenario where a mobile app wants its customer card information for monthly subscription. This is recurring model, where customer will store its instrument such as card details or account details for later use. It is recommended for merchant to get a temporary transaction (refer to this section) token against the card details and perform a small amount of transaction (refer to this section) (which will be refunded) to validate instrument details, based on temporary token. Again, an OTP will be sent to customer and once transaction gets success, merchant will add instruments details and will get the permanent transaction token (refer to this section) which could be used later on for recurring transaction API (refer to this section) and no OTP will be sent to customer.

Scenario 3

Consider a situation where merchant do not want a token against card/account details and want to perform a transaction with the instrument details on the fly. In this case, a simple transaction API (refer to section API Transaction) will serve the purpose. In this process, merchant would call "validate" (refer to section API Customer Account Validation) API to get the OTP for customer and it will be sent with other transaction parameter.

API End Points

This section contains the details of all APIs provided by APPS. These APIs could be called by the merchants, acquirers and/or aggregators. These API’S are based on REST architecture and serve standard HTTP codes for the response payload.

Get Access Token

Following API will provide you the Authentication token, which will be used to call APIs. Merchant_id and Secured_key is mandatory to get the access token. This token will be sent on all the APIs with standard HTTP header ‘Authorization’.

curl -X POST \
  /token \
  -H 'content-type: application/x-www-form-urlencoded' \
  -d 'merchant_id=<MERCHANT_ID>&grant_type=client_credentials&secured_key=<KEY>'
var client = new RestClient("<BASE_URL>/token");
var request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
IRestResponse response = client.Execute(request);
var data = null;

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

xhr.addEventListener("readystatechange", function () {
  if (this.readyState === 4) {
    console.log(this.responseText);
  }
});

xhr.open("POST", "<BASE_URL>/token");
xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
xhr.setRequestHeader("cache-control", "no-cache");

xhr.send(data);
<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_PORT => "8443",
  CURLOPT_URL => "<BASE_URL>/token",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "",
  CURLOPT_HTTPHEADER => array(
    "cache-control: no-cache",
    "content-type: application/x-www-form-urlencoded"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

The above command following JSON structure:

{
  "token": "<token>",
  "refresh_token": "<refresh token>",
  "code": "",
  "message": null,
  "expiry": <no.ofseconds>
}

HTTP Request

POST /token

Post Parameters

Parameter Required Description
merchant_id Yes Merchant ID
secured_key Yes Secured Key
grant_type Yes Grant type ( client_credentials )
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version (optional)

Refresh Access Token

Any access token can be refreshed upon expiry. A refresh token is given along with original token.

curl -X POST \
  <BASE_URL>/refreshtoken \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/x-www-form-urlencoded' 
var client = new RestClient("<BASE_URL>/refreshtoken");
var request = new RestRequest(Method.POST);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
IRestResponse response = client.Execute(request);
var request = require("request");

var options = { method: 'POST',
  url: '<BASE_URL>/refreshtoken',
  headers: 
   { 'postman-token': 'd3aa4037-7744-58b6-9557-0094116be350',
     'cache-control': 'no-cache',
     'content-type': 'application/x-www-form-urlencoded' },
  form: {} };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_PORT => "8443",
  CURLOPT_URL => "<BASE_URL>/refreshtoken",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "",
  CURLOPT_HTTPHEADER => array(
    "cache-control: no-cache",
    "content-type: application/x-www-form-urlencoded"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

The above command returns JSON structured like this:

{
  "token": "<token>",
  "refresh_token": "<refresh token>",
  "code": "",
  "message": null,
  "expiry": <no.ofseconds>
}

HTTP Request

POST /refreshtoken

HTTP Header

Authorization: Bearer <token>

Post Parameters

Parameter Required Description
refresh_token Yes Refresh token
grant_type Yes Grant type ( refresh_token )
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version (optional)

Issuer/Banks

This API will provide the available list of issuer/bank.

curl -X GET \
  <BASE_URL>/list/banks \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/x-www-form-urlencoded' 
var client = new RestClient("<BASE_URL>/list/banks");
var request = new RestRequest(Method.GET);
request.AddHeader("postman-token", "c9dd8d8f-7b5b-13f1-bfb0-2cc97549cd44");
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
IRestResponse response = client.Execute(request);
var request = require("request");

var options = { method: 'GET',
  url: '<BASE_URL>/list/banks',
  headers: 
   { 
     'cache-control': 'no-cache',
     'content-type': 'application/x-www-form-urlencoded' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_PORT => "8443",
  CURLOPT_URL => "<BASE_URL>/list/banks",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => array(
    "cache-control: no-cache",
    "content-type: application/x-www-form-urlencoded"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

The above command returns following JSON structure:

{
  "banks": [
    {
      "bank_code": "xxxxxxxx",
      "name": "xxxxxxxxxxxxx",
      “recurring_allowed”: true/false,
      “otp_required”: true/false
    },
    ……..
  ]"code": null,
  "message": null
}

HTTP Request

GET /list/banks

HTTP Header

Authorization: Bearer <access token>

Query Parameter

Parameter Required Description
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version

Payment (Instrument) Types

This API will provide the Payment type (or account type, e.g. Account, Wallet or Debit Card) based on selected issuer/bank.

curl -X GET \
  '<BASE_URL>/list/instruments?bank_code=<bank code>' \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/x-www-form-urlencoded' 
var client = new RestClient("<BASE_URL>/list/instruments?
bank_code=<bank code>");
var request = new RestRequest(Method.GET);
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
IRestResponse response = client.Execute(request);
var request = require("request");

var options = { method: 'GET',
  url: '<BASE_URL>/list/instruments',
  qs: { bank_code: '<bank code>' },
  headers: 
   { 
     'cache-control': 'no-cache',
     'content-type': 'application/x-www-form-urlencoded' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_PORT => "8443",
  CURLOPT_URL => "<BASE_URL>/list/instruments?bank_code=<bank code>",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => array(
    "cache-control: no-cache",
    "content-type: application/x-www-form-urlencoded"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

The above command returns following JSON structure:

{
  "bankInstruments": [
    {
      "id": "xxxx",
      "name": "xxxxxxx"
    },

  ],
  "code": "00",
  "message": null
}

HTTP Request

GET /list/instruments

HTTP Header

Authorization: Bearer <access token>

Query Parameter

Parameter Required Description
bank_code Yes Bank Code
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version (optional)

Issuer/Banks By Issuer Instrument ID

This API will provide the available list of issuer/bank based on instrument id.

curl -X GET \
  '<BASE_URL>/list/instrumentbanks?instrument_id=1' \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/x-www-form-urlencoded' 
var client = new RestClient("<BASE_URL>/list/instrumentbanks
?instrument_id=1");
var request = new RestRequest(Method.GET);
request.AddHeader("postman-token", "55665a44-4f7e-450b-e6fc-357293e65d6f");
request.AddHeader("cache-control", "no-cache");
request.AddHeader("content-type", "application/x-www-form-urlencoded");
IRestResponse response = client.Execute(request);
var request = require("request");

var options = { method: 'GET',
  url: '<BASE_URL>/list/instrumentbanks',
  qs: { instrument_id: '1' },
  headers: 
   { 
     'cache-control': 'no-cache',
     'content-type': 'application/x-www-form-urlencoded' } };

request(options, function (error, response, body) {
  if (error) throw new Error(error);

  console.log(body);
});

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_PORT => "8443",
  CURLOPT_URL => "<BASE_URL>/list/instrumentbanks?instrument_id=1",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 30,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => array(
    "cache-control: no-cache",
    "content-type: application/x-www-form-urlencoded"
  ),
));

$response = curl_exec($curl);
$err = curl_error($curl);

curl_close($curl);

if ($err) {
  echo "cURL Error #:" . $err;
} else {
  echo $response;
}

The above command returns following JSON structure:

{
  "banks": [
    {
      "bank_code": "xxxxxxxx",
      "name": "xxxxxxxxxxxxx",
      "recurring_allowed": true/false,
      "otp_required": true/false
    },

  ],
  "code": null,
  "message": null
}

HTTP Request

GET /list/instrumentbanks

HTTP Header

Authorization: Bearer <access token>

Query Parameter

Parameter Required Description
instrument_id Yes Instrument Id
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version (optional)

Customer Account Validation

This API will be used if you choose to send OTP to registered mobile number of the customer with that respective Issuer/Bank.

# Example using a bank account

curl --location --request POST '<BASE_URL>/customer/validate' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer <access token>' \
--data-urlencode 'basket_id=<basket_id>' \
--data-urlencode 'txnamt=<total amount>' \
--data-urlencode 'customer_mobile_no=<mobile number>' \
--data-urlencode 'customer_email_address=<email address>' \
--data-urlencode 'account_type_id=3' \
--data-urlencode 'bank_code=<bank code>' \
--data-urlencode 'cnic_number=<cnic number>' \
--data-urlencode 'account_number=<bank account number>' \
--data-urlencode 'order_date=<transaction date>' \
--data-urlencode 'otp_required=yes/no' \
--data-urlencode 'recurring_txn=yes/no'
// Example using a bank account

var client = new RestClient("<BASE_URL>/customer/validate");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Bearer <access token>");
request.AddParameter("basket_id", "<basket_id>");
request.AddParameter("txnamt", "<total amount>");
request.AddParameter("customer_mobile_no", "<mobile>");
request.AddParameter("customer_email_address", "<email>");
request.AddParameter("account_type_id", "3");
request.AddParameter("bank_code", "<bank code>");
request.AddParameter("cnic_number", "<cnic number>");
request.AddParameter("account_number", "<bank account number>");
request.AddParameter("order_date", "<transaction date>");
request.AddParameter("otp_required", "yes/no");
request.AddParameter("recurring_txn", "no");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
// Example using a card number

var request = require('request');
var options = {
  'method': 'POST',
  'url': '<BASE_URL>/customer/validate',
  'headers': {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Bearer <access token>'
  },
  form: {
    'basket_id': '<basket_id>',
    'txnamt': '<total amount>',
    'customer_mobile_no': '<mobile>',
    'customer_email_address': '<email>',
    'account_type_id': '2',         // 2 for card base transaction
    'bank_code': '<bank code>',
    'card_number': '<card_number>'
    'expiry_month': '<expiry_month>',
    'expiry_year': '<expiry_year>',
    'cvv': '<CVV>',
    'order_date': '<transaction date>',
    'otp_required': 'yes/no',
    'recurring_txn': 'no/yes'
  }
};
request(options, function (error, response) { 
  if (error) throw new Error(error);
  console.log(response.body);
});
<?php

//  Example using a card

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "<BASE_URL>/customer/validate",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "basket_id=<basket_id>&txnamt=<total amount>"
  ."&customer_mobile_no=<mobile>&customer_email_address=<email>&account_type_id=2&"
  ."bank_code=<bank code>&card_number=<card number>&expiry_month=<expiry_month>"
  ."&expiry_year=<expiry_year>&cvv=<CVV>"
  ."&order_date=<transaction date>&otp_required=yes/no&recurring_txn=yes/no",
  CURLOPT_HTTPHEADER => array(
    "Content-Type: application/x-www-form-urlencoded",
    "Authorization: Bearer <access token>"
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

The above command returns following JSON structure:

{
    "code": "",
    "message": "",
    "data_3ds_acsurl": "",
    "data_3ds_pareq": "",
    "data_3ds_html": "",
    "data_3ds_secureid": "",
    "data_3ds_gatewayrecommendation": "",
    "transaction_id": ""
}

HTTP Request

POST /customer/validate

HTTP Header

Authorization: Bearer <access token>

Post Parameter

Common Parameters

Parameter Required Description
basket_id Yes Merchant's Basket ID/Order ID/Cart ID/Reference ID
txnamt Yes Transaction total amount
order_date Yes Merchant's order date
customer_mobile_no Yes Customer's mobile number (format: 92-345XXXXXX)
customer_email_address Yes Customer's email address
account_type_id Yes Account type id / Payment instrument id
merCatCode Yes Merchant category code
subMerId No Sub merchant id (for Acquirer)
subMerName No Sub merchant name (for Acquirer)
subMerAbbr No Sub merchant abbreviation (for Acquirer)
secured_hash Conditional Secured hash value
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version (optional)

Card Payment Parameters: (UPI,Master,VISA and Paypak Cards)

Parameter Required Description
card_number Yes Card Number (without spaces/hyphens)
expiry_month Yes Card Expiry Month (e.g. 09 for September)
expiry_year Yes Card Expiry Year (e.g. 2021)
cvv Yes CVV Value
data_3ds_pagemode For 3DS Pagemode for 3DS auth. Required value CUSTOMIZED / SIMPLE
data_3ds_callback_url For 3DS Merchant's call back URL for 3DS auth.

(Refer to 3DS for 3DS verification process)

Bank Account Payment Parameters

Parameter Required Description
bank_code Yes Bank code
account_number Yes Bank account number
cnic_number Yes Customer's CNIC number registered with Bank

Wallet Payment Parameters

Parameter Required Description
bank_code Yes Bank code
account_number Yes Bank account number
cnic_number Yes Customer's CNIC number registered with Bank

Initiate Transaction

This API will initiate payment/transaction request without token.

# Example using bank account
curl --location --request POST '<BASE_URL>/transaction' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer <access token>' \
--data-urlencode 'basket_id=<basket_id>' \
--data-urlencode 'txnamt=<total a amount>' \
--data-urlencode 'customer_email_address=<email>' \
--data-urlencode 'account_type_id=3' \
--data-urlencode 'customer_mobile_no=<mobile>' \
--data-urlencode 'account_number=<account number>' \
--data-urlencode 'cnic_number=<cnic number>' \
--data-urlencode 'account_title=<account title>' \
--data-urlencode 'bank_code=<bank code>' \
--data-urlencode 'order_date=<transaction dat3>' \
--data-urlencode 'otp_required=yes/no' \
--data-urlencode 'recurring_txn=yes/no' \
--data-urlencode 'otp=<OTP>' \
--data-urlencode 'transaction_id=<transaction_id>'
//  Example using bank account

var client = new RestClient("<BASE_URL>/transaction");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Bearer <access token>");
request.AddParameter("basket_id", "<basket_id>");
request.AddParameter("txnamt", "<total amount>");
request.AddParameter("customer_email_address", "<email>");
request.AddParameter("account_type_id", "3");
request.AddParameter("customer_mobile_no", "<mobile>");
request.AddParameter("account_number", "<bank account number>");
request.AddParameter("cnic_number", "<cnic number>");
request.AddParameter("account_title", "<account title>");
request.AddParameter("bank_code", "<bank code>");
request.AddParameter("order_date", "<transaction date>");
request.AddParameter("otp_required", "yes/no");
request.AddParameter("recurring_txn", "yes/no");
request.AddParameter("otp", "<OTP>");
request.AddParameter("transaction_id", "<transaction_id>");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
//  Example using bank account

var request = require('request');
var options = {
  'method': 'POST',
  'url': '<BASE_URL>/transaction',
  'headers': {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Bearer <access token>'
  },
  form: {
    'basket_id': '<basket_id>',
    'txnamt': '<total amount>',
    'customer_email_address': '<email>',
    'account_type_id': '3',
    'customer_mobile_no': '<mobile>',
    'account_number': '<bank account_number>',
    'cnic_number': '<cnic_number>',
    'account_title': '<account_title>',
    'bank_code': '<bank code>',
    'order_date': '<transaction date>',
    'otp_required': 'yes/no',
    'recurring_txn': 'yes/no',
    'otp': '<OTP>',
    'transaction_id': '<transaction_id>'
  }
};
request(options, function (error, response) { 
  if (error) throw new Error(error);
  console.log(response.body);
});

<?php

#  Example using bank account

$client = new http\Client;
$request = new http\Client\Request;
$request->setRequestUrl('<BASE_URL>/transaction');
$request->setRequestMethod('POST');
$body = new http\Message\Body;
$body->append(new http\QueryString(array(
  'basket_id' => '<basket_id>',
  'txnamt' => '<total amount>',
  'customer_email_address' => '<email>',
  'account_type_id' => '3',
  'customer_mobile_no' => '<mobile>',
  'account_number' => 'bank account_number',
  'cnic_number' => '<cnic_number>',
  'account_title' => '<account title>',
  'bank_code' => '<bank code>',
  'order_date' => '<transaction date>',
  'otp_required' => 'yes/no',
  'recurring_txn' => 'yes/no',
  'otp' => '<OTP>',
  'transaction_id' => '')));$request->setBody($body);
$request->setOptions(array());
$request->setHeaders(array(
  'Content-Type' => 'application/x-www-form-urlencoded',
  'Authorization' => 'Bearer <access token>'
));
$client->enqueue($request)->send();
$response = $client->getResponse();
echo $response->getBody();

HTTP Request

`POST /transaction

HTTP Header

Authorization: Bearer <access token>

The above command returns following JSON structure:

{
  "status_code": "",
  "status_msg": "",
  "rdv_message_key": "",
  "basket_id": "",
  "transaction_id": "<transaction id>",
  "code": ""
}

Common Parameters

Parameter Required Description
basket_id Yes Merchant's Basket ID/Order ID/Cart ID/Reference ID
txnamt Yes Transaction total amount
order_date Yes Merchant's order date
customer_mobile_no Yes Customer's mobile number (format: 92-345XXXXXX)
customer_email_address Yes Customer's email address
account_type_id Yes Account type id / Payment instrument id
merCatCode Yes Merchant category code
subMerId No Sub merchant id (for Acquirer)
subMerName No Sub merchant name (for Acquirer)
subMerAbbr No Sub merchant abbreviation (for Acquirer)
secured_hash Conditional Secured hash value
otp Conditional Not required when issuer managed OTP
transaction_id Yes received from customer validation response
eci Yes Received from customer validation response
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version (optional)

Card Payment Parameters: (UPI,Master,VISA and Paypak Cards)

Parameter Required Description
card_number Yes Card Number (without spaces/hyphens)
expiry_month Yes Card Expiry Month (e.g. 09 for September)
expiry_year Yes Card Expiry Year (e.g. 2021)
cvv Yes CVV Value
data_3ds_pagemode For 3DS Pagemode for 3DS auth. Required value CUSTOMIZED / SIMPLE
data_3ds_callback_url For 3DS Merchant's call back URL for 3DS auth.

(Refer to 3DS for 3DS verification process)

Bank Account Payment Parameters

Parameter Required Description
bank_code Yes Bank code
account_number Yes Bank account number
cnic_number Yes Customer's CNIC number registered with Bank
account_title No Account's title

Wallet Payment Parameters

Parameter Required Description
bank_code Yes Bank code
account_number Yes Bank account number
cnic_number Yes Customer's CNIC number registered with Bank
account_title No Account's title

Temporary Transaction Token

Get a temporary payment request token, against card, wallet or account details. This token can be used to send a tokenized transaction.

curl --location --request POST '<BASE_URL>/transaction/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer <access token>' \
--data-urlencode 'merchant_user_id=<merchant's user id>' \
--data-urlencode 'user_mobile_number=<user mobile number>' \
--data-urlencode 'basket_id=<basket_id>' \
--data-urlencode 'txnamt=<total amount>' \
--data-urlencode 'account_type=4' \
--data-urlencode 'bank_code=<bank code>' \
--data-urlencode 'cnic_number=<cnic_number>' \
--data-urlencode 'account_number=<account_number>' \
--data-urlencode 'account_title=<account title>'
var client = new RestClient("<BASE_URL>/transaction/token");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Bearer <access token>");
request.AddParameter("merchant_user_id", "<merchant's user id>");
request.AddParameter("user_mobile_number", "<user mobile number>");
request.AddParameter("basket_id", "<basket_id>");
request.AddParameter("txnamt", "<total amount>");
request.AddParameter("account_type", "4");
request.AddParameter("bank_code", "<bank code>");
request.AddParameter("cnic_number", "<cnic_number>");
request.AddParameter("account_number", "<account_number>");
request.AddParameter("account_title", "<account title>");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
var request = require('request');
var options = {
  'method': 'POST',
  'url': '<BASE_URL>/transaction/token',
  'headers': {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Bearer <access token>'
  },
  form: {
    'merchant_user_id': '<merchant's user id>',
    'user_mobile_number': '<user mobile number>',
    'basket_id': '<basket_id>',
    'txnamt': '<total amount>',
    'account_type': '4',
    'bank_code': '<bank code>',
    'cnic_number': '<cnic_number>',
    'account_number': '<account_number>',
    'account_title': '<account title>'
  }
};
request(options, function (error, response) { 
  if (error) throw new Error(error);
  console.log(response.body);
});

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "<BASE_URL>/transaction/token",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "merchant_user_id=<merchant's user id>&user_mobile_number=<user mobile number>&basket_id=<basket_id>&txnamt=<total amount>&account_type=4&bank_code=<bank code>&cnic_number=<cnic_number>&account_number=<account_number>&account_title=<account_title>",
  CURLOPT_HTTPHEADER => array(
    "Content-Type: application/x-www-form-urlencoded",
    "Authorization: Bearer <access token>"
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

HTTP Request

POST /transaction/token

HTTP Header

Authorization: Bearer <access token>

The above command returns following JSON structure:

{
  "status_code": "*****",
  "status_msg": "*******",
  "instrument_alias": "**********",
  "instrument_token": "********",
  "transaction_id": "<transaction id>",
  "otp_required": true/false,
  "eci": true/false,
  "data_3ds_acsurl": <3DSVerificationURL>,
  "data_3ds_pareq": <3DSRequestValue>,
  "data_3ds_html": <3DSRedirectionHTMLPage>,
  "data_3ds_secureid": <3DSecureID>,
  "data_3ds_gatewayrecommendation": <3DSRecommendation>
}

Common Parameters

Parameter Required Description
basket_id Yes Merchant's Basket ID/Order ID/Cart ID/Reference ID
txnamt Yes Transaction total amount
order_date Yes Merchant's order date
merchant_user_id Yes Merchant's customer/user id
user_mobile_number Yes Merchant's customer/user id
instrument_alias No Instrument alias
account_type Yes Account type id/instrument type id
secured_hash Conditional Secured hash value
merCatCode Yes Merchant category code
subMerId No Sub merchant id (for Acquirer)
subMerName No Sub merchant name (for Acquirer)
subMerAbbr No Sub merchant abbreviation (for Acquirer)
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version (optional)

Card Payment Parameters: (UPI,Master,VISA and Paypak Cards)

Parameter Required Description
card_number Yes Card Number (without spaces/hyphens)
expiry_month Yes Card Expiry Month (e.g. 09 for September)
expiry_year Yes Card Expiry Year (e.g. 2021)
cvv Yes CVV Value
data_3ds_pagemode For 3DS Pagemode for 3DS auth. Required value CUSTOMIZED / SIMPLE
data_3ds_callback_url For 3DS Merchant's call back URL for 3DS auth.

Bank Account Payment Parameters

Parameter Required Description
bank_code Yes Bank code
account_number Yes Bank account number
cnic_number Yes Customer's CNIC number registered with Bank
account_title No Account's title

Wallet Payment Parameters

Parameter Required Description
bank_code Yes Bank code
account_number Yes Bank account number
cnic_number Yes Customer's CNIC number registered with Bank
account_title No Account's title

Tokenized Transaction

Initiate a payment request based on “temporary” transaction token.

curl --location --request POST '<BASE_URL>/transaction/tokenized' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer <access token>' \
--data-urlencode 'instrument_token=<instrument token>' \
--data-urlencode 'transaction_id=<transaction_id>' \
--data-urlencode 'merchant_user_id=<merchant's user id>' \
--data-urlencode 'user_mobile_number=<user mobile number>' \
--data-urlencode 'basket_id=<basket_id>' \
--data-urlencode 'order_date=<transaction date>' \
--data-urlencode 'txndesc=<description>' \
--data-urlencode 'txnamt=<total amount>' \
--data-urlencode 'otp=<OTP>'
var client = new RestClient("<BASE_URL>/transaction/tokenized");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Bearer <access token>");
request.AddParameter("instrument_token", "<instrument token>");
request.AddParameter("transaction_id", "<transaction_id>");
request.AddParameter("merchant_user_id", "<merchant's user id>");
request.AddParameter("user_mobile_number", "<user mobile number>");
request.AddParameter("basket_id", "<basket_id>");
request.AddParameter("order_date", "<transaction date>");
request.AddParameter("txndesc", "<description>");
request.AddParameter("txnamt", "<total amount>");
request.AddParameter("otp", "<OTP>");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
var request = require('request');
var options = {
  'method': 'POST',
  'url': '<BASE_URL>/transaction/tokenized',
  'headers': {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Bearer <access token>'
  },
  form: {
    'instrument_token': '<instrument token>',
    'transaction_id': '<transaction_id>',
    'merchant_user_id': '<merchant's user id>',
    'user_mobile_number': '<user mobile number>',
    'basket_id': '<basket_id>',
    'order_date': '<transaction date>',
    'txndesc': '<description>',
    'txnamt': '<total amount>',
    'otp': '<OTP>'
  }
};
request(options, function (error, response) { 
  if (error) throw new Error(error);
  console.log(response.body);
});

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "<BASE_URL>/transaction/tokenized",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "instrument_token=<instrument token>&transaction_id=<transaction date>&merchant_user_id=<merchant's user id>&user_mobile_number=<user mobile number>&basket_id=<basket_id>&order_date=<transaction date>&txndesc=<description>&txnamt=<total amount>&otp=<OTP>",
  CURLOPT_HTTPHEADER => array(
    "Content-Type: application/x-www-form-urlencoded",
    "Authorization: Bearer <access token>"
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

HTTP Request

POST /transaction/tokenized

HTTP Header

Authorization: Bearer <access token>

The above command returns following JSON structure:

{
  "status_code": "",
  "status_msg": "",
  "rdv_message_key": "",
  "basket_id": "",
  "transaction_id": "<transaction id>",
  "code": ""
}
Parameter Required Description
instrument_token Yes Instrument token
merchant_user_id Yes Merchant's customer/user id
user_mobile_number Yes Merchant's customer/user id
basket_id Yes Merchant's Basket ID/Order ID/Cart ID/Reference ID
txnamt Yes Transaction total amount
order_date Yes Merchant's order date
txndesc Yes Transaction description
otp Conditional Not required when issuer managed OTP
transaction_id Yes Received from get transaction token API
eci Yes Received from customer validation response
merCatCode Yes Merchant category code
subMerId No Sub merchant id (for Acquirer)
subMerName No Sub merchant name (for Acquirer)
subMerAbbr No Sub merchant abbreviation (for Acquirer)
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version
data_3ds_secureid 3DS required-3DS Only
data_3ds_pares 3DS required-3DS Only
secured_hash Conditional Secured hash

Add Permanent Payment Instrument

This API will allow merchants to add permanent payment instrument on PayFast. A token value will be returned to initiate the transaction for this instrument.

curl --location --request POST '<BASE_URL>/user/instruments' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer <access token>' \
--data-urlencode 'merchant_user_id=<merchant's user id>' \
--data-urlencode 'user_mobile_number=<user mobile number>' \
--data-urlencode 'basket_id=<basket_id>' \
--data-urlencode 'txnamt=<total amount>' \
--data-urlencode 'account_type=4' \
--data-urlencode 'bank_code=<bank code>' \
--data-urlencode 'cnic_number=<cnic_number>' \
--data-urlencode 'account_number=<account_number>' \
--data-urlencode 'account_title=<account title>'
var client = new RestClient("<BASE_URL>/user/instruments");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Bearer <access token>");
request.AddParameter("merchant_user_id", "<merchant's user id>");
request.AddParameter("user_mobile_number", "<user mobile number>");
request.AddParameter("basket_id", "<basket_id>");
request.AddParameter("txnamt", "<total amount>");
request.AddParameter("account_type", "4");
request.AddParameter("bank_code", "<bank code>");
request.AddParameter("cnic_number", "<cnic_number>");
request.AddParameter("account_number", "<account_number>");
request.AddParameter("account_title", "<account title>");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
var request = require('request');
var options = {
  'method': 'POST',
  'url': '<BASE_URL>/user/instruments',
  'headers': {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Bearer <access token>'
  },
  form: {
    'merchant_user_id': '<merchant's user id>',
    'user_mobile_number': '<user mobile number>',
    'basket_id': '<basket_id>',
    'txnamt': '<total amount>',
    'account_type': '4',
    'bank_code': '<bank code>',
    'cnic_number': '<cnic_number>',
    'account_number': '<account_number>',
    'account_title': '<account title>'
  }
};
request(options, function (error, response) { 
  if (error) throw new Error(error);
  console.log(response.body);
});

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "<BASE_URL>/user/instruments",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "merchant_user_id=<merchant's user id>&user_mobile_number=<user mobile number>&basket_id=<basket_id>&txnamt=<total amount>&account_type=4&bank_code=<bank code>&cnic_number=<cnic_number>&account_number=<account_number>&account_title=<account_title>",
  CURLOPT_HTTPHEADER => array(
    "Content-Type: application/x-www-form-urlencoded",
    "Authorization: Bearer <access token>"
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

HTTP Request

POST /user/instruments

HTTP Header

Authorization: Bearer <access token>

The above command returns following JSON structure:

{
  "status_code": "*****",
  "status_msg": "*******",
  "instrument_alias": "**********",
  "instrument_token": "********"
}
Parameter Required Description
merchant_user_id Yes Merchant's customer/user id
user_mobile_number Yes Merchant's customer/user id
instrument_alias No Instrument alias
account_type Yes Account type id/Payment type id
transaction_id Yes Last successfull transaction id
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version
merCatCode Yes Merchant category code
subMerId No Sub merchant id (for Acquirer)
subMerName No Sub merchant name (for Acquirer)
subMerAbbr No Sub merchant abbreviation (for Acquirer)
data_3ds_secureid 3DS required-3DS Only
secured_hash Conditional Secured hash value

Card Payment Parameters: (UPI,Master,VISA and Paypak Cards)

Parameter Required Description
card_number Yes Card Number (without spaces/hyphens)
expiry_month Yes Card Expiry Month (e.g. 09 for September)
expiry_year Yes Card Expiry Year (e.g. 2021)
cvv Yes CVV Value

Bank Account Payment Parameters

Parameter Required Description
bank_code Yes Bank code
account_number Yes Bank account number
cnic_number Yes Customer's CNIC number registered with Bank
account_title No Account's title

Wallet Payment Parameters

Parameter Required Description
bank_code Yes Bank code
account_number Yes Bank account number
cnic_number Yes Customer's CNIC number registered with Bank
account_title No Account's title

OTP for Recurring Transaction

This API will generate an OTP and send to customer, which could be used for sending recurring transaction. OTP will be sent by the bank to customer's mobile number registered with the bank account.

curl --location --request POST '<BASE_URL>/transaction/recurring/otp' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer <access token>' \
--data-urlencode 'instrument_token=<instrument token>' \
--data-urlencode 'merchant_user_id=<merchant's user id>' \
--data-urlencode 'user_mobile_number=<mobile>' \
--data-urlencode 'basket_id=<basket_id>' \
--data-urlencode 'txnamt=<Total amount>' \
--data-urlencode 'order_date=<transaction date>'
var client = new RestClient("<BASE_URL>/transaction/recurring/otp");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Bearer <access token>");
request.AddParameter("instrument_token", "<instrument token>");
request.AddParameter("merchant_user_id", "<merchant's user id>");
request.AddParameter("user_mobile_number", "<mobile>");
request.AddParameter("basket_id", "<basket_id>");
request.AddParameter("txnamt", "<Total amount>");
request.AddParameter("order_date", "<Transaction date>");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
var request = require('request');
var options = {
  'method': 'POST',
  'url': '<BASE_URL>/transaction/recurring/otp',
  'headers': {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Bearer a68adea0cc508db888625bd9a4b9d850521219df68cbf33926c4bbff8c50c00f'
  },
  form: {
    'instrument_token': '<instrument token>',
    'merchant_user_id': '<merchant's user id>',
    'user_mobile_number': '<mobile>',
    'basket_id': '<basket_id>',
    'txnamt': '<Total amount>',
    'order_date': '<Transaction date>'
  }
};
request(options, function (error, response) { 
  if (error) throw new Error(error);
  console.log(response.body);
});

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "<BASE_URL>/transaction/recurring/otp",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "instrument_token=<instrument token>&merchant_user_id=<merchant's user id>&user_mobile_number=<mobile>&basket_id=<basket_id>&txnamt=<Total amount>&order_date=<Transaction date>",
  CURLOPT_HTTPHEADER => array(
    "Content-Type: application/x-www-form-urlencoded",
    "Authorization: Bearer <access token>"
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

{
  "status_code": "",
  "status_msg": "",
  "rdv_message_key": "",
  "basket_id": "",
  "transaction_id": "<transaction id>",
  "code": "",
  “eci”: true/false,
  "data_3ds_acsurl": <3DSVerificationURL>,
  "data_3ds_pareq": <3DSRequestValue>,
  "data_3ds_html": <3DSRedirectionHTMLPage>,
  "data_3ds_secureid": <3DSecureID>,
  "data_3ds_gatewayrecommendation": <3DSRecommendation>
}

HTTP Request

POST /transaction/recurring/otp

HTTP Header

Authorization: Bearer <access token>

Parameter Required Description
instrument_token Yes Instrument token
merchant_user_id Yes Merchant's customer/user id
user_mobile_number Yes Merchant's customer/user id
basket_id Yes Merchant's Basket ID/Order ID/Cart ID/Reference ID
txnamt Yes Transaction total amount
order_date Yes Merchant's order date
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version
secured_hash Conditional Secured hash

Initiate Recurring Transaction

Initiate payment transaction based on permanent token. This API will serve recurring payment requests.

curl --location --request POST '<BASE_URL>/transaction/recurring' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer <access token>' \
--data-urlencode 'instrument_token=<instrument token>' \
--data-urlencode 'transaction_id=<transaction_id>' \
--data-urlencode 'merchant_user_id=<merchant's user id>' \
--data-urlencode 'user_mobile_number=<user mobile number>' \
--data-urlencode 'basket_id=<basket_id>' \
--data-urlencode 'order_date=<transaction date>' \
--data-urlencode 'txndesc=<description>' \
--data-urlencode 'txnamt=<total amount>' \
--data-urlencode 'otp=<OTP>'
var client = new RestClient("<BASE_URL>/transaction/recurring");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Bearer <access token>");
request.AddParameter("instrument_token", "<instrument token>");
request.AddParameter("transaction_id", "<transaction_id>");
request.AddParameter("merchant_user_id", "<merchant's user id>");
request.AddParameter("user_mobile_number", "<user mobile number>");
request.AddParameter("basket_id", "<basket_id>");
request.AddParameter("order_date", "<transaction date>");
request.AddParameter("txndesc", "<description>");
request.AddParameter("txnamt", "<total amount>");
request.AddParameter("otp", "<OTP>");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
var request = require('request');
var options = {
  'method': 'POST',
  'url': '<BASE_URL>/transaction/recurring',
  'headers': {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Bearer <access token>'
  },
  form: {
    'instrument_token': '<instrument token>',
    'transaction_id': '<transaction_id>',
    'merchant_user_id': '<merchant's user id>',
    'user_mobile_number': '<user mobile number>',
    'basket_id': '<basket_id>',
    'order_date': '<transaction date>',
    'txndesc': '<description>',
    'txnamt': '<total amount>',
    'otp': '<OTP>'
  }
};
request(options, function (error, response) { 
  if (error) throw new Error(error);
  console.log(response.body);
});

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "<BASE_URL>/transaction/recurring",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "instrument_token=<instrument token>&transaction_id=<transaction date>&merchant_user_id=<merchant's user id>&user_mobile_number=<user mobile number>&basket_id=<basket_id>&order_date=<transaction date>&txndesc=<description>&txnamt=<total amount>&otp=<OTP>",
  CURLOPT_HTTPHEADER => array(
    "Content-Type: application/x-www-form-urlencoded",
    "Authorization: Bearer <access token>"
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

HTTP Request

POST /transaction/recurring

HTTP Header

Authorization: Bearer <access token>

The above command returns following JSON structure:

{
  "status_code": "",
  "status_msg": "",
  "rdv_message_key": "",
  "basket_id": "",
  "transaction_id": "<transaction id>",
  "code": ""
}
Parameter Required Description
instrument_token Yes Instrument token
merchant_user_id Yes Merchant's customer/user id
user_mobile_number Yes Merchant's customer/user id
basket_id Yes Merchant's Basket ID/Order ID/Cart ID/Reference ID
txnamt Yes Transaction total amount
order_date Yes Merchant's order date
txndesc Yes Transaction description
otp Conditional Not required when issuer managed OTP
transaction_id Yes Received from get transaction token API
eci Yes Received from customer validation response
merCatCode Yes Merchant category code
subMerId No Sub merchant id (for Acquirer)
subMerName No Sub merchant name (for Acquirer)
subMerAbbr No Sub merchant abbreviation (for Acquirer)
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version
data_3ds_secureid 3DS required-3DS Only
data_3ds_pares 3DS required-3DS Only
secured_hash Conditional Secured hash

Fetch Permanent Instrument Token

This API will fetch a list of stored permanent instrument tokens against provided merchant's users information.

var client = new RestClient("<BASE_URL>/user/instruments?merchant_user_id=<merchant's user id>
&user_mobile_number=<user mobile number>");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Bearer <access token>");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
var client = new RestClient("<BASE_URL>/user/instruments?merchant_user_id=<merchant's user id>&
user_mobile_number=<user mobile number>");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Bearer <access token>");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
var request = require('request');
var options = {
  'method': 'GET',
  'url': '<BASE_URL>/user/instruments?merchant_user_id=<merchant's user id>
  &user_mobile_number=<user mobile number>',
  'headers': {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Bearer <access token>'
  },
  form: {

  }
};
request(options, function (error, response) { 
  if (error) throw new Error(error);
  console.log(response.body);
});

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "<BASE_URL>/user/instruments?merchant_user_id=<merchant's user id>&user_mobile_number=<user mobile number>",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => array(
    "Content-Type: application/x-www-form-urlencoded",
    "Authorization: Bearer <access token>"
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

HTTP Request

GET /user/instruments

HTTP Header

Authorization: Bearer <access token>

[
  {
    "instrument_token": "**********",
    "account_type": "*",
    "description": "***",
    "instrument_alias": "**********"
  }
]
Parameter Required Description
merchant_user_id Yes Merchant's customer/user id
user_mobile_number Yes Merchant's customer/user id
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version
secured_hash Conditional Secured hash

Delete Instrument

This API will allow merchants to delete stored permanent instrument tokens.

curl --location --request DELETE '<BASE_URL>/user/instruments' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer <access token>' \
--data-urlencode 'merchant_user_id=<merchant user id>' \
--data-urlencode 'user_mobile_number=<user mobile number>' \
--data-urlencode 'instrument_alias=<instrument alias>' \
--data-urlencode 'instrument_token=<instrument_token>'
var client = new RestClient("<BASE_URL>/user/instruments");
client.Timeout = -1;
var request = new RestRequest(Method.DELETE);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Bearer <access token>");
request.AddParameter("merchant_user_id", "<merchant user id>");
request.AddParameter("user_mobile_number", "<user mobile number>");
request.AddParameter("instrument_alias", "<instrument alias>");
request.AddParameter("instrument_token", "<instrument_token>");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
var request = require('request');
var options = {
  'method': 'DELETE',
  'url': '<BASE_URL>/user/instruments',
  'headers': {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Bearer <access token>'
  },
  form: {
    'merchant_user_id': '<merchant user id>',
    'user_mobile_number': '<user mobile number>',
    'instrument_alias': '<instrument alias>',
    'instrument_token': '<instrument_token>'
  }
};
request(options, function (error, response) { 
  if (error) throw new Error(error);
  console.log(response.body);
});

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "<BASE_URL>/user/instruments",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "DELETE",
  CURLOPT_POSTFIELDS => "merchant_user_id=<merchant user id>&user_mobile_number=<user mobile number>&instrument_alias=<instrument alias>&instrument_token=<instrument_token>",
  CURLOPT_HTTPHEADER => array(
    "Content-Type: application/x-www-form-urlencoded",
    "Authorization: Bearer <access token>"
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

The above command returns following JSON structure:

{
  "code": Error/SuccessCode,
  "message": ResponseMessage,

}

HTTP Request

DELETE /user/instruments

HTTP Header

Authorization: Bearer <access token>

Parameter Required Description
merchant_user_id Yes Merchant's customer/user id
user_mobile_number Yes Merchant's customer/user id
instrument_token Yes Instrument token
instrument_alias No Instrument alias
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version
secured_hash Conditional Secured hash

Refund Transaction Request

This API will allow merchant to initiate the request for transaction refund in case of any dispute in the transaction.

curl --location --request POST '<BASE_URL>/transaction/refund/<transaction_id>' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer a68adea0cc508db888625bd9a4b9d850521219df68cbf33926c4bbff8c50c00f' \
--data-urlencode 'txnamt=<total amount>' \
--data-urlencode 'refund_reason=Refund'
var client = new RestClient("<BASE_URL>/transaction/refund/<transaction_id>");
client.Timeout = -1;
var request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Bearer a68adea0cc508db888625bd9a4b9d850521219df68cbf33926c4bbff8c50c00f");

request.AddParameter("txnamt", "<total amount>");
request.AddParameter("refund_reason", "Refund");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);
var request = require('request');
var options = {
  'method': 'POST',
  'url': '<BASE_URL>/transaction/refund/<transaction_id>',
  'headers': {
    'Content-Type': 'application/x-www-form-urlencoded',
    'Authorization': 'Bearer a68adea0cc508db888625bd9a4b9d850521219df68cbf33926c4bbff8c50c00f'
  },
  form: {
    'txnamt': '<total amount>',
    'refund_reason': 'Refund'
  }
};
request(options, function (error, response) { 
  if (error) throw new Error(error);
  console.log(response.body);
});

<?php

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "<BASE_URL>/transaction/refund/<transaction_id>",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "POST",
  CURLOPT_POSTFIELDS => "&txnamt=<total amount>&refund_reason=Refund",
  CURLOPT_HTTPHEADER => array(
    "Content-Type: application/x-www-form-urlencoded",
    "Authorization: Bearer a68adea0cc508db888625bd9a4b9d850521219df68cbf33926c4bbff8c50c00f"
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

HTTP Request

POST /user/instruments

HTTP Header

Authorization: Bearer <access token>

The above command returns following JSON structure:

{
  "code": Error/SuccessCode,
  "message": ResponseMessage,

}
Parameter Required Description
transaction_id Yes Successfull transaction id to be refunded
txnamt Yes Original transaction amount
refund_reason Yes Reason of refund
reserved_1 Conditional Reserved Field 1
reserved_2 Conditional Reserved Field 2
reserved_3 Conditional Reserved Field 3
api_version No API Version

Get Transaction Status

This API will provide transaction status with respect to the transaction id or the merchant's provided basket id/order id.

// Example code for getting transaction status
// using transaction id

curl --location --request GET '<BASE_URL>/transaction/<transaction_id>' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--header 'Authorization: Bearer <access token>'

// Example code for getting transaction status
// using transaction id

var client = new RestClient("<BASE_URL>/transaction/<transaction_id>");
client.Timeout = -1;
var request = new RestRequest(Method.GET);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Authorization", "Bearer <access token>");
IRestResponse response = client.Execute(request);
Console.WriteLine(response.Content);

// Example code for getting transaction status
// using merchant's provided basket id

var request = require('request');
var options = {
  'method': 'GET',
  'url': '<BASE_URL>/transaction/basket_id/<basket id>?order_date=<order/transaction date>',
  'headers': {
    'Content-Type': 'application/x-www-form-urlencoded'
  },
  formData: {

  }
};
request(options, function (error, response) { 
  if (error) throw new Error(error);
  console.log(response.body);
});

<?php

// Example code for getting transaction status
// using transaction id

$curl = curl_init();

curl_setopt_array($curl, array(
  CURLOPT_URL => "<BASE_URL>/transaction/<transaction_id>",
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_ENCODING => "",
  CURLOPT_MAXREDIRS => 10,
  CURLOPT_TIMEOUT => 0,
  CURLOPT_FOLLOWLOCATION => true,
  CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  CURLOPT_CUSTOMREQUEST => "GET",
  CURLOPT_HTTPHEADER => array(
    "Content-Type: application/x-www-form-urlencoded",
    "Authorization: Bearer <access token>"
  ),
));

$response = curl_exec($curl);

curl_close($curl);
echo $response;

The above command returns following JSON structure:

{
  "status_code": "",
  "status_msg": "",
  "rdv_message_key": "",
  "basket_id": "",
  "transaction_id": "<transaction id>",
  "code": ""
}

HTTP Request

Transaction status by transaction id

GET /transaction/<transaction_id>

Parameter Required Description
api_version No API Version
Transaction status by basket id/order id (last)

GET /transaction/basket_id/<basket_id>

Parameter Required Description
order_date Yes Transaction/Order Date
api_version No API Version

HTTP Header

Authorization: Bearer <access token>

Error Codes

CODES DESCRIPTION
00 Processed OK
002 Time Out
001 Pending
97 Dear Customer, you have an insufficient Balance to proceed
106 Dear Customer, Your transaction Limit has been exceeded please contact your bank
3 You have entered an Inactive Account
14 Entered details are Incorrect
55 You have entered an Invalid OTP/PIN
54 Card Expired
13 You have entered an Invalid Amount
126 Dear Customer your provided Account details are Invalid
75 Maximum PIN Retries has been Exceeded
14 Dear Customer, You have entered an In-Active Card number
15 Dear Customer, You have entered an In-Active Card number
42 Dear Customer, You have entered an Invalid CNIC
423 Dear Customer, We are unable to process your request at the moment please try again later
41 Dear Customer, entered details are Mismatched
801 {0} is your PayFast OTP (One Time Password). Please do not share with anyone.
802 OTP could not be sent. Please try again later.
803 OTP has been sent to your email address.
804 OTP has been sent to your mobile number.
805 OTP Verified
806 OTP could not be verified.
807 Too many attempts. Please try again later in few minutes.
808 Passwords do not match
809 Invalid Password
810 Password could not be changed
811 Password changed successfully
812 Request could not be validated. Please try again.
813 Email address already registered
850 OTP not required because issuer manages OTP itself.
851 OTP required for permanent token.
79 Alternate Success response

Hashed Parameters

API Reference Parameters to be Hashed Data
Temp. Token For Transaction merchant_user_id For Card :- merchant_user_id+ user_mobile_number+card_number+ expiry_month+expiry_year+cvv
user_mobile_number For Account :- merchant_user_id+ user_mobile_number+account_number+ cnic_number
Card card_number
expiry_month For Wallet: - merchant_user_id+ user_mobile_number+account_number+ cnic_number
expiry_year
cvv
Account account_number
cnic_number
Wallet account_number
cnic_number
Temp. Token Transaction instrument_token instrument_token + merchant_user_id + user_mobile_number + txnamt + otp
merchant_user_id
user_mobile_number
txnamt
otp
OTP for Recurring Transaction instrument_token instrument_token + merchant_user_id + user_mobile_number
merchant_user_id
user_mobile_number
Initiate Recurring Transaction instrument_token instrument_token + merchant_user_id + user_mobile_number + txnamt + basket_id
merchant_user_id [+ otp]
user_mobile_number
txnamt
basket_id
otp (if applicable)
Customer Account Validation
Common basket_id
txnamt
Card card_number basket_id + txnamt + card_number+ expiry_month+expiry_year+cvv
expiry_month
expiry_year basket_id + txnamt + account_number + cnic_number
cvv
Account account_number basket_id + txnamt + account_number + cnic_number
cnic_number
Wallet account_number
cnic_number
Inititate Transaction without Token
Common basket_id
txnamt
otp
Card card_number basket_id + txnamt + card_number+ expiry_month+expiry_year+cvv+otp
expiry_month
expiry_year basket_id + txnamt + account_number + cnic_number+otp
cvv
Account account_number basket_id + txnamt + account_number + cnic_number+otp
cnic_number
Wallet account_number
cnic_number
txnamt
otp
Add Permanent Instrument
merchant_user_id merchant_user_id+ user_mobile_number+card_number+ expiry_month+expiry_year+cvv
user_mobile_number merchant_user_id+ user_mobile_number+account_number+ cnic_number
Card card_number
expiry_month merchant_user_id+ user_mobile_number+account_number+ cnic_number
expiry_year
cvv
Account account_number
cnic_number
Wallet account_number
cnic_number
Fetch Permanent Instruments
merchant_user_id merchant_user_id+ user_mobile_number
user_mobile_number
Delete Stored Instrument
merchant_user_id merchant_user_id + user_mobile_number + instrument_token
user_mobile_number
instrument_token

Note:

  1. “+” symbol denotes simple string concatenation.

  2. Hashed data will be received in “secured_hash” parameter in designated APIs

  3. A key will be provided separately for the hash calculation.

3D Secure Transaction

In case of Visa/Master card transaction, 3DS verification will be performed.

A. APIs Customer Account Validation, Get Temp. Transaction Token, OTP for Recurring Transaction, will accept an additional parameter data_3ds_pagemode, it may have 2 values: CUSTOMIZED / SIMPLE.

i. In CUSTOMIZED case, data_3ds_acsurl will be returned in APIs Customer Account Validation, Get Temp. Transaction Token, OTP for Recurring Transaction, which contains 3DS verification URL where the customer should be redirected to verify the transaction with OTP.

To redirect customer, a form will be posted to data_3ds_acsurl along with following fields:

>>>1. PaReq: <data_3ds_pareq> (will be returned in response in APIs Customer Account Validation, Get Temp. Transaction Token, OTP for Recurring Transaction)

>>>2. TermUrl: Call Back URL (URL where customer will be returned after 3DS verification)

>>>3. MD: "" (empty value)

ii. In SIMPLE case, data_3ds_html, will contain a pre-formatted html page which can be used directly to redirect customer to verify OTP. TermUrl will be prepopulated due to data_3ds_callback_url parameter in APIs 3.10, 3.3, 3.5.

B. APIs Initiate Transaction without Token, Temp. Token Transaction, Initiate Recurring Transaction, will accept 2 more additional parameters for 3DS verification:

i. data_3ds_secureid: This will be returned in response of APIs Customer Account Validation, Get Temp. Transaction Token, OTP for Recurring Transaction (Customer Validation).

ii. data_3ds_pares: This value will be returned in call back URL which is called after 3DS verification, the parameter is returned paRes.