Signature and Parameter Integrity
Introduction
TendoPay uses HMAC-SHA256 to sign all data in API communication with the merchant. Before sending a TendoPay Signature request, make sure the following conditions are met:
- All parameters that start with
tp_xxx
in the request/response are all strings. - Sort key-value pairs in the payload in alphabetic order by keys.
- Concatenate all key-value pairs to not have any space.
- Make a hash with the payload string and client secret.
- The message outputs should be a lowercase hexadecimal digit.
Cleanup Example
In the example below we will clean up the request and make sure it follows the conditions required in order to get a successful response.
- Make sure the necessary key / value pairs are met.
{
"tp_amount": 1000,
"tp_currency": "PHP",
"tp_merchant_order_id": "TEST_ORDER_ID_12345",
"tp_redirect_url": "https://domain.com/redirect_url_path?query=string",
"tp_merchant_user_id": "unique_user_id_in_merchant_side",
"tp_description": "Test order",
"some_other_value": "6789012"
}
- Only filter the Key / value pairs that start with
tp_xxx
.
"tp_amount": 1000,
"tp_currency": "PHP",
"tp_merchant_order_id": "TEST_ORDER_ID_12345",
"tp_redirect_url": "https://domain.com/redirect_url_path?query=string",
"tp_merchant_user_id": "unique_user_id_in_merchant_side",
"tp_description": "Test order",
- Sort the keys alphabetically.
"tp_amount": 1000,
"tp_currency": "PHP",
"tp_description": "Test order",
"tp_merchant_order_id": "TEST_ORDER_ID_12345",
"tp_merchant_user_id": "unique_user_id_in_merchant_side",
"tp_redirect_url": "https://domain.com/redirect_url_path?query=string",
- Trim all unnecessary quotations and spaces, like the example below.
tp_amount1000tp_currencyPHPtp_descriptionTest order
tp_merchant_order_idTEST_ORDER_ID_12345tp_merchant_user_idunique_user_id_in_merchant_side
tp_redirect_urlhttps://domain.com/redirect_url_path?query=string
- Make a hash with SHA256 with the client_secret. The response should output a hexadecimal digit like below.
67d0a6d3fa13679039826e64ee7a76bf2e8185c3184407914c0f76d793b222df
PHP Example
$payload = [
"tp_amount" => 1000,
"tp_currency"=> "PHP",
"tp_merchant_order_id" => "TEST_ORDER_ID_12345",
"tp_redirect_url" => "https://domain.com/redirect_url_path?query=string",
"tp_merchant_user_id" => "unique_user_id_in_merchant_side",
"tp_description" => "Test order",
"some_other_value" => "6789012"
];
$client_secret = '1234567890';
ksort($payload);
$message = array_reduce(array_keys($payload), static function ($p, $k) use ($payload) {
return strpos($k, 'tp_') === 0 ? $p.$k.trim($payload[$k]) : $p;
}, '');
$hash = hash_hmac('sha256', $message, $client_secret);
Node Example
const crypto = require('crypto');
const payload = {
"tp_amount": 1000,
"tp_currency": "PHP",
"tp_merchant_order_id": "TEST_ORDER_ID_12345",
"tp_redirect_url": "https://domain.com/redirect_url_path?query=string",
"tp_merchant_user_id": "unique_user_id_in_merchant_side",
"tp_description": "Test order",
"some_other_value": "6789012"
}
const client_secret = '1234567890'
const message = Object.keys(payload)
.sort()
.filter(p => p.indexOf('tp_') === 0)
.reduce((p, k) => (`${p}${k}${String(payload[k]).trim()}`), '')
var hash = crypto.createHmac('sha256', client_secret).update(message).digest('hex');
Go Example
package main
import (
"fmt"
"bytes"
"strings"
"sort"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
)
func main() {
payload := map[string]string{
"tp_amount": "1000",
"tp_currency": "PHP",
"tp_merchant_order_id": "TEST_ORDER_ID_12345",
"tp_redirect_url": "https://domain.com/redirect_url_path?query=string",
"tp_merchant_user_id": "unique_user_id_in_merchant_side",
"tp_description": "Test order",
"some_other_value": "6789012",
}
client_secret := []byte("1234567890")
// Make message
var message bytes.Buffer
keys := make([]string, 0, len(payload))
for k := range payload {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
if strings.Index(k, "tp_") != 0 {
continue
}
message.WriteString(k)
message.WriteString(strings.Trim(payload[k], " "))
}
// Make hash
hashHmac := hmac.New(sha256.New, client_secret)
hashHmac.Write(message.Bytes())
hash := hex.EncodeToString(hashHmac.Sum(nil))
fmt.Println(hash)
}
Java Example
import java.util.HashMap;
import java.util.TreeMap;
import java.util.stream.Collectors;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.math.BigInteger;
class Main {
public static void main(String[] args) {
// Example
String clientSecret = "1234567890";
HashMap<String, String> payload = new HashMap<String, String>();
payload.put("tp_amount", "1000");
payload.put("tp_currency", "PHP");
payload.put("tp_merchant_order_id", "TEST_ORDER_ID_12345");
payload.put("tp_redirect_url", "https://domain.com/redirect_url_path?query=string");
payload.put("tp_merchant_user_id", "unique_user_id_in_merchant_side");
payload.put("tp_description", "Test order");
payload.put("some_other_value", "6789012");
// Make message
TreeMap<String, String> sortedPayload = new TreeMap<>(payload);
String message = sortedPayload.entrySet().stream()
.filter(map -> map.getKey().indexOf("tp_") == 0)
.map(map -> map.getKey() + map.getValue().trim())
.collect(Collectors.joining());
// Make hash
byte[] hmacSha256 = null;
String hash = null;
try {
Mac mac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(clientSecret.getBytes("UTF-8"), "HmacSHA256");
mac.init(secretKeySpec);
hmacSha256 = mac.doFinal(message.getBytes("UTF-8"));
hash = String.format("Hex: %032x", new BigInteger(1, hmacSha256));
}
catch (Exception e) {}
System.out.println(message);
System.out.println(hash);
}
}