This section will explain how merchants can generate a signature using SNAP specification.
B2B Access Token Specification
B2B Access Token API header consists of the following fields:
Field Name | Field Type | Mandatory | Field Description |
---|---|---|---|
Content-type | String | M | Media type of the resource, i.e. application/json |
X-TIMESTAMP | String | M | Client’s current local time in ISO-8601 format |
X-SIGNATURE | String | M | Created using asymmetric signature SHA256withRSA algorithm |
X-CLIENT-KEY | String | M | Client ID |
Content-type: application/json
X-TIMESTAMP: 2023-01-01T00:00:00+07:00
X-SIGNATURE: da1fa417c72d6b91c257e01e54fac824
X-CLIENT-KEY: 962489e9-de5d-4eb7-92a4-b07d44d64bf4
Asymmetric Signature SHA256withRSA
Based on the specification above, merchant should use asymmetric signature SHA256withRSA for Access Token API.
Merchant should use this formula to create X-SIGNATURE for Access Token API:
Client ID + “|” + Timestamp encrypted with merchant’s private key by using SHA256withRSA algorithm
For a better understanding, please refer to this sample scenario for generating signature using the formula above on B2B Access Token API:
No. | Steps |
---|---|
1 | Given merchant has private key -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCVZu2WHd4z5tbe fkAWPjbB10Lgvu1CpsuIIVtO/wrrvKeD2LArmYSf5QxeJGG8HYlVYVp8I8/EVqay JUbcyOd4aMLQFiCambDipecCfI7ecJ0K4+2ZneWOEq9JM91ZnGtwuLVdr1X/4/Nv yGuNvmLAyfpJkU14yZliAUqJJ656jpM+WfY+hOpcLOzUe2wzx5RV7wYCyMl7/d1i X33afJc6kbbUaA27yjlPdY6m0+DoZ0NNaRNZS7jzfJFtVXLTg/PgvwMNlMO/5Zyw BWIOfWsD+MHUC6P9QYrHai8ZwEf9r+GivlNsAFs4NTpw9MdPngrexJm29k1Xh4UM HV//jB1/AgMBAAECggEADcXGyitns/4oOauGyeYjUxw+eIxxP88zfRGiIralMZUb Fi7wEpzc2oaZbMZK0jYg1mOanU4J1a4tQMfp7+l/WRzDNL6Nc+MOKN6lXJfR7dSQ zZO0cBBbvIyhZwymb5/ZUbNdWM0UjvnbE6d0rsTpwp77+TMxYpynDJ9U2S70ySxe fGi7x4rK02/8xmj1fY/Ls+uoYhF4IbGJIkFvrlAdWyFvv6sCGBYxl5sPuitgQxPv Yc7dS7HE+6nZ+wWgoIXjC4CfxHBa9u3dfu3XAnPY3xpnVsbxCAH9OOIrZPY0C/Pq gj+EElCBChUzDiedV3XipKUweDA+64sNm5NjqlqU8QKBgQDFf5a9vHVf9r2CTOgm z5wTrK7aTwKwCNC4zpZUxDR8k7AV2gTelabpPWqUMZyRMUPs2hyBvhEWVNKxx3qs 7BAVpmcttuS5/dLNVdhNPUC2IgZqf3jWH2aS3lkci8DATQCphrEXZ3jrNaIRtrSC kflE+yY8YP8b7ObktgvHz39JMQKBgQDBqCsMmOsQAveIQk5FGAMxsFU9G7OIBFHo xpwkKUCjoqGxM+YQqDvcVeXNd/HdLnmR05//eoONemuDyYy8XRoud4tpaRKU/FK7 06WdljztB5rn2+cdUbmcU0fZiRQk+WuXsopZn3GUNvA/fQOW9qs7B5Rm5W71FeGk ZgsvlAUlrwKBgQCUgFFaLWCcXa01UpqkxCp5aLi5EfvVXWuD6mKDLlzA51PZums6 6o/shO+kqoEtczu91mrk64NxpSof3vxRFdcqUEr4xrLJXx+oocnYmhwUVxU38s1r Q4UfHe0nV7YBYmUDE3IJRRZY1aUdaKHmI9iok6e2csCfwMwEYRYOkekFoQKBgCyy Oa1gpfA+Hw+N7i64ShRv1FyURi2Agb8uB9+4vbiG0rbpeZIioh5KnQ19P4+DKH/l zinTBwXiWWpDXH4lJuPOp5ierbFBQ38ibDkg8dLrTG9zK7ZypFpWRmEI6GNYReLv TEs/J6HDxFOC8Q8ow4COUUwmbCOY90lQXAiRK1b1AoGAScK1db9X5Ct+JtnjYyzi GYLwyPhHCQIVmEDfuvbMs74woQsrBm3dBB3b2vf9pqcSZB4WAbTQ3wCdxCJKg6MN 6eXBO1M1lcIpZjCDyiXXi7PozNmkasRDUyDFwxlnyDLdRHL3FD+dh6do32FwGLBQ u256RIyMB13qIuqrc91Z0hM= -----END PRIVATE KEY----- |
2 | Given merchant wants to create a request with header:Content-type: application/json X-TIMESTAMP: 2023-07-31T07:10:00+07:00 X-CLIENT-KEY: G1234325-SNAP |
3 | X-SIGNATURE value for encryption will be: G1234325-SNAP|2023-07-31T07:10:00+07:00 *same value as X-CLIENT-KEY + “|” + X-TIMESTAMP |
4 | By using merchant’s private key to encrypt the value with SHA256withRSA algorithm, merchant will generate iv5YorKVVFOFS59l0HChDvPe+HeoE/jY5CfVgCg5i16nj5/DVnKg49ilkv8PyeU7y38apHhgO+cUrvkfUs5BhDD69yLn7xp8hzN9RcR0UDy5+nCrQ3GGCVSzZJvlEXN+TB0j4Q6pMsjrq4+YRoyDa4mTlsjJTU9aGjLRFBYgY4MyMQ5x11JyLnoFwbS8TJ5e/q4mUozrp49VyHe7OQSFnJNwvMSFrDyAIxoOK0IZQIlY29PaDIFcWoR+RJAY42H2FryjJcPpNfeercbkj9jsBLV3wmEKiNoN6lgFFLk5QicDnAAQzL45s92EYeCqDNuTTJOydaJbUqEo1d/ZPVNBtQ== |
5 | Final result of merchant's header will be: Content-type: application/json X-TIMESTAMP: 2023-07-31T07:10:00+07:00 X-CLIENT-KEY: G1234325-SNAP X-SIGNATURE: <SIGNATURE SAMPLE RESULT> |
Sample code
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
public class SignatureGenerator {
public static void main(String[] args) throws Exception {
// Generate key pairs
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
keyPairGenerator.initialize(2048); // Adjust the key size as needed
// Get the private and public keys
// Insert private key content here
PrivateKey privateKey = generatePrivateKey("-----BEGIN PRIVATE KEY-----MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCVZu2WHd4z5tbefkAWPjbB10Lgvu1CpsuIIVtO/wrrvKeD2LArmYSf5QxeJGG8HYlVYVp8I8/EVqayJUbcyOd4aMLQFiCambDipecCfI7ecJ0K4+2ZneWOEq9JM91ZnGtwuLVdr1X/4/NvyGuNvmLAyfpJkU14yZliAUqJJ656jpM+WfY+hOpcLOzUe2wzx5RV7wYCyMl7/d1iX33afJc6kbbUaA27yjlPdY6m0+DoZ0NNaRNZS7jzfJFtVXLTg/PgvwMNlMO/5ZywBWIOfWsD+MHUC6P9QYrHai8ZwEf9r+GivlNsAFs4NTpw9MdPngrexJm29k1Xh4UMHV//jB1/AgMBAAECggEADcXGyitns/4oOauGyeYjUxw+eIxxP88zfRGiIralMZUbFi7wEpzc2oaZbMZK0jYg1mOanU4J1a4tQMfp7+l/WRzDNL6Nc+MOKN6lXJfR7dSQzZO0cBBbvIyhZwymb5/ZUbNdWM0UjvnbE6d0rsTpwp77+TMxYpynDJ9U2S70ySxefGi7x4rK02/8xmj1fY/Ls+uoYhF4IbGJIkFvrlAdWyFvv6sCGBYxl5sPuitgQxPvYc7dS7HE+6nZ+wWgoIXjC4CfxHBa9u3dfu3XAnPY3xpnVsbxCAH9OOIrZPY0C/Pqgj+EElCBChUzDiedV3XipKUweDA+64sNm5NjqlqU8QKBgQDFf5a9vHVf9r2CTOgmz5wTrK7aTwKwCNC4zpZUxDR8k7AV2gTelabpPWqUMZyRMUPs2hyBvhEWVNKxx3qs7BAVpmcttuS5/dLNVdhNPUC2IgZqf3jWH2aS3lkci8DATQCphrEXZ3jrNaIRtrSCkflE+yY8YP8b7ObktgvHz39JMQKBgQDBqCsMmOsQAveIQk5FGAMxsFU9G7OIBFHoxpwkKUCjoqGxM+YQqDvcVeXNd/HdLnmR05//eoONemuDyYy8XRoud4tpaRKU/FK706WdljztB5rn2+cdUbmcU0fZiRQk+WuXsopZn3GUNvA/fQOW9qs7B5Rm5W71FeGkZgsvlAUlrwKBgQCUgFFaLWCcXa01UpqkxCp5aLi5EfvVXWuD6mKDLlzA51PZums66o/shO+kqoEtczu91mrk64NxpSof3vxRFdcqUEr4xrLJXx+oocnYmhwUVxU38s1rQ4UfHe0nV7YBYmUDE3IJRRZY1aUdaKHmI9iok6e2csCfwMwEYRYOkekFoQKBgCyyOa1gpfA+Hw+N7i64ShRv1FyURi2Agb8uB9+4vbiG0rbpeZIioh5KnQ19P4+DKH/lzinTBwXiWWpDXH4lJuPOp5ierbFBQ38ibDkg8dLrTG9zK7ZypFpWRmEI6GNYReLvTEs/J6HDxFOC8Q8ow4COUUwmbCOY90lQXAiRK1b1AoGAScK1db9X5Ct+JtnjYyziGYLwyPhHCQIVmEDfuvbMs74woQsrBm3dBB3b2vf9pqcSZB4WAbTQ3wCdxCJKg6MN6eXBO1M1lcIpZjCDyiXXi7PozNmkasRDUyDFwxlnyDLdRHL3FD+dh6do32FwGLBQu256RIyMB13qIuqrc91Z0hM=-----END PRIVATE KEY-----");
// Create a signature instance with SHA-256 with RSA algorithm
Signature signature = Signature.getInstance("SHA256withRSA");
// Initialize the signature instance with the private key
signature.initSign(privateKey);
// Data to be signed
// Insert your payload here
String data = "G1234325-SNAP|2023-07-31T07:10:00+07:00";
// Update the data to be signed
signature.update(data.getBytes());
// Generate the signature
byte[] digitalSignature = signature.sign();
String finalSignature = Base64.getEncoder().encodeToString(digitalSignature);
System.out.println("Original data: " + data);
System.out.println("Digital signature: " + finalSignature);
}
private static PrivateKey generatePrivateKey(String privateKey) throws Exception {
String privateKeyString = privateKey
.replace("-----BEGIN PRIVATE KEY-----", "")
.replace("-----END PRIVATE KEY-----", "")
.replaceAll("\\s", "");
// Decode Base64 encoded private key
byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyString);
// Generate PrivateKey object from byte array
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
return keyFactory.generatePrivate(keySpec);
}
}
Payouts API Specification
Payouts API header consists of the following fields:
Field Name | Field Type | Mandatory | Field Description |
---|---|---|---|
Content-type | String | M | Media type of the resource, i.e. application/json |
X-TIMESTAMP | String | M | Client’s current local time in ISO-8601 format |
X-SIGNATURE | String | M | Created using symmetric signature HMAC_SHA512 algorithm |
Authorization | String | M | Represents access_token of a request, received from Access Token API response |
X-PARTNER-ID | String | M | Merchant’s partner ID |
X-EXTERNAL-ID | String | M | Merchant’s unique ID per transaction request |
CHANNEL-ID | String | M | Channel id Device identification on which the API services are currently being accessed by the end user. Given by BI |
Content-type:application/json
X-TIMESTAMP:2020-01-01T00:00:00+07:00
X-SIGNATURE: da1fa417c72d6b91c257e01e54fac824
Authorization: Bearer gp9HjjEj813Y9JGoqwOeOPWbnt4CupvIJbU1Mmu4a11MNDZ7Sg5u9a
X-PARTNER-ID: BMRI
X-EXTERNAL-ID:12345678901234567890
CHANNEL-ID:12345
Symmetric Signature HMAC_SHA512
Based on the specification above, merchant should use symmetric signature HMAC_SHA512 for Transactional API.
Merchant should use this formula to create X-SIGNATURE for Transactional API:
HTTPMethod +”:“+ EndpointUrl +":"+ AccessToken +":“+ Lowercase(HexEncode(SHA-256(minify(RequestBody))))+ ":“ + TimeStamp encrypted with merchant’s client secret by using HMAC_SHA512 algorithm.
For a better understanding, please refer to this sample scenario for generating signature using the formula above on Transactional API:
No. | Steps |
---|---|
1 | Given merchant has client secret fdppqbF5wq7vVegyvsV1CROMv646nJ7A |
2 | Given merchant wants to create a request with header for POST https://api.midtrans.com/v1.0/registration-account-bindingContent-type:application/json X-TIMESTAMP:2020-01-01T00:00:00+07:00 Authorization: Bearer gp9HjjEj813Y9JGoqwOeOPWbnt4CupvIJbU1Mmu4a11MNDZ7Sg5u9a X-PARTNER-ID: BMRI X-DEVICE-ID: 0987ADCASA X-EXTERNAL-ID:12345678901234567890 CHANNEL-ID:12345 |
3 | X-SIGNATURE value for encryption will be: POST:/v1.0/debit/payment-host-to-host:gp9HjjEj813Y9JGoqwOeOPWbnt4CupvIJbU1Mmu4a11MNDZ7Sg5u9a:56fa5f4999ad8014de49d7898c1d1d53472569db8999de3c1b752a0dd181e98c:2020-01-01T00:00:00+07:00 *same value as HTTPMethod +”:“+ EndpointUrl +":"+ AccessToken +":“+ Lowercase(HexEncode(SHA-256(minify(RequestBody))))+ ":“ + TimeStamp |
4 | By using merchant’s client secret to encrypt the value with HMAC_SHA512 algorithm, merchant will generate FSlidRHe4ow9qppNifGVQNcdv67lBjgCiP0BHylh+IKXo4fs2rHaGsFNUY0N8t0rPlZs4iAHAOCDklnCccwuJw== |
5 | Final result of merchant's header will be: Content-type:application/json X-TIMESTAMP:2020-01-01T00:00:00+07:00 Authorization: Bearer gp9HjjEj813Y9JGoqwOeOPWbnt4CupvIJbU1Mmu4a11MNDZ7Sg5u9a X-PARTNER-ID: BMRI X-DEVICE-ID: 0987ADCASA X-EXTERNAL-ID:12345678901234567890 CHANNEL-ID:12345 X-SIGNATURE: FSlidRHe4ow9qppNifGVQNcdv67lBjgCiP0BHylh+IKXo4fs2rHaGsFNUY0N8t0rPlZs4iAHAOCDklnCccwuJw== |
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public class HmacSHA512Example {
public static void main(String[] args) {
try {
// Insert client secret given by midtrans here
String clientSecret = "fdppqbF5wq7vVegyvsV1CROMv646nJ7A";
// Generate a secret key for HMAC-SHA512
SecretKey secretKey = generateSecretKeyFromString(clientSecret);
// Insert you payload here
String payLoad = "POST:/v1.0/debit/payment-host-to-host:gp9HjjEj813Y9JGoqwOeOPWbnt4CupvIJbU1Mmu4a11MNDZ7Sg5u9a:56fa5f4999ad8014de49d7898c1d1d53472569db8999de3c1b752a0dd181e98c:2020-01-01T00:00:00+07:00";
// Compute the HMAC SHA-512 hash
String hmac = computeHmacSHA512(secretKey, payLoad);
// Print the resulting hash
System.out.println("HMAC SHA-512 signature: " + hmac);
} catch (Exception e) {
e.printStackTrace();
}
}
private static SecretKey generateSecretKeyFromString(String keyString) throws Exception {
// Create a byte array from the string
byte[] keyBytes = keyString.getBytes(StandardCharsets.UTF_8);
// Create a SecretKeySpec from the byte array
SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "HmacSHA512");
return secretKeySpec;
}
private static String computeHmacSHA512(SecretKey secretKey, String message) throws Exception {
// Get an HMAC-SHA512 Mac instance and initialize with the secret key
Mac mac = Mac.getInstance("HmacSHA512");
mac.init(secretKey);
// Compute the HMAC on the input message bytes
byte[] hmacBytes = mac.doFinal(message.getBytes());
// Convert the HMAC bytes to a base64 encoded string
return Base64.getEncoder().encodeToString(hmacBytes);
}
}