Generate and verify HMAC-SHA256 checksums for API requests and callbacks.
To guarantee data integrity and authenticity for all communications, Facilero uses a checksum verification mechanism based on HMAC-SHA256 and your merchantSecret.
The checksum ensures that:
- Signed request and callback fields have not been altered in transit.
- The sender is authenticated using the shared secret.
When to Use the Checksum
| Direction | Checksum location | Fields used, in order |
|---|---|---|
| APM request to Facilero | Body field: checksum | accountId → amount → currency → requestId |
| Callback from Facilero | HTTP header: X-Checksum | See the callback matrix below |
Callback Checksum Matrix
| Callback type | Fields used, in order |
|---|---|
| APM payment | accountId → orderAmount → orderCurrency → transactionId |
| Card payment | transactionId → requestId → transactionStatus |
| Card payout | transactionId → requestId → transactionStatus |
| APM payout | transactionId → requestId → transactionStatus |
| Card refund | transactionId (original card-payment ID) → requestId → transactionStatus |
Important: Select the checksum formula based on the API flow that originated the callback. Do not infer APM payment versus APM payout from the presence of
accountId.
For card refunds:
transactionIdis the original card-payment transaction ID, notrefundId.requestIdidentifies the individual refund.refundIdremains available in the callback payload for reconciliation, but it is not included in the checksum.
How to Generate the Checksum
1. Merchant → Facilero: APM Request
Concatenate these fields in this exact order:
accountId|amount|currency|requestId2. Facilero → Merchant: APM Payment Callback
Concatenate these fields in this exact order:
accountId|orderAmount|orderCurrency|transactionId3. Facilero → Merchant: Transaction-Result Callback
For card payments, card payouts, card refunds, and APM payouts, concatenate:
transactionId|requestId|transactionStatus4. Apply HMAC-SHA256
- Join the required values with the pipe character
|. - Sign the resulting string with HMAC-SHA256 using your
merchantSecret. - Encode the resulting bytes using standard Base64.
Do not use hexadecimal encoding or URL-safe Base64. Standard Base64 may contain
+,/, and=; these characters must be preserved.
Example: Request
For an APM request, build the checksum input from:
accountId|amount|currency|requestIdThen:
HMAC-SHA256(checksum_string, merchantSecret)Finally, Base64-encode the HMAC bytes:
checksum = Base64(HMAC-SHA256(checksum_string, merchantSecret))Send the result in the request body's checksum field.
Example: APM Payment Callback
For an APM-payment callback, build the checksum input from:
accountId|orderAmount|orderCurrency|transactionIdThen:
HMAC-SHA256(checksum_string, merchantSecret)Base64-encode the HMAC bytes:
expected_checksum = Base64(HMAC-SHA256(checksum_string, merchantSecret))Compare the result with the value received in the X-Checksum header.
The APM-payment checksum formula is unchanged.
Example: Card Payment, Card Payout, Card Refund, or APM Payout Callback
For these transaction-result callbacks, build the checksum input from:
transactionId|requestId|transactionStatusThen:
HMAC-SHA256(checksum_string, merchantSecret)Base64-encode the HMAC bytes:
expected_checksum = Base64(HMAC-SHA256(checksum_string, merchantSecret))Compare the result with the X-Checksum header using a constant-time comparison.
+,/, and=are valid standard-Base64 characters. Do not replace or remove them.
Card Refund Identity Example
For a card-refund callback, transactionId contains the original card-payment ID. refundId is available for refund reconciliation but is not signed.
Secret:
test-secretCallback data:
transactionId: payment_1001
refundId: refund_3001 (not signed)
requestId: refund_request_2001
transactionStatus: SUCCEEDChecksum string:
payment_1001|refund_request_2001|SUCCEEDSent as header:
X-Checksum: /BFiJrrWikPLUdHEWjCRssGHROiKy5U68NxFqjmc/d0=APM Amount Formatting
This section applies only to:
- APM requests
- APM-payment callbacks
Card payments, card payouts, card refunds, and APM payouts use the three-field transaction-result checksum and therefore do not sign amount or currency.
APM Requests
Use the exact decimal string that appears in the request body. Do not multiply it, convert it to minor units, or otherwise reformat it.
| Currency | Request amount | Value used in checksum |
|---|---|---|
| USD | "10.00" | "10.00" |
| EUR | "25.50" | "25.50" |
| GBP | "99.99" | "99.99" |
| JPY | "500" | "500" |
| ILS | "3.75" | "3.75" |
Rule: Use exactly the same string value in the checksum that you send in the request body.
APM-Payment Callbacks
The numeric representation differs by message direction:
- Request:
amountis a string, for example"200.00". - APM-payment callback:
orderAmountis a numeric value, for example200.0.
Facilero processes amounts using native numeric types. Therefore, formatting such as trailing zeros is not guaranteed to be preserved in callbacks.
Example:
Request amount: "200.00"
Callback orderAmount: 200.0When validating an APM-payment callback:
Always use the exact
orderAmountrepresentation received in the callback payload.
Do not reuse or reformat the amount from the original request. Doing so can cause checksum mismatches.
Validation Rules
Incoming Requests
APM requests without a valid checksum body field are rejected with:
400 BadRequestIncoming Callbacks
Ignore callbacks when the X-Checksum header is missing or invalid.
For card payment, card payout, card refund, and APM payout callbacks, the checksum protects only:
transactionId
requestId
transactionStatusAmount, currency, and all other payload fields are intentionally unsigned. If your business logic depends on those fields, validate them separately.
Card Refund Deduplication
For card refunds, deduplicate verified callbacks using the combination of:
transactionId
requestId
transactionStatusDo not deduplicate only by transactionId and status. Multiple refunds can reference the same original card-payment transaction, while each refund has its own requestId.
Preserving APM Callback Amounts
For APM-payment callbacks, parse JSON numbers without losing their source representation.
For Node.js 20, the JavaScript implementation can use lossless-json:
npm install lossless-jsonImplementation Pattern
The general verification flow is:
1. Determine the callback type from the originating API flow.
2. Select the correct ordered fields.
3. Preserve each signed value exactly as required.
4. Join the values with "|".
5. Calculate HMAC-SHA256 using merchantSecret.
6. Encode the HMAC bytes with standard Base64.
7. Compare the calculated checksum with X-Checksum using constant-time comparison.
8. Reject or ignore the message if verification fails.Common Mistakes
| Mistake | Symptom | Fix | ||
|---|---|---|---|---|
Converting amount to minor units, e.g. "1000" | Checksum mismatch | Use the decimal string as-is, e.g. "10.00" | ||
| Using the wrong field order | Checksum mismatch | Use the exact order defined in the callback matrix | ||
| Using hexadecimal or URL-safe Base64 | Checksum mismatch | Use standard Base64 and preserve +, /, and = | ||
| Using the APM-payment formula for an APM payout | Checksum mismatch | APM payouts use **`transactionId | requestId | transactionStatus`** |
Using refundId as the refund transactionId | Checksum mismatch | Use the original card-payment ID; requestId identifies the refund | ||
Reformatting APM-payment orderAmount | Checksum mismatch | Use the exact representation received in the callback | ||
| Comparing checksums as normal strings | Timing-attack vulnerability | Use a constant-time comparison such as timingSafeEqual or hmac.compare_digest |
Security Best Practices
- Never expose
merchantSecretin client-side code or application logs. - Verify checksums before processing requests or callbacks.
- Use constant-time comparison when comparing checksum values.
- Process card-refund callbacks idempotently using
transactionId,requestId, andtransactionStatus. - For other applicable callbacks, use
transactionIdand status for idempotency according to your integration logic. - Validate unsigned payload fields separately whenever your business logic relies on them.
- Rotate
merchantSecretperiodically according to your security policy.
Quick Reference
APM Request
accountId|amount|currency|requestIdChecksum location:
Request body: checksumAPM Payment Callback
accountId|orderAmount|orderCurrency|transactionIdChecksum location:
HTTP header: X-ChecksumCard Payment / Card Payout / Card Refund / APM Payout Callback
transactionId|requestId|transactionStatusChecksum location:
HTTP header: X-ChecksumEncoding
HMAC algorithm: HMAC-SHA256
Secret: merchantSecret
Output: Standard Base64
Delimiter: |
