FEED Integration
Configure End Point URL
If FEED integration does not appear in the Settings module of your iPOSpays account, contact your Dejavoo executive to enable it for you.
Watch This Video for a Visual Walkthrough of the Steps
Transaction & Settlement
This section describes the transaction and settlement payloads sent by FEED to your configured endpoint. Use these payloads to process, reconcile, and report transaction and settlement data within your application.
Below is the Sample Code provided for HMAC Validation with Languages.
Transaction
This section describes the transaction event payload sent by FEED to your configured endpoint whenever a transaction is processed.
Java :
private String convertTxnDetailsToString(FeedReceiverTxnPayload data) {
if (data == null) {
return "";
}
// Use TreeMap to store fields in alphabetical order
Map<String, Object> detailFields = new TreeMap<>();
detailFields.put("dba", getOrDefault(data.getData().getDba()));
detailFields.put("tpn", getOrDefault(data.getData().getTpn()));
detailFields.put("tpnLabel", getOrDefault(data.getData().getTagLabel()));
detailFields.put("deviceModel", getOrDefault(data.getData().getDeviceModel()));
detailFields.put("buildNumber", getOrDefault(data.getData().getBuildNumber()));
detailFields.put("maskedPan", getOrDefault(data.getData().getMaskedPan()));
detailFields.put("rrn", getOrDefault(data.getData().getRrn()));
detailFields.put("txName", getOrDefault(data.getData().getTxName()));
detailFields.put("transactionId", getOrDefault(data.getData().getTransactionId()));
detailFields.put("externalReferenceId", getOrDefault(data.getData().getExternalReferenceId()));
detailFields.put("amount", data.getData().getAmount());
detailFields.put("baseAmount", data.getData().getBaseAmount());
detailFields.put("totalFee", data.getData().getTotalFee());
detailFields.put("trueCashDiscountFee", data.getData().getTrueCashDiscountFee());
detailFields.put("commercialTaxAmount", data.getData().getCommercialTaxAmount());
detailFields.put("localTaxAmount", data.getData().getLocalTaxAmount());
detailFields.put("stateTaxAmount", data.getData().getStateTaxAmount());
detailFields.put("tipAmount", data.getData().getTipAmount());
detailFields.put("tipAdjAmount", data.getData().getTipAdjAmount());
detailFields.put("posRequestDate", getOrDefault(data.getData().getPosRequestDate()));
detailFields.put("posRequestTime", getOrDefault(data.getData().getPosRequestTime()));
detailFields.put("txTime", getOrDefault(data.getData().getTxTime()));
detailFields.put("hostResponseDate", getOrDefault(data.getData().getHostResponseDate()));
detailFields.put("txDuration", getOrDefault(data.getData().getTxDuration()));
detailFields.put("invoiceNumber", getOrDefault(data.getData().getInvoiceNumber()));
detailFields.put("hostResponseCode", getOrDefault(data.getData().getHostResponseCode()));
detailFields.put("responseText", getOrDefault(data.getData().getResponseText()));
detailFields.put("approvalCode", getOrDefault(data.getData().getApprovalCode()));
detailFields.put("tpnBatchNumber", getOrDefault(data.getData().getTpnBatchNumber()));
detailFields.put("cardLabel", getOrDefault(data.getData().getCardLabel()));
detailFields.put("sourceType", getOrDefault(data.getData().getSourceType()));
detailFields.put("posEntryMode", getOrDefault(data.getData().getPosEntryMode()));
detailFields.put("l2L3uploadflag", getOrDefault(data.getData().getL2L3uploadflag()));
detailFields.put("commMedia", getOrDefault(data.getData().getCommMedia()));
detailFields.put("destType", getOrDefault(data.getData().getDestType()));
detailFields.put("email", getOrDefault(data.getData().getEmail()));
detailFields.put("phoneNumber", getOrDefault(data.getData().getPhoneNumber()));
detailFields.put("tagLabel", getOrDefault(data.getData().getTagLabel()));
detailFields.put("tagValue", getOrDefault(data.getData().getTagValue()));
detailFields.put("chName", getOrDefault(data.getData().getChName()));
detailFields.put("additionalDetails", getOrDefault(data.getData().getAdditionalDetails()));
detailFields.put("performedBy", getOrDefault(data.getData().getPerformedBy()));
detailFields.put("cardInfo", getOrDefault(data.getData().getCardInfo()));
detailFields.put("cardType",getOrDefault(data.getData().getCardType()));
detailFields.put("transactionType", getOrDefault(data.getData().getTransactionType()));
detailFields.put("cardCategory", getOrDefault(data.getData().getCardCategory()));
detailFields.put("termId", getOrDefault(data.getData().getTermId()));
detailFields.put("mid", getOrDefault(data.getData().getMid()));
detailFields.put("description", getOrDefault(data.getData().getDescription()));
detailFields.put("posMode", getOrDefault(data.getData().getPosMode()));
detailFields.put("minorChannel", getOrDefault(data.getData().getMinorChannel()));
detailFields.put("txDate", getOrDefault(data.getData().getTxDate()));
detailFields.put("cardToken", getOrDefault(data.getData().getCardToken()));
detailFields.put("originalRRN", getOrDefault(data.getData().getOriginalRRN()));
detailFields.put("reconId", getOrDefault(data.getData().getReconId()));
detailFields.put("hostTxId", getOrDefault(data.getData().getHostTxId()));
detailFields.put("consumerId", getOrDefault(data.getData().getConsumerId()));
detailFields.put("subscriptionId", getOrDefault(data.getData().getSubscriptionId()));
// Add other fields from TxnDetailsModel as needed
return detailFields.values().stream().map(value -> getOrDefault(value)).collect(Collectors.joining("|"));
}
private static final String HMAC_SHA512_ALGORITHM = "HmacSHA512";
Map<String, Object> fields = new TreeMap<>();
fields.put("id", getOrDefault(feedFinalTxnPosting.getId()));
fields.put("version", getOrDefault(feedFinalTxnPosting.getVersion()));
fields.put("createdDt", getOrDefault(feedFinalTxnPosting.getCreatedDt()));
fields.put("eventType", getOrDefault(feedFinalTxnPosting.getEventType()));
fields.put("subEventType", getOrDefault(feedFinalTxnPosting.getSubEventType()));
fields.put("requestType", getOrDefault(feedFinalTxnPosting.getRequestType()));
fields.put("data", convertTxnDetailsToString(feedFinalTxnPosting.getData()));
// Concatenate fields with | as delimiter
String concatenatedString = fields.values().stream().map(value -> getOrDefault(value)).collect(Collectors.joining("|"));
try {
System.out.println("Before MAC Generation Value= " + concatenatedString);
Mac sha512Hmac = Mac.getInstance(HMAC_SHA512_ALGORITHM);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), HMAC_SHA512_ALGORITHM);
sha512Hmac .init(secretKeySpec);
byte[] hashBytes = sha512Hmac.doFinal(concatenatedString.getBytes(StandardCharsets.UTF_8));
StringBuilder hexString = new StringBuilder();
for (byte b : hashBytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (Exception e) {
throw new RuntimeException("Error while generating HMAC-SHA512", e);
}
public static String getOrDefault(Object value) {
return value != null ? value.toString() : "";
}using Microsoft.AspNetCore.Mvc;
using System.Security.Cryptography;
using System.Text;
using Newtonsoft.Json.Linq;
namespace FeedWebhookAPI.Controllers
{
[ApiController]
[Route("feed-webhook")]
public class FeedWebhookController : ControllerBase
{
public class FeedReceiverTxnPayload
{
public string id { get; set; } = "";
public string eventType { get; set; } = "";
public string subEventType { get; set; } = "";
public string requestType { get; set; } = "";
public string? signature { get; set; } // ignored in HMAC
public string version { get; set; } = "";
public string createdDt { get; set; } = "";
public Dictionary<string, object> data { get; set; } = new();
}
private static string FormatNumber(object? value)
{
if (value == null) return "";
if (value is long || value is int || value is double || value is float || value is decimal)
{
double v = Convert.ToDouble(value);
return Math.Abs(v % 1) < double.Epsilon ? v.ToString("F1") : v.ToString();
}
return value.ToString() ?? "";
}
private static string GenerateHmacSignature(string secret, FeedReceiverTxnPayload payload)
{
var p = payload.data;
var values = new List<string>
{
payload.createdDt,
p.GetValueOrDefault("additionalDetails")?.ToString() ?? "",
FormatNumber(p.GetValueOrDefault("amount")),
p.GetValueOrDefault("approvalCode")?.ToString() ?? "",
FormatNumber(p.GetValueOrDefault("baseAmount")),
p.GetValueOrDefault("buildNumber")?.ToString() ?? "",
p.GetValueOrDefault("cardCategory")?.ToString() ?? "",
p.GetValueOrDefault("cardInfo")?.ToString() ?? "",
p.GetValueOrDefault("cardLabel")?.ToString() ?? "",
p.GetValueOrDefault("cardToken")?.ToString() ?? "",
p.GetValueOrDefault("cardType")?.ToString() ?? "",
p.GetValueOrDefault("chName")?.ToString() ?? "",
FormatNumber(p.GetValueOrDefault("commercialTaxAmount")),
p.GetValueOrDefault("commMedia")?.ToString() ?? "",
p.GetValueOrDefault("consumerId")?.ToString() ?? "",
p.GetValueOrDefault("dba")?.ToString() ?? "",
p.GetValueOrDefault("description")?.ToString() ?? "",
p.GetValueOrDefault("destType")?.ToString() ?? "",
p.GetValueOrDefault("deviceModel")?.ToString() ?? "",
p.GetValueOrDefault("email")?.ToString() ?? "",
p.GetValueOrDefault("externalReferenceId")?.ToString() ?? "",
p.GetValueOrDefault("hostResponseCode")?.ToString() ?? "",
p.GetValueOrDefault("hostResponseDate")?.ToString() ?? "",
p.GetValueOrDefault("hostTxId")?.ToString() ?? "",
p.GetValueOrDefault("invoiceNumber")?.ToString() ?? "",
p.ContainsKey("L2l3UploadFlag")
? p["L2l3UploadFlag"]?.ToString() ?? ""
: p.GetValueOrDefault("l2L3uploadflag")?.ToString() ?? "",
FormatNumber(p.GetValueOrDefault("localTaxAmount")),
p.GetValueOrDefault("maskedPan")?.ToString() ?? "",
p.ContainsKey("Mid")
? p["Mid"]?.ToString() ?? ""
: p.GetValueOrDefault("mid")?.ToString() ?? "",
p.GetValueOrDefault("minorChannel")?.ToString() ?? "",
p.GetValueOrDefault("originalRRN")?.ToString() ?? "",
p.GetValueOrDefault("performedBy")?.ToString() ?? "",
p.GetValueOrDefault("phoneNumber")?.ToString() ?? "",
p.GetValueOrDefault("posEntryMode")?.ToString() ?? "",
p.GetValueOrDefault("posMode")?.ToString() ?? "",
p.GetValueOrDefault("posRequestDate")?.ToString() ?? "",
p.GetValueOrDefault("posRequestTime")?.ToString() ?? "",
p.GetValueOrDefault("reconId")?.ToString() ?? "",
p.GetValueOrDefault("responseText")?.ToString() ?? "",
p.GetValueOrDefault("rrn")?.ToString() ?? "",
p.GetValueOrDefault("sourceType")?.ToString() ?? "",
FormatNumber(p.GetValueOrDefault("stateTaxAmount")),
p.GetValueOrDefault("subscriptionId")?.ToString() ?? "",
p.GetValueOrDefault("tagLabel")?.ToString() ?? "",
p.GetValueOrDefault("tagValue")?.ToString() ?? "",
p.ContainsKey("TermId")
? p["TermId"]?.ToString() ?? ""
: p.GetValueOrDefault("termId")?.ToString() ?? "",
FormatNumber(p.GetValueOrDefault("tipAdjAmount")),
FormatNumber(p.GetValueOrDefault("tipAmount")),
FormatNumber(p.GetValueOrDefault("totalFee")),
p.GetValueOrDefault("tpn")?.ToString() ?? "",
p.GetValueOrDefault("tpnBatchNumber")?.ToString() ?? "",
p.GetValueOrDefault("tpnLabel")?.ToString() ?? "",
p.GetValueOrDefault("transactionId")?.ToString() ?? "",
p.GetValueOrDefault("transactionType")?.ToString() ?? "",
FormatNumber(p.GetValueOrDefault("trueCashDiscountFee")),
p.GetValueOrDefault("txDate")?.ToString() ?? "",
p.GetValueOrDefault("txDuration")?.ToString() ?? "",
p.GetValueOrDefault("txName")?.ToString() ?? "",
p.GetValueOrDefault("txTime")?.ToString() ?? "",
payload.eventType,
payload.id,
payload.requestType,
payload.subEventType,
payload.version
};
var concatenatedString = string.Join("|", values);
Console.WriteLine("Concatenated Values String for HMAC:\n" + concatenatedString);
using var hmac = new HMACSHA512(Encoding.UTF8.GetBytes(secret));
var hash = hmac.ComputeHash(Encoding.UTF8.GetBytes(concatenatedString));
return BitConverter.ToString(hash).Replace("-", "").ToLower();
}
[HttpPost]
public IActionResult Post([FromBody] JObject body)
{
try
{
Console.WriteLine("Received Transaction Payload: " + body.ToString());
var receivedSignature = (body["signature"]?.ToString() ?? "").Trim();
Console.WriteLine("Received Signature: " + receivedSignature);
const string sharedSecretKey = "Hjkuredd456Ay4aBHjkVRES9vf69b9nXcY8LYoH644eertY"; // Your HMAC key
// Build strongly typed payload
var payload = body.ToObject<FeedReceiverTxnPayload>();
payload.signature = null; // ignore in HMAC
string generatedSignature = GenerateHmacSignature(sharedSecretKey, payload);
if (receivedSignature != generatedSignature)
{
Console.WriteLine("❌ Signature mismatch");
return Unauthorized(new
{
success = false,
message = "Unauthorized: Invalid HMAC signature"
});
}
Console.WriteLine("✅ Signature verified");
return Ok(new
{
success = true,
message = "Authentication Success...!"
});
}
catch (Exception ex)
{
Console.WriteLine("Error in /feed-webhook: " + ex.Message);
return StatusCode(500, new
{
success = false,
message = "Internal Server Error",
error = ex.Message
});
}
}
}
}from flask import Flask, request, jsonify
import hmac
import hashlib
app = Flask(__name__)
class FeedReceiverTxnPayload:
def __init__(self, body):
self.id = body.get("id", "")
self.eventType = body.get("eventType", "")
self.subEventType = body.get("subEventType", "")
self.requestType = body.get("requestType", "")
self.signature = None # ignore signature during HMAC
self.version = body.get("version", "")
self.createdDt = body.get("createdDt", "")
data = body.get("data", {}) or {}
self.data = {
"dba": data.get("dba", ""),
"tpn": data.get("tpn", ""),
"tpnLabel": data.get("tpnLabel", ""),
"deviceModel": data.get("deviceModel", ""),
"buildNumber": data.get("buildNumber", ""),
"maskedPan": data.get("maskedPan", ""),
"rrn": data.get("rrn", ""),
"txName": data.get("txName", ""),
"transactionId": data.get("transactionId", ""),
"externalReferenceId": data.get("externalReferenceId", ""),
"amount": data.get("amount", 0),
"baseAmount": data.get("baseAmount", 0),
"totalFee": data.get("totalFee", 0),
"trueCashDiscountFee": data.get("trueCashDiscountFee", 0),
"commercialTaxAmount": data.get("commercialTaxAmount", 0),
"localTaxAmount": data.get("localTaxAmount", 0),
"stateTaxAmount": data.get("stateTaxAmount", 0),
"tipAmount": data.get("tipAmount", 0),
"tipAdjAmount": data.get("tipAdjAmount", 0),
"posRequestDate": data.get("posRequestDate", ""),
"posRequestTime": data.get("posRequestTime", ""),
"txTime": data.get("txTime", ""),
"hostResponseDate": data.get("hostResponseDate", ""),
"txDuration": data.get("txDuration", ""),
"invoiceNumber": data.get("invoiceNumber", ""),
"hostResponseCode": data.get("hostResponseCode", ""),
"responseText": data.get("responseText", ""),
"approvalCode": data.get("approvalCode", ""),
"tpnBatchNumber": data.get("tpnBatchNumber", ""),
"cardLabel": data.get("cardLabel", ""),
"sourceType": data.get("sourceType", ""),
"posEntryMode": data.get("posEntryMode", ""),
"l2L3uploadflag": data.get("L2l3UploadFlag",
data.get("l2L3uploadflag", "")),
"commMedia": data.get("commMedia", ""),
"destType": data.get("destType", ""),
"email": data.get("email", ""),
"phoneNumber": data.get("phoneNumber", ""),
"tagLabel": data.get("tagLabel", ""),
"tagValue": data.get("tagValue", ""),
"chName": data.get("chName", ""),
"additionalDetails": data.get("additionalDetails", ""),
"performedBy": data.get("performedBy", ""),
"cardInfo": data.get("cardInfo", ""),
"cardType": data.get("cardType", ""),
"transactionType": data.get("transactionType", ""),
"cardCategory": data.get("cardCategory", ""),
"termId": data.get("TermId", data.get("termId", "")),
"mid": data.get("Mid", data.get("mid", "")),
"description": data.get("description", ""),
"posMode": data.get("posMode", ""),
"minorChannel": data.get("minorChannel", ""),
"txDate": data.get("txDate", ""),
"cardToken": data.get("cardToken", ""),
"originalRRN": data.get("originalRRN", ""),
"reconId": data.get("reconId", ""),
"hostTxId": data.get("hostTxId", ""),
"consumerId": data.get("consumerId", ""),
"subscriptionId": data.get("subscriptionId", ""),
}
def format_number(value):
if isinstance(value, (int, float)):
return f"{value:.1f}" if float(value).is_integer() else str(value)
elif isinstance(value, str):
return value
return ""
def generate_hmac_signature(secret, payload):
p = payload.data
values = [
payload.createdDt,
p["additionalDetails"],
format_number(p["amount"]),
p["approvalCode"],
format_number(p["baseAmount"]),
p["buildNumber"],
p["cardCategory"],
p["cardInfo"],
p["cardLabel"],
p["cardToken"],
p["cardType"],
p["chName"],
format_number(p["commercialTaxAmount"]),
p["commMedia"],
p["consumerId"],
p["dba"],
p["description"],
p["destType"],
p["deviceModel"],
p["email"],
p["externalReferenceId"],
p["hostResponseCode"],
p["hostResponseDate"],
p["hostTxId"],
p["invoiceNumber"],
p["L2L3uploadflag"],
format_number(p["localTaxAmount"]),
p["maskedPan"],
p["Mid"],
p["minorChannel"],
p["originalRRN"],
p["performedBy"],
p["phoneNumber"],
p["posEntryMode"],
p["posMode"],
p["posRequestDate"],
p["posRequestTime"],
p["reconId"],
p["responseText"],
p["rrn"],
p["sourceType"],
format_number(p["stateTaxAmount"]),
p["subscriptionId"],
p["tagLabel"],
p["tagValue"],
p["TermId"],
format_number(p["tipAdjAmount"]),
format_number(p["tipAmount"]),
format_number(p["totalFee"]),
p["tpn"],
p["tpnBatchNumber"],
p["tpnLabel"],
p["transactionId"],
p["transactionType"],
format_number(p["trueCashDiscountFee"]),
p["txDate"],
p["txDuration"],
p["txName"],
p["txTime"],
payload.eventType,
payload.id,
payload.requestType,
payload.subEventType,
payload.version,
]
concatenated_string = "|".join([str(v) for v in values])
print("Concatenated Values String for HMAC:\n", concatenated_string)
hmac_obj = hmac.new(secret.encode("utf-8"), concatenated_string.encode("utf-8"), hashlib.sha512)
return hmac_obj.hexdigest()
//Need to Provide User end Point
@app.route("/feed-webhook", methods=["POST"])
def feed_webhook():
try:
payload = request.json
print("Received Transaction Payload:", payload)
received_signature = (payload.get("signature") or "").strip()
print("Received Signature:", received_signature)
shared_secret_key = "Hjkuredd456Ay4aBHjkVRES9vf69b9nXcY8LYoH644eertY" # Your HMAC key
feed_receiver_txn_payload = FeedReceiverTxnPayload({**payload, "signature": None})
generated_signature = generate_hmac_signature(shared_secret_key, feed_receiver_txn_payload)
if received_signature != generated_signature:
print("❌ Signature mismatch")
return jsonify({
"success": False,
"message": "Unauthorized: Invalid HMAC signature"
}), 401
print("✅ Signature verified")
return jsonify({
"success": True,
"message": "Authentication Success...!"
}), 200
except Exception as e:
print("Error in /feed-webhook:", str(e))
return jsonify({
"success": False,
"message": "Internal Server Error",
"error": str(e)
}), 500
if __name__ == "__main__":
app.run(port=3000, debug=True)<?php
// feed-webhook.php
// Utility: format number same as JS/Python
function formatNumber($value) {
if (is_numeric($value)) {
return (fmod($value, 1) == 0) ? number_format($value, 1, '.', '') : strval($value);
} elseif (is_string($value)) {
return $value;
}
return "";
}
class FeedReceiverTxnPayload {
public $id;
public $eventType;
public $subEventType;
public $requestType;
public $signature;
public $version;
public $createdDt;
public $data;
public function __construct($body) {
$this->id = $body["id"] ?? "";
$this->eventType = $body["eventType"] ?? "";
$this->subEventType = $body["subEventType"] ?? "";
$this->requestType = $body["requestType"] ?? "";
$this->signature = null; // ignore signature during HMAC
$this->version = $body["version"] ?? "";
$this->createdDt = $body["createdDt"] ?? "";
$data = $body["data"] ?? [];
$this->data = [
"dba" => $data["dba"] ?? "",
"tpn" => $data["tpn"] ?? "",
"tpnLabel" => $data["tpnLabel"] ?? "",
"deviceModel" => $data["deviceModel"] ?? "",
"buildNumber" => $data["buildNumber"] ?? "",
"maskedPan" => $data["maskedPan"] ?? "",
"rrn" => $data["rrn"] ?? "",
"txName" => $data["txName"] ?? "",
"transactionId" => $data["transactionId"] ?? "",
"externalReferenceId" => $data["externalReferenceId"] ?? "",
"amount" => $data["amount"] ?? 0,
"baseAmount" => $data["baseAmount"] ?? 0,
"totalFee" => $data["totalFee"] ?? 0,
"trueCashDiscountFee" => $data["trueCashDiscountFee"] ?? 0,
"commercialTaxAmount" => $data["commercialTaxAmount"] ?? 0,
"localTaxAmount" => $data["localTaxAmount"] ?? 0,
"stateTaxAmount" => $data["stateTaxAmount"] ?? 0,
"tipAmount" => $data["tipAmount"] ?? 0,
"tipAdjAmount" => $data["tipAdjAmount"] ?? 0,
"posRequestDate" => $data["posRequestDate"] ?? "",
"posRequestTime" => $data["posRequestTime"] ?? "",
"txTime" => $data["txTime"] ?? "",
"hostResponseDate" => $data["hostResponseDate"] ?? "",
"txDuration" => $data["txDuration"] ?? "",
"invoiceNumber" => $data["invoiceNumber"] ?? "",
"hostResponseCode" => $data["hostResponseCode"] ?? "",
"responseText" => $data["responseText"] ?? "",
"approvalCode" => $data["approvalCode"] ?? "",
"tpnBatchNumber" => $data["tpnBatchNumber"] ?? "",
"cardLabel" => $data["cardLabel"] ?? "",
"sourceType" => $data["sourceType"] ?? "",
"posEntryMode" => $data["posEntryMode"] ?? "",
"l2L3uploadflag" => $data["L2l3UploadFlag"]
?? $data["l2L3uploadflag"] ?? "",
"commMedia" => $data["commMedia"] ?? "",
"destType" => $data["destType"] ?? "",
"email" => $data["email"] ?? "",
"phoneNumber" => $data["phoneNumber"] ?? "",
"tagLabel" => $data["tagLabel"] ?? "",
"tagValue" => $data["tagValue"] ?? "",
"chName" => $data["chName"] ?? "",
"additionalDetails" => $data["additionalDetails"] ?? "",
"performedBy" => $data["performedBy"] ?? "",
"cardInfo" => $data["cardInfo"] ?? "",
"cardType" => $data["cardType"] ?? "",
"transactionType" => $data["transactionType"] ?? "",
"cardCategory" => $data["cardCategory"] ?? "",
"termId" => $data["TermId"]
?? $data["termId"] ?? "",
"mid" => $data["Mid"]
?? $data["mid"] ?? "",
"description" => $data["description"] ?? "",
"posMode" => $data["posMode"] ?? "",
"minorChannel" => $data["minorChannel"] ?? "",
"txDate" => $data["txDate"] ?? "",
"cardToken" => $data["cardToken"] ?? "",
"originalRRN" => $data["originalRRN"] ?? "",
"reconId" => $data["reconId"] ?? "",
"hostTxId" => $data["hostTxId"] ?? "",
"consumerId" => $data["consumerId"] ?? "",
"subscriptionId" => $data["subscriptionId"] ?? "",
];
}
}
// Generate HMAC signature from concatenated values
function generateHmacSignature($secret, $payload) {
$p = $payload->data;
$values = [
$payload->createdDt,
$p["additionalDetails"],
formatNumber($p["amount"]),
$p["approvalCode"],
formatNumber($p["baseAmount"]),
$p["buildNumber"],
$p["cardCategory"],
$p["cardInfo"],
$p["cardLabel"],
$p["cardToken"],
$p["cardType"],
$p["chName"],
formatNumber($p["commercialTaxAmount"]),
$p["commMedia"],
$p["consumerId"],
$p["dba"],
$p["description"],
$p["destType"],
$p["deviceModel"],
$p["email"],
$p["externalReferenceId"],
$p["hostResponseCode"],
$p["hostResponseDate"],
$p["hostTxId"],
$p["invoiceNumber"],
$p["L2L3uploadflag"],
formatNumber($p["localTaxAmount"]),
$p["maskedPan"],
$p["Mid"],
$p["minorChannel"],
$p["originalRRN"],
$p["performedBy"],
$p["phoneNumber"],
$p["posEntryMode"],
$p["posMode"],
$p["posRequestDate"],
$p["posRequestTime"],
$p["reconId"],
$p["responseText"],
$p["rrn"],
$p["sourceType"],
formatNumber($p["stateTaxAmount"]),
$p["subscriptionId"],
$p["tagLabel"],
$p["tagValue"],
$p["TermId"],
formatNumber($p["tipAdjAmount"]),
formatNumber($p["tipAmount"]),
formatNumber($p["totalFee"]),
$p["tpn"],
$p["tpnBatchNumber"],
$p["tpnLabel"],
$p["transactionId"],
$p["transactionType"],
formatNumber($p["trueCashDiscountFee"]),
$p["txDate"],
$p["txDuration"],
$p["txName"],
$p["txTime"],
$payload->eventType,
$payload->id,
$payload->requestType,
$payload->subEventType,
$payload->version
];
$concatenatedString = implode("|", $values);
error_log("Concatenated Values String for HMAC:\n" . $concatenatedString);
return hash_hmac("sha512", $concatenatedString, $secret);
}
// -------------------- API Endpoint(User End Point) --------------------
if ($_SERVER["REQUEST_METHOD"] === "POST" && $_SERVER["REQUEST_URI"] === "/feed-webhook") {
try {
$raw = file_get_contents("php://input");
$payload = json_decode($raw, true);
error_log("Received Transaction Payload: " . json_encode($payload));
$receivedSignature = trim($payload["signature"] ?? "");
error_log("Received Signature: " . $receivedSignature);
$sharedSecretKey = "Hjkuredd456Ay4aBHjkVRES9vf69b9nXcY8LYoH644eertY"; // Your HMAC key
$feedReceiverTxnPayload = new FeedReceiverTxnPayload(array_merge($payload, ["signature" => null]));
$generatedSignature = generateHmacSignature($sharedSecretKey, $feedReceiverTxnPayload);
if ($receivedSignature !== $generatedSignature) {
error_log("❌ Signature mismatch");
http_response_code(401);
echo json_encode([
"success" => false,
"message" => "Unauthorized: Invalid HMAC signature"
]);
exit;
}
error_log("✅ Signature verified");
http_response_code(200);
echo json_encode([
"success" => true,
"message" => "Authentication Success...!"
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode([
"success" => false,
"message" => "Internal Server Error",
"error" => $e->getMessage()
]);
}
}const express = require("express");
const crypto = require("crypto");
const bodyParser = require("body-parser");
const app = express();
app.use(bodyParser.json());
class FeedReceiverTxnPayload {
constructor(body) {
this.id = body.id || "";
this.eventType = body.eventType || "";
this.subEventType = body.subEventType || "";
this.requestType = body.requestType || "";
this.signature = undefined; // ignore signature during HMAC
this.version = body.version || "";
this.createdDt = body.createdDt || "";
this.data = {
dba: body.data?.dba || "",
tpn: body.data?.tpn || "",
tpnLabel: body.data?.tpnLabel || "",
deviceModel: body.data?.deviceModel || "",
buildNumber: body.data?.buildNumber || "",
maskedPan: body.data?.maskedPan || "",
rrn: body.data?.rrn || "",
txName: body.data?.txName || "",
transactionId: body.data?.transactionId || "",
externalReferenceId: body.data?.externalReferenceId || "",
amount: body.data?.amount || 0,
baseAmount: body.data?.baseAmount || 0,
totalFee: body.data?.totalFee || 0,
trueCashDiscountFee: body.data?.trueCashDiscountFee || 0,
commercialTaxAmount: body.data?.commercialTaxAmount || 0,
localTaxAmount: body.data?.localTaxAmount || 0,
stateTaxAmount: body.data?.stateTaxAmount || 0,
tipAmount: body.data?.tipAmount || 0,
tipAdjAmount: body.data?.tipAdjAmount || 0,
posRequestDate: body.data?.posRequestDate || "",
posRequestTime: body.data?.posRequestTime || "",
txTime: body.data?.txTime || "",
hostResponseDate: body.data?.hostResponseDate || "",
txDuration: body.data?.txDuration || "",
invoiceNumber: body.data?.invoiceNumber || "",
hostResponseCode: body.data?.hostResponseCode || "",
responseText: body.data?.responseText || "",
approvalCode: body.data?.approvalCode || "",
tpnBatchNumber: body.data?.tpnBatchNumber || "",
cardLabel: body.data?.cardLabel || "",
sourceType: body.data?.sourceType || "",
posEntryMode: body.data?.posEntryMode || "",
l2L3uploadflag: body.data?.L2l3UploadFlag ||
body.data?.l2L3uploadflag || "",
commMedia: body.data?.commMedia || "",
destType: body.data?.destType || "",
email: body.data?.email || "",
phoneNumber: body.data?.phoneNumber || "",
tagLabel: body.data?.tagLabel || "",
tagValue: body.data?.tagValue || "",
chName: body.data?.chName || "",
additionalDetails: body.data?.additionalDetails || "",
performedBy: body.data?.performedBy || "",
cardInfo: body.data?.cardInfo || "",
cardType: body.data?.cardType || "",
transactionType: body.data?.transactionType || "",
cardCategory: body.data?.cardCategory || "",
termId: body.data?.TermId || body.data?.termId || "",
mid: body.data?.Mid || body.data?.mid || "",
description: body.data?.description || "",
posMode: body.data?.posMode || "",
minorChannel: body.data?.minorChannel || "",
txDate: body.data?.txDate || "",
cardToken: body.data?.cardToken || "",
originalRRN: body.data?.originalRRN || "",
reconId: body.data?.reconId || "",
hostTxId: body.data?.hostTxId || "",
consumerId: body.data?.consumerId || "",
subscriptionId: body.data?.subscriptionId || "",
};
}
}
function formatNumber(value) {
if (typeof value === "number") {
// Convert number to string and preserve decimals
return value % 1 === 0 ? value.toFixed(1) : value.toString();
} else if (typeof value === "string") {
return value; // keep string as-is
}
return "";
}
// Generate HMAC signature from concatenated values
function generateHmacSignature(secret, payload) {
const p = payload.data;
const values = [
payload.createdDt,
p.additionalDetails,
formatNumber(p.amount),
p.approvalCode,
formatNumber(p.baseAmount),
p.buildNumber,
p.cardCategory,
p.cardInfo,
p.cardLabel,
p.cardToken,
p.cardType,
p.chName,
formatNumber(p.commercialTaxAmount),
p.commMedia,
p.consumerId,
p.dba,
p.description,
p.destType,
p.deviceModel,
p.email,
p.externalReferenceId,
p.hostResponseCode,
p.hostResponseDate,
p.hostTxId,
p.invoiceNumber,
p.L2L3uploadflag,
formatNumber(p.localTaxAmount),
p.maskedPan,
p.Mid,
p.minorChannel,
p.originalRRN,
p.performedBy,
p.phoneNumber,
p.posEntryMode,
p.posMode,
p.posRequestDate,
p.posRequestTime,
p.reconId,
p.responseText,
p.rrn,
p.sourceType,
formatNumber(p.stateTaxAmount),
p.subscriptionId,
p.tagLabel,
p.tagValue,
p.TermId,
formatNumber(p.tipAdjAmount),
formatNumber(p.tipAmount),
formatNumber(p.totalFee),
p.tpn,
p.tpnBatchNumber,
p.tpnLabel,
p.transactionId,
p.transactionType,
formatNumber(p.trueCashDiscountFee),
p.txDate,
p.txDuration,
p.txName,
p.txTime,
payload.eventType,
payload.id,
payload.requestType,
payload.subEventType,
payload.version,
];
const concatenatedString = values.join("|");
console.log("Concatenated Values String for HMAC:\n", concatenatedString);
const hmac = crypto.createHmac("sha512", secret);
hmac.update(concatenatedString, "utf8");
const hash = hmac.digest();
const hexString = hash.toString("hex");
return hexString;
}
//User End Point
app.post("/feed-webhook", (req, res) => {
// Provide your Corresponding EndPoint
try {
const payload = req.body;
console.log("Received Transaction Payload", payload);
const receivedSignature = payload.signature?.trim();
console.log("Received Signature:", receivedSignature);
const sharedSecretKey =
"Hjkuredd456Ay4aBHjkVRES9vf69b9nXcY8LYoH644eertY"; //Provide your Corresponding HMac Key
const feedReceiverTxnPayload = new FeedReceiverTxnPayload({
...payload,
signature: undefined,
});
const generatedSignature = generateHmacSignature(
sharedSecretKey,
feedReceiverTxnPayload
);
if (receivedSignature !== generatedSignature) {
console.warn("❌ Signature mismatch");
return res.status(401).json({
success: false,
message: "Unauthorized: Invalid HMAC signature",
});
}
console.log("✅ Signature verified");
return res.status(200).json({
success: true,
message: "Authentication Success...!"
});
} catch (error) {
console.error("Error in /pre-authenticate:", error);
return res.status(500).json({
success: false,
message: "Internal Server Error",
error: error.message,
});
}
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`🚀 Server running on http://localhost:${PORT}`);
});const crypto = require("crypto");
const HMAC_SHA512_ALGORITHM = "sha512";
function getOrDefault(value) {
return value !== null && value !== undefined ? value.toString() : "";
}
function convertTxnDetailsToString(data) {
if (!data) {
return "";
}
// Store fields in alphabetical order like Java TreeMap
const detailFields = new Map(
Object.entries({
dba: getOrDefault(data.data?.dba),
tpn: getOrDefault(data.data?.tpn),
tpnLabel: getOrDefault(data.data?.tagLabel),
deviceModel: getOrDefault(data.data?.deviceModel),
buildNumber: getOrDefault(data.data?.buildNumber),
maskedPan: getOrDefault(data.data?.maskedPan),
rrn: getOrDefault(data.data?.rrn),
txName: getOrDefault(data.data?.txName),
transactionId: getOrDefault(data.data?.transactionId),
externalReferenceId: getOrDefault(data.data?.externalReferenceId),
amount: getOrDefault(data.data?.amount),
baseAmount: getOrDefault(data.data?.baseAmount),
totalFee: getOrDefault(data.data?.totalFee),
trueCashDiscountFee: getOrDefault(data.data?.trueCashDiscountFee),
commercialTaxAmount: getOrDefault(data.data?.commercialTaxAmount),
localTaxAmount: getOrDefault(data.data?.localTaxAmount),
stateTaxAmount: getOrDefault(data.data?.stateTaxAmount),
tipAmount: getOrDefault(data.data?.tipAmount),
tipAdjAmount: getOrDefault(data.data?.tipAdjAmount),
posRequestDate: getOrDefault(data.data?.posRequestDate),
posRequestTime: getOrDefault(data.data?.posRequestTime),
txTime: getOrDefault(data.data?.txTime),
hostResponseDate: getOrDefault(data.data?.hostResponseDate),
txDuration: getOrDefault(data.data?.txDuration),
invoiceNumber: getOrDefault(data.data?.invoiceNumber),
hostResponseCode: getOrDefault(data.data?.hostResponseCode),
responseText: getOrDefault(data.data?.responseText),
approvalCode: getOrDefault(data.data?.approvalCode),
tpnBatchNumber: getOrDefault(data.data?.tpnBatchNumber),
cardLabel: getOrDefault(data.data?.cardLabel),
sourceType: getOrDefault(data.data?.sourceType),
posEntryMode: getOrDefault(data.data?.posEntryMode),
l2L3uploadflag: getOrDefault(data.data?.L2L3uploadflag),
commMedia: getOrDefault(data.data?.commMedia),
destType: getOrDefault(data.data?.destType),
email: getOrDefault(data.data?.email),
phoneNumber: getOrDefault(data.data?.phoneNumber),
tagLabel: getOrDefault(data.data?.tagLabel),
tagValue: getOrDefault(data.data?.tagValue),
chName: getOrDefault(data.data?.chName),
additionalDetails: getOrDefault(data.data?.additionalDetails),
performedBy: getOrDefault(data.data?.performedBy),
cardInfo: getOrDefault(data.data?.cardInfo),
cardType: getOrDefault(data.data?.cardType),
transactionType: getOrDefault(data.data?.transactionType),
cardCategory: getOrDefault(data.data?.cardCategory),
termId: getOrDefault(data.data?.TermId),
mid: getOrDefault(data.data?.Mid),
description: getOrDefault(data.data?.description),
posMode: getOrDefault(data.data?.posMode),
minorChannel: getOrDefault(data.data?.minorChannel),
txDate: getOrDefault(data.data?.txDate),
cardToken: getOrDefault(data.data?.cardToken),
originalRRN: getOrDefault(data.data?.originalRRN),
reconId: getOrDefault(data.data?.reconId),
hostTxId: getOrDefault(data.data?.hostTxId),
consumerId: getOrDefault(data.data?.consumerId),
subscriptionId: getOrDefault(data.data?.subscriptionId)
}).sort(([a], [b]) => a.localeCompare(b))
);
return Array.from(detailFields.values())
.map(value => getOrDefault(value))
.join("|");
}
function generateHmac(feedFinalTxnPosting, secretKey) {
// Store fields in alphabetical order
const fields = new Map(
Object.entries({
id: getOrDefault(feedFinalTxnPosting.id),
version: getOrDefault(feedFinalTxnPosting.version),
createdDt: getOrDefault(feedFinalTxnPosting.createdDt),
eventType: getOrDefault(feedFinalTxnPosting.eventType),
subEventType: getOrDefault(feedFinalTxnPosting.subEventType),
requestType: getOrDefault(feedFinalTxnPosting.requestType),
data: convertTxnDetailsToString(feedFinalTxnPosting.data)
}).sort(([a], [b]) => a.localeCompare(b))
);
// Concatenate fields with |
const concatenatedString = Array.from(fields.values())
.map(value => getOrDefault(value))
.join("|");
console.log("Before MAC Generation Value = " + concatenatedString);
// Generate HMAC SHA512
const hmac = crypto
.createHmac(HMAC_SHA512_ALGORITHM, Buffer.from(secretKey, "utf8"))
.update(concatenatedString, "utf8")
.digest("hex");
return hmac;
}
const secretKey = "YOUR_SECRET_KEY";
const signature = generateHmac(payload, secretKey);
console.log("Generated HMAC Signature:", signature);Settlement
This section describes the settlement event payload sent by FEED when a batch is settled. It includes the request payload, field definitions, and settlement transaction details to help you reconcile settled transactions within your application.
Map<String, String> fields = new TreeMap<>();
fields.put("id", feedFinalTxnPosting.getId() != null ? feedFinalTxnPosting.getId().toString() : "");
fields.put("tpn", feedFinalTxnPosting.getTpn());
fields.put("version", feedFinalTxnPosting.getVersion());
fields.put("createdDt", feedFinalTxnPosting.getVersionCreatedDt());
fields.put("eventType", feedFinalTxnPosting.getEventType());
fields.put("subEventType", feedFinalTxnPosting.getSubEventType());
fields.put("requestType", feedFinalTxnPosting.getRequestType());
fields.put("batchNumber", feedFinalTxnPosting.getBatchNumber());
fields.put("settlementDate", feedFinalTxnPosting.getSettlementDate());
fields.put("settlementCount", String.valueOf(feedFinalTxnPosting.getSettlementCount()));
fields.put("settlementAmount", String.valueOf(feedFinalTxnPosting.getSettlementCount()));
fields.put("settlementTxnDetails", convertSettlementTxnDetailsToString(feedFinalTxnPosting.getSettlementTxnDetails()));
// Concatenate fields with | as delimiter
String concatenatedString = fields.values().stream()
.collect(Collectors.joining("|"));
try {
Mac sha256Hmac = Mac.getInstance(HMAC_SHA512_ALGORITHM);
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(StandardCharsets.UTF_8), HMAC_SHA512_ALGORITHM);
sha256Hmac.init(secretKeySpec);
byte[] hashBytes = sha256Hmac.doFinal(concatenatedString.getBytes(StandardCharsets.UTF_8));
StringBuilder hexString = new StringBuilder();
for (byte b : hashBytes) {
String hex = Integer.toHexString(0xff & b);
if (hex.length() == 1) {
hexString.append('0');
}
hexString.append(hex);
}
return hexString.toString();
} catch (Exception e) {
throw new RuntimeException("Error while generating HMAC-SHA256", e);
}
public static String convertSettlementTxnDetailsToString(List<SettlementTxnDetails> details) {
if (details == null || details.isEmpty()) {
return "";
}
return details.stream()
.map(detail -> {
Map<String, Object> detailFields = new TreeMap<>();
detailFields.put("txnDate", detail.getTxnDate());
detailFields.put("transactionType", detail.getTransactionType());
detailFields.put("transactionId", detail.getTransactionId());
detailFields.put("txnAmount", detail.getTxnAmount());
detailFields.put("baseAmount", detail.getBaseAmount());
return detailFields.values().stream()
.map(value -> getOrDefault(value))
.collect(Collectors.joining("|"));
})
.collect(Collectors.joining("|"));
}using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
public class HMACGenerator
{
private const string HMAC_SHA512_ALGORITHM = "HmacSHA512";
public string GenerateHMACSHA256(FeedFinalTxnPosting feedFinalTxnPosting, string secretKey)
{
var fields = new SortedDictionary<string, string>
{
{ "id", feedFinalTxnPosting.Id != null ? feedFinalTxnPosting.Id.ToString() : "" },
{ "tpn", feedFinalTxnPosting.Tpn },
{ "version", feedFinalTxnPosting.Version },
{ "createdDt", feedFinalTxnPosting.VersionCreatedDt },
{ "eventType", feedFinalTxnPosting.EventType },
{ "subEventType", feedFinalTxnPosting.SubEventType },
{ "requestType", feedFinalTxnPosting.RequestType },
{ "batchNumber", feedFinalTxnPosting.BatchNumber },
{ "settlementDate", feedFinalTxnPosting.SettlementDate },
{ "settlementCount", feedFinalTxnPosting.SettlementCount.ToString() },
{ "settlementAmount", feedFinalTxnPosting.SettlementCount.ToString() },
{ "settlementTxnDetails", ConvertSettlementTxnDetailsToString(feedFinalTxnPosting.SettlementTxnDetails) }
};
// Concatenate fields with | as delimiter
string concatenatedString = string.Join("|", fields.Values);
try
{
using (var hmac = new HMACSHA512(Encoding.UTF8.GetBytes(secretKey)))
{
byte[] hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(concatenatedString));
StringBuilder hexString = new StringBuilder();
foreach (var b in hashBytes)
{
hexString.AppendFormat("{0:x2}", b);
}
return hexString.ToString();
}
}
catch (Exception e)
{
throw new InvalidOperationException("Error while generating HMAC-SHA256", e);
}
}
public string ConvertSettlementTxnDetailsToString(List<SettlementTxnDetails> details)
{
if (details == null || !details.Any())
{
return "";
}
return string.Join("|", details.Select(detail =>
{
var detailFields = new SortedDictionary<string, object>
{
{ "txnDate", detail.TxnDate },
{ "transactionType", detail.TransactionType },
{ "transactionId", detail.TransactionId },
{ "txnAmount", detail.TxnAmount },
{ "baseAmount", detail.BaseAmount }
};
return string.Join("|", detailFields.Values.Select(value => GetOrDefault(value)));
}));
}
private string GetOrDefault(object value)
{
return value?.ToString() ?? "";
}
}
public class FeedFinalTxnPosting
{
public string Id { get; set; }
public string Tpn { get; set; }
public string Version { get; set; }
public string VersionCreatedDt { get; set; }
public string EventType { get; set; }
public string SubEventType { get; set; }
public string RequestType { get; set; }
public string BatchNumber { get; set; }
public string SettlementDate { get; set; }
public int SettlementCount { get; set; }
public List<SettlementTxnDetails> SettlementTxnDetails { get; set; }
}
public class SettlementTxnDetails
{
public string TxnDate { get; set; }
public string TransactionType { get; set; }
public string TransactionId { get; set; }
public string TxnAmount { get; set; }
}import hmac
import hashlib
from collections import OrderedDict
class HMACGenerator:
HMAC_SHA512_ALGORITHM = 'sha512'
def generate_hmac_sha256(self, feed_final_txn_posting, secret_key):
fields = OrderedDict({
"id": str(feed_final_txn_posting.get("id", "")) if feed_final_txn_posting.get("id") else "",
"tpn": feed_final_txn_posting.get("tpn", ""),
"version": feed_final_txn_posting.get("version", ""),
"createdDt": feed_final_txn_posting.get("versionCreatedDt", ""),
"eventType": feed_final_txn_posting.get("eventType", ""),
"subEventType": feed_final_txn_posting.get("subEventType", ""),
"requestType": feed_final_txn_posting.get("requestType", ""),
"batchNumber": feed_final_txn_posting.get("batchNumber", ""),
"settlementDate": feed_final_txn_posting.get("settlementDate", ""),
"settlementCount": str(feed_final_txn_posting.get("settlementCount", 0)),
"settlementAmount": str(feed_final_txn_posting.get("settlementAmount", 0)),
"settlementTxnDetails": self.convert_settlement_txn_details_to_string(feed_final_txn_posting.get("settlementTxnDetails", []))
})
# Concatenate fields with '|' as delimiter
concatenated_string = '|'.join(fields.values())
try:
# Generate HMAC-SHA512
hmac_obj = hmac.new(secret_key.encode('utf-8'), concatenated_string.encode('utf-8'), hashlib.sha512)
hex_string = hmac_obj.hexdigest()
return hex_string
except Exception as e:
raise Exception("Error while generating HMAC-SHA256") from e
def convert_settlement_txn_details_to_string(self, details):
if not details:
return ""
return '|'.join([
'|'.join([
self.get_or_default(detail.get("txnDate")),
self.get_or_default(detail.get("transactionType")),
self.get_or_default(detail.get("transactionId")),
self.get_or_default(detail.get("txnAmount")),
self.get_or_default(detail.get("baseAmount"))
]) for detail in details
])
def get_or_default(self, value):
return str(value) if value is not None else ""
# Example usage
feed_final_txn_posting = {
"id": 123,
"tpn": "TPN123",
"version": "1.0",
"versionCreatedDt": "2025-02-11",
"eventType": "event1",
"subEventType": "sub_event1",
"requestType": "req1",
"batchNumber": "batch123",
"settlementDate": "2025-02-11",
"settlementCount": 2,
"settlementAmount": 200,
"settlementTxnDetails": [
{
"txnDate": "2025-02-11",
"transactionType": "type1",
"transactionId": "txn123",
"txnAmount": 100,
“baseAmount”: 80
},
{
"txnDate": "2025-02-12",
"transactionType": "type2",
"transactionId": "txn124",
"txnAmount": 100,
"baseAmount": 80
}
]
}
secret_key = "your_secret_key"
hmac_generator = HMACGenerator()
hmac_sha256 = hmac_generator.generate_hmac_sha256(feed_final_txn_posting, secret_key)
print("Generated HMAC-SHA256:", hmac_sha256)<?php
class HMACGenerator
{
const HMAC_SHA512_ALGORITHM = 'sha512';
public function generateHMACSHA256($feedFinalTxnPosting, $secretKey)
{
$fields = [
"id" => isset($feedFinalTxnPosting["id"]) ? (string)$feedFinalTxnPosting["id"] : "",
"tpn" => $feedFinalTxnPosting["tpn"] ?? "",
"version" => $feedFinalTxnPosting["version"] ?? "",
"createdDt" => $feedFinalTxnPosting["versionCreatedDt"] ?? "",
"eventType" => $feedFinalTxnPosting["eventType"] ?? "",
"subEventType" => $feedFinalTxnPosting["subEventType"] ?? "",
"requestType" => $feedFinalTxnPosting["requestType"] ?? "",
"batchNumber" => $feedFinalTxnPosting["batchNumber"] ?? "",
"settlementDate" => $feedFinalTxnPosting["settlementDate"] ?? "",
"settlementCount" => (string)($feedFinalTxnPosting["settlementCount"] ?? 0),
"settlementAmount" => (string)($feedFinalTxnPosting["settlementAmount"] ?? 0),
"settlementTxnDetails" => $this->convertSettlementTxnDetailsToString($feedFinalTxnPosting["settlementTxnDetails"] ?? [])
];
// Concatenate fields with | as delimiter
$concatenatedString = implode("|", $fields);
try {
// Generate HMAC-SHA512
$hash = hash_hmac(self::HMAC_SHA512_ALGORITHM, $concatenatedString, $secretKey, false);
return $hash;
} catch (Exception $e) {
throw new Exception("Error while generating HMAC-SHA256", 0, $e);
}
}
public function convertSettlementTxnDetailsToString($details)
{
if (empty($details)) {
return "";
}
$txnDetailsStrings = array_map(function ($detail) {
return implode("|", [
$this->getOrDefault($detail["txnDate"]),
$this->getOrDefault($detail["transactionType"]),
$this->getOrDefault($detail["transactionId"]),
$this->getOrDefault($detail["txnAmount"]),
$this->getOrDefault($detail["baseAmount"])
]);
}, $details);
return implode("|", $txnDetailsStrings);
}
public function getOrDefault($value)
{
return $value !== null ? (string)$value : "";
}
}
// Example usage
$feedFinalTxnPosting = [
"id" => 123,
"tpn" => "TPN123",
"version" => "1.0",
"versionCreatedDt" => "2025-02-11",
"eventType" => "event1",
"subEventType" => "sub_event1",
"requestType" => "req1",
"batchNumber" => "batch123",
"settlementDate" => "2025-02-11",
"settlementCount" => 2,
"settlementAmount" => 200,
"settlementTxnDetails" => [
[
"txnDate" => "2025-02-11",
"transactionType" => "type1",
"transactionId" => "txn123",
"txnAmount" => 100
],
[
"txnDate" => "2025-02-12",
"transactionType" => "type2",
"transactionId" => "txn124",
"txnAmount" => 100
]
]
];
$secretKey = "your_secret_key";
$hmacGenerator = new HMACGenerator();
$hmacSHA256 = $hmacGenerator->generateHMACSHA256($feedFinalTxnPosting, $secretKey);
echo "Generated HMAC-SHA256: " . $hmacSHA256;
?>const crypto = require('crypto');
class HMACGenerator {
static HMAC_SHA512_ALGORITHM = 'sha512';
generateHMACSHA256(feedFinalTxnPosting, secretKey) {
const fields = {
"id": feedFinalTxnPosting.id != null ? String(feedFinalTxnPosting.id) : "",
"tpn": feedFinalTxnPosting.tpn || "",
"version": feedFinalTxnPosting.version || "",
"createdDt": feedFinalTxnPosting.versionCreatedDt || "",
"eventType": feedFinalTxnPosting.eventType || "",
"subEventType": feedFinalTxnPosting.subEventType || "",
"requestType": feedFinalTxnPosting.requestType || "",
"batchNumber": feedFinalTxnPosting.batchNumber || "",
"settlementDate": feedFinalTxnPosting.settlementDate || "",
"settlementCount": String(feedFinalTxnPosting.settlementCount || 0),
"settlementAmount": String(feedFinalTxnPosting.settlementAmount || 0),
"settlementTxnDetails": this.convertSettlementTxnDetailsToString(feedFinalTxnPosting.settlementTxnDetails || [])
};
// Concatenate fields with '|' as delimiter
const concatenatedString = Object.values(fields).join("|");
try {
// Generate HMAC-SHA512
const hmac = crypto.createHmac(HMACGenerator.HMAC_SHA512_ALGORITHM, secretKey);
hmac.update(concatenatedString);
const hash = hmac.digest('hex');
return hash;
} catch (e) {
throw new Error("Error while generating HMAC-SHA256");
}
}
convertSettlementTxnDetailsToString(details) {
if (!details || details.length === 0) {
return "";
}
return details.map(detail => {
return [
this.getOrDefault(detail.txnDate),
this.getOrDefault(detail.transactionType),
this.getOrDefault(detail.transactionId),
this.getOrDefault(detail.txnAmount),
this.getOrDefault(detail.baseAmount)
].join("|");
}).join("|");
}
getOrDefault(value) {
return value != null ? String(value) : "";
}
}
// Example usage
const feedFinalTxnPosting = {
id: 123,
tpn: "TPN123",
version: "1.0",
versionCreatedDt: "2025-02-11",
eventType: "event1",
subEventType: "sub_event1",
requestType: "req1",
batchNumber: "batch123",
settlementDate: "2025-02-11",
settlementCount: 2,
settlementAmount: 200,
settlementTxnDetails: [
{
txnDate: "2025-02-11",
transactionType: "type1",
transactionId: "txn123",
txnAmount: 100,
baseAmount: 80
},
{
txnDate: "2025-02-12",
transactionType: "type2",
transactionId: "txn124",
txnAmount: 100,
baseAmount: 80
}
]
};
const secretKey = "your_secret_key";
const hmacGenerator = new HMACGenerator();
const hmacSHA256 = hmacGenerator.generateHMACSHA256(feedFinalTxnPosting, secretKey);
console.log("Generated HMAC-SHA256:", hmacSHA256);import CryptoJS from "crypto-js";
const HMAC_SHA512_ALGORITHM = "sha512";
function getOrDefault(value) {
return value !== null && value !== undefined
? value.toString()
: "";
}
function convertSettlementTxnDetailsToString(details) {
if (!details || details.length === 0) {
return "";
}
return details
.map((detail) => {
// Same as Java TreeMap sorting
const detailFields = Object.entries({
txnDate: getOrDefault(detail?.txnDate),
transactionType: getOrDefault(detail?.transactionType),
transactionId: getOrDefault(detail?.transactionId),
txnAmount: getOrDefault(detail?.txnAmount),
baseAmount: getOrDefault(detail?.baseAmount)
}).sort(([a], [b]) => a.localeCompare(b));
return detailFields
.map(([key, value]) => getOrDefault(value))
.join("|");
})
.join("|");
}
export function generateSettlementHmac(feedFinalTxnPosting, secretKey) {
// TreeMap sorting
const fields = Object.entries({
id: getOrDefault(feedFinalTxnPosting?.id),
tpn: getOrDefault(feedFinalTxnPosting?.tpn),
version: getOrDefault(feedFinalTxnPosting?.version),
createdDt: getOrDefault(feedFinalTxnPosting?.versionCreatedDt),
eventType: getOrDefault(feedFinalTxnPosting?.eventType),
subEventType: getOrDefault(feedFinalTxnPosting?.subEventType),
requestType: getOrDefault(feedFinalTxnPosting?.requestType),
batchNumber: getOrDefault(feedFinalTxnPosting?.batchNumber),
settlementDate: getOrDefault(feedFinalTxnPosting?.settlementDate),
settlementCount: getOrDefault(feedFinalTxnPosting?.settlementCount),
// Same as Java code
settlementAmount: getOrDefault(feedFinalTxnPosting?.settlementCount),
settlementTxnDetails: convertSettlementTxnDetailsToString(
feedFinalTxnPosting?.settlementTxnDetails
)
}).sort(([a], [b]) => a.localeCompare(b));
// Concatenate with |
const concatenatedString = fields
.map(([key, value]) => getOrDefault(value))
.join("|");
console.log("Before MAC Generation Value =", concatenatedString);
// Generate HMAC SHA512
const hmac = CryptoJS.HmacSHA512(
concatenatedString,
secretKey
).toString(CryptoJS.enc.Hex);
return hmac;
}
Transaction Types
- BALANCE ENQ
- INC AUTH
- INC SALE
- PREAUTH
- REFUND
- SALE
- TICKET
- TIP ADJUST
- VOID
- VOID PREAUTH
- VOID REFUND
- VOID SALE
- VOID TICKET
- Source Types
- ECOM
- TPOS
- ITAP
- TOPH
- Event Types
- Transaction
- Settlement
Sub Events
- Transaction
- INC AUTH
- INC SALE
- PREAUTH
- REFUND
- SALE
- TICKET
- TIP ADJUST
- VOID
- VOID PREAUTH
- VOID REFUND
- VOID SALE
- VOID TICKET
Settlement Batch
Closed Batch
Transaction Event - Transaction Process Payload.
| Field | Type | Description |
|---|---|---|
| id * | string | Unique id generated for each request |
| version * | string | Version of the FEED API you are currently using |
| createdDt * | string | Created Version Date of the FEED API, Format: yyyy-MM-dd HH:mm:ss (Eastern Standard Time – EST) |
| eventType * | string | Transaction Event Type |
| subEventType * | string | Transaction type which was done by the Customer |
| requestType * | string | Type of Request whether to insert or update the existing record |
| signature | string | Mandatory only for HMAC enabled Merchants |
| Field | Type | Description |
|---|---|---|
| dba | string | Merchant Registered as Doing Business as with Dejavoo |
| tpnLabel | string | Label name which provided while onboarding |
| deviceModel | string | Device Name where merchant is performing the transactions this data will received in Second posting which we are Additional Transaction Details as request Type as Update |
| tpnBatchNumber | string | Unique number will be generated to process the settlement of merchant transactions |
| externalReferenceId | string | Merchant unique transaction reference id. |
| hostResponseDate | string | Date time of transaction initiated |
| amount | double | (USD) Amount to be charged. Amount will sent in decimal format (example 10.01) |
| baseAmount fee | double | Basic amount In cents is $100.00 |
| description | string | Description of the transaction this data will received in Second posting which we are Additional Transaction Details as request Type as Update |
| commercialTaxAmount | double | Tax Calculation for the Transaction if merchant Opted [Decimal format (Ex : 10.00)] |
| localTaxAmount | double | Local Tax Calculation for the Transaction if merchant Opted [Decimal format (Ex : 10.00)] |
| tipAmount | double | Tip Amount if Customer raised [Decimal format(Ex : 10.00)] |
| tipAdjAmount | double | TipAdjust calculations [Decimal format(Ex : 10.00)] |
| posRequestDate | string | Request Received from the POS Terminal Date in EST(yyyy-MM-dd) |
| posRequestTime | string | Request Received from the POS Terminal Time in EST(yyyy-MM-dd) |
| transactionDate | string | Transaction Initiated Date in EST(yyyy-MM-dd) |
| transactionTime | string | Transaction Initiated Time in EST(yyyy-MM-dd) |
| hostResponseDate | string | Transaction Date and Time in EST(yyyy-MM-dd) |
| txDuration | string | Transaction Duration for complete process this data will received in Second posting which we are Additional Transaction Details as request Type as Update |
| invoiceNumber | string | Transaction Number generated by Issuing Bank |
| hostResponseCode | string | Received from the Issuing Bank |
| responseText | string | Received from the Issuing Bank |
| approvalCode | string | Received from the Issuing Bank |
| buildNumber | string | Transaction Processing Build this data will received in Second posting which we are Additional Transaction Details as request Type as Update |
| cardLabel | string | Card Scheme |
| chName | string | Name of Cardholder this data will received in Second posting which we are Additional Transaction Details as request Type as Update |
| maskedPan | string | First 6 digits and Last 4 digits of card number |
| posMode | string | Transaction Initiated Process |
| channel(sourceType) | string | Transaction Initiated Channel |
| minorChannel | string | Request Received Channel based on Source this data will received in Second posting which we are Additional Transaction Details as request Type as Update |
| commMedia | string | Request Process Communication Mode this data will received in Second posting which we are Additional Transaction Details as request Type as Update |
| destType | string | Transaction Acceptor Name(processor) |
| string | Customer Email this data will received in Second posting which we are Additional Transaction Details as request Type as Update | |
| phoneNumber | string | Customer Mobile Number this data will received in Second posting which we are Additional Transaction Details as request Type as Update |
| tagLabel | string | Additional Information provided in the Edit Parameters in registration process which will shown as additional Details in HPP and GHPP Page |
| tagValue | string | Additional Information provided in the Edit Parameters in registration process which will shown as additional Details in HPP and GHPP Page |
| performedBy | string | Customer Name who performed the transaction this data will received in Second posting which we are Additional Transaction Details as request Type as Update |
| cardInfo | string | Transaction Initiated by CardHolder or Others |
| cardType | string | Card Type used by the cardholder |
| transactionMode | string | Mode of the transaction |
| cardCategory | string | Card category based on the service provider of the Card Holder |
| additionalDetails | string | Future Use this data will received in Second posting which we are Additional Transaction Details as request Type as Update |
| termId | string | TerminalId registered by the Processor to Merchant |
| Mid | string | MerchantId provided by the processor to the Merchant |
| l2L3uploadflag | string | Whether transaction has performed with L2L3 Packet or not |
| transactionId | string | iPOSpay response unique transaction id |
| transactionType | string | Type of transactions to be processed. |
| tpn | string | TPN Registered with Dejavoo System |
| rrn | string | Unique Reference Number generated by Bank System |
| cardToken | string | This is used as Tokenized Card for complete the void and Refund transactions using iposTransact API |
| totalFee | double | Total Fee Amount, Decimal format(Ex : 10.00) |
| trueCashDiscountFee | double | Discount fee if customer has using the debit card transactions , Decimal format(Ex : 10.00) |
| stateTaxAmount | double | State Tax Calculation for the Transaction if merchant Opted , Decimal format(Ex : 10.00) |
| posEntryMode | string | PosEntryMode |
| originalRRN | string | For Mapping Refund and void Sale with the original transaction |
| reconId | string | Id from Processor to track transaction and settlement |
| hostTxId | string | TransactionId from Processor |
| consumerId | string | Used for fetching customer details |
| subscriptionId | string | To Track Recurring Transactions |
Authentication with Basic Authentication
{
"id": "6ea412fc-7181-4eb6-bb43-d07684ceff72",
"eventType": "Transaction",
"subEventType": "SALE",
"requestType": "N",
"signature": null,
"version": "1.0",
"createdDt": "2024-06-04 08:53:39",
"data": {
"dba": "Virat",
"tpn": "342824380352",
"tpnLabel": "",
"deviceModel": "Cloud POS",
"buildNumber": "10597",
"maskedPan": "424242******4242",
"rrn": "428209503179",
"txName": "SALE",
"transactionId": "41151701526138035220241008151118",
"externalReferenceId": "0",
"amount": 1.3,
"baseAmount": 0.96,
"totalFee": 0.0,
"trueCashDiscountFee": 0.04,
"commercialTaxAmount": 0.0,
"localTaxAmount": 0.2,
"stateTaxAmount": 0.1,
"tipAmount": 0.0,
"tipAdjAmount": 0.0,
"posRequestDate": "2024-10-08",
"posRequestTime": "11:11:18",
"txTime": "05:41:18",
"hostResponseDate": "2024-10-08",
"txDuration": "0.902",
"invoiceNumber": "000002",
"hostResponseCode": "00",
"responseText": "NO MATCH",
"approvalCode": "TAS176",
"tpnBatchNumber": "469",
"cardLabel": "VISA",
"sourceType": "ECOM",
"posEntryMode": "100",
"L2L3uploadflag": "No",
"commMedia": null,
"destType": "TSYS - QA",
"email": "femeyey365@seosnaps.com",
"phoneNumber": "+19898989898",
"tagLabel": "Clerk ID,Table ID,Invoice No",
"tagValue": "1,2,3",
"chName": "Dhoni",
"additionalDetails": "",
"performedBy": "nowona1660@ipnuc.com",
"cardInfo": "PERSONAL",
"cardType": "CREDIT",
"transactionType": "CREDIT",
"cardCategory": "CLASSIC",
"TermId": "75091651",
"Mid": "888000003338",
"description": "Description",
"posMode": "COF",
"minorChannel": "E-Commerce",
"txDate": "2024-10-08",
"cardToken": "7CB5968FACE5A7127CB5968FACE5A712390655538A3C6C73",
"originalRRN":"503711500457",
"reconId": "616010856328",
"hostTxId": "000000000123456",
"consumerId": "S9U2W8UED6RS7V",
"subscriptionId": "A1985KA6E911F"
},
"headers": {
"Authorization": "Basic Authorization Token",
"Content-Type": "application/json"
}
}HMAC Sample Request
{
"id": "2d4f1c3d-b1eb-4867-a168-a05e74ccd245",
"eventType": "Transaction",
"subEventType": "SALE",
"requestType": "N",
"signature": "df0047bae2540420e7645c6c323ee18a106008063fce8491b983031419429378558b74ffa16385470d7d36aa8bcc0d2a45a38e3f6d57410df90d9f970a0848c7",
"version": "1.0",
"createdDt": "2024-06-04 08:53:39",
"data": {
"dba": "Virat",
"tpn": "",
"tpnLabel": "",
"deviceModel": "Cloud POS",
"buildNumber": "10597",
"maskedPan": "424242******4242",
"rrn": "428209503926",
"txName": "SALE",
"transactionId": "35018720494938035220241008152830",
"externalReferenceId": "0",
"amount": 2.6,
"baseAmount": 1.92,
"totalFee": 0.0,
"trueCashDiscountFee": 0.08,
"commercialTaxAmount": 0.0,
"localTaxAmount": 0.4,
"stateTaxAmount": 0.2,
"tipAmount": 0.0,
"tipAdjAmount": 0.0,
"posRequestDate": "2024-10-08",
"posRequestTime": "11:28:30",
"txTime": "05:58:30",
"hostResponseDate": "2024-10-08",
"txDuration": "1.26",
"invoiceNumber": "000001",
"hostResponseCode": "00",
"responseText": "NO MATCH",
"approvalCode": "TAS575",
"tpnBatchNumber": "470",
"cardLabel": "VISA",
"sourceType": "ECOM",
"posEntryMode": "100",
"L2L3uploadflag": "No",
"commMedia": null,
"destType": "TSYS - QA",
"email": "nowoa1660@ipnuc.comn",
"phoneNumber": "+19898989898",
"tagLabel": "Clerk ID,Table ID,Invoice No",
"tagValue": "1,2,3",
"chName": "Ak Arun",
"additionalDetails": "",
"performedBy": "femeyey365@seosnaps.com",
"cardInfo": "PERSONAL",
"cardType": "CREDIT",
"transactionType": "CREDIT",
"cardCategory": "CLASSIC",
"TermId": "75091651",
"Mid": "888000003338",
"description": "Description",
"posMode": "COF",
"minorChannel": "E-Commerce",
"txDate": "2024-10-08",
"cardToken": "7CB5968FACE5A7127CB5968FACE5A712390655538A3C6C73",
"originalRRN": "503711500457",
"reconId": "616010856328",
"hostTxId": "000000000123456",
"consumerId": "S9U2W8UED6RS7V",
"subscriptionId": "A1985KA6E911F"
},
"headers": {
"Content-Type": "application/json"
}
}Settlement Event
This event provided the settlement of the merchants with transaction details which got settled on the batch. It will be easy for an integrator to find the transaction with count.
| Field | Type | Description |
|---|---|---|
| id * | string | Unique id generated for each request |
| version * | string | Version of the FEED API you are currently using |
| createdDt * | string | Created Version Date of the FEED API, Format: yyyy-MM-dd HH:mm:ss (Eastern Standard Time – EST) |
| eventType * | string | Transaction Event Type |
| subEventType * | string | Transaction type which was done by the Customer |
| requestType * | string | Type of Request whether to insert or update the existing record |
| signature | string | Mandatory only for HMAC enabled Merchants |
| settlementAmount | double | Total Amount Settled for that TPN in Batch , Decimal format(Ex : 10.00) |
| batchNumber | string | Settled Batch Number it will unique |
| settlementDate | string | Settled Batch Date and Time in EST format in EST (yyyy-MM-dd HH:mm:ss) |
| SettledTxnCount | long | Total Txn’s Settled with Batch |
| tpn | string | TPN Registered with Dejavoo System |
| Field | Type | Description |
|---|---|---|
| txnDate | string | Transaction Initiated Date and Time in EST (yyyy-MM-dd HH:mm:ss) |
| transactionId | string | iPOSpays response unique transaction id |
| transactionType | string | Type of transactions to be processed. |
| txnAmount | double | Transaction Amount , Decimal format(Ex : 10.00) |
| baseAmount | decimal | Basic amount In cents is $100.00 |
Basic Authentication Sample Payload
{
"id": "f5b15dac-359f-439d-8977-a0226c467dc7",
"eventType": "Settlement",
"subEventType": "ClosedBatch",
"requestType": "HTTPS",
"signature": null,
"version": "1.0",
"versionCreatedDt": "2024-06-04 08:53:39",
"batchNumber": "469",
"settlementDate": "2024-10-08",
"settlementCount": 2,
"settlementAmount": 2.6,
"tpn": "",
"settlementTxnDetails": [
{
"txnDate": "2024-10-08 04:06:18",
"transactionType": "SALE",
"transactionId": "38939820834338035220241008133618",
"txnAmount": 1.3,
"baseAmount":1.1
},
{
"txnDate": "2024-10-08 05:41:18",
"transactionType": "SALE",
"transactionId": "41151701526138035220241008151118",
"txnAmount": 1.3,
"baseAmount":1.1
}
],
"headers": {
"Authorization": "Basic Authorization Token",
"Content-Type": "application/json"
}
}HMAC Sample Payload
{
"id": "46f69dec-fce4-444c-ba7b-64a11b9357a3",
"eventType": "Settlement",
"subEventType": "ClosedBatch",
"requestType": "HTTPS",
"signature": "464ebd627992eb451b8d8a4075d6869c685e6ce7552535318a607f7ee68cccde0856fc78ae9e84f88778c4f935b4953cc61764e3f4152032d1c0e12dd5280ff0",
"version": "1.0",
"versionCreatedDt": "2024-06-04 08:53:39",
"batchNumber": "470",
"settlementDate": "2024-10-08",
"settlementCount": 2,
"settlementAmount": 3.9,
"tpn": "",
"settlementTxnDetails": [
{
"txnDate": "2024-10-08 05:58:30",
"transactionType": "SALE",
"transactionId": "35018720494938035220241008152830",
"txnAmount": 2.6,
"baseAmount": 1.1
},
{
"txnDate": "2024-10-08 06:00:28",
"transactionType": "SALE",
"transactionId": "64838090846138035220241008153028",
"txnAmount": 1.3,
"baseAmount": 1.1
}
],
"headers": {
"Content-Type": "application/json"
}
}For each transaction, two requests will be sent. In the first request, certain data fields (such as chName, minorChannel, phoneNumber, buildNumber, etc.) will be sent as null.
In the second request, you will receive the actual values for these fields. These two requests can be mapped together based on the transactionId.
POS Entry mode
| Code | Description |
|---|---|
| 051 | CHIP |
| 011 | MANUAL |
| 711 | CONTACTLESS |
| 071 | CONTACTLESS |
| 901 | SWIPE |
| 010 | MANUAL |
| 012 | MANUAL |
| 050 | CHIP |
| 052 | CHIP |
| 112 | CONTACTLESS |
| 110 | CONTACTLESS |
| 710 | CONTACTLESS |
| 712 | CONTACTLESS |
| 900 | SWIPE |
| 902 | SWIPE |
| 800 | SWIPE |
| 802 | SWIPE |
| 100 | COF |
| 801 | SWIPE |
| 004 | CHECK |
Last updated on July 30, 2026