SHA-256 and Sermepa payment module (Spain)

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.
8 Jahre weitere
Could someone confirm if the Sermepa payment module (Spain) version Nop 3.60 can use SHA-256?
Redsys warns that from 23/11/2015 SHA-256 will replace the obsolete SHA-1 and the key change will be necessary.
8 Jahre weitere
Hi, I'm waiting for that too.
8 Jahre weitere
I have seen the code of Sermepa project and I can confirm it not has been upgrade to allow request with SHA256.
8 Jahre weitere
So, I'm modyfing the code to allow to send request with SHA256 signature.
8 Jahre weitere
ciscocabe wrote:
So, I'm modyfing the code to allow to send request with SHA256 signature.

Thanks, muchas gracias!!

He intentado hacerlo, aunque no uso nopCommerce, pero me he quedado bloqueado, el cifrado no es mi fuerte.
Nos comunicarás cuando lo tengas?
Un saludo y gracias de nuevo.
8 Jahre weitere
El envío de datos ya lo he hecho, lo que me falta es la recepción pero bueno es muy parecido.

El código de este método es el que he modificado:

public void PostProcessPayment(PostProcessPaymentRequest postProcessPaymentRequest)
        {            
            //**********
            //PARAMETERS
            //**********
            //Notificación On-Line
            string strDs_Merchant_MerchantURL = _webHelper.GetStoreLocation(false) + "Plugins/PaymentSermepa/Return";

            //URL OK
            string strDs_Merchant_UrlOK = _webHelper.GetStoreLocation(false) + "checkout/completed";

            //URL KO
            string strDs_Merchant_UrlKO = _webHelper.GetStoreLocation(false) + "Plugins/PaymentSermepa/Error";

            //Numero de pedido
            //You have to change the id of the orders table to begin with a number of at least 4 digits.
            string strDs_Merchant_Order = postProcessPaymentRequest.Order.Id.ToString("0000");

            //Nombre del comercio
            string strDs_Merchant_MerchantName = _sermepaPaymentSettings.NombreComercio;

            //Importe
            string amount = ((int)Convert.ToInt64(postProcessPaymentRequest.Order.OrderTotal * 100)).ToString();
            string strDs_Merchant_Amount = amount;

            //Código de comercio
            string strDs_Merchant_MerchantCode = _sermepaPaymentSettings.FUC;

            //Moneda
            string strDs_Merchant_Currency = _sermepaPaymentSettings.Moneda;

            //Terminal
            string strDs_Merchant_Terminal = _sermepaPaymentSettings.Terminal;

            //Tipo de transaccion (0 - Autorización)
            string strDs_Merchant_TransactionType = "0";

            //Clave
            string clave = "";
            if (_sermepaPaymentSettings.Pruebas)
            {
                clave = _sermepaPaymentSettings.ClavePruebas;
            }
            else
            {
                clave = _sermepaPaymentSettings.ClaveReal;
            }
            
            //Creamos el POST
            var remotePostHelper = new RemotePost();
            remotePostHelper.FormName = "form1";
            remotePostHelper.Url = GetSermepaUrl();

            //*******************************************************************
            //Create a JSON string with above parameters and convert it to base64
            //Ds_MerchantParameters
            //*******************************************************************            
            var merchantParameters = new
            {
                Ds_Merchant_MerchantName = strDs_Merchant_MerchantName,
                Ds_Merchant_Amount = strDs_Merchant_Amount,
                Ds_Merchant_Order = strDs_Merchant_Order,
                Ds_Merchant_MerchantCode = strDs_Merchant_MerchantCode,
                Ds_Merchant_Currency = strDs_Merchant_Currency,                                
                Ds_Merchant_TransactionType = strDs_Merchant_TransactionType,
                Ds_Merchant_Terminal = strDs_Merchant_Terminal,
                Ds_Merchant_MerchantURL = strDs_Merchant_MerchantURL,
                Ds_Merchant_UrlOK = strDs_Merchant_UrlOK,
                Ds_Merchant_UrlKO = strDs_Merchant_UrlKO
            };

            string strDs_MerchantParameters = JsonConvert.SerializeObject(merchantParameters);
            strDs_MerchantParameters = Convert.ToBase64String(Encoding.UTF8.GetBytes(strDs_MerchantParameters));
            
            //****************
            //Create signature
            //Ds_Signature
            //****************            
            //Get 3DES key
            byte[] key3DES;
            using (var tdes = new TripleDESCryptoServiceProvider())
            {                                
                tdes.Key = Convert.FromBase64String(clave);
                tdes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
                tdes.Mode = CipherMode.CBC;
                tdes.Padding = PaddingMode.Zeros;

                var cTransform = tdes.CreateEncryptor();
                var orderByteArray = Encoding.UTF8.GetBytes(strDs_Merchant_Order);
                key3DES = cTransform.TransformFinalBlock(orderByteArray, 0, orderByteArray.Length);                                
            }
            
            //Get HMAC SHA256 signature with 3DES key and DS_MerchantParameters value
            string strDs_Signature;
            using (var hmac = new HMACSHA256(key3DES))
            {
                strDs_Signature =
                    Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(strDs_MerchantParameters)));
            }

            
            remotePostHelper.Add("Ds_SignatureVersion", "HMAC_SHA256_V1");
            remotePostHelper.Add("Ds_MerchantParameters", strDs_MerchantParameters);
            remotePostHelper.Add("Ds_Signature", strDs_Signature);
            
            remotePostHelper.Post();
       }
8 Jahre weitere
ciscocabe, funciona perfectamente!!!
Donde estaba atascado era en el TripleDes, no contaba con los ceros...
Una cosa, Ds_Merchant_MerchantName no hace falta, en la documentación de Redsys lo tienen como parámetro opcional, y, en nuestra aplicación, tampoco lo mandamos.
Si ya nos haces el favor, postea la decriptación y, si estás en Madrid o te pasas, tienes cervezas pagadas!!
Muchas gracias!!!
8 Jahre weitere
bobneville Me alegro de que haya ido bien y muchas gracias por la invitación!!!

Aquí está el retorno, a ver si te funciona...


public ActionResult Return(FormCollection form)
        {
            var processor = _paymentService.LoadPaymentMethodBySystemName("Payments.Sermepa") as SermepaPaymentProcessor;
            if (processor == null ||
                !processor.IsPaymentMethodActive(_paymentSettings) || !processor.PluginDescriptor.Installed)
                throw new NopException("Sermepa module cannot be loaded");

            string version = Request["Ds_SignatureVersion"];
            string data = Request["Ds_MerchantParameters"];
            string receivedSignature = Request["Ds_Signature"];

            string decodeData = Encoding.UTF8.GetString(Convert.FromBase64String(data));
            var objData =  JsonConvert.DeserializeObject<IDictionary<string, string>>(decodeData);
            
            //ID de Pedido
            string orderId = objData["Ds_Order"];
            string strDs_Merchant_Order = objData["Ds_Order"];

            string strDs_Merchant_Amount = objData["Ds_Amount"];
            string strDs_Merchant_MerchantCode = objData["Ds_MerchantCode"];
            string strDs_Merchant_Currency = objData["Ds_Currency"];

            //Respuesta del TPV
            string str_Merchant_Response = objData["Ds_Response"];
            int Ds_Response = Convert.ToInt32(objData["Ds_Response"]);

            //Clave
            bool pruebas = _sermepaPaymentSettings.Pruebas;
            string clave = "";
            if (pruebas) { clave = _sermepaPaymentSettings.ClavePruebas; }
            else { clave = _sermepaPaymentSettings.ClaveReal; };

            //Calculo de la firma
            //Get 3DES key
            byte[] key3DES;
            using (var tdes = new TripleDESCryptoServiceProvider())
            {
                tdes.Key = Convert.FromBase64String(clave);
                tdes.IV = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0 };
                tdes.Mode = CipherMode.CBC;
                tdes.Padding = PaddingMode.Zeros;

                var cTransform = tdes.CreateEncryptor();
                var orderByteArray = Encoding.UTF8.GetBytes(strDs_Merchant_Order);
                key3DES = cTransform.TransformFinalBlock(orderByteArray, 0, orderByteArray.Length);
                tdes.Clear();
            }

            //Get HMAC SHA256 signature with 3DES key and DS_MerchantParameters value
            string signature;
            using (var hmac = new HMACSHA256(key3DES))
            {
                signature =
                    Convert.ToBase64String(hmac.ComputeHash(Encoding.UTF8.GetBytes(data)));
                signature = signature.Replace("+", "-").Replace("/", "_");
            }

            //Firma enviada
            string recivedSignature = CommonHelper.EnsureNotNull(receivedSignature);

            //Comprobamos la integridad de las comunicaciones con las claves
            //LogManager.InsertLog(LogTypeEnum.OrderError, "TPV SERMEPA: Clave generada", "CLAVE GENERADA: " + SHAresultStr);
            //LogManager.InsertLog(LogTypeEnum.OrderError, "TPV SERMEPA: Clave obtenida", "CLAVE OBTENIDA: " + signature);
            if (!recivedSignature.Equals(signature))
            {
                _logger.Error("TPV SERMEPA: Clave incorrecta. Las claves enviada y generada no coinciden: " + receivedSignature + " != " + signature);

                return RedirectToAction("Index", "Home", new { area = "" });
            }

            //Pedido
            var order = _orderService.GetOrderById(Convert.ToInt32(orderId));
            if (order == null)
                throw new NopException(string.Format("El pedido de ID {0} no existe", orderId));

            //Actualizamos el pedido
            if (Ds_Response > -1 && Ds_Response < 100)
            {
                //Lo marcamos como pagado
                if (_orderProcessingService.CanMarkOrderAsPaid(order))
                {
                    _orderProcessingService.MarkOrderAsPaid(order);
                }

                //order note
                order.OrderNotes.Add(new OrderNote()
                {
                    Note = "Información del pago: " + decodeData,
                    DisplayToCustomer = false,
                    CreatedOnUtc = DateTime.UtcNow
                });
                _orderService.UpdateOrder(order);
                return RedirectToRoute("CheckoutCompleted");
            }
            else
            {
                _logger.Error("TPV SERMEPA: Pago no autorizado con ERROR: " + Ds_Response);

                //order note
                order.OrderNotes.Add(new OrderNote()
                {
                    Note = "!!! PAGO DENEGADO !!! " + decodeData,
                    DisplayToCustomer = false,
                    CreatedOnUtc = DateTime.UtcNow
                });
                _orderService.UpdateOrder(order);
                return RedirectToAction("Index", "Home", new { area = "" });
            }
        }
8 Jahre weitere
Gracias ciscocabe, la semana que viene me pondré definitivamente con ello. Si veo alguna cosa te comento.

Muchas gracias, y no me olvido de las cervezas!!!
8 Jahre weitere
Hola ciscocabe,
Me han surgido dos problemas, al encriptar y desencriptar.
1) Con el primero iba bien hasta que añadí las url a los parámetros. El ejemplo de PHP convierte las url así:
de http://www.ejemplo.es/tpv a http:\/\/www.ejemplo.es\/tpv. El encode65 de PHP no parece funcionar igual que en .net.
2) Al desencriptar, siempre me devuelve que las firmas no son correctas, esto pasa incluso sin mandar urls en los parámetros.

¿Has conseguido tú algo? Estoy en proceso de pruebas y me está saltando eso.
This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.