Callback validation and rounding#
This page describes how to validate transaction callback authenticity using the hash field and explains the rounding behaviour for monetary amounts.
Hash validation#
Each transaction callback includes a hash field that allows you to verify the callback originated from XGateway and has not been tampered with. The hash is generated using SHA-512 and encoded in Base64.
Hash composition#
The hash is computed from a concatenated string of specific transaction fields, separated by dots:
{transactionId}.{customerId}.{amount}.{currency}.{secretKey}
| Component | Description |
|---|---|
transactionId | The unique transaction ID (id field in the callback) |
customerId | The customer ID (customerId field in the callback). If no customer ID is present, the literal string N/A is used |
amount | The original transaction amount as sent by the customer |
currency | The original transaction currency |
secretKey | Your merchant secret key (provided during integration setup) |
The amount and currency used in the hash are the original values as sent by the customer, not the converted or reference amounts.
Validation example#
For a callback with the following relevant fields:
{
"id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"customerId": "customer_123",
"amount": "100.50",
"currency": "EUR",
"hash": "YOUR_EXPECTED_HASH_HERE"
}
Using a secret key of your_secret_key_here, you would construct the string:
a1b2c3d4-e5f6-7890-abcd-ef1234567890.customer_123.100.50.EUR.your_secret_key_here
Then compute the SHA-512 hash of this string and encode the result in Base64. Compare your computed hash with the hash field in the callback to verify authenticity.
Example validation in pseudocode
concatenated = transactionId + "." + customerId + "." + amount + "." + currency + "." + secretKey
computedHash = base64Encode(sha512(concatenated))
isValid = (computedHash == callback.hash)
If the callback is for a transaction without a customer ID (such as settlement transactions), use the literal string N/A as the customer ID component.
Amount rounding#
Transaction amounts may undergo currency conversion during processing. Understanding how these amounts are rounded is important for reconciliation.
Original amount as source of truth#
The original amount (amount and currency fields) represents the exact value as submitted by the customer. This is the authoritative source for the transaction value.
When converting to reference or transaction currencies, the system applies the exchange rate and then rounds the result to the target currency's precision (typically 2 decimal places for fiat currencies).
Rounding behaviour change#
Historically, XGateway used truncation (round down) after the precision digit. This behaviour is being updated to use round half to even (banker's rounding), which is the IEEE 754 standard for financial calculations.
| Method | Description | Example (2 decimal places) |
|---|---|---|
| Truncation (legacy) | Discards digits beyond precision | 13.53999... → 13.53 |
| Round half to even (new) | Rounds to nearest; ties go to even | 13.53999... → 13.54 |
Practical example#
Consider a transaction with:
- Original amount: 0.005691801955558544 ETH
- Exchange rate: 2378.86 USD/ETH
The conversion calculation:
2378.86 × 0.005691801955558544 = 13.53999999999999797984
| Rounding method | Result |
|---|---|
| Truncation (legacy) | 13.53 USD |
| Round half to even (new) | 13.54 USD |
The difference arises because truncation simply discards the digits beyond the precision point, whilst round half to even considers the value of those digits to determine whether to round up or down.
Reconciliation guidance#
When reconciling transactions:
- Use the original amount and original currency as your primary reference
- Be aware that converted amounts (reference amount, transaction amount) may differ slightly due to rounding
- For precise verification, replicate the conversion using the provided exchange rate and apply the appropriate rounding method
If you observe small discrepancies (typically ≤ 0.01 in the target currency) between your calculated values and the callback amounts, this is likely due to the rounding method. Always verify using the original amount as the source of truth.