Fiat Currency Asynchronous Notification

# Asynchronous Notification of Collection

This interface is provided by the merchant. The platform requests this interface when the collection is successful When an HTTP request is received, please respond with httpcode=200 (HTTP Response Status Code). Otherwise, 16 notifications will be repeatedly sent within 5 hours.

⚠️⚠️Note: Users might only pay part of the amount, so it is necessary to check the 'actual payment amount'.⚠️⚠️

# Best Practice

  1. To determine successful payment, the orderStatus is 1.
  2. For partial payment determination: orderStatus is 3. Merchants should handle this according to their own business logic.
  3. On refund determination: orderStatus is 2. This situation generally occurs in case of customer complaints and bank risk control returns. Merchants should record related information and handle it according to their own business processes (a callback success will be received before getting this callback status).
  4. If the merchant's business logic fails to process, please change the HTTP response status code to 400 or 500 (non-200).
  5. Cheezeepay may send multiple callbacks with the same status. The merchant should handle this compatibility accordingly. If the merchant's own business has been processed successfully, they must return the HTTP response status code to 200.
  6. Please ensure to verify the callback IP➕signature verification.
Parameter Type Required Description Example
merchantId string Y Merchant ID CH10001165
mchOrderNo string Y Merchant Order Number C202401090023
platOrderNo string Y Platform Order Number 1746060142200229888
orderStatus int Y Order Status 1-Success 2-Refund 3-Partial Payment(There is no status 3 at the moment)
payAmount string Y Order Actual Payment Amount 800
amountCurrency string Y Order Amount Currency INR
fee string Y Handling Fee 88
feeCurrency string Y Handling Fee Currency INR
gmtEnd long Y Completion Time (Timestamp: Milliseconds) 1705128180000
utr string N UTR (bank transaction reference); may be absent or an empty string 923238912091
paymentInfo object N Payment / collection vehicle and related details; shaped by paymentMethodType; see paymentInfo below. May be omitted. See below
payerUpiId string N Payer UPI Id; may be absent, null, or empty xxxxxx1@iob
kycPass Boolean N Whether KYC verification passed true
kycInfo String N KYC verification info KYC name verification passed
sign string Y Signature EcCs7GlN1UzEAflgpyJ4lKIHe9+lS/gdkcEvWI
+nSlcvvz1c+Dg4Zi8sFmpYaoOMGxS3/BY66F
Gq2TXzVGhAciq2kFmwZFtYbFvr7xPTaNf/n0R
u7ZtV/jo3uBnfc9/JI6XlEWlIbPtGsVpH7CLhzgh
dqIT+YbPEdhX2ZKETFTTfIj6PzctY9eOsZ51Yb
HYnXv0jTWn1hdPoDofAF9RmoNaXK4FGYibQ
BSvVA6w4rM6hCXlbTeHdMc4askrZlrHeG3uhyD
34VlXs74cdzAZ+oROMXBcgXR4ObsJVDFz9tsJ
H92iaxlKTF0lEFZa/To5ezLpHvTOAK0uY/K6IxvO
AAg==

# paymentInfo fields

paymentInfo carries the payment or collection instrument type plus the fields merchants need for display or settlement. paymentMethodType usually labels the rail (Current payment method types include: [UPI], [PhonePe], [Paytm], [IMPS], [Bank Transfer(India)]trust the literal returned by the platform). Merchants who do not require this bundle will omit paymentInfo; it remains optional. The webhook matches the fiat collection query API for payload shape and meanings—apply platform serialization rules when nested maps participate in signing.

# UPI-Example parameters of payment method

Field Type Required Meaning
paymentMethodType String N example literals include [UPI]
name String N Name
upiId String N UPI Id (often exposed as upiId—follow the live field name)

# PhonePe-Example parameters of payment method

Field Type Required Meaning
paymentMethodType String N example literals include [PhonePe]
name String N Name
phoneNumber String N Phone Number

# Paytm-Example parameters of payment method

Field Type Required Meaning
paymentMethodType String N example literals include [Paytm]
name String N Name
phoneNumber String N Phone Number

# IMPS-Example parameters of payment method

Field Type Required Meaning
paymentMethodType String N example literals include [IMPS]
name String N Name
accountNumber String N Account Number / Card Number
ifscCode String N IFSC code
bankName String N Bank name
accountType String N Account Type (optional)
branchName String N Branch name

# Bank Transfer (India)-Example parameters of payment method

Field Type Required Meaning
paymentMethodType String N example literals include [Bank Transfer(India)]
name String N Name
accountNumber String N Account Number / Card Number
ifscCode String N IFSC code
bankName String N Bank name
accountType String N Account Type (optional)
branchName String N Branch name

# Asynchronous Notification Message Example

{
  "merchantId": "CH10001201",
  "mchOrderNo": "20260520181913",
  "platOrderNo": "2057043474855759872",
  "orderStatus": 1,
  "payAmount": "1000.1",
  "amountCurrency": "INR",
  "fee": "0.5022599",
  "feeCurrency": "USDT",
  "gmtEnd": 1779272767000,
  "utr": "923238912091",
  "paymentInfo": {
    "paymentMethodType": "[PhonePe]",
    "name": "KLAS LL HU",
    "phoneNumber": "319712389127@phone"
  },
  "payerUpiId": "",
  "kycPass": true,
  "kycInfo": "KYC name verification passed",
  "sign": "ZhCW+JRABhkeEpo5mvp9xjU1OoSd6y4Mj4t/586gcJNyyBzApMK/HM9ZP+7eo4tPi4n6AVKlTnFTKs8Lpu51ahRXHdfim8ZPx+ulzoYYyqyDT6KMSQ4hn5Fp+YqLZr5CEBjdh1IPffP3tJB/OWc7RNRMAeuh3m7GhHdcbC1GiKqy4rfb3DyXNnabke/lkfbvmpi33YmcrosUcXOOfr8dc8sOqZHSUAwPwcsaGAkTDdq3wFundWC/EI0HBOjXdDcyUDvAGXx7brZdBJoL602V6SW12HOmumm+x3smYP0HG0ZbYvABHTRXZAIziCgi9V137uziDwd1X5ej8QCIgU+hNg=="
}

# Asynchronous Notification Code Implementation Demo

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;

@RestController
@RequestMapping("callback")
public class TestCallbackController {

    /**
     * Platform public key
     */
    public static String PLATFORM_PUB_KEY = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1dad35S74jfLPbHJh8P0jDHiTvkxwrtITK97ovVu19B24UdiHyHoEZgtNlS6alFQj1ULQ71d6EPh2rWCNkS2b5HGQXwDYBtwvesVQ8h4Sf3eVPTTLGw3BS7Os4vtDEN6BezMdv3sUG2N5i6JF+5H4CQTq3MD2Cx6u/Cv7oFOdFqeDT0AH+TR7uyZxn69OtkJaHHr834EUcdShJKKMQtbC11WCcut7ilDUgdvZnThiVTq7cfl8mcC9FDKcQ9bMWamScWIB5cJQdUW23Kr0c1NvZlpgPS8U5VODM4Uc4muHJPD2cJmquuJ+4AGP36rEk27lUB3h7B6JI1QGiuh1yyPDwIDAQAB";

    /**
     * payInCallback
     * @param paramMap
     * @param response
     */
    @PostMapping("payIn")
    public void payInCallback(@RequestBody Map<String, Object> paramMap, HttpServletResponse response) throws Exception{
        boolean verifyResult = CheeseTradeRSAUtil.verifySign(paramMap, PLATFORM_PUB_KEY);
        if (verifyResult) {
            //Signature verification successful
            //Order notification business processing logic
            String merchantId = (String)paramMap.get("merchantId");
            String mchOrderNo = (String)paramMap.get("mchOrderNo");
            String platOrderNo = (String)paramMap.get("platOrderNo");
            Integer orderStatus = (Integer)paramMap.get("orderStatus");
            String payAmount = (String)paramMap.get("payAmount");
            String amountCurrency = (String)paramMap.get("amountCurrency");
            String fee = (String)paramMap.get("fee");
            String feeCurrency = (String)paramMap.get("feeCurrency");
            Long gmtEnd = (Long)paramMap.get("gmtEnd");
            // utr——optional
            String utr = paramMap.containsKey("utr") ? String.valueOf(paramMap.get("utr")) : null;
            // paymentInfo——optional
            Object paymentInfoObj = paramMap.get("paymentInfo");
            // payerUpiId——optional
            String payerUpiId = paramMap.containsKey("payerUpiId") ? (paramMap.get("payerUpiId") != null ? String.valueOf(paramMap.get("payerUpiId")) : "") : "";
            // kycPass——optional
            Boolean kycPass = paramMap.containsKey("kycPass") ? (Boolean) paramMap.get("kycPass") : null;
            // kycInfo——optional
            String kycInfo = paramMap.containsKey("kycInfo") ? String.valueOf(paramMap.get("kycInfo")) : null;
            if (paymentInfoObj instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> paymentInfo = (Map<String, Object>) paymentInfoObj;
                // Read fields as needed, e.g. paymentMethodType / name / phoneNumber
            }
        } else {
            //Signature verification failed
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        }
    }

}
<?php
//This code has been verified in PHP5.6 version
class TestCallbackController 
{

    /**
     * Platform public key
     */
    const PLATFORM_PUB_KEY = '-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyVqixSZsLJ75yL7r1SwFn0QsOrDAIv4JU6PCIyLwH5Y1m7QY3nm5k+D4VX9qNvTq9R6CT9fPuXH8AVfx7iDCahHA0j//SiTUNlRqgXZVwrhYoytW4PqSl8+QkMimS6Mm9zPC8JsyaUrv0DfIfVxLPZ42JMrKo6+/neIORTduhyO8VYcfNbFAa+fzKl1B0dmXyvat9BmMDYexKPtqKb3WkXKzAERPSizdbib6GvpV6boEnEDlyLT32rtWNEb26NWvmyiaQjBb2J32sDHSPga5kOT8L1dyi3YA4T2JtHMCAMyw9hPz7tjtl1XwSBsJ3h4q9Zra6G2K+NfPxQsBIFVgtwIDAQAB
-----END PUBLIC KEY-----';

    /**
     * payInCallback
     * @param paramMap
     * @param response
     */
    public function payInCallback($paramMap, $response) {
        //Sign
        $Sign = $paramMap["sign"];
        
        //$paramMap need remove sign
        unset($paramMap['sign']); 
        
        //Splicing callback parameters
        $keys = array_keys($paramMap);
        sort($keys);
        $str = '';
        foreach ($keys as $key) {
            $str .=  $key . '=' . $paramMap[$key] . '&';
        }
        $str = rtrim($str, '&');
        echo $str;

        //Verify the signature    
        $verifyResult = verifySign($str,$Sign ,self::PLATFORM_PUB_KEY);
        if ($verifyResult) {
        //Verification is successful, you can implement your logic
            //Signature verification successful
            //Order notification business processing logic
            $merchantId = $paramMap["merchantId"];
            $mchOrderNo = $paramMap["mchOrderNo"];
            $platOrderNo = $paramMap["platOrderNo"];
            $orderStatus = $paramMap["orderStatus"];
            $payAmount = $paramMap["payAmount"];
            $amountCurrency = $paramMap["amountCurrency"];
            $fee = $paramMap["fee"];
            $feeCurrency = $paramMap["feeCurrency"];
            $gmtEnd = $paramMap["gmtEnd"];
            $utr = isset($paramMap["utr"]) ? (string) $paramMap["utr"] : null;
            $paymentInfo = isset($paramMap["paymentInfo"]) && is_array($paramMap["paymentInfo"]) ? $paramMap["paymentInfo"] : null;
            $payerUpiId = isset($paramMap["payerUpiId"]) ? (string) $paramMap["payerUpiId"] : "";
            // kycPass——optional
            $kycPass = isset($paramMap["kycPass"]) ? $paramMap["kycPass"] : null;
            // kycInfo——optional
            $kycInfo = isset($paramMap["kycInfo"]) ? (string) $paramMap["kycInfo"] : null;
        //After implementing the logic, we finally need to return an HTTP200 response to us.
        } else {
            //Signature verification failed
            http_response_code(400);
        }
    }

}
//校验签名
function verifySign($data, $sign, $pubKey){
    $sign = base64_decode($sign);
    $key = openssl_pkey_get_public($pubKey);
    $result = openssl_verify($data, $sign, $key, OPENSSL_ALGO_SHA256) === 1;
    return $result;
}

?>

const express = require('express');
const bodyParser = require('body-parser');
//The functions of the introduced signature tool class (CheeseTradeRSAUtil). 
//The signature tool class can refer to the DEMO in "Signature & Verification Algorithm"
const  {verifySign}= require('./CheeseTradeRSAUtil');

const app = express();
//Parsing JSON request body
app.use(bodyParser.json()); 

// Platform public key
const platPublicKey = `-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1dad35S74jfLPbHJh8P0jDHiTvkxwrtITK97ovVu19B24UdiHyHoEZgtNlS6alFQj1ULQ71d6EPh2rWCNkS2b5HGQXwDYBtwvesVQ8h4Sf3eVPTTLGw3BS7Os4vtDEN6BezMdv3sUG2N5i6JF+5H4CQTq3MD2Cx6u/Cv7oFOdFqeDT0AH+TR7uyZxn69OtkJaHHr834EUcdShJKKMQtbC11WCcut7ilDUgdvZnThiVTq7cfl8mcC9FDKcQ9bMWamScWIB5cJQdUW23Kr0c1NvZlpgPS8U5VODM4Uc4muHJPD2cJmquuJ+4AGP36rEk27lUB3h7B6JI1QGiuh1yyPDwIDAQAB
-----END PUBLIC KEY-----`;

// callback interface
app.post('/callback/payIn', (req, res) => {
    const paramMap = req.body; // Get the parameters in the request body

    try {
        const verifyResult = verifySign(paramMap,platPublicKey);
        if (verifyResult) {
            // Signature verification successful
            const merchantId = paramMap.merchantId;
            const mchOrderNo = paramMap.mchOrderNo;
            const platOrderNo = paramMap.platOrderNo;
            const orderStatus = paramMap.orderStatus;
            const payAmount = paramMap.payAmount;
            const amountCurrency = paramMap.amountCurrency;
            const fee = paramMap.fee;
            const feeCurrency = paramMap.feeCurrency;
            const gmtEnd = paramMap.gmtEnd;
            const utr = paramMap.utr != null && paramMap.utr !== '' ? paramMap.utr : undefined;
            const paymentInfo = paramMap.paymentInfo;
            const kycPass = paramMap.kycPass != null ? paramMap.kycPass : undefined;
            const kycInfo = paramMap.kycInfo != null ? String(paramMap.kycInfo) : undefined;
            const payerUpiId = paramMap.payerUpiId != null ? String(paramMap.payerUpiId) : '';

            // Order notification business processing logic
            console.log('Signature verification successful, order processed:', {
                merchantId,
                mchOrderNo,
                platOrderNo,
                orderStatus,
                payAmount,
                amountCurrency,
                fee,
                feeCurrency,
                gmtEnd,
                utr,
                paymentInfo,
                payerUpiId,
                kycPass,
                kycInfo,
            });

            // Return a successful response
            res.status(200).json({ message: 'Successful processing' });
        } else {
            // Signature verification failed
            res.status(400).json({ message: 'Signature verification failed' });
        }
    } catch (error) {
        // Handling Exceptions
        console.error('An error occurred while processing the callback:', error);
        res.status(500).json({ message: 'Internal server error' });
    }
});