FIRMA DE LOS REQUERIMIENTOS A LA API
Firma requerimientos
El proceso de firmar un requerimiento es algo muy sencillo. Consiste en enviar un encabezado HTTP con un valor que se calcula al momento de hacer la llamada. Firmar las llamadas a la API permite a Khipu verificar que la llamada efectivamente viene de un servidor del comercio. Para esto se usa el valor “secret” de la cuenta de cobro. Recuerda que este valor no debe ser compartido con nadie.
El encabezado de la firma
Para firmar una llamada a la API debemos enviar el siguiente encabezado HTTP:
Authorization: receiverId:hash
El valor de Authorization
está formado por dos valores:
- receiverId : Es tu identificador de comercio. Es parte de las credenciales.
- hash : Es un valor que se calcula usando el “secret”, el tipo de requerimiento (POST, GET, etc), la URL del requerimiento y los valores que envías al servidor.
Cuando se envía un requerimiento a la API, Khipu usa el “secret” del comercio para calcular el valor de “hash”. Si el valor calculado coincide con el valor enviado en el encabezado, el requerimiento es aceptado.
Es importante notar que, aunque el requerimiento sea aceptado no necesariamente funcionará, pues los valores enviados podrían tener problemas, como por ejemplo un monto negativo.
Cálculo de HASH
El proceso de generar consiste en varios pasos sencillos. Básicamente, se debe construir un “String” usando el tipo de llamado (POST, GET, etc), la url de la llamada, los parámetros que se enviarán y sus valores.
El siguiente es un código de referencia de como construir la firma para un llamado “POST” al la URL “https://khipu.com/api/2.0/payments”, usando 3 parámetros para crear un pago.
$receiver_id = identificador_de_comercio;
$secret = 'secret-key';
$method = 'POST';
$url = 'https://khipu.com/api/2.0/payments';
$params = array('subject' => 'ejemplo de compra'
, 'amount' => '1000'
, 'currency' => 'CLP'
);
$keys = array_keys($params);
sort($keys);
$toSign = "$method&" . rawurlencode($url);
foreach ($keys as $key) {
$toSign .= "&" . rawurlencode($key) . "=" . rawurlencode($params[$key]);
}
$hash = hash_hmac('sha256', $toSign , $secret);
$value = "$receiver_id:$hash";
print "$value";
Integer receiverId = identificadorDeComercio;
String secret = "secret-key";
String method = "POST";
String url = "https://khipu.com/api/2.0/payments";
String toSign = method.toUpperCase() + "&" + percentEncode(url);
HashMap<String, String> map = new HashMap<String, String>();
map.put("subject", "ejemplo de compra");
map.put("amount", "1000");
map.put("currency", "CLP");
List<String> keys = new LinkedList<String>(map.keySet());
Collections.sort(keys);
for (String key : keys) {
toSign += "&" + percentEncode(key) + "=" + percentEncode(map.get(key));
}
String sign = hmacSHA256(secret, toSign);
String value = receiverId + ":" + sign;
System.out.println(value);
Primero debemos tener todas las variables involucradas: receiverId
, secret
, method
, url
y params
(los parámetros que enviaremos al servidor).
Veamos los pasos uno a uno:
-
Debemos crear un string
toSign
que contiene el método (POST). -
Agregamos URL al string separados por
&
. URL debe estar codificado usando percent encode. En PHP la funciónrawurlencode
es el equivalente. - Obtenemos un listado de todos los nombres de los parámetros ordenados de manera lexicográfica.
-
Recorremos el listado de nombres y por cada uno agregamos un
&
, el nombre, el signo=
y el valor. Es muy importante notar que tanto el nombre como el valor deben ir codificados usando percent encode. -
Usando el string obtenido y nuestro secret, aplicamos la función HMAC-SHA256. El resultado que se obtiene es la variable hash. En PHP se usa la función
hash_hmac
. -
Ahora creamos el valor del header concatenando el
receiverId
, un:
y el valor hash.
El último valor que obtenemos es el que debemos enviar en el encabezado Authorization
de la llamada HTTP.
Cálculo de HMAC-SHA256
En PHP el cáculo de HMAC es sencillo pues se usa la función nativa hash_hmac
. En Java debemos crear nuestra propia función para generar el Hash. El siguiente es un ejemplo de implementación:
public static String hmacSHA256(String secret, String data) {
try {
if (secret == null || data == null) {
return "";
}
SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes("UTF-8"), "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secretKeySpec);
byte[] digest = mac.doFinal(data.getBytes("UTF-8"));
return byteArrayToString(digest);
} catch (InvalidKeyException ignored) {
throw new RuntimeException("Invalid key exception while converting to HMac SHA256");
} catch (NoSuchAlgorithmException | UnsupportedEncodingException exception) {
e.printStackTrace();
}
return null;
}
private static String byteArrayToString(byte[] data) {
BigInteger bigInteger = new BigInteger(1, data);
String hash = bigInteger.toString(16);
while (hash.length() < 64) {
hash = "0" + hash;
}
return hash;
}
Percent Encode
PHP provee una función nativa para hacer percentEncode llamada rawurlencode
. La siguiente es una implementación de referencia para Java:
public static String percentEncode(String string) {
if (string == null) {
return "";
}
try {
return URLEncoder.encode(string, "UTF-8")
.replace("+", "%20")
.replace("*", "%2A")
.replace("%7E", "~");
} catch (UnsupportedEncodingException exception) {
throw new RuntimeException(exception.getMessage(), exception);
}
}