Revoke Transaction Status to Merchant URL

Dinger Callback Response

After the payment, Dinger will invoke to your callback url if the transaction is success or fail. If it is in pending status, dinger will not invoke the callback url.
Dinger will also send encrypted response to your callback url. You will need to use your callback key you generated in the dashboard to decrypt the payment result.

• paymentResult (Needs to decrypt the paymentResult response with Base64 first and then with AES/ECB/PKCS7Padding algorithm)

• checksum(sha256(jsonString(resultObj))

Testing Callback Key - d655c33205363f5450427e6b6193e466 (Call Back Key from Data Dash)

Method - POST

Content-Type: application/json

{  
“paymentResult":"5zDOSGSI/YE7wsmX/IlBZb6iH0NpwgRuUL13RY9sEKIvo8oebHCh1FKZ0MKTkvlKPQ6Cn2qYg7UDQssBuMbkVAGWcVPK0WvvrrACrx480jdydrNUcsqX4vsaqHuRlCQa/7Qfur+W6WNKO4exvOvN25FtE8hDB7ENu37r54wUlGC21bojhq9M15/Ql5P9+w1x/+Ep13nmyptGOHfI4a4V3D57v0HQ8KqUnOy5P6E4FYOSeOVeVuCJ516RK94OIVAUB3F9jqy4NLm9jqc243pWOR9fwGJK5YplMbOuQFEZKt0=",  “checksum":"3e6dc224539d382f300f658a26775e75029b26ba0b885196e7ec66feb6a756ae"
}

curl example

curl --location -g --request POST 'https://merchanturl.com/api/callBack' \
--header 'Content-Type: application/json' \
--data-raw '{"paymentResult":"uQZVb6O5qTxp7dz3c4yB3T5WBlc/EWVXsAaapcc2BXquHcJIQYdyjc2zW+EIWYOznH2XEUkL1h5BSvhr+XWgiBZpkDK4klcGRigE61vRhAxLFs32HypMdcVvPQ17yeDrmhOPtrsyiVWTZ/Ub4Iu97DPBCHp+gTM1qyj+NqjccS+06wnLG+IzyWMXgW8fuEa2ODczp3NpsUv3tEUjIKvp99x4inG/FObzU8y4lyeu2mJDL1WBdKgnNLCR+tUU2qfzry8M/mDbbPAxb6Hi2Q4s/zVwyeXQE27jBRlUieXTP1ePrQ9fdj7dJxBQ7fsGmEWQ9iPuzL367lJvFlIPcTgQThg9XFieqG9ZBz9eTzaMAuw=","checksum":"99f13ebc78ae100494c41409139552f838371ac87e55f907ce23dd63dfec2bed"}'

Sample Response After Decryption

{
   "totalAmount":1000,
   "createdAt":"20210916 085233",
   "transactionStatus":"SUCCESS",
   "methodName":"QR",
   "merchantOrderId":"0586",
   "transactionId":"TRX312720215216085254",
   "customerName":"Min Thu Kyaw",
   "providerName":"KBZ Pay"
}

Decryption Code Sample

import * as aesEcb from 'aes-ecb';

const SECRET_KEY = 'd655c33205363f5450427e6b6193e466';
const cipherText ="5zDOSGSI/YE7wsmX/IlBZb6iH0NpwgRuUL13RY9sEKIvo8oebHCh1FKZ0MKTkvlKPQ6Cn2qYg7UDQssBuMbkVAGWcVPK0WvvrrACrx480jdydrNUcsqX4vsaqHuRlCQa/7Qfur+W6WNKO4exvOvN25FtE8hDB7ENu37r54wUlGC21bojhq9M15/Ql5P9+w1x/+Ep13nmyptGOHfI4a4V3D57v0HQ8KqUnOy5P6E4FYOSeOVeVuCJ516RK94OIVAUB3F9jqy4NLm9jqc243pWOR9fwGJK5YplMbOuQFEZKt0=";

const resultObj = aesEcb.decrypt(SECRET_KEY, cipherText);
console.log(resultObj);

import { sha256 } from "js-sha256";
let sign = sha256(resultObj);
console.log(sign);

* This sign value and the checksum value(from the URL) must be same, if not we have to assume incorrect signature and return error *
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "dbname";

$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection

if ($conn->connect_error) {
  die("Connection failed: " . $conn->connect_error);
}

$result = file_get_contents("php://input");

$decodeValue = json_decode($result, true);

$paymentResult = $decodeValue['paymentResult'];
$checkSum = $decodeValue['checksum'];

$secrectKey =  "d655c33205363f5450427e6b6193e466";
$decrypted = openssl_decrypt($paymentResult,"AES-256-ECB", $secrectKey);


if(hash("sha256", $decrypted) !== $checkSum){

    echo "incorrect Singanature.";

} elseif (hash("sha256", $decrypted) == $checkSum) {

    $decryptedValues = json_decode($decrypted, true);

    $transactionStatus = $decryptedValues["transactionStatus"];

    $merchantOrderId = $decryptedValues["merchantOrderId"];

    $totalAmount = $decryptedValues["totalAmount"];

    $methodName = $decryptedValues["methodName"];

    $providerName = $decryptedValues["providerName"];

    $transactionId = $decryptedValues["transactionId"];

    $customerName = $decryptedValues["customerName"];
    
    $date = date("Y-m-d H:i:s");

    if ( $transactionStatus == "SUCCESS" ) {
        $success = "display your success message to customer and save the transaction to db. and do some success function for your app";
        $sql = "INSERT INTO callback_data (totalAmount, transactionStatus, methodName, providerName, merchantOrderId, transactionId, customerName, created_at, updated_at )
VALUES ('$totalAmount', '$transactionStatus', '$methodName', '$providerName', '$merchantOrderId', '$transactionId', '$customerName', '$date', '$date' )";

        if ($conn->query($sql) === TRUE) {
          echo "New record created successfully";
        } else {
          echo "Error: " . $sql . "<br>" . $conn->error;
        }
    } else {
        $error = "display error message and save the transaction to db";
    }

    $callBackResponse = array(
        'transactionStatus' => $transactionStatus,
        'merchantOrderId' => $merchantOrderId,
        'totalAmount' => $totalAmount,
        'methodName' => $methodName,
        'providerName' => $providerName,
        'transactionId' => $transactionId,
        'customerName' => $customerName,
    );

    
    header('Content-Type: application/json');
    echo json_encode(array("data"=>$callBackResponse));
}
// decrypt callback data
       
        $paymentResult = $request->paymentResult;
        $secrectKey =  "7f4917e39f698ac433ac1d207e139adc";
        $decrypted = openssl_decrypt($paymentResult,"AES-256-ECB", $secrectKey);
        
        // get callback data
        
        $decryptedValues = json_decode($decrypted, true);
        $transactionStatus = $decryptedValues["transactionStatus"];
        $merchantOrderId = $decryptedValues["merchantOrderId"];
        $totalAmount = $decryptedValues["totalAmount"];
        $methodName = $decryptedValues["methodName"];
        $providerName = $decryptedValues["providerName"];
        $transactionId = $decryptedValues["transactionId"];
        $customerName = $decryptedValues["customerName"];
from Crypto.Util.Padding import pad
from Crypto.Cipher import AES
import base64
 
class PrpCrypt(object):
 
    def __init__(self):
        self.unpad = lambda date: date[0:-ord(date[-1])]
 
    def aes_cipher(self, key, aes_str):
        aes = AES.new(key.encode('utf-8'), AES.MODE_ECB)
        pad_pkcs7 = pad(aes_str.encode('utf-8'), AES.block_size, style='pkcs7') 
        encrypt_aes = aes.encrypt(pad_pkcs7)
        encrypted_text = str(base64.encodebytes(encrypt_aes), encoding='utf-8') 
        encrypted_text_str = encrypted_text.replace("\n", "")
 
        return encrypted_text_str
 
    def decrypt(self, key, decrData):
        res = base64.decodebytes(decrData.encode("utf8"))
        aes = AES.new(key.encode('utf-8'), AES.MODE_ECB)
        msg = aes.decrypt(res).decode("utf8")
        return self.unpad(msg)
 
 
if __name__ == '__main__':
    # replace with your callback key
    key = "d655c33205363f5450427e6b6193e466"

    # callback data to be decrypted
    paymentResult = "mX2Ku+jslxIXDSQMxl3wzDclIiKilKlakN2149ijiV467m3lgyQTHuUlobdKDUrfxa5xlMg5HLj9MuO0WWssf+EjcRnPI8LMxU7LnM8lDYO52QeuU1bZ/0WV95jzJ1jOt6AJXNiSl+wu6wy6sQioCjVyyRXIpSug/4pokaXkIdQCpZo/YKa5QpVtSf1DucnCG/ESsxTw1sJQ39Pox8tdbcSvTPZrdFzcAyi/C8JFVtavTaUS8X1QndfKQphquZVhfKhVBA8etZaMwsoclfB8/e8mCSMUn+2s7/LYf9i9QHqlVdoS2ssTqJT+Rcfs9/Wa"
        
    result = PrpCrypt().decrypt(key,paymentResult)
    print(result)
val paymentDecrypted = decryptWithAES(callbackKey, paymentResult)

fun decryptWithAES(key: String, strToDecrypt: String?): String? {
        Security.addProvider(BouncyCastleProvider())
        val keyBytes: ByteArray

        try {
            keyBytes = key.toByteArray(charset("UTF8"))
            val skey = SecretKeySpec(keyBytes, "AES")
            val input = org.bouncycastle.util.encoders.Base64
                .decode(strToDecrypt?.trim { it <= ' ' }?.toByteArray(charset("UTF8")))

            synchronized(Cipher::class.java) {
                val cipher = Cipher.getInstance("AES/ECB/PKCS7Padding")
                cipher.init(Cipher.DECRYPT_MODE, skey)

                val plainText = ByteArray(cipher.getOutputSize(input.size))
                var ptLength = cipher.update(input, 0, input.size, plainText, 0)
                ptLength += cipher.doFinal(plainText, ptLength)
                val decryptedString = String(plainText)
                return decryptedString.trim { it <= ' ' }
            }
        } catch (uee: UnsupportedEncodingException) {
            uee.printStackTrace()
        } catch (ibse: IllegalBlockSizeException) {
            ibse.printStackTrace()
        } catch (bpe: BadPaddingException) {
            bpe.printStackTrace()
        } catch (ike: InvalidKeyException) {
            ike.printStackTrace()
        } catch (nspe: NoSuchPaddingException) {
            nspe.printStackTrace()
        } catch (nsae: NoSuchAlgorithmException) {
            nsae.printStackTrace()
        } catch (e: ShortBufferException) {
            e.printStackTrace()
        }

        return null
    }

Callback Response Parameters

Content of Payment Result

NameTypeMandataryDescription
totalAmountIntegerYes
createdAtStringYesyyyyMMdd HHmmss
transactionStatusStringYesSUCCESS, ERROR, CANCELLED, TIMEOUT,
DECLINED, SYSTEM_ERROR
methodNameStringYes
providerNameStringYes
customerNameStringYes
merchantOrderIdStringYes
transactionIdStringYes