No mostrar más este mensaje

ecommerce

Firma del comercio

 

Se dotará al comercio de una clave, que se utilizará para firmar los datos aportados por el mismo, pudiendo verificarse no solo la identificación del comercio, sino que los datos no han sido alterados en ningún momento. Se utilizará como algoritmo de securización el Hash SHA-1, que garantiza los requisitos mínimos de seguridad en cuanto a la autenticación del origen. La clave se proporcionará para ser incluida en la web del comercio.

Este mismo algoritmo se utilizará para asegurar al comercio la autenticidad de los datos de la respuesta, en caso de que se proporcione URL de notificación por parte del comercio.

Más información en los apartados 2.4.1 y 2.4.2 de esta misma guía

En caso de problemas al acceder al TPV (datos erróneos):

  • Una vez que se ha generado la firma no se deben modificar los datos de ningún modo ya que el TPV los utiliza para validar la firma y si lo que recibimos no es exactamente lo que se utilizó para generar la firma no pasa la validación.
  • El Importe vendrá multiplicado por 100, sin decimales y sin ceros a la izquierda.
  • El número de pedido será diferente cada transacción y las 4 primeras posiciones han de ser numéricas.
  • Si aparece en pantalla un mensaje de Datos Erróneos:
    • Comprobar que el comercio (FUC/TERMINAL) está dado de alta en el módulo de administración del entorno al que se está enviando la transacción.
    • Verificar que la clave que está utilizando para hacer la firma es la que está asignada al comercio en el módulo de administración del TPV Virtual.

Su entidad adquirente podrá suministrarle la información de configuración del comercio (código de comercio, terminal, clave, tipo de clave etc.).

A continuación mostramos un ejemplo en JSP:

Indicamos que es una página JSP

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"  pageEncoding="ISO-8859-1"%>

Importamos la clase FirmaUtils que usaremos para el cálculo de la firma

<%@ page import="es.lacaixa.redsys.FirmaUtils" %>

Declaramos las variables a usar. En un entorno real algunos de estos datos no serán fijos y los recuperaremos de la request

<%!
    double    IMPORTE            = (double)1.45;
    String    PEDIDO            = "123456";
    String    COMERCIO        = "99999999";
    String    MONEDA            = "978";
    String    TIPO_TRANSACCION        = "0";
    String    TERMINAL        = "2";
    String    CLAVE_SECRETA        = "poiuytrewq9876543210";
    String    FECHA            = "1212"; 
    String    URL_OK            = "http://prueba.com/OK.html";
    String    URL_KO            = "http://prueba.com/KO.html";
    String     URL_ACTION        = "https://sis-t.redsys.es:25443/sis/realizarPago";
%>

Calculamos la firma del comercio usando la clase importada previamente

<%!
    String FIRMA = FirmaUtils.getFirmaSHA1CompletoAmpliada(Double.valueOf(IMPORTE), 
                                                           PEDIDO, 
                                                           COMERCIO, 
                                                           MONEDA, 
                                                           CLAVE_SECRETA, 
                                                           TIPO_TRANSACCION, 
                                                           null);
%>

Creamos el código HTML con el form correspondiente. El form se ha de enviar por POST y los nombres de los campos han de ser los indicados en la tabla anterior. Cualquier diferencia en el nombre de un campo será crítica para el buen funcionamiento de la pasarela.

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
        <title>Ejemplo peticion POST</title>
    </head>
    
    <body>
        <h1>Ejemplo petición POST</h1>
        <form id="peticionPostForm" action="<%= URL_ACTION %>" method="post">
            <input type="hidden" name="Ds_Merchant_Amount"         value="<%= (int)(IMPORTE*100)%>"/>
            <input type="hidden" name="Ds_Merchant_Order"         value="<%= PEDIDO%>"/>
            <input type="hidden" name="Ds_Merchant_MerchantCode"     value="<%= COMERCIO%>"/>
            <input type="hidden" name="Ds_Merchant_Currency"     value="<%= MONEDA%>"/>
            <input type="hidden" name="Ds_Merchant_TransactionType"     value="<%= TIPO_TRANSACCION%>"/>
            <input type="hidden" name="Ds_Merchant_MerchantURL"     value="<%= URL_NOTIFICACION%>"/>
            <input type="hidden" name="Ds_Merchant_UrlOK"     value="<%= URL_OK%>"/>
            <input type="hidden" name="Ds_Merchant_UrlKO"     value="<%= URL_KO%>"/>
            <input type="hidden" name="Ds_Merchant_MerchantSignature"     value="<%= FIRMA%>"/>
            <input type="hidden" name="Ds_Merchant_Terminal"     value="<%= TERMINAL%>"/>
            <input type="submit"     value="Realizar Pago"/>
        </form>
    </body>
</html>

La respuesta http es un proceso independiente de la conexión con el navegador del cliente y no tiene ningún reflejo en pantalla del mismo. Evidentemente, en el lado del comercio, deberá haber un proceso que recoja esta respuesta http.

El protocolo utilizado en las respuestas puede ser http o https, el formato de este mensaje es un formulario HTML, enviado con el método POST, y cuyos campos son los siguientes (en los campos Ds_Currency; Ds_Terminal; Ds_ConsumerLanguage la longitud se considera máxima por lo que no es imprescindible el relleno con ceros a la izquierda; la firma será generada con los campos exactamente como se envíen):

La conexión utilizada para comunicar la confirmación “on-line” entre el TPV Virtual y el comercio puede ser SSL. Opcionalmente el comercio puede activar un filtro para limitar la recepción de la confirmación “on-line” solo desde el TPV Virtual para evitar comunicaciones fraudulentas.

El TPV Virtual por defecto puede comunicar a los puertos 80, 443, 8080 y 8081 del comercio. Otros puertos deberán ser consultados.

Una vez que el comercio recibe el formulario, el código de respuesta (ds_response) tendrá los siguientes valores posibles:

Nota: solo en el caso de las preautenticaciones (preautorizaciones separadas), se devuelve un 0 si está autorizada y el titular se autentica y, un 1 si está autorizada y el titular no se autentica.

El TPV Virtual efectúa el envío de las notificaciones on-line para las operaciones de compra autorizadas y denegadas por la entidad emisora de la tarjeta, así como en aquellas situaciones en las que el proceso de compra ha sido interrumpido al haberse producido uno de los siguientes errores:

SIS0051 -> Pedido repetido. Se envía notificación con código 913.
SIS0078 -> Método de pago no disponible para su tarjeta. Se envía notificación con código 118
SIS0093 -> Tarjeta no válida. Se envía notificación con código 180.
SIS0094 -> Error en la llamada al MPI sin controlar. Se envía notificación con código 184

No se envía notificación en aquellos casos en los que la operación no termine, por ejemplo por que el usuario no indica la tarjeta o cierra el navegador antes de que finalice la autenticación.

Para recibir la notificación HTTP montaremos un Servlet que se encargará de leer los parámetros enviados por la pasarela de pago. Este Servlet no implementa ninguna lógica de negocio, simplemente vuelca el contenido en el log del servidor. En un entorno real esos datos se explotarán de la manera más conveniente en cada caso.

package es.lacaixa.redsys.servlet;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPathExpressionException;
import org.xml.sax.SAXException;
public class RespuestaPasarelaServlet extends HttpServlet {
    private static final long    serialVersionUID    = -8991452242470541541L;
    public RespuestaPasarelaServlet() {
        super();
    }
    /*
     * En el método doGet redirigimos al doPost. En principio no debería
     * entrarnos ninguna petición por aquí pero lo dejamos así por si acaso
     * algún día cambian la operativa
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws     ServletException,                                                IOException {
        doPost(request, response);
    }
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws     ServletException, 
                                                IOException {
        System.out.println("Request (redsys): ");
        try {
            // Leemos los parámetros de la response
            Map<String, String> responseMap = readResponse(request);
            // Y los imprimimos en la consola del servidor
            for (Entry<String, String> entry : responseMap.entrySet()) {
                System.out.println(entry.getKey() + ": " + entry.getValue());
            }
        } catch (XPathExpressionException e) {
            e.printStackTrace();
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        }
    }
    // Lee de la request los parámetros recibidos y los guarda en un mapa para su posterior explotación
    private static Map<String, String> readResponse(HttpServletRequest request) throws 
    ParserConfigurationException,        SAXException,
            IOException,
        XPathExpressionException {
        String Ds_Date = request.getParameter("Ds_Date");
        String Ds_Hour = request.getParameter("Ds_Hour");
        String Ds_Amount = request.getParameter("Ds_Amount");
        String Ds_Currency = request.getParameter("Ds_Currency");
        String Ds_Order = request.getParameter("Ds_Order");
        String Ds_MerchantCode = request.getParameter("Ds_MerchantCode");
        String Ds_Terminal = request.getParameter("Ds_Terminal");
        String Ds_Signature = request.getParameter("Ds_Signature");
        String Ds_Response = request.getParameter("Ds_Response");
        String Ds_MerchantData = request.getParameter("Ds_MerchantData");
        String Ds_SecurePayment = request.getParameter("Ds_SecurePayment");
        String Ds_TransactionType = request.getParameter("Ds_TransactionType");
        String Ds_Card_Country = request.getParameter("Ds_Card_Country");
        String Ds_AuthorisationCode = request.getParameter("Ds_AuthorisationCode");
        String Ds_ConsumerLanguage = request.getParameter("Ds_ConsumerLanguage");
        Map<String, String> responseMap = new HashMap<String, String>();
        responseMap.put("Ds_Date""Ds_Date", Ds_Date);
        responseMap.put("Ds_Hour""Ds_Hour", Ds_Hour);
        responseMap.put("Ds_Amount""Ds_Amount", Ds_Amount);
        responseMap.put("Ds_Currency""Ds_Currency", Ds_Currency);
        responseMap.put("Ds_Order""Ds_Order", Ds_Order);
        responseMap.put("Ds_MerchantCode""Ds_MerchantCode", Ds_MerchantCode);
        responseMap.put("Ds_Terminal""Ds_Terminal", Ds_Terminal);
        responseMap.put("Ds_Signature""Ds_Signature", Ds_Signature);
        responseMap.put("Ds_Response""Ds_Response", Ds_Response);
        responseMap.put("Ds_MerchantData""Ds_MerchantData", Ds_MerchantData);
        responseMap.put("Ds_SecurePayment""Ds_SecurePayment", Ds_SecurePayment);
        responseMap.put("Ds_TransactionType""Ds_TransactionType", Ds_TransactionType);
        responseMap.put("Ds_Card_Country""Ds_Card_Country", Ds_Card_Country);
        responseMap.put("Ds_AuthorisationCode""Ds_AuthorisationCode", Ds_AuthorisationCode);
        responseMap.put("Ds_ConsumerLanguage""Ds_ConsumerLanguage", Ds_ConsumerLanguage);
        return responseMap;
    }
}

En el fichero web.xml añadiremos la configuración de este servlet:

  
  <servlet>
    <description></description>
    <display-name>RespuestaPasarelaServlet</display-name>
    <servlet-name>RespuestaPasarelaServlet</servlet-name>
    <servlet-class>es.lacaixa.redsys.servlet.RespuestaPasarelaServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>RespuestaPasarelaServlet</servlet-name>
    <url-pattern>/RespuestaPasarelaServlet</url-pattern>
  </servlet-mapping>

Para poder usar la notificación sobre este servlet necesitaremos instalarlo bajo algún dominio público. Es decir, no podemos probarlo en local ni en una red privada. Esto es porqué la pasarela no hace la petición a través del navegador como en el caso de las URL’s de OK y KO. En este caso la pasarela lanza una petición directamente desde su servidor al nuestro. Si nuestro servidor no es visible para la pasarela nunca nos llegará la modificación.

Esto solo afecta a la notificación, con lo que podríamos tener nuestra aplicación en local realizando pruebas y el servlet instalado en algún entorno de pruebas (siempre que sea visible para el exterior). De esta manera la notificación la recibiríamos en el entorno de pruebas pero el resto de proceso lo llevaríamos a cabo desde nuestro entorno local.

Para probarlo, entramos al navegador (el host y context-root variarán en función de cada desarrollo):

3_3_1_a

Cuando realicemos el submit del formulario este nos enviará a la pasarela de pago. En ese punto el control lo tiene la pasarela y estamos fuera de nuestra aplicación.

3_3_1_b

Una vez rellenados los datos confirmamos la compra, esto nos dirigirá una página de confirmación:

3_3_1_c

Cuando le demos a continuar el navegador nos redirigirá a la URL de OK que hayamos enviado en el formulario:

3_3_1_d

Si hay algún problema durante el proceso nos redigirá a la URL de KO.

En los logs del servidor donde tenemos instalado el Servlet deberíamos ver las siguientes trazas correspondientes a la notificación: