Payin P2P Non-Seamless IMPS Integration
Request Payment from Web/Android/iOS App Follow these steps to integrate the payment request from your app.
Note: The IP must be whitelisted by us to accept the request. This process involves back-end to back-end communication
PAYMENT REQUEST :
Gateway Endpoint: {baseURL}/pay/request.php
Required Fields:
order_id: Your unique order ID (at least 10 characters long).
pid: The provided merchant ID.
amount: The amount to be sent.
name: The customer's name.
email: The customer's email address.
phone: The customer's phone number.
Procedure Steps:
Send the following details to the given API
GET
https://<domain>/pay/request.php
{
"pid":"0951272386617",
"order_id":"iaudjeh",
"amount":"50",
"phone":"9000000000",
"email":"anushk@gmail.com",
"name":"Anushk"
}
Note: If the status is 'error,' a reason will be provided in the 'message' field.
Append
hash_value
toconnect.php
for transferring customers to the payment completion page.You can now redirect customers to complete the payment using the payment hash.
redirect https://<domain>/pay/connect.php?code=1304d033331712f0de5d44665d10a2285241fe7d6a78753d779941cb7cd7f9c3
Launch this URL from your app using the
Launch Mode.external
application.
Here are examples in Kotlin, Java, and Flutter for launching a URL with Launch Mode.external
:
Val webIntent: Intent=
Uri.parse('{baseUrl}/pay/connect.php?code=b3JkZXJfaWQ9
eW91cl9vcmRIcl9pZCZwaWQ9Z2l2ZW5fbWVyY2hhbnR
faWQmcHVycG9zZT1hbnlfcHVycG9zZSZhbXQ9eW91cl9
hbW91bnQmZW1haWw9eW91cmVtYWlsQGV4YW1wbG
UuY29t').let{webpage>Intent(Intent.ACTION_VIEW,web
page)
WEBHOOK INTEGRATION :
The WebHook needs to be integrated into your server so that our application can notify you about the status change of the payment.
Required Fields:
secret_key: Given secret key;
In the POST JSON request body, you will receive the following values:
order_id
requested_amount
status
post_hash
received_amount
bank_ref
sender_upi
ref_code
refund_info (if a refund request has occurred, these are the data):
refunded_upi
refund_amount
refund_initiated_time
refund_completed_time
refund_status
refund_notes
From the WebHook, you may receive the following transaction statuses:
Pending: Waiting to finish the payment.
Approved: Payment is approved by our system.
Late Approved: Payment is approved late after reconciliation.
Declined: Our system declines the payment.
No Matching Payment for UTR: The system waited until timeout but no UTR was received against the payment (only for P2P).
User Timed Out: User did not finish the payment within the session period.
Refund Initiated: Refund has been initiated for this order ID.
Refund Completed: Refund has been completed for this order ID.
Note:
please consider received_amount
for final transaction processing
Steps to Integrate WebHook:
Base64 Decode
post_hash
:Decode the received
post_hash
to verify the integrity of the received data.Use
base64_decode()
function.
$encrypted_hash=base64_decode($JSON['post_hash']);
To decrypt the hash and view the actual hash, you can use the following example:
// Function to decrypt
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);
}
//remote hash
$remote_hash=decrypt($encrypted_hash,$secret_key)
To compute the local hash and verify the integrity of the data in PHP, follow these steps:
Compute the Local Hash:
Use the MD5 128-bit hashing algorithm to generate the hash.
Concatenate the values of
$order_id
,$amount_received
,$status
, and$secret_key
to create the hash.
Verify Hash
Compare the remote hash provided in the request with the local hash.
// Verify Hash
if ($remote_hash === $local_hash) {
// Hashes match; data integrity is verified
// Mark the transaction as successful and process the order
// Write code here to process the order
// Confirm the amount, order_id, etc., with your records
// Update your database with payment success
$hash_status = "HashMatched";
// Example: Update your database with payment success
// updateDatabaseWithPaymentSuccess($order_id, $amount_received, $status);
} else {
// Verification failed
$hash_status = "HashMismatch";
}
You should acknowledge back to the payment gateway that you have logged the status of the payment.
Failure to do so may result in receiving multiple acknowledgments from our system.
This measure is implemented to prevent issues related to network failure, packet loss, and system overload when updating data.
$data['hash_status']=$hash_status; //'HashMatched'or "HashMismatch'
Sdata['acknowledge']=$acknowledge;//'yes'or'no'
header('Content-Type:application/json;charset=utf-8');
echo json_encode($data);//output as a json file
STATUS POLLING INTEGRATION :
API Endpoint:
https://<domain>/api/status_polling.php
This API is used to poll the status of a particular transaction.
Required Fields:
secret_key: The given secret key.
url_of_polling_api: The URL for polling the API (provided to you).
In the POST request, you will receive the following values:
pid: Merchant ID.
ref_code: The reference code received during the payment request.
post_hash: Instructions for generating this hash will be provided.
Step 1: Create Hash from Data
Create an MD5 hash by concatenating the values of
ref_code
,pid
, andsecret_key
.This process helps to generate the signature of the data.
The result will be a local hash created from the data.
$local_hash=md5($ref_code.$pid.$row['secret_key'])
NodeJS Example:
const local_hash = crypto.createHash('md5').update(ref_code + pid + secret_key).digest('hex');
const encodedStr=encrypt(local_hash, secret_key).toString('base64');
Step 2: Encrypt Hash
Encrypt the hash using the secret key.
This step ensures the integrity and verifies the signature of the data.
$encrypted_hash=encrypt($local_hash,$row['secret_key']);
//function for encryption
function encrypt($plaintext,$password)
{
$method="AES-256-CBC";
$key=hash('sha256',$password,true);
$iv=openssl_$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;
}
Step 3: Base64 Encode Local Hash
The local hash, which is now the encrypted hash, should be base64 encoded for safe delivery.
Use the
base64_encode
function in PHP.
//Compute the payment hash locally in PHP Example
$encoded_hash=base64_encode($encrypted_hash)
Step 4: Send a POST JSON Request to the Given URL
Send a POST request containing
pid
,order_id
, andpost_hash
(as normal key-value pairs) to theurl_of_polling_api
.You will receive a response after the data is validated.
Here’s an example of how to send the POST request using cURL
in PHP:
<?php
//a straightforward PHP example that sends an HTTP POST to a remote site
$ch=curl_init();
$url=curl_setopt($ch,CURLOPT_URL,$url); //you will get API URL in the call
curl_setopt($ch,CURLOPT_POST,1);
//In real life you should use something like:
curl_setopt($ch,CURLOPT_POSTFIELDS, json_encode(array('pid'=>$given_pid,'order_id'=>$checking_order_id,'post_hash'=>$generated_post_ hash),true));
//Receive server response...
curl_setopt($ch,CURLOPT_RETURNTRANSFER,true);
$se$server_output=curl_exec($ch);
curl_close($ch);
if($server_output)
{
//You can follow step 5 to process the response
}
?>
Step 5: Process Response
You will receive a JSON response containing the following fields:
order_id: The order ID associated with the transaction.
upi_id: The collection UPI ID.
amount: The amount involved in the transaction.
webhook_acknowledged: Webhook acknowledgment status (0 or 1).
status: The payment approval status (e.g., Approved, Declined, Pending).
post_hash: Encrypted hash for payload verification.
refund_info: Details about any refunds, if applicable. This includes:
refunded_upi: UPI ID associated with the refund.
refund_amount: Amount refunded.
refund_initiated_time: Time when the refund was initiated.
refund_completed_time: Time when the refund was completed.
refund_status: Status of the refund.
refund_notes: Additional notes about the refund
PHP example for capturing and processing a callback:
$data=file_get_contents("php://input");
$row1=json_decode($data,true);
$row1['order_id'];
$row1['upi_id'];
$$row1['amount'];
$row1['webhook_acknowledged'];
$row1['status'];
$row1['post_hash'];
$row1['refund_info'];
$encrypted_hash=base64_decode($row1['post_hash']);
//decode post hash
$remote_hash=decrypt($encrypted_hash,$row['secret
_key']);//decrypt encrypted hash
$local_hash=md5($order_id.$data['recieved_amount'].$ data['status'].
$row['secret_key']);//generatelocalhash
Step 6: Verify Response
To ensure the integrity of the data received from the callback:
// if $local_hash equal to $remote_hash then the data is verified.
if($remote_hash==$local_hash)
{
//validated status
}
else
{{
//Invalid status
};
$hash=hash_hmac('sha256',$ciphertext.$iv,$key,true);
return $iv.$hash.$ciphertext;
}
Example:
{
"ref_code": "asas63elfe596ed8",
"pid":"5345f345345",
"post_hash":"kvDFE0f/iUuVQ4bZKufsjnUNxs4CNBHqn6yvApqmoZQZ+h+HUidxTRv6UxKVBnYwyNA3GamOwGFrtLsIvQf20GOcFUz73wqHkvMSZdmUIXRKdbTOWm8YRzsxxXAJqpr",
"amount:"100"
}
Python Example for encrypt, decrypt, hashing functions
import base64
import os
# pip install pycryptodome
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import hashlib
import hmac
def hash_hmac(algorithm, data, key, raw_output=False):
if isinstance(data, str):
data = data.encode('utf-8')
if isinstance(key, str):
key = key.encode('utf-8')
hmac_hash = hmac.new(key, data, getattr(hashlib, algorithm.lower()))
if raw_output:
return hmac_hash.digest()
else:
return hmac_hash.hexdigest()
def encrypt(plaintext, password):
key = hashlib.sha256(password.encode()).digest()
iv = os.urandom(16)
cipher = AES.new(key, AES.MODE_CBC, iv)
ciphertext = cipher.encrypt(pad(plaintext.encode(), AES.block_size))
hash_value = hash_hmac("SHA256",ciphertext + iv,key,True)
return iv + hash_value + ciphertext
def decrypt(ivHashCiphertext, password): # Segregating IV,Hash,Cipher
iv = ivHashCiphertext[:16]
hash_val = ivHashCiphertext[16:48]
ciphertext = ivHashCiphertext[48:]
key = hashlib.sha256(password.encode()).digest()
if not hmac.compare_digest( hash_hmac("SHA256",ciphertext + iv,key,True), hash_val
):
return None
backend = default_backend()
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=backend)
decryptor = cipher.decryptor()
decrypted_text = decryptor.update(ciphertext) + decryptor.finalize()
return decrypted_text.decode()
# Example usage:
plaintext = "Hello, worlds!"
password = "mysecretpassword"
encrypted_data = encrypt(plaintext, password) # Encode cipher for transport encoded_cipher=base64.b64encode(encrypted_data) # decode cipher for decrypt
iv_hash_ciphertext=base64.b64decode(encoded_cipher)
decrypted_data = decrypt(iv_hash_ciphertext, password)
print(decrypted_data)
Node JS Decrypt function
const crypto = require('crypto');
function decrypt(ivHashCiphertext, password) {
// If ivHashCiphertext is a string, assume it's base64 encoded
if (typeof ivHashCiphertext === 'string') {
ivHashCiphertext = Buffer.from(ivHashCiphertext, 'base64');
}
const method = 'aes-256-cbc';
// Extract the initialization vector (first 16 bytes)
const iv = ivHashCiphertext.slice(0, 16);
// Extract the hash (next 32 bytes)
const hash = ivHashCiphertext.slice(16, 48);
// Extract the ciphertext (remaining bytes)
const ciphertext = ivHashCiphertext.slice(48);
// Generate the key using SHA-256 hash of the password
const key = crypto.createHash('sha256').update(password, 'utf8').digest();
// Compute HMAC-SHA256 of (ciphertext || iv) using the key
const hmac = crypto.createHmac('sha256', key)
.update(Buffer.concat([ciphertext, iv]))
.digest();
// Compare the computed HMAC with the extracted hash
if (!crypto.timingSafeEqual(hmac, hash)) {
return null; // Hashes do not match; return null
}
try {
// Decrypt the ciphertext using AES-256-CBC
const decipher = crypto.createDecipheriv(method, key, iv);
const plaintext = Buffer.concat([
decipher.update(ciphertext),
decipher.final()
]);
return plaintext.toString('utf8'); // Return the decrypted text as a UTF-8 string
} catch (err) {
// Decryption failed; return null
return null;
}
}
P2P Accounts Wallet Balance API Overview
This API provides detailed information about an operator's payment gateway (PG) accounts, including balance details and account details. It allows you to query and retrieve information related to UPI, IMPS, IMPS with UPI, wallet and payout accounts linked to the operator. For more details on how to use this API, refer to the link below.
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.
RECONCILIATION
This API endpoint allows authorized users to retrieve payment transactions based on a specific pid
(Partner ID) and date
. The API performs authentication using a token and signature verification to ensure secure communication.
WALLET TRANSACTION SUMMARY
This API endpoint retrieves a summary of transactions for wallets associated with an operator, filtered by account number, date type, and date. It requires authentication via an API key and returns transaction counts and amounts grouped by status.
WALLET DATA ENDPOINT
This API endpoint retrieves cumulative wallet data for an operator, including both active and inactive wallet accounts across multiple wallet types. The endpoint requires authentication via an API key passed in the request headers.
Last updated