Payout Seamless UPI Integration

For onboarding in both the UAT and production environments, the merchant needs to provide the following information:

Technical Information Required:

  1. IP Address: (For dynamic IPs, please provide a range of IP addresses.)

  2. Callback URL/WebHook URL: This is used to notify your server of any changes in transaction status from our back office.

Once the merchant has provided all the required technical details, we will complete the necessary configuration in our back office and provide the Merchant ID (MID/PID) along with login credentials.

Let's see how it works:

  1. The merchant will send a payout request through our API. Along with that, the merchant has to send the customer's UPI id, account_holder, the amount, payment_mode as upi.

  2. Once we receive the request in the correct format, we will share a payment reference string (ref_code) that is necessary for any future references or actions related to the transaction.

  3. After the payout is processed, whether successful or failed, we will send callback data to the provided callback URL (WebHook URL) with the details of the transaction.

  4. To confirm the status of the payout, the merchant can use the polling API to check the current status and update their system accordingly with the payment outcome. (Status Polling)

Api Key

Your provided API key must be passed in X-Api-Key header for all requests.

Security

All requests are secured with signature verification and IP whitelisting.

AUTHENTICATION :

Required Authentication Headers

All requests must include the following authentication:

  • API Key: Your provided API key must be passed in X-Api-Key header

  • Signature: SHA256 signature for request validation

  • IP Whitelisting: Your server IP must be pre-registered in our system.

All V2 API requests must be authenticated by including a signature parameter in the JSON body.

The logic for creating this signature is identical for all V2 endpoints. Please follow the detailed instructions on our central guide:

➡️ V2 API Signature Generation

PAYOUT REQUEST :

Before proceeding with this section of the document, the developer must review the basic workflow. This page explains how to submit a payment request.

Note: All requests must originate from whitelisted IP addresses. (Please ensure that your IP is whitelisted before making a request.)

Payout Request

POST baseurl/payout/api/v2/request.php

Headers

Name
Type
Required
Value

Content-Type

string

Required

application/json

X-Api-Key

string

Required

Your API Key for authentication

Request Parameters

Name
Type
Required
Description

pid

string

Required

Your provided merchant/partner ID

amount

integer

Required

Payout amount

order_id

string

Required

unique order identifier (minimum 7 characters, alphanumeric)

payment_mode

string

Required

Payment method: upi

email

string

Required

Recipient's email address

phone

string

Required

Recipient's phone number

latitude

string

Required

latitude coordinate

longitude

string

Required

longitude coordinate

signature

string

Required

SHA256 hash for request verification

ip

string

Required

Customer's IP address

vpa

string

Required

Benificiary UPI Virtual Payment Address (e.g., user@paytm)

account_holder

string

Required

Benificiary Account holder name

Important: The signature is mandatory for all requests to ensure security and data integrity.

Sample Request

{
    "pid": "MERCHANT123",
    "amount": 10000,
    "order_id": "ORDER123456789",
    "payment_mode": "upi",
    "email": "[email protected]",
    "phone": "9876543210",
    "latitude": "28.7041",
    "longitude": "77.1025",
    "vpa": "user@paytm",
    "account_holder": "John Doe",
    "signature": "a1b2c3d4e5f6789012345678901234567890123456789012345678901234567890"
    "ip": "your_ip_address"
}

Sample Response Body

HTTP status: 200 OK

Content-Type: application/json

{
    "status": "success",
    "ref_code": "a1b2c3d4e5f6789012345678901234567890",
    "message": "Request accepted"
}

Error Response Examples

HTTP status: 200 OK

Content-Type: application/json

Note

This endpoint always returns an HTTP 200 status code, even in case of errors. To determine whether the request was successful or not, check the value of the status field in the JSON response — it will be "success" for successful requests and "error" for failed ones.

{
    "status": "error",
    "message": "pid, amount, order_id, email, phone, latitude, longitude and payment_mode are required fields"
}

Important Notes

  • Order ID: Must be unique and atleast 7 characters long, containing only alphanumeric characters, hyphens and underscores

  • Phone Number: If longer than 10 digits, only the last 10 digits will be used

  • IP Whitelisting: Your IP address must be whitelisted for production use

  • Rate Limiting: API calls are rate-limited based on your merchant configuration

  • Signature: Always include a valid signature for request authentication

  • API Key: Must be included in the X-Api-Key header for all requests

Status Codes

Status
Description

success

Payout request accepted and processing

error

Request failed due to validation, authentication or system error

CALLBACK

We trigger your callback URL whenever there is a change in the transaction status.

Valid Transaction status are:

  1. Approved

  2. Declined

  3. Pending

  4. Processing

  5. Failed

  6. Refunded

The most famous transaction changes are (but not limited):

  1. Pending => Processing

  2. Pending => Approved

  3. Pending => Declined/Failed

  4. Approved => Declined/Failed

The callback landing page must be hosted on your server at a secret path, but it should still be publicly accessible from our whitelisted IPs. (Please ensure you accept data only from our IPs by implementing proper IP whitelisting.)

In the POST body, you will receive the following properties in JSON format:

Name
Type
Description

order_id

string

Your order id shared

requested_amount

int

requested amount

processed_amount

int

received amount

bank_ref

string

bank reference/UTR details if available

sender_pg

string

sender account name id if available

ref_code

string

unique code for the transaction

status

string

status of payment at this time

post_hash

string

post hash for security verification

payment_type

string

payment type

request_time

string

payment request time

action_time

string

payment made time

upi_vpa

string

receiver vpa

account_no

string

receiver account number

account_holder

string

receiver name

ifsc

string

receiver IFSC code

bank_name

string

receiver bank name if available

bank_address

string

receiver bank address if available

transaction_info

array

status change log of transaction

Follow the steps to verify the integrity of received data:

  1. Capture and Decode the Payload

Capture the raw JSON data from the POST body and decode it into an array or object.

// Capture and decode the raw POST body
$data = file_get_contents("php://input");
$array = json_decode($data, true);
  1. Extract and Decode the post_hash

The post_hash field in the JSON payload is a Base64-encoded string. You must Base64-decode it to get the raw binary data.

// Get the Base64 string from the array
$post_hash_base64 = $array['post_hash'];
 
// Decode it to get the raw binary ciphertext
$ivHashCiphertext = base64_decode($post_hash_base64);
  1. Decrypt the Binary Data to get the Remote Hash

Pass the raw binary data (not the Base64 string) to the decrypt function along with your secret key.

//$secret_key is your provided SECRET KEY
$remote_hash = decrypt($ivHashCiphertext, $secret_key);

The decrypt function for your language is provided in the reference section below.

function decrypt($ivHashCiphertext, $password) 
{    
    $method = "AES-256-CBC";    
    $iv = substr($ivHashCiphertext, 0, 16);    
    $hash = substr($ivHashCiphertext, 16, 32);    
    $ciphertext = substr($ivHashCiphertext, 48);    
    $key = hash('sha256', $password, true);    
    if (!hash_equals(hash_hmac('sha256', $ciphertext . $iv, $key, true),$hash)) 
    return null;    
    return openssl_decrypt($ciphertext, $method, $key,    		    
    OPENSSL_RAW_DATA, $iv);                               
}
  1. Compute the Local Hash

Compute the local hash using the MD5 128-bit hashing algorithm. Use the order_id, processed_amount, and status received in the callback array.

Note: processed_amount may be a number or null. You must convert it to a string exactly as shown below to match our server's hash.

// Get the values from the same callback $array
$order_id = $array['order_id'];
$processed_amount = (string)$array['processed_amount']; 
$status = $array['status'];
 
$local_hash = md5($order_id . $processed_amount . $status . $secret_key);
  1. Verify Hash

Compare the decrypted remote_hash from the request and the computed local_hash.

if ($remote_hash !== null && hash_equals($local_hash, $remote_hash)) {  
    // Mark the transaction as success & process the order  
    $hash_status = "Hash Matched";    
} else {  
    // Verification failed       
    $hash_status = "Hash Mismatch";  
}
  1. Acknowledge the Payment Gateway

To confirm you have received the callback and to prevent our gateway from sending retries, you must do two things:

  • Respond with an HTTP 200 OK status code.

  • Respond with a JSON body containing the key "acknowledge" set to the string "yes".

// --- This is the required acknowledgment ---

// 1. Set the HTTP 200 OK status code
http_response_code(200);

// 2. Prepare the required JSON response body
$response_data = [
    'acknowledge' => 'yes',
    'hash_status' => $hash_status // You can include this for your own logs
];

// 3. Send the response
header('Content-Type: application/json; charset=utf-8');
echo json_encode($response_data);
exit;

STATUS POLLING :

The payout Status Polling API allows you to retrieve real-time status information for payout transactions. This endpoint provides comprehensive transaction details including payment status, amounts and verification hashes.

Note : All requests must include proper authentication headers and IP whitelisting may be required for production environments.

POST {baseurl}/payout/api/v2/status_polling.php

Authentication

This API required the following authentication mechanism:

Required Headers

Header
Type
Required
Description

Content-Type

string

Yes

Must be application/json

X-Api-Key

string

Yes

Your provided API key for authentication

IP Whitelisting

Your server IP address must be whitelisted in production environments. Contact your administration to ensure your IP is properly configured.

Request Parameters

Parameter
Type
Required
Description

pid

string

Yes

Your unique merchant/partner ID

ref_code

string

Yes

unique ref_code which is generated in payout request

post_hash

string

Yes

The Base64-encoded encrypted hash. (See steps below).

Steps to generate post_hash :

  1. Generate the Request post_hash

The following code shows the complete flow of creating the MD5 hash, encrypting it, and Base64-encoding it to get the final post_hash string.

$ref_code = "YOUR_REF_CODE";
$pid = "YOUR_PID";
$secret_key = "YOUR_SECRET_KEY";

// 1.1 Create Plaintext Hash
$local_hash = md5($ref_code . $pid . $secret_key);

// 1.2 Encrypt the Hash
// (Assumes encrypt() function is defined from reference)
$encrypted_hash = encrypt($local_hash, $secret_key);

// 1.3 Base64 Encode
$post_hash = base64_encode($encrypted_hash);

// $post_hash is now ready to be sent

Refer the encrpt function below of your language:

function encrypt($plaintext, $password) 
{    
    $method = "AES-256-CBC";    
    $key = hash('sha256', $password, true);    
    $iv = openssl_random_pseudo_bytes(16);    
    $ciphertext = openssl_encrypt($plaintext, $method, $key,       
        OPENSSL_RAW_DATA, $iv);    
    $hash = hash_hmac('sha256', $ciphertext . $iv, $key, true);    
    return $iv . $hash . $ciphertext;
}
  1. Send the POST Request

Send a POST request containing pid, ref_code, and post_hash as a JSON body , and you will receive a response after validating the data.

<?php
// --- Step 1: Data (from above) ---
$pid = "YOUR_PID";
$ref_code = "YOUR_REF_CODE";
$secret_key = "YOUR_SECRET_KEY";
$api_key = "YOUR_API_KEY_HERE";

$local_hash = md5($ref_code . $pid . $secret_key);
$encrypted_hash = encrypt($local_hash, $secret_key);
$post_hash = base64_encode($encrypted_hash);

// --- Step 2: Send Request ---
$api_url = "https://<domain>/payout/api/v2/status_polling.php";

$data = [
    'pid' => $pid,
    'ref_code' => $ref_code,
    'post_hash' => $post_hash
];

$headers = [
    'Content-Type: application/json',
    'X-Api-Key: ' . $api_key
];

$ch = curl_init();

curl_setopt($ch, CURLOPT_URL, $api_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);

$server_output = curl_exec($ch);
curl_close($ch);

// $server_output now contains the JSON response
?>
  1. Processing the API Response

The API will respond with a JSON object. If the request is successful and the ref_code is found, it will return the transaction details. If it fails (e.g., bad hash, ref_code not found), it will return an error message.

Success Response Parameters

A successful response will contain the following parameters as JSON body:

order_id

String

Your order id shared

requested_amount

Number

Requested amount (as a float)

processed_amount

Number

Processed amount (as a float, e.g., 100.0 or 0.0)

bank_reference

String

Bank reference/UTR details if available

ref_code

String

Unique code for the transaction

status

String

Status of payment at this time

time

Number

Raw Unix timestamp of the last action

payment_type

String

Payment type (e.g., UPI, IMPS)

request_time

String

Payment request time (ISO 8601 format)

action_time

String

Payment made time (ISO 8601 format)

upi_vpa

String

Receiver VPA if available

account_no

String

Receiver account number

account_holder

String

Receiver name

ifsc

String

Receiver IFSC code

bank_name

String

Receiver bank name if available

bank_address

String

Receiver bank address if available

transaction_info

Array

Status change log of the transaction

post_hash

String

Post hash for security verification

Error Response

If your request fails, the server will respond with an appropriate HTTP status code and a JSON error body.

{
    "error": "error message"
}

Common Errors and HTTP status

HTTP Status
Error Message
Description

400

No input data received

The raw POST body was empty.

400

Invalid JSON format in request body

The data sent was not valid JSON.

400

Missing required parameters

Your JSON body is missing pid, ref_code, or post_hash.

400

Invalid base64 encoding in post_hash

The post_hash string was not valid Base64.

400

Invalid hash

The signature verification failed. The hashes do not match.

400

Reference code not found

The requested ref_code does not exist for this PID.

4S1

Invalid PID

The pid sent does not exist in our system.

401

X-Api-Key header is required

The X-Api-Key was not found in the request headers.

401

Invalid API key

The provided X-Api-Key is incorrect.

403

Please contact admin to request white list your IP: ...

Your server's IP is not in the payout whitelist.

429

[Dynamic rate limit message]

You have exceeded the allowed request rate.

500

Database connection failed

A server-side error occurred. Please contact support.

  1. Verify the Response post_hash

Before trusting any data from the response, you must verify its post_hash. This verification logic is identical to the logic used for verifying a callback.

You do not need to send an "Acknowledge" response for a status poll.

4.1 Compute the Local Hash

From the JSON response, get the order_id, processed_amount, and status. Concatenate them with your secret_key and create an MD5 hash.

Note: The processed_amount is a float in the response. The code below correctly handles converting it to a string to match our server's hash.

// $response_data is the decoded JSON response
$order_id = $response_data['order_id'];
// PHP converts the float (e.g., 100.0) to "100"
$processed_amount = $response_data['processed_amount'];
$status = $response_data['status'];
 
$local_hash = md5($order_id . $processed_amount . $status . $secret_key);

4.2 Decrypt the Remote Hash

Get the post_hash string from the JSON response. Base64-decode it, then pass the raw binary data to the decrypt function.

// Get the Base64 string from the response
$post_hash_base64 = $response_data['post_hash'];
 
// Decode it to get the raw binary ciphertext
$ivHashCiphertext = base64_decode($post_hash_base64);
 
// Decrypt using the function from the reference section below
$remote_hash = decrypt($ivHashCiphertext, $secret_key);

The decrypt function for your language is provided in the reference section below.

function decrypt($ivHashCiphertext, $password) 
{    
    $method = "AES-256-CBC";    
    $iv = substr($ivHashCiphertext, 0, 16);    
    $hash = substr($ivHashCiphertext, 16, 32);    
    $ciphertext = substr($ivHashCiphertext, 48);    
    $key = hash('sha256', $password, true);    
    if (!hash_equals(hash_hmac('sha256', $ciphertext . $iv, $key, true),$hash)) 
    return null;    
    return openssl_decrypt($ciphertext, $method, $key,    		    
    OPENSSL_RAW_DATA, $iv);                               
}

4.3 Compare the Hashes

Securely compare the local_hash you just computed with the remote_hash you decrypted.

if ($remote_hash !== null && hash_equals($local_hash, $remote_hash)) {
    // --- SUCCESS: Data is verified ---
    // You can now trust the data and update your database.
    // echo "Status: " . $response_data['status'];
} else {
    // --- FAILURE: Hash mismatch! ---
    // Do NOT trust this data.
}

TRANSACTION STATUS :

  1. Approved: Payment has been successfully approved by our system.

  2. Failed: Payment failed on the bank's side.

  3. Processing: The bank is currently processing the payment.

  4. Declined: Payment has been declined by our system.

  5. Pending: The user session is active, and the payment is awaiting completion.

  6. Refunded : The amount is refunded to customer

ERROR :

  1. No valid channel found : This error raise if no active channel/provider mapped against your MID

  2. Bank error , please contact admin : Unknown bank side error

  3. Hash value is not defined : This error raises for status check if the provided hash is not matched with genereted one

  4. Ref code does not exist : This error raise in the status check , if ref_code is not provided for status check

COMPLAINT

We have a dedicated Complaint Section where merchants can manage transaction-related complaints. Through this section, merchants can submit complaints with all necessary details and optional evidence. Upon submission, a unique complaint reference ID is generated, allowing merchants to track the complaint’s status and receive real-time updates via the status-check API. This ensures a smooth, secure, and efficient process for resolving any transaction issues.

Complaint

RECONCILIATION

This API endpoint allows authorized users to retrieve approved payout transactions based on a specific pid (Partner ID) and date. The API performs authentication using a token and signature verification to ensure secure communication.

Reconciliation

Last updated