Payatom
  • PayAtom
    • India
      • Payin
      • Payout
    • Bangladesh
      • Payin
      • Payout
  • API Integration
    • India
      • Payin P2P Seamless UPI Integration
      • Payin P2P Non-Seamless UPI Integration
      • Payin P2P Non-Seamless IMPS Integration
      • Payout P2P Seamless IMPS Integration
      • Payin P2C Seamless UPI Integration
      • Payin P2C Non-Seamless UPI Integration
      • Payout P2C Seamless UPI Integration
    • Bangladesh
      • Payin P2P Seamless Wallet Integration
      • Payin P2P Non-Seamless Wallet Integration
      • Payout P2P Seamless wallet Integration
      • Payin P2C Seamless Wallet Integration
      • Payin P2C Non-Seamless Wallet Integration
      • Payout P2C Seamless wallet Integration
    • Pakistan
      • Pakistan H2H Integration
    • P2P Accounts Wallet Balance
    • Complaint
    • Payin Reconciliation
    • Payout Reconciliation
    • Wallet Transaction Summary
    • Wallet Data Endpoint
Powered by GitBook
On this page
  • PAYMENT REQUEST :
  • WEBHOOK INTEGRATION :
  • STATUS POLLING INTEGRATION :
  • Python Example for encrypt, decrypt hashing functions
  • P2P Accounts Wallet Balance API Overview
  • COMPLAINT
  • RECONCILIATION
  • WALLET TRANSACTION SUMMARY
  • WALLET DATA ENDPOINT
  1. API Integration
  2. India

Payin P2P Non-Seamless UPI 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.

Optional Fields:

  • redirect_url : The redirect url where user redirect back to merchant page

Procedure Steps:

  1. Send the following details to the given API

POST https://<domain>/pay/request.php

{
    "pid":"000000000000",
    "amount":"1",
    "order_id":"20",
    "phone":"9000000000",
    "email":"anushk@gmail.com",
    "name":"Anushk"
}

Note: If the status is 'error,' a reason will be provided in the 'message' field.

  1. Append hash_value to connect.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
  1. 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)
Uri webpage= Uri.parse('{baseUrl}/pay/connect.php?code= b3JkZXJfaWQ9eW91cl9vcmRlcl9pZCZwaWQ9Z2l2ZW5fb WVyY2hhbnRfaWQmcHVycG9zZT1hbnlfcHVycG9zZSZhb XQ9eW91cl9hbW91bnQmZW1haWw9eW91cmVtYW lsQGV4YW1wbGUuY29t');
Intent webIntent = new Intent(Intent.ACTION_VIEW,webpage)
canLaunchUrl(Uri.parse('{baseUrl}/pay/connect.php?code
=b3JkZXJfaWQ9e W91cl9vcmRlcl9pZCZwaW Q9Z2l2ZW5fbWVyY2hhbnRfaWQmcHVycG9zZT1hbnlfcH VycG9zZSZhbXQ9eW91cl9hbW91bnQmZW1haWw9eW 91cmVtYWlsQGV4YW1wbGUuY29t')).then((result)=>{ if(result==true)
{laun{launchUrl(Uri.parse(url),mode:LaunchMode.external Application)} else

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:

  1. 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']);
  1. 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)
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;
    }
}
  1. To compute the local hash and verify the integrity of the data in PHP, follow these steps:

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

    2. Verify Hash

      1. 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, and secret_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;
}
encrypted_hash=encrypt(local_hash,  row['secret_key']); 
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 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()

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, and post_hash (as normal key-value pairs) to the url_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"
}
{
    "order_id": "63elfe596ed8", 
    "upi_id": "fsdfs",
    "amount": "2000", 
    "webhook_acknowledged", "0",
    "status": "Approved", 
    "post_hash":"NIxxow|7alamQQYCzfJ7|X7t8Q9GJUzn1XAQa01XHvXF4Qfym]dCTUAk4uqwAeWFB60EqG5ttJy9PsVunuÖrBaDriChF7m8QhplRp3CyO74d9E3+QxGI9sdQDsdf55opo",
    "refund_info": []
}

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.

P2P Accounts Wallet Balance

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

Reconciliation

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 Transaction Summary

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.

Wallet Data Endpoint

PreviousPayin P2P Seamless UPI IntegrationNextPayin P2P Non-Seamless IMPS Integration

Last updated 1 month ago