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
  • COMPLAINT
  • RECONCILIATION
  1. API Integration
  2. Bangladesh

Payin P2C Non-Seamless Wallet 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.

  • wallet type:

  • name: The customer's name.

  • email: The customer's email address.

  • phone: The customer's phone number.

Procedure Steps:

  1. Send the following details to the given API

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

{
    "pid":"000000000000",
    "amount":"1",
    "order_id":"20",
    "wallet_type":"bKash",
    "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.

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;
    }
}

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

PreviousPayin P2C Seamless Wallet IntegrationNextPayout P2C Seamless wallet Integration

Last updated 7 months ago