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:

Copy
Copied
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.

phpjava
Copy
Copied
$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";
Copy
Copied
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ón rawurlencode 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:

Copy
Copied
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:

Copy
Copied
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);
    }
}