se basa en la comunicación con el core Bancario utilizando el estandar ISO 8583
En la siguiente dirección se puede ver un poco de teoría de la definición de ISO 8583: http://es.wikipedia.org/wiki/ISO_8583
Para implementar el proyecto nos basamos en la librería http://sourceforge.net/projects/trx-framework/ la cual es muy buena para el tema de armar los mensajes ISO8583
Crear los mensajes y procesar las respuestas es muy facil, lo interesante en este proyecto fue el manejo de los puertos de comunicación con el core.
Basicamente para mandar un mensaje hay que:
Iso8583Message MensajeEnviar = new Iso8583Message(2000);
MensajeEnviar.Fields.Add(32, 310000);
MensajeEnviar.Fields.Add(2, sNumTarjetaDebito);
Comun Sender = new Comun();
RespuestaDTO = Sender.EnviarMensaje(MensajeEnviar);
//Si la respuesta es exitosa vamos a revisar el mensaje
if (RespuestaDTO.bExitoso)
{
//Se verifica el campo 11 que es el utilizado como identificador unico del mensaje
if (RespuestaDTO.MensajeRecibido.Fields[TipoCampo.Field11Trace] != null)
//Revisamos el campo 39 que contiene el código de respuesta del mensaje
switch (int.Parse(RespuestaDTO.MensajeRecibido.Fields[TipoCampo.Field39Respuesta].ToString()))
{
//En caso que sea exitoso (00) procesamos el mensaje
//La respuesta del mensaje generalmente viene como un string en uno de los campos, en este caso
//La respuesta es todos la consulta consolidada del cliente dado la cual viene en el campo 48
//En este ejemplo está definido que cada registro de la respuesta es de 126 caracteres con lo cual
//procedemos a construir un array con cada uno de los registros del campo 48 y de ahí sacamos la data
case CodigoRespuesta.RespuestaExitosa:
ProductoDTO.bExitoso = true;
ModeloObjetos.Banca.Cliente Cliente = new ModeloObjetos.Banca.Cliente();
string sProducto = RespuestaDTO.MensajeRecibido.Fields[TipoCampo.Field48Productos].ToString();
//Se crea el array de de registros (a través de un metodo utilitario)
Array sRegistros = Comun.ProcesarResultado(RespuestaDTO.MensajeRecibido.Fields[TipoCampo.Field48Productos].ToString(), 126, 0);
//se recorren cada uno de los registros
foreach (string sRegistro in sRegistros)
{
ModeloObjetos.Banca.Producto Producto = new ModeloObjetos.Banca.Producto();
//Extraemos la data de cada campo de acuerdo a lo especificado por el core
Producto.sNumeroProducto = sRegistro.Substring(0, 20).Trim();
Producto.sCodProducto = sRegistro.Substring(20, 6).Trim();
Producto.sProductoNombre = sRegistro.Substring(26, 40).Trim();
Producto.enumTipoProducto = Comun.ObtenerTipoProducto(Producto.sCodProducto);
Producto.dSaldo1 = double.Parse(Comun.FormatearMontoIso(sRegistro.Substring(66, 12)));
Producto.dSaldo2 = double.Parse(Comun.FormatearMontoIso(sRegistro.Substring(78, 12)));
Producto.dSaldo3 = double.Parse(Comun.FormatearMontoIso(sRegistro.Substring(90, 12)));
if (Producto.enumTipoProducto == enumTipoProducto.DPF)
{
Producto.dtFecha = Comun.FormatearFechaStringCorta(sRegistro.Substring(106, 8));
Producto.dSaldo4 = double.Parse(Comun.FormatearMontoIso(sRegistro.Substring(114, 12)));
}
else if (Producto.enumTipoProducto == enumTipoProducto.TDC)
{
Producto.dSaldo4 = double.Parse(Comun.FormatearMontoIso(sRegistro.Substring(102, 12)));
Producto.dtFecha = Comun.FormatearFechaStringCorta(sRegistro.Substring(118, 8));
}
else
{
Producto.dSaldo4 = double.Parse(Comun.FormatearMontoIso(sRegistro.Substring(102, 12)));
}
ProductoDTO.Items.Add(Producto);
}
ProductoDTO.Items.Sort(ProductoDTO);
break;
default:
ProductoDTO.bExitoso = false;
ProductoDTO.sMensaje = RespuestaDTO.sMensaje;
break;
}
else
{
ProductoDTO.bExitoso = false;
ProductoDTO.sMensaje = "Error campo de respuesta (39) nulo";
}
}
else
{
// El mensaje ha expirado
ProductoDTO.bExitoso = false;
ProductoDTO.sMensaje = "MensajeExpiro";
}
Como comenté antes, lo interesante es el manejo de los puertos, que aunque en realidad es sencillo, tuvimos que darle muchas vueltas para asegurar la confiabilidad y estabilidad del servicio
La solución implementada se basa en utilizar un pool de puertos para comunicarnos con el core, este pool fue implementado como una cola:
static Queue
De está cola, antes de enviar un mensaje el código agarra un puerto, verifica que esté bueno, se envía el mensaje y se devuelve el puerto a la cola.
Si el mensaje falla o expira, se considera que el puerto está malo y por ende se agrega el mismo a otra cola de puertos en cuarentena.
Cada cierto tiempo se intenta mandar un mensaje ISO 8583 de prueba para los puertos en cuarenta, si el mensaje es exitoso, se saca el puerto de cuarentena y se devuelve el mismo a la cola de puertos disponibles para los mensajes
///
/// Metodo encargado del envío de mensasjes ISO8583
///
///
Mensaje ISO 8583 a ser enviado
///
public ModeloObjetos.DTO.MensajeDTO EnviarMensaje(Iso8583Message Mensaje)
{
int campoConsecutivo = 11;
//se agrega el campo 11 que sirve como identificador unico del mensaje (es obligatorio)
Mensaje.Fields.Add(campoConsecutivo, NuevoNumeroSecuencia.ToString());
ModeloObjetos.DTO.MensajeDTO RespuestaDTO = new ModeloObjetos.DTO.MensajeDTO();
Trx.Messaging.Iso8583.Iso8583Message Respuesta = null;
int iNumIntentos = 0;
ClientPeer Canal = null;
int canalesDisponibles;
//se asegura la exclusividad mientras se revisa si hay puertos disponibles para el envío del mensaje
lock (SyncRoot)
{
while (CanalesComunicacion.Count == 0 && iNumIntentos < _NumeroIntentosEsperaCanalISO)
{
System.Threading.Thread.Sleep(_TiempoEsperaCanalDisponibleISO);
iNumIntentos++;
}
if ((canalesDisponibles = CanalesComunicacion.Count) > 0)
{
iNumIntentos = 0;
while (iNumIntentos < _NumeroIntentosEsperaCanalISO)
{
for (int i = 0; i < canalesDisponibles; i++)
{
//al tener el canal disponible lo saco de la cola para usarlo
Canal = CanalesComunicacion.Dequeue();
if (Canal.IsConnected)
{
break;
}
else
{
CanalesComunicacion.Enqueue(Canal);
}
}
if (Canal.IsConnected)
{
break;
}
System.Threading.Thread.Sleep((int)System.Math.Pow(2, iNumIntentos) * 1000);
iNumIntentos++;
}
}
}
bool canalQuebrado = false;
//cuando se determina que hay un canal disponible nos disponemos a usarlo
if (canalesDisponibles > 0)
{
try
{
if (Canal.IsConnected)
{
RespuestaDTO.Trazas.Add(new ModeloObjetos.DTO.TrazaOperacionDTO(false, "MensajeAenviar:" + Mensaje.ToString()));
//Asigno el canal al request a enviar
PeerRequest request = new PeerRequest(Canal, Mensaje);
//Se envía el mensaje
request.Send();
//esperamos por la respuesta del mensaje ( de acuerdo al timeout establecido)
request.WaitResponse(_TimeoutRecepcionMensajeISO);
//Si el request no expiró, entonces está todo bien y guardamos la respuesta ISO en nuestro DTO de respuesta
if (!request.Expired)
{
if (ServiciosISOServer.Comun.Logger.IsDebugEnabled)
ServiciosISOServer.Comun.Logger.Debug("Respuesta: " + request.ResponseMessage.ReceivedData);
Respuesta = (Iso8583Message)request.ResponseMessage;
RespuestaDTO.bExitoso = true;
RespuestaDTO.Trazas.Add(new ModeloObjetos.DTO.TrazaOperacionDTO(false, "RespuestaMensaje:" + Respuesta.ToString()));
}
else
{
//Mandamos un correo avisando que el puerto está malo (para que la gente del core lo revise)
RespuestaDTO.Trazas.Add(new ModeloObjetos.DTO.TrazaOperacionDTO(false, "MensajeExpiro"));
string puerto = ((TcpChannel)Canal.Channel).Port.ToString();
string correo = ConfigurationManager.AppSettings["CorreoOperador"];
string sText = String.Format("Expiró un mensaje enviado al Servidor {0} Puerto: {1}", ((TcpChannel)Canal.Channel).HostName, puerto);
string sSubject = String.Format(ConfigurationManager.AppSettings["Subject"], puerto);
string sResultado = EnviarCorreo.EnvioCorreo(correo, sSubject, sText);
if (ServiciosISOServer.Comun.Logger.IsDebugEnabled)
ServiciosISOServer.Comun.Logger.Debug(sText + " enviado a " + correo + " Resultado: " + sResultado);
canalQuebrado = true;
}
//if(request.ResponseMessage != null)
// RespuestaDTO.Trazas.Add(new ModeloObjetos.DTO.TrazaOperacionDTO( false, "DataRecibida:" + request.ResponseMessage.ReceivedData));
}
else
{
RespuestaDTO.Trazas.Add(new ModeloObjetos.DTO.TrazaOperacionDTO(false, "CanalNoConectado"));
}
}
finally
{
lock (SyncRoot)
{
//Si el canal fue utilizado satisfactoriamente, lo agregamos de nuevo a la cola de puertos disponibles para envío de mensajes
if (canalQuebrado == false)
{
CanalesComunicacion.Enqueue(Canal);
}
else
{
if (ServiciosISOServer.Comun.Logger.IsDebugEnabled)
{
ServiciosISOServer.Comun.Logger.DebugFormat("El canal {0} ha sido movido a la cola de cuarentena", Canal.Name);
}
//Si el mensaje expiró, agregamos el puerto a la cola de cuarentena (para que no sea nuevamente usado para enviar mensajes de la app hasta que esté sano)
MonitorCuarentena.Current.AgregarObjetoCuarentena(Canal);
}
}
}
}
else
{
RespuestaDTO.Trazas.Add(new ModeloObjetos.DTO.TrazaOperacionDTO(false, "CanalesNoDisponibles"));
}
RespuestaDTO.MensajeEnviado = Mensaje;
RespuestaDTO.MensajeRecibido = (Respuesta != null ? Respuesta : new Iso8583Message());
if (ServiciosISOServer.Comun.Logger.IsDebugEnabled)
ServiciosISOServer.Comun.Logger.Debug("Retornando: " + RespuestaDTO.bExitoso);
return RespuestaDTO;
}
Otra cosa a tener en cuenta es que seguro tendrá que hacer cambios en la especificación del mensaje ISO8583 en la clase: Iso8583Ascii1987MessageFormatter, en esta clase
están definidos como debe ser cada uno de los campos de los mensajes ISO por ejemplo:
FieldFormatters.Add( new StringFieldFormatter(
48, new VariableLengthManager(0, 9999, StringLengthEncoder.GetInstance(9999)),
StringEncoder.GetInstance(), "Additional data - private" ) );
Aquí se indica que el campo 48 es tipo String de longitud variable y en este caso con una longitud maxima de 9999 caracteres
En este otro ejemplo el campo 42 es string pero con longitud fija de 15
FieldFormatters.Add( new StringFieldFormatter(
42, new FixedLengthManager( 15 ), StringEncoder.GetInstance(),
"Card acceptor identification code" ) );
Estas definiciones son muy importantes ya que son las primeras que arrojan errores en las pruebas ya que cada server implementa a veces el estandar a su manera
Si está interesado en consultoría en comunicación ISO 8583 o necesita implementar una solución de este tipo, por favor no dude en contactarme.
Luego publicaré una aplicación de pruebas llamada ISO Tester que sirve para hacer pruebas puntuales de este tipo de mensajería
Hola Omar Rodríguez esta interesante el codigo ,mira yo estoy desarrollando la aplicacion completa en vb.net 2008 empaquetado desempaquetado el socket y todo lo demas , me puedes colaborar en algo por favor en decime que elementos o posiciones llevan cada uno de los siguientes mensajes
ResponderBorrar200,210,400,410,800,810 y cuales son las funciones del mensaje
te estare agradesido
Saludos..
atte: David Rodriguez
Buen dia Omar, estoy esperando que publique la aplicacion de iso tester, queria saber si puede mandarme una pagina donde encontrar informacion, sobre http://sourceforge.net/projects/trx-framework/, como utilizar la dll en una aplicacion web en visual studio 2005, buen dia y gracias.
ResponderBorrarDavid, no entiendo la ayuda que necesitas, a que te refieres con los elementos o posiciones de esos números de mensajes que indicas. Dame por favor mas información y contexto para poder ayudarte
ResponderBorrarCarlos, tratare de estar publicando mi IsoTester este fin de semana en este Blog
ResponderBorrarBuen Dia omar.
ResponderBorrarMe refiero al DataElemen que arma el mapa de Bit,cada mensaje indica en el mapa de bit
los elementos que tiene el mensaje.. por ejemplo la siguiene cadena indica el mensaje 200 seguidos de dos mapas de bit .
MTI MAPA 1 MAPA 2
0200 B23A800128A180180000000014000000
este mapa al desempaquetarlo trae los siguienetes dataelemen
1,3,4,7,11,12,13,15,17,32,35,37,41,43,48,49,60,
61,100,102
bueno en lo que necesito ayuda es en saber que data element traen los mensajes 300,310,400,410,,800,810
SALUDES
Atte: David R.
Acabo de publicar el IsoTester
ResponderBorrarDavid con respecto al mapa de bit, de verdad no te podría ayudar sin investigar primero, te aconsejo que utilices un framework como el que coloco en el blog para que tu no tengas que preocuparte en armar el mapa de bits, el framework lo hace por ti.
Asumiendo que cuando dices DataElements te refieres a los campos del mensaje, entonces te digo que debes solicitar la especificación de tu proveedor de mensajes ISO ya que aunque es un estandar, todo el mundo tiene su propia implementación
Gracias Omar, si fijate que descargue el trx-framework que utilizas tu , tienes informacion o manual que me ayude a utilizar el trx-framework para manejar el envio y recepcion de mensajes.
ResponderBorrarSaludes.
He seguido tu consejo y me descargue el iso tester
ResponderBorrarse ve muy bien tu iso tester por ahora lo estoy probando y cambiando varias rutas de archivos..
necesito saber donde recibo ese mensaje que envio en el iso tester para probar bien la aplicacion (creo falta la parte servidor). quiero hacer la prueba de enviar un mensaje del iso tester y recibir una respuesta simulando una transaccion bancaria
. ayudame Por Favor en eso omar
Saludes
ATTE : J. David Rodriguez
Buen dia Omar, he bajado el Iso tester y si lo hice funcionar pero la verdad no se como fucniona, bueno no se quien recibe el mensaje, y al parecer David tiene el mismo problema segun lei el blog, si me pudiera ayudar con eso le agradeceria mucho y gracias por publicar el Iso Tester
ResponderBorrarLes publico un server ISO de pruebas en http://iso8583tester.codeplex.com/releases/view/49475 es el mismo que provee la gente del Framework TRX
ResponderBorrardescargue el ejemplo de: http://iso8583tester.codeplex.com/releases/view/49475
ResponderBorrary lo intento ejecutar en vs.net 2008, me salen varios errores de que no puede encontrar las referencias a:"name space trx could not be found" trx.xxxxx, ademas en las referencias no se pudo encontrar la referencia a: tcpipframework.
Te agradezco mucho el post, eres todo un profesional. saludos
ResponderBorrarHola Omar. Ando buscando a alguien quien me pueda desarrollar todo el armado de la aplicación por medio de ISO8583 para una App en un HandHeld y meter proceso de pagos. Tengo el contacto con PROSA, ya solo requiero de una API que haga todo el proceso de pago y esta poderla integrar a nuestra App. Te dejo mi correo para ponernos en contacto y platicar sobre una propuesta. Salu2. Fernando Martínez G. fermaga@qsistemas.mx
ResponderBorrarHola Omar, estoy realizando el desarrollo de una aplicacion para Android en donde necesito implementar la norma ISO8583. Como pudieras ayudarme para dicha implementacion de codigo para cada campo de la norma, ya que soy un poco principiante en este cuento. Te agradesco mucho. Att. Sebastian.
ResponderBorrarHola Omar buen dia mi nombre es Eduardo me gustaria platicar contigo ya que estoy desarrollando una aplicacion. hypsyst@hotmail.com
ResponderBorrarSaludos, a la trama ISO8583 que construye y a la vez se enviá, es posible asignar un campo TPDU con la librería que utiliza?.
ResponderBorrarHola amigos, estoy muy intersado en el manejo de puertos, quien me puede ayudar por favor. mi correo es jaaf2000@hotmail.com
ResponderBorrarHola podrías mostrar un ejemplo para decodificar la trama iso8583 con este framework gracias.
ResponderBorrarHola Omar, del core me esta llegando un campo nuevo y lo observo en el ISO el campo 54, pero me esta llegando truncado, se que debo definir , pero no se donde ni como. gracias.
ResponderBorrar