Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Se adjunta información tècnica técnica de interes interés que se estuvo generando durante el desarrollo del proyecto por parte de Eldar.


Image Added

Image Added



View file
nameDocumentacioin Tecnica - VERSIOIN FINAL.pdf
height250

El presente informe detalla el desarrollo y configuración de una aplicación de procesamiento de transacciones basada en el framework jPOS. Este sistema proporciona una solución robusta y escalable para el procesamiento de transacciones financieras, haciendo uso del estándar ISO 8583.

...

Estructura de Carpetas de la app


 Archivos de cgf

...

 En la ruta src/dist/cfg, vamos a encontrar el archivo de configuracion

...

`iso93ascii.xml` y `iso87ascii.xml`

...

 iso92ascii.xml & iso87ascii.xml:

...

Los archivos `iso93ascii.xml` y `iso87ascii.xml` son una configuración de jPOS para el empaquetador genérico (GenericPackager).

...

- id: El número del campo.
- length: La longitud máxima del campo.
- name: Un nombre descriptivo para el campo.
- class: La clase de jPOS que se utiliza para empaquetar y desempaquetar el campo.

**El archivo `iso93ascii.xml` Se utiliza para el flujo 1, mientras que el archivo de configuracion `iso87ascii.xml` se utiliza para los flujos 2 y 3**#


Archivos de Configuraciones

...

 En la ruta src/dist/deploy/ vamos a encontrar todos los archivos de configuraciones XML de jpos

...

 00_logger.xml

**Este archivo configura el registro de logs para el sistema (imprime los mensajes de log en la consola, almacena en un buffer y escribe en archivos que se rotan y comprimen diariamente).**

...

1. `SimpleLogListener`: Este listener imprime los mensajes de log en la consola. El nivel de log se establece en "DEBUG", lo que significa que se imprimirán todos los mensajes de log, incluyendo los de nivel DEBUG, INFO, WARN y ERROR.
2. `BufferedLogListener`: Este listener almacena los mensajes de log en un buffer. El tamaño máximo del buffer se establece en "100", lo que significa que se almacenarán hasta 100 mensajes de log. El nombre del buffer es "logger.Q2.buffered".
3. `DailyLogListener`: Este listener escribe los mensajes de log en un archivo que se rota diariamente. Los archivos de log se guardan en la carpeta "log/q2" y tienen la extensión ".log". Los archivos se comprimen en formato gzip después de que se roten.
- Archivo

```xml
<?xml version="1.0" encoding="UTF-8"?>

<logger name="Q2" class="org.jpos.q2.qbean.LoggerAdaptor">

<property name="redirect" value="stdout, stderr" />

<log-listener class="org.jpos.util.SimpleLogListener" />

<log-listener class="org.jpos.util.BufferedLogListener">
<property name="max-size" value="100" />
<property name="name" value="logger.Q2.buffered" />
</log-listener>

<log-listener class="org.jpos.util.DailyLogListener">
<property name="window" value="86400" />
<property name="prefix" value="log/q2" />
<property name="suffix" value=".log"/>
<property name="date-format" value="-yyyy-MM-dd-HH"/>
<property name="compression-format" value="gzip"/>
</log-listener>

</logger>
```###

10_channel_cabal_ar.xml

**Configura un adaptador de canal para la comunicación ISO 8583 con un servidor específico, utilizando el formato ASCII para los mensajes y una configuración de empaquetador específica..**

...

- `<channel-adaptor>`: Este es el elemento raíz que define un adaptador de canal. Un adaptador de canal en jPOS es una entidad que maneja la comunicación de red para un canal específico.
- `name='cabal-ar-channel'`: Este es el nombre del adaptador de canal. Se utiliza para identificar el adaptador de canal en la configuración de jPOS.
- `class="org.jpos.q2.iso.ChannelAdaptor"`: Esta es la clase Java que implementa el adaptador de canal. En este caso, se utiliza la clase `ChannelAdaptor` proporcionada por jPOS.
- `logger="Q2"`: Este es el logger que se utilizará para registrar los eventos relacionados con este adaptador de canal.
- `<channel>`: Este elemento define el canal que se utilizará para la comunicación de red.
- `class="org.jpos.iso.channel.ASCIIChannel"`: Esta es la clase Java que implementa el canal. En este caso, se utiliza la clase `ASCIIChannel` proporcionada por jPOS, que es un canal que utiliza el formato ASCII para las transacciones ISO 8583.
- `packager="org.jpos.iso.packager.GenericPackager"`: Define la clase que se utilizará para empaquetar y desempaquetar los mensajes ISO 8583.
- `<property name="packager-config" value="cfg/iso93ascii.xml"/>`: Esta propiedad define la configuración del empaquetador (packager). En este caso, se utiliza la configuración definida en el archivo `iso93ascii.xml`.
- `<property name="host" value="@cabal.ar.host@"/>` y `<property name="port" value="@cabal.ar.port@"/>`: Estas propiedades definen el host y el puerto del servidor al que se conectará este canal. En este caso, se setearán dinámicamente por el tag del target del `gradle.properties` (por defecto está en develop, con las IP y puerto de Cabal Argentina).
- `<filter class="com.app.gateway.filters.IncomingFilterCabalAr" direction="incoming"/>`: Filtro para mensajes entrantes.
- `<filter class="com.app.gateway.filters.OutgoingFilterCabalAr" direction="outgoing"/>`: Filtro para mensajes salientes.
- `<filter class="org.jpos.iso.filter.MacroFilter" direction="outgoing">`: Filtro macro para mensajes salientes.
- `<property name="valid" value="0 2 11 12 22 23 41 55"/>`: Define los campos válidos para el filtro macro.
- `<in>` y `<out>`: Estos elementos definen las colas de mensajes entrantes y salientes para este adaptador de canal. En este caso, los mensajes que se envían a través de este canal se colocarán en la cola `cabal-ar-send` y los mensajes que se reciben a través de este canal se colocarán en la cola `cabal-ar-receive`.
- `<reconnect-delay>`: Este elemento define el tiempo de espera (en milisegundos) antes de intentar reconectar después de una desconexión. En este caso, el adaptador de canal intentará reconectar después de 10 segundos (10000 milisegundos) si se desconecta.
- Archivo

```xml
<?xml version="1.0" ?>

<channel-adaptor name='cabal-ar-channel' class="org.jpos.q2.iso.ChannelAdaptor" logger="Q2">
<channel class="org.jpos.iso.channel.ASCIIChannel"
packager="org.jpos.iso.packager.GenericPackager"
type="client"
connect="no" logger="Q2" realm="cabal-ar-channel">
<property name="packager-config" value="cfg/iso93ascii.xml"/>
<property name="host" value="@cabal.ar.host@" />
<property name="port" value="@cabal.ar.port@" />
<filter class="com.app.gateway.filters.IncomingFilterCabalAr" direction="incoming"/>
<filter class="com.app.gateway.filters.OutgoingFilterCabalAr" direction="outgoing"/>
<filter class="org.jpos.iso.filter.MacroFilter" direction="outgoing">
<property name="valid" value="0 2 11 12 22 23 41 55 " />
</filter>
</channel>
<in>cabal-ar-send</in>
<out>cabal-ar-receive</out>
<reconnect-delay>10000</reconnect-delay>
</channel-adaptor>

```### 20

 20_mux_cabal_ar.xml

Este archivo XML define un multiplexor (`mux`) para jPOS, un marco de trabajo de Java para transacciones financieras ISO 8583.

...

- Archivo

```xml
<?xml version="1.0" ?>

<mux class="org.jpos.q2.iso.QMUX" logger="Q2" name="mux-cabal-ar">
<in>cabal-ar-receive</in>
<out>cabal-ar-send</out>
<ready>cabal-ar-channel.ready</ready>
</mux>

```### 30

 30_txnmgr.xml

El archivo `30_txnmgr.xml` es una configuración de jPOS que define un administrador de transacciones (TransactionManager).

...

1. **Recibir la transacción**: El `TransactionManager` recibe una transacción de la cola de transacciones "TXNMGR".
2. **Iniciar la transacción**: El `TransactionManager` inicia la transacción creando un nuevo contexto de transacción.
3. **Procesar la transacción**: El `TransactionManager` invoca a cada uno de los participantes en el orden en que aparecen en el archivo de configuración.
- **EntryModeFilter**: Filtra el modo de entrada.
- **EntryModeSelector**: Selecciona el modo de entrada.
- **PrepareForCeibo**: Prepara la transacción para Ceibo.
- **CeiboAuthorization**: Maneja la autorización de Ceibo.
- **SendResponse**: Envía la respuesta al cliente.
- **Grupo `group-contact-contactless`**: Procesa transacciones contact o contactless.
- **QueryHostDGW**: Consulta el host DGW.
- **CheckResponseCabalAr**: Verifica la respuesta de Cabal AR.
4. **Preparar la transacción**: Verifica si alguno de los participantes ha marcado la transacción para ser deshecha.
5. **Confirmar o deshacer la transacción**: Si la transacción ha sido marcada para ser deshecha, se invoca el método `abort` de cada participante en orden inverso. Si no, se invoca el método `commit`.
6. **Finalizar la transacción**: Limpia cualquier recurso utilizado por la transacción.
</aside>### 30

 30_txnmgr_dci.xml

El archivo `30_txnmgr_dci.xml` es una configuración de jPOS que define un administrador de transacciones (TransactionManager).

...

1. **Recibir la transacción**: El `TransactionManager` recibe una transacción de la cola de transacciones "TXNMGR-DCI".
2. **Iniciar la transacción**: El `TransactionManager` inicia la transacción creando un nuevo contexto de transacción.
3. **Procesar la transacción**: El `TransactionManager` invoca a cada uno de los participantes en el orden en que aparecen en el archivo de configuración.
- **CeiboAuthorizationDCI**: Maneja la autorización de Ceibo DCI.
- **SendResponse**: Envía la respuesta al cliente.
4. **Preparar la transacción**: Verifica si alguno de los participantes ha marcado la transacción para ser deshecha.
5. **Confirmar o deshacer la transacción**: Si la transacción ha sido marcada para ser deshecha, se invoca el método `abort` de cada participante en orden inverso. Si no, se invoca el método `commit`.
6. **Finalizar la transacción**: Limpia cualquier recurso utilizado por la transacción.
</aside>### 50

 50_srv_asc_93.xml

**El archivo `50_srv_asc_93.xml` es una configuración de jPOS que define un servidor ISO 8583 (en formato ascii) .**

...

- Archivo

```xml
<server class="org.jpos.q2.iso.QServer" logger="Q2" name="ascii-93-server" realm="ascii-93-server">
<attr name="port" type="java.lang.Integer">7002</attr>
<channel class="org.jpos.iso.channel.ASCIIChannel"
packager="org.jpos.iso.packager.GenericPackager"
type="server"
logger="Q2">
<property name="packager-config" value="cfg/iso93ascii.xml"/>
<property name="timeout" value="180000"/>
<property name="debug" value="true" />
</channel>
<request-listener class="org.jpos.iso.IncomingListener" logger="Q2" realm="incoming-request-listener">
<property name="queue" value="TXNMGR" />
<property name="ctx.DESTINATION" value="mux-cabal-ar" />
</request-listener>
</server>

```### 50

 50_srv_asc_dci_87.xml

**El archivo `50_srv_asc_dci_87.xml` es una configuración de jPOS que define un servidor ISO 8583 (en formato ASCII).**

...

- Archivo

```xml
<server class="org.jpos.q2.iso.QServer" logger="Q2" name="ascii-87-server-dci" realm="ascii-87-server-dci">
<attr name="port" type="java.lang.Integer">7004</attr>
<channel class="org.jpos.iso.channel.ASCIIChannel"
packager="org.jpos.iso.packager.GenericPackager"
type="server"
logger="Q2">
<property name="packager-config" value="cfg/iso87ascii.xml"/>
<property name="timeout" value="180000"/>
<property name="debug" value="true" />
</channel>
<request-listener class="org.jpos.iso.IncomingListener" logger="Q2" realm="incoming-request-listener">
<property name="queue" value="TXNMGR-DCI" />
</request-listener>
</server>

```### 99

 99_sysmon.xml

**El archivo `99_sysmon.xml` es una configuración del monitor del sistema (SysMon) para el framework jPOS. SysMon es un servicio que proporciona información sobre el estado del sistema.**

...

- Archivo

```xml
<sysmon logger="Q2">
<!-- Environment: '@target@' -->
<attr name="sleepTime" type="java.lang.Long">3600000</attr>
<attr name="detailRequired" type="java.lang.Boolean">true</attr>
<property name="metrics-dir" value="log" />
</sysmon>

```# Archivos

 Archivos java (FILTERS)

...

En la ruta src/main/java/com/app/gateway/filters vamos a encontrar los filtros

...

IncomingFilterCabalAr :

...

**La clase `IncomingFilterCabalAr` es un filtro ISO 8583 en jPOS. Esta clase es responsable de procesar mensajes entrantes de Cabal AR.**

...

- Archivo

```java
package com.app.gateway.filters;

import org.jpos.core.Configurable;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.*;
import org.jpos.space.Space;
import org.jpos.space.SpaceFactory;
import org.jpos.tlv.TLVList;
import org.jpos.transaction.Context;
import org.jpos.util.LogEvent;

public class IncomingFilterCabalAr implements ISOFilter, Configurable {
@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {

}

@Override
public ISOMsg filter(ISOChannel channel, ISOMsg message, LogEvent evt) throws VetoException {
final Space sp = SpaceFactory.getSpace();
evt.addMessage("-------------- py_incoming_from_cabal_ar, incoming message --------------");
evt.addMessage((Object)message);
ISOMsg r=null;
try{
final String firstValue = message.hasField(11) ? ISOUtil.zeropad(message.getString(11).trim(), 12) : "";
final String secondValue = message.hasField(41) ? this.get41(message) : "";
final String key = "msg_py" + firstValue + "-" + secondValue;
r = (ISOMsg)sp.inp((Object)key);
r.setResponseMTI();
if(message.hasField(39)){
if(message.getString(39).equals("081")){
if (message.hasField(55)) {
r.set(55,message.getString(55));
}
}
r.set(39,message.getString(39));
}
} catch (ISOException e) {
throw new RuntimeException(e);
}

return r;
}

private String get41(final ISOMsg message) throws ISOException {
String resultado = ISOUtil.strpad(message.getString(41).trim(), 8);
if (resultado.length() > 8) {
resultado = resultado.substring(resultado.length() - 8, resultado.length());
}
return ISOUtil.padright(resultado, 8, ' ');
}
}

```

### **IncomingFilterPy:**

...

 IncomingFilterPy:

La clase `IncomingFilterPy` es un filtro ISO 8583 en jPOS. Esta clase es responsable de procesar mensajes entrantes de Py.**

Detalle de este filtro:

- `setConfiguration(Configuration cfg)`: Este método se utiliza para configurar el filtro. En este caso, no se realiza ninguna configuración específica.
- `filter(ISOChannel channel, ISOMsg message, LogEvent evt)`: Este es el método principal que se ejecuta durante el filtrado del mensaje ISO 8583. En este método, se realiza lo siguiente:
- Se clona el mensaje entrante.
- Se verifica si el MTI del mensaje es igual a `ECHO_MTI`. Si es así, se devuelve el mensaje sin modificaciones.
- Si el mensaje tiene el campo 55, se convierte el contenido del campo 55 a una cadena hexadecimal.
- Se agrega un mensaje de log indicando que se ha recibido un mensaje entrante.
- Se agrega el mensaje clonado al log.

...

- Archivo

```java
package com.app.gateway.filters;

import org.jpos.core.Configurable;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.*;
import org.jpos.space.Space;
import org.jpos.space.SpaceFactory;
import org.jpos.util.LogEvent;

import static com.app.gateway.Constants.ECHO_MTI;
import static com.app.gateway.Constants.MESSAGE_INCOMING_BANCARD;

public class IncomingFilterPy implements ISOFilter, Configurable {

@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {

}

@Override
public ISOMsg filter(ISOChannel channel, ISOMsg message, LogEvent evt) throws VetoException {
ISOMsg m = (ISOMsg) message.clone();
try {
if(m.getMTI().equals(ECHO_MTI)){
return m;
}
if (m.hasField(55)) {
byte[] f55 = m.getBytes(55);
m.set(55, ISOUtil.hexString(f55));
}
} catch (ISOException e) {
throw new RuntimeException(e);
}
evt.addMessage("-------------- py_incoming_filter, incoming message --------------");
evt.addMessage(m);
return m;
}
}

```### **OutgoingFilterCabalAr:**

 OutgoingFilterCabalAr:

**La clase `OutgoingFilterCabalAr` es un filtro ISO 8583 en jPOS. Esta clase es responsable de procesar mensajes salientes hacia Cabal AR.**

...

- Archivo

```java
package com.app.gateway.filters;

import org.jpos.core.Configurable;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.*;
import org.jpos.space.Space;
import org.jpos.space.SpaceFactory;
import org.jpos.util.LogEvent;

public class OutgoingFilterCabalAr implements ISOFilter, Configurable {
@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {

}

@Override
public ISOMsg filter(ISOChannel channel, ISOMsg message, LogEvent evt) throws VetoException {
final Space sp = SpaceFactory.getSpace();
ISOMsg m = (ISOMsg) message.clone();
evt.addMessage((Object)"--- py_outgoing_to_cabal_ar, unfiltered message ---");
evt.addMessage((Object)message);
try {
final String firstValue = message.hasField(11) ? ISOUtil.zeropad(message.getString(11).trim(), 12) : "";
final String secondValue = message.hasField(41) ? this.get41(message) : "";
final String key = "msg_py" + firstValue + "-" + secondValue;
sp.out((Object) key, (Object) m, 300000L);
evt.addMessage((Object) ("message key out: " + key));
String entryMode= String.valueOf(message.getString(22).charAt(6));
m.set(22,ISOUtil.zeropad(entryMode,12));
} catch (ISOException e) {
throw new RuntimeException(e);
}
evt.addMessage((Object)"--- py_outgoing_to_cabal_ar, outgoing message ---");
return m;
}

private String get41(final ISOMsg message) throws ISOException {
String resultado = ISOUtil.strpad(message.getString(41).trim(), 8);
if (resultado.length() > 8) {
resultado = resultado.substring(resultado.length() - 8, resultado.length());
}
return ISOUtil.padright(resultado, 8, ' ');
}
}

```# Archivos

 Archivos java (PARTICIPANTS)

...

 En la ruta src/main/java/com/app/gateway/participants vamos a encontrar los participants

...

 CeiboAuthorization:

**La clase `CeiboAuthorization` es un participante en una transacción de jPOS. Esta clase es responsable de autorizar la transacción a través de la llamada a un procedimiento almacenado `PR_CABAL_DISCOVER`. Este participant es el validador del flujo 1**

...

- Archivo

```java
package com.app.gateway.participants;

import org.hibernate.Session;
import org.hibernate.internal.SessionImpl;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.ISOMsg;
import org.jpos.transaction.AbortParticipant;
import org.jpos.transaction.Context;
import org.jpos.transaction.TxnSupport;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Types;
import java.util.concurrent.*;

import org.jpos.ee.DB;

import static com.app.gateway.Constants.*;
import static com.app.gateway.utils.Utilities.*;

/**
* This participant is responsible for authorizing the transaction through the so-called
* to a stored procedure called PR_CABAL_DISCOVER.
*/
public class CeiboAuthorization extends TxnSupport implements AbortParticipant {

private String target;

@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
super.setConfiguration(cfg);
this.target = cfg.get("target");
}

@Override
public synchronized DB getDB(Context ctx) {
DB db = (DB) ctx.get(DB.class);
if (db == null) {
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<DB> task = DB::new;
Future<DB> future = executor.submit(task);

try {
db = future.get(20, TimeUnit.SECONDS);
ctx.put(DB.class, db);
} catch (TimeoutException e) {
future.cancel(true);
error("Timeout: No se pudo conectar a la base de datos en 20 segundos");
throw new RuntimeException();
} catch (InterruptedException | ExecutionException e) {
error("Error al crear la instancia de DB");
throw new RuntimeException();
} finally {
executor.shutdown();
}
}
return db;
}

@Override
protected int doPrepare(long id, Context ctx) throws Exception {
ISOMsg messageIncomingBancard = ctx.get("REQUEST");
ISOMsg messageIncomingBancardToCeibo= (ISOMsg) messageIncomingBancard.clone();
ISOMsg[] isoMsgsArrayToSPCeibo = createParamsToCeibo(ctx,messageIncomingBancardToCeibo);
try {
DB db = getDB(ctx);
try (Session session = db.open(); Connection con = ((SessionImpl) session).connection();
CallableStatement sp = con.prepareCall("{call PR_CABAL_DISCOVER(?, ?, ?,?,?)}")) {

sp.setQueryTimeout(30);
prepareParamsToCeiboSP(isoMsgsArrayToSPCeibo,sp);
sp.registerOutParameter(4, Types.VARCHAR);
sp.registerOutParameter(5, Types.NUMERIC);
sp.executeUpdate();
String respuestaISO = sp.getString(4);
ISOMsg isoMsgResponse = convertStringToISOMsg(respuestaISO, "ISO93ASCII");
if(!ctx.hasKey(TRANSACTION_TYPE)){
info("Respuesta desde sp ceibo - Transaccion no determinada\n"+respuestaISO);
ctx.put("RESPONSE", isoMsgResponse);
return ABORTED;
}
else {
String typeTransaction=ctx.get(TRANSACTION_TYPE);
if (typeTransaction.equals(BANDA)) {
info("Respuesta desde sp ceibo - Transaccion banda\n" + respuestaISO);
ctx.put("RESPONSE", isoMsgResponse);
if (!ctx.hasKey(RESPONSE_INTERNAL_DISCOVER_ERROR)) {
String responseCodeRespuestaSP = isoMsgResponse.getString(39);
if (responseCodeRespuestaSP.equals("081")) {
return PREPARED;
} else {
ctx.put(REJECTION_REASON, NOT_AUTHORIZED_BY_CEIBO);
return ABORTED;
}
}
} else {
info("Respuesta desde sp ceibo - Transaccion chip\n" + respuestaISO);
if (ctx.hasKey(RESPONSE_FROM_CABAL_AR)) {
ISOMsg responseFromCabalArgentina = ctx.get(RESPONSE_FROM_CABAL_AR);
boolean isResponseFromCabalArgentina081 = responseFromCabalArgentina.getString(39).equals("081");
if (isResponseFromCabalArgentina081 && !ctx.hasKey(RESPONSE_INTERNAL_DISCOVER_ERROR)) {
String responseCodeRespuestaSP = isoMsgResponse.getString(39);
if (responseCodeRespuestaSP.equals("081")) {
isoMsgResponse.set(55, responseFromCabalArgentina.getString(55));
ctx.put("RESPONSE", isoMsgResponse);
return PREPARED;
} else {
isoMsgResponse.set(55, messageIncomingBancard.getString(55));
ctx.put("RESPONSE", isoMsgResponse);
ctx.put(REJECTION_REASON, NOT_AUTHORIZED_BY_CEIBO);
return ABORTED;
}
} else if (!isResponseFromCabalArgentina081 || ctx.hasKey(RESPONSE_INTERNAL_DISCOVER_ERROR)) {
isoMsgResponse.set(55, messageIncomingBancard.getString(55));
ctx.put("RESPONSE", isoMsgResponse);
return ABORTED;
}
} else {
isoMsgResponse.set(55, messageIncomingBancard.getString(55));
ctx.put("RESPONSE", isoMsgResponse);
return ABORTED;
}
}
}
} catch (Exception e) {
error("Error during stored procedure call");
handleException(ctx, messageIncomingBancard);
return ABORTED;
}
} catch (Exception e) {
error("Error during connection to db");
handleException(ctx, messageIncomingBancard);
return ABORTED;
}
return PREPARED;
}

private void prepareParamsToCeiboSP(ISOMsg[] isoMsgsArrayToSPCeibo, CallableStatement sp) throws Exception {
for (int i = 0; i < isoMsgsArrayToSPCeibo.length; i++) {
if (isoMsgsArrayToSPCeibo[i] != null) {
String isoMsgString = convertISOMsgToString(isoMsgsArrayToSPCeibo[i]);
if (!target.equals("production")) {
if (i == 0) {
info("The ISO message received by DiscoverGW: " + isoMsgString);
} else if (i == 1) {
info("The ISO message response by Cabal AR: " + isoMsgString);
} else {
info("The ISO message response to terminal by Discover GW error: " + isoMsgString);
}
}
sp.setString(i + 1, isoMsgString);
} else {
sp.setString(i + 1, null);
}
}
}

private ISOMsg[] createParamsToCeibo(Context ctx, ISOMsg messageIncomingBancardToCeibo) throws Exception {
ISOMsg[] isoMsgsArrayToSPCeibo = new ISOMsg[3];

if(!ctx.hasKey(TRANSACTION_TYPE)) {
info("Transaccion no determinada - creando parametros de entrada para ceibo sp");
if(messageIncomingBancardToCeibo.hasField(55)){
messageIncomingBancardToCeibo.unset(55);
}
isoMsgsArrayToSPCeibo[0] = messageIncomingBancardToCeibo;
if (ctx.hasKey(RESPONSE_INTERNAL_DISCOVER_ERROR)) {
ISOMsg responseToTerminalDiscoverError = ctx.get(RESPONSE_INTERNAL_DISCOVER_ERROR);
ISOMsg responseToTerminalDiscoverErrorToCeibo = (ISOMsg) responseToTerminalDiscoverError.clone();
responseToTerminalDiscoverErrorToCeibo.unset(55);
isoMsgsArrayToSPCeibo[2] = responseToTerminalDiscoverErrorToCeibo;
}
}else {
String typeTransaction=ctx.get(TRANSACTION_TYPE);
if (typeTransaction.equals(BANDA)) {
info("Transaccion banda - creando parametros de entrada para ceibo sp");
if(messageIncomingBancardToCeibo.hasField(55)){
messageIncomingBancardToCeibo.unset(55);
}
isoMsgsArrayToSPCeibo[0] = messageIncomingBancardToCeibo;
if (ctx.hasKey(RESPONSE_INTERNAL_DISCOVER_ERROR)) {
ISOMsg responseToTerminalDiscoverError = ctx.get(RESPONSE_INTERNAL_DISCOVER_ERROR);
ISOMsg responseToTerminalDiscoverErrorToCeibo = (ISOMsg) responseToTerminalDiscoverError.clone();
responseToTerminalDiscoverErrorToCeibo.unset(55);
isoMsgsArrayToSPCeibo[2] = responseToTerminalDiscoverErrorToCeibo;
}
} else {
info("Transaccion chip - creando parametros de entrada para ceibo sp");
messageIncomingBancardToCeibo.unset(55);
isoMsgsArrayToSPCeibo[0] = messageIncomingBancardToCeibo;
if (ctx.hasKey(RESPONSE_FROM_CABAL_AR)) {
ISOMsg responseFromCabalArgentina = ctx.get(RESPONSE_FROM_CABAL_AR);
ISOMsg responseFromCabalArgentinaToCeibo = (ISOMsg) responseFromCabalArgentina.clone();
responseFromCabalArgentinaToCeibo.unset(55);
isoMsgsArrayToSPCeibo[1] = responseFromCabalArgentinaToCeibo;
}
if (ctx.hasKey(RESPONSE_INTERNAL_DISCOVER_ERROR)) {
ISOMsg responseToTerminalDiscoverError = ctx.get(RESPONSE_INTERNAL_DISCOVER_ERROR);
ISOMsg responseToTerminalDiscoverErrorToCeibo = (ISOMsg) responseToTerminalDiscoverError.clone();
responseToTerminalDiscoverErrorToCeibo.unset(55);
isoMsgsArrayToSPCeibo[2] = responseToTerminalDiscoverErrorToCeibo;
}
}
}
return isoMsgsArrayToSPCeibo;
}

private void handleException(Context ctx, ISOMsg messageIncomingBancard){
try {
error("...... Devolviendo respuesta con codigos de respuesta internos ......");
if(!ctx.hasKey(TRANSACTION_TYPE)){
ISOMsg errorDiscoverGw= ctx.get(RESPONSE_INTERNAL_DISCOVER_ERROR);
ctx.put("RESPONSE",errorDiscoverGw);
}
else {
String typeTransaction=ctx.get(TRANSACTION_TYPE);
if (typeTransaction.equals(BANDA)) {
if (!ctx.hasKey(RESPONSE_INTERNAL_DISCOVER_ERROR)) {
ISOMsg responseToTerminalExceptionInSP = (ISOMsg) messageIncomingBancard.clone();
responseToTerminalExceptionInSP.setResponseMTI();
ctx.put(REJECTION_REASON, ERROR_CONNECTION_TO_CEIBO);
String responseCode = mapRejectionReasonToResponseCodeError(ERROR_CONNECTION_TO_CEIBO);
responseToTerminalExceptionInSP.set(39, responseCode);
ctx.put("RESPONSE", responseToTerminalExceptionInSP);
} else {
ISOMsg errorDiscoverGw = ctx.get(RESPONSE_INTERNAL_DISCOVER_ERROR);
ctx.put("RESPONSE", errorDiscoverGw);
}
} else {
if (ctx.hasKey(RESPONSE_FROM_CABAL_AR)) {
ISOMsg responseFromCabalArgentina = ctx.get(RESPONSE_FROM_CABAL_AR);
boolean isResponseFromCabalArgentina081 = responseFromCabalArgentina.getString(39).equals("081");
if (isResponseFromCabalArgentina081) {
ISOMsg responseToTerminalExceptionInSP = (ISOMsg) responseFromCabalArgentina.clone();
ctx.put(REJECTION_REASON, ERROR_CONNECTION_TO_CEIBO);
String responseCode = mapRejectionReasonToResponseCodeError(ERROR_CONNECTION_TO_CEIBO);
responseToTerminalExceptionInSP.set(39, responseCode);
ctx.put("RESPONSE", responseToTerminalExceptionInSP);
} else {
ctx.put("RESPONSE", responseFromCabalArgentina);
}
} else {
ISOMsg errorDiscoverGw = ctx.get(RESPONSE_INTERNAL_DISCOVER_ERROR);
ctx.put("RESPONSE", errorDiscoverGw);
}
}
}
}catch (Exception e){
error(e);
}
}

@Override
protected int doPrepareForAbort(long id, Context ctx) throws Exception {
return doPrepare(id, ctx);
}
}

```### **CeiboAuthorizationDCI:**

 CeiboAuthorizationDCI:

**La clase `CeiboAuthorizationDCI` es un participante en una transacción de jPOS. Esta clase es responsable de autorizar la transacción a través de la llamada a un procedimiento almacenado `PR_DISCOVER_INTERNACIONAL`. Este participant es el validador de los flujos 2 y 3.**

...

- Archivo

```java
package com.app.gateway.participants;

import org.hibernate.Session;
import org.hibernate.internal.SessionImpl;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.ee.DB;
import org.jpos.iso.ISOMsg;
import org.jpos.transaction.Context;
import org.jpos.transaction.TxnSupport;
import java.io.Serializable;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.Types;
import java.util.concurrent.*;

import static com.app.gateway.utils.Utilities.convertISOMsgToString;
import static com.app.gateway.utils.Utilities.convertStringToISOMsg;

public class CeiboAuthorizationDCI extends TxnSupport implements Serializable {

private String target;

@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
super.setConfiguration(cfg);
this.target=cfg.get("target");
}

@Override
public synchronized DB getDB(Context ctx) {
DB db = (DB) ctx.get(DB.class);
if (db == null) {
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<DB> task = DB::new;
Future<DB> future = executor.submit(task);

try {
db = future.get(20, TimeUnit.SECONDS);
ctx.put(DB.class, db);
} catch (TimeoutException e) {
future.cancel(true);
error("Timeout: No se pudo conectar a la base de datos en 20 segundos");
throw new RuntimeException();
} catch (InterruptedException | ExecutionException e) {
error("Error al crear la instancia de DB");
throw new RuntimeException();
} finally {
executor.shutdown();
}
}
return db;
}

@Override
public int prepare(long id, Serializable context) {
Context ctx = (Context) context;
ISOMsg isoMsg = ctx.get("REQUEST");

// Es un clon del msj iso que viene del contexto. Pero sin el campo 55 para enviarlo al stored procedure
ISOMsg isoMsgSP = (ISOMsg) isoMsg.clone();
isoMsgSP.unset(55);

try {
DB db = getDB(ctx);
try (Session session = db.open(); Connection con = ((SessionImpl) session).connection();
CallableStatement sp = con.prepareCall("{call PR_DISCOVER_INTERNACIONAL(?, ?, ?)}")) {

String isoMsgRequest=convertISOMsgToString(isoMsgSP);
sp.setString(1,isoMsgRequest);
sp.registerOutParameter(2, Types.VARCHAR);
sp.registerOutParameter(3, Types.INTEGER);

info("Excute the Ceibo stored procedure PR_DISCOVER_INTERNACIONAL");
sp.executeUpdate();

int puntoDestino = sp.getInt(3);
String respuestaISO = sp.getString(2);
ISOMsg isoMsgResponse = convertStringToISOMsg(respuestaISO, "ISO87Ascii");

if(!target.equals("production")){
info("The ISO message RECIVED by the Ceibo stored procedure: " + isoMsgRequest);
info("----------------------------------");
info("The ISO message RESPONSE sent by the Ceibo stored procedure: ", respuestaISO);
}

isoMsgResponse.set(55,isoMsg.getString(55));

info("Sending ISO message request to Cabal AR");
ctx.put("RESPONSE",isoMsgResponse);

return PREPARED;
} catch (Exception e) {
error("Error during stored procedure call ");
return ABORTED;
}
} catch (Exception e) {
error("Error during connection to db " );
return ABORTED;
}
}

}

```### **CheckResponseCabalAr:**

 CheckResponseCabalAr:

**La clase `CheckResponseCabalAr` es un participante en una transacción de jPOS. Esta clase es responsable de verificar la respuesta de Cabal AR.**

...

- Archivo

```java
package com.app.gateway.participants;

import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.ISOMsg;
import org.jpos.transaction.Context;
import org.jpos.transaction.TxnSupport;

import java.io.Serializable;

import static com.app.gateway.Constants.*;
import static org.jpos.transaction.TransactionConstants.PREPARED;

public class CheckResponseCabalAr extends TxnSupport {

@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException{
super.setConfiguration(cfg);
}

@Override
protected int doPrepare(long id, Context ctx) {
ISOMsg isoMsg = ctx.get(RESPONSE_FROM_CABAL_AR);
if(ctx.hasKey(REJECTION_REASON)){
String rejectionReason = ctx.get(REJECTION_REASON);
if (CONNECTION_TIMEOUT_TO_CABAL_AR.equals(rejectionReason) || CABAL_AR_NOT_CONNECTED.equals(rejectionReason)) {
return ABORTED;
}
}

// Verificar si el mensaje ISO tiene el campo 39 y si su valor no es "081"
if (isoMsg.hasField(39)) {
String isoField39 = isoMsg.getString(39);
if (isoField39 != null && !"081".equals(isoField39)) {
ctx.put(REJECTION_REASON, INVALID_CRYPTOGRAM);
ctx.put("RESPONSE",isoMsg);
return ABORTED;
}

}
return PREPARED;
}

}

```### **EntryModeFilter:**

 EntryModeFilter:

**La clase `EntryModeFilter` es un participante en una transacción de jPOS. Esta clase es responsable de preparar el mensaje para su procesamiento basado en reglas específicas según el modo de entrada.** **Determina el tipo de transacción (CHIP o BANDA)**

...

- Archivo

```java
package com.app.gateway.participants;

import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.ISOMsg;
import org.jpos.space.Space;
import org.jpos.space.SpaceFactory;
import org.jpos.transaction.Context;
import org.jpos.transaction.TxnSupport;

import static com.app.gateway.Constants.*;

public class EntryModeFilter extends TxnSupport {

@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
super.setConfiguration(cfg);
}

/**
* Prepara el mensaje para procesamiento basado en reglas específicas según el modo de entrada.
*/
@Override
protected int doPrepare(long id, Context ctx) {
ISOMsg isoMsg = ctx.get("REQUEST");

if (!isoMsg.hasField(22)) {
error("El campo 22 no esta presente en el mensaje");
ctx.put(REJECTION_REASON, INVALID_FORMAT);
return ABORTED;
}

String f22 = isoMsg.getString(22);
char entryMode;
try {
entryMode = f22.charAt(6);
} catch (IndexOutOfBoundsException e) {
error("El campo 22 no presenta el 6to caracter que indica el entry mode");
ctx.put(REJECTION_REASON, INVALID_FORMAT);
return ABORTED;
}

if (entryMode != '2' && entryMode != '5' && entryMode != '7') {
error("Modo de entrada no permitido: " + entryMode);
ctx.put(REJECTION_REASON, INVALID_ENTRY_MODE);
return ABORTED;
}

if (entryMode == '5' || entryMode == '7') {
info("Tarjeta chip");
ctx.put(TRANSACTION_TYPE,CHIP);
// Campos obligatorios para tarjetas chip
int[] mandatoryFields = {2, 11, 12, 22, 23, 41, 55};
if (!isoMsg.hasFields(mandatoryFields)) {
error("Campos obligatorios para tarjetas chip faltantes");
ctx.put(REJECTION_REASON, INVALID_FORMAT);
return ABORTED;
}
} else if (entryMode == '2') {
info("Tarjeta banda");
ctx.put(TRANSACTION_TYPE,BANDA);
// TODO: Agregar campos mandatorios para tarjetas banda
}

return PREPARED;
}
}

```### **EntryModeSelector:**

 EntryModeSelector:

**La clase `EntryModeSelector` es un participante en una transacción de jPOS que implementa la interfaz `GroupSelector`. Esta clase es responsable de seleccionar un grupo de transacciones basado en el modo de entrada del mensaje ISO.**

...

- Archivo

```java
package com.app.gateway.participants;

import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.ISOMsg;
import org.jpos.space.Space;
import org.jpos.space.SpaceFactory;
import org.jpos.transaction.Context;
import org.jpos.transaction.GroupSelector;
import org.jpos.transaction.TxnSupport;

import java.io.Serializable;

import static com.app.gateway.Constants.*;

public class EntryModeSelector extends TxnSupport implements GroupSelector {

@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
super.setConfiguration(cfg);
}

@Override
public String select(long l, Serializable serializable) {
Context context = (Context) serializable;
ISOMsg isoMsg=context.get("REQUEST");
String groupSelector="";
if(!context.hasKey(REJECTION_REASON)){
String f22 = isoMsg.getString(22);
char entryMode = f22.charAt(6);
if(entryMode=='5' || entryMode=='7') {
groupSelector="group-contact-contactless";
}
}
return groupSelector;
}

@Override
public int prepare(long l, Serializable serializable) {
return PREPARED;
}
}

```### **PrepareForCeibo:**

 PrepareForCeibo:

**La clase `PrepareForCeibo` es un participante en una transacción de jPOS que implementa la interfaz `AbortParticipant`. Esta clase es responsable de preparar el contexto para Ceibo y manejar los abortos de transacción.**

...

- Archivo

```java
package com.app.gateway.participants;

import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.ISOMsg;
import org.jpos.transaction.AbortParticipant;
import org.jpos.transaction.Context;
import org.jpos.transaction.TxnSupport;

import static com.app.gateway.Constants.*;
import static com.app.gateway.utils.Utilities.mapRejectionReasonToResponseCodeError;

public class PrepareForCeibo extends TxnSupport implements AbortParticipant {

@Override
public void setConfiguration(Configuration cfg) throws ConfigurationException {
super.setConfiguration(cfg);
}

@Override
protected int doPrepare(long id, Context ctx) throws Exception {
//
return PREPARED | NO_JOIN;
}

@Override
protected int doPrepareForAbort(long id, Context ctx) throws Exception {
String rejectionReason=ctx.get(REJECTION_REASON);
if(!rejectionReason.equals(INVALID_CRYPTOGRAM)){
ISOMsg isoMsg=ctx.get("REQUEST");
String responseCode=mapRejectionReasonToResponseCodeError(rejectionReason);
ISOMsg isoMsgResponseToTerminalDiscoverError= (ISOMsg) isoMsg.clone();
isoMsgResponseToTerminalDiscoverError.setResponseMTI();
isoMsgResponseToTerminalDiscoverError.set(39,responseCode);
ctx.put(RESPONSE_INTERNAL_DISCOVER_ERROR,isoMsgResponseToTerminalDiscoverError);
}
return PREPARED | NO_JOIN;
}

}

```### **QueryHostDGW:**

 QueryHostDGW:

**La clase `QueryHostDGW` es un participante en una transacción de jPOS que implementa las interfaces `TransactionParticipant`, `ISOResponseListener` y `Configurable`. Esta clase es responsable de enviar una solicitud ISO a un host y manejar la respuesta.**

...

- Archivo

```java
package com.app.gateway.participants;

import org.jpos.core.Configurable;
import org.jpos.core.Configuration;
import org.jpos.core.ConfigurationException;
import org.jpos.iso.*;
import org.jpos.rc.CMF;
import org.jpos.rc.Result;
import org.jpos.transaction.Context;
import org.jpos.transaction.ContextConstants;
import org.jpos.transaction.TransactionParticipant;
import org.jpos.transaction.participant.QueryHost;
import org.jpos.util.Caller;
import org.jpos.util.Chronometer;
import org.jpos.util.NameRegistrar;

import java.io.Serializable;

import static com.app.gateway.Constants.*;

public class QueryHostDGW implements TransactionParticipant, ISOResponseListener, Configurable {
public static final String TIMEOUT_NAME = "QUERYHOST_TIMEOUT";
private static final long DEFAULT_TIMEOUT = 30000L;
private static final long DEFAULT_WAIT_TIMEOUT = 1000L;
private long timeout;
private long waitTimeout;
private String timeoutName = "QUERYHOST_TIMEOUT";
private String requestName;
private String responseName;
private String destination;
private boolean continuations;
private Configuration cfg;
private boolean ignoreUnreachable;
private boolean checkConnected = true;

public QueryHostDGW() {
}

public int prepare(long id, Serializable ser) {
Context ctx = (Context)ser;
Result result = ctx.getResult();
String ds = ctx.getString(this.destination);
if (ds == null) {
return result.fail(CMF.MISCONFIGURED_ENDPOINT, Caller.info(), "'%s' not present in Context", new Object[]{this.destination}).FAIL();
} else {
String muxName = this.cfg.get("mux." + ds, "mux." + ds);
MUX mux = (MUX)NameRegistrar.getIfExists(muxName);
if (mux == null) {
return result.fail(CMF.MISCONFIGURED_ENDPOINT, Caller.info(), "MUX '%s' not found", new Object[]{muxName}).FAIL();
} else {
ISOMsg m = (ISOMsg)ctx.get(this.requestName);
if (m == null) {
return result.fail(CMF.INVALID_REQUEST, Caller.info(), "'%s' is null", new Object[]{this.requestName}).FAIL();
} else {
Chronometer chronometer = new Chronometer();
if (this.isConnected(mux)) {
long t = Math.max(this.resolveTimeout(ctx) - chronometer.elapsed(), 1000L);

try {
if (this.continuations) {
mux.request(m, t, this, ctx);
return 197;
}

ISOMsg resp = mux.request(m, t);
if (resp != null) {
ctx.put(this.responseName, resp);
return 193;
}

if (!this.ignoreUnreachable) {
return result.fail(CMF.HOST_UNREACHABLE, Caller.info(), "'%s' does not respond", new Object[]{muxName}).FAIL();
}

ctx.log(String.format("MUX '%s' no response", muxName));
} catch (ISOException var14) {
ISOException e = var14;
return result.fail(CMF.SYSTEM_ERROR, Caller.info(), e.getMessage(), new Object[0]).FAIL();
}
} else {
ctx.put(REJECTION_REASON,CABAL_AR_NOT_CONNECTED);
if (!this.ignoreUnreachable) {
return result.fail(CMF.HOST_UNREACHABLE, Caller.info(), "'%s' is not connected", new Object[]{muxName}).FAIL();
}
ctx.log(String.format("MUX '%s' not connected", muxName));
}

return 193;
}
}
}
}

public void responseReceived(ISOMsg resp, Object handBack) {
Context ctx = (Context)handBack;
ctx.put(this.responseName, resp);
ctx.resume();
}

public void expired(Object handBack) {
Context ctx = (Context)handBack;
String ds = ctx.getString(this.destination);
String muxName = this.cfg.get("mux." + ds, "mux." + ds);
ctx.getResult().fail(CMF.HOST_UNREACHABLE, Caller.info(), "'%s' does not respond", new Object[]{muxName}).FAIL();
ctx.getPausedTransaction().forceAbort();
ctx.put(REJECTION_REASON, CONNECTION_TIMEOUT_TO_CABAL_AR);
ctx.resume();
}

public void setConfiguration(Configuration cfg) throws ConfigurationException {
this.cfg = cfg;
this.timeout = cfg.getLong("timeout", 18000L);
this.waitTimeout = cfg.getLong("wait-timeout", 1000L);
this.timeoutName = cfg.get("timeout-name", this.timeoutName);
this.requestName = cfg.get("request", ContextConstants.REQUEST.toString());
this.responseName = cfg.get("response", ContextConstants.RESPONSE.toString());
this.destination = cfg.get("destination", ContextConstants.DESTINATION.toString());
this.continuations = cfg.getBoolean("continuations", true);
this.ignoreUnreachable = cfg.getBoolean("ignore-host-unreachable", false);
this.checkConnected = cfg.getBoolean("check-connected", this.checkConnected);
}

protected long resolveTimeout(Context ctx) {
Object o = ctx.get(this.timeoutName);
if (o == null) {
return this.timeout;
} else {
return o instanceof Number ? ((Number)o).longValue() : Long.parseLong(o.toString());
}
}

protected boolean isConnected(MUX mux) {
if (this.checkConnected && !mux.isConnected()) {
long timeout = System.currentTimeMillis() + this.waitTimeout;

while(System.currentTimeMillis() < timeout) {
if (mux.isConnected()) {
return true;
}
ISOUtil.sleep(500L);
}

return false;
} else {
return true;
}
}

}

```#

Archivos java (UTILS)

...

 En la ruta src/main/java/com/app/gateway/utils/Utilities.java vamos a encontrar el utilities

...

Utilities:

...

La clase `Utilities` proporciona métodos utilitarios para manejar mensajes ISO y mapear razones de rechazo a códigos de respuesta. A continuación se detallan los métodos y su uso:

...

1. **`convertISOMsgToString(ISOMsg isoMsg)`**:
- **Descripción**: Convierte un objeto `ISOMsg` a su representación en cadena.
- **Uso**: Se utiliza para serializar un mensaje ISO a una cadena ASCII.
2. **`convertStringToISOMsg(String isoMsgString, String packagerType)`**:
- **Descripción**: Convierte una cadena a un objeto `ISOMsg` utilizando un tipo de empaquetador específico.
- **Uso**: Se utiliza para deserializar una cadena ASCII a un mensaje ISO.
3. **`mapRejectionReasonToResponseCodeError(String rejectionReason)`**:
- **Descripción**: Mapea una razón de rechazo a un código de respuesta de error.
- **Uso**: Se utiliza para obtener el código de respuesta correspondiente a una razón de rechazo específica.
- Archivo

```java
package com.app.gateway.utils;

import org.jpos.iso.ISOMsg;
import org.jpos.iso.packager.GenericPackager;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

import static com.app.gateway.Constants.*;

public class Utilities {

/**
* Convert the ISOMsg to a string
*/
public static String convertISOMsgToString(ISOMsg isoMsg) throws Exception {
if (isoMsg == null) {
throw new Exception("IsoMsg cannot be null");
}
try {
byte[] isoMsgBytes;
isoMsgBytes = isoMsg.pack();
return new String(isoMsgBytes, StandardCharsets.US_ASCII);
} catch (Exception e) {
throw new Exception("Error converting from ISOMsg to String");
}
}

/**
* Convert string to an ISOMsg
*/
public static ISOMsg convertStringToISOMsg(String isoMsgString, String packagerType) throws Exception {
if (isoMsgString == null) {
throw new Exception("IsoMsg string cannot be null");
}
if (packagerType == null) {
throw new Exception("Packager type cannot be null");
}
try {
byte[] isoMsgBytes = isoMsgString.getBytes(StandardCharsets.US_ASCII);
ISOMsg isoMsg = new ISOMsg();

switch (packagerType) {
case "ISO93ASCII":
isoMsg.setPackager(new GenericPackager("cfg/iso93ascii.xml"));
break;
case "ISO87Ascii":
isoMsg.setPackager(new GenericPackager("cfg/iso87ascii.xml"));
break;
default:
throw new Exception("Unsupported packager type: " + packagerType);
}

isoMsg.unpack(isoMsgBytes);
return isoMsg;
} catch (Exception e) {
throw new Exception("Error converting from string to IsoMsg", e);
}
}

public static String mapRejectionReasonToResponseCodeError(String rejectionReason) {

Map<String,String> map = new HashMap<>();
map.put(CABAL_AR_NOT_CONNECTED,"182");
map.put(INVALID_ENTRY_MODE,"100");
map.put(INVALID_FORMAT,"904");
map.put(CONNECTION_TIMEOUT_TO_CABAL_AR,"183");
map.put(ERROR_CONNECTION_TO_CEIBO,"194");
map.put(ERROR_CONNECTION_TO_DB,"204");
return map.get(rejectionReason);
}

}

```# Archivos

 Archivos java (CONSTANTS & LISTEINER)

...

En la ruta src/main/java/com/app/gateway/Constants.java vamos a encontrar las constantes y el listeiner

...

 Constants:

**Detalle de las Constantes:**

1. **`ECHO_MTI`**:
- **Descripción**: Código de tipo de mensaje ISO para mensajes de eco.
- **Uso**: Utilizado en `IncomingListener` para identificar y responder a mensajes de eco.
2. **`REJECTION_REASON`**:
- **Descripción**: Clave para almacenar la razón de rechazo en el contexto.
- **Uso**: Utilizado en varios participantes para manejar razones de rechazo.
3. **`INVALID_CRYPTOGRAM`**:
- **Descripción**: Razón de rechazo para criptograma inválido.
- **Uso**: Utilizado en `PrepareForCeibo` para verificar razones de rechazo.
4. **`NOT_AUTHORIZED_BY_CEIBO`**:
- **Descripción**: Razón de rechazo para no autorizado por Ceibo.
- **Uso**: Utilizado en participantes relacionados con la autorización de Ceibo.
5. **`CONNECTION_TIMEOUT_TO_CABAL_AR`**:
- **Descripción**: Razón de rechazo para tiempo de espera de conexión a Cabal AR.
- **Uso**: Utilizado en `QueryHostDGW` y `Utilities` para manejar tiempos de espera.
6. **`ERROR_CONNECTION_TO_CEIBO`**:
- **Descripción**: Razón de rechazo para error de conexión a Ceibo.
- **Uso**: Utilizado en `Utilities` para mapear razones de rechazo.
7. **`ERROR_CONNECTION_TO_DB`**:
- **Descripción**: Razón de rechazo para error de conexión a la base de datos.
- **Uso**: Utilizado en `Utilities` para mapear razones de rechazo.
8. **`CABAL_AR_NOT_CONNECTED`**:
- **Descripción**: Razón de rechazo para Cabal AR no conectado.
- **Uso**: Utilizado en `QueryHostDGW` y `Utilities` para manejar conexiones.
9. **`INVALID_ENTRY_MODE`**:
- **Descripción**: Razón de rechazo para modo de entrada inválido.
- **Uso**: Utilizado en `Utilities` para mapear razones de rechazo.
10. **`INVALID_FORMAT`**:
- **Descripción**: Razón de rechazo para formato inválido.
- **Uso**: Utilizado en `Utilities` para mapear razones de rechazo.
11. **`RESPONSE_INTERNAL_DISCOVER_ERROR`**:
- **Descripción**: Clave para almacenar la respuesta de error interno de Discover en el contexto.
- **Uso**: Utilizado en `PrepareForCeibo` para manejar respuestas de error.
12. **`RESPONSE_FROM_CABAL_AR`**:
- **Descripción**: Clave para almacenar la respuesta de Cabal AR en el contexto.
- **Uso**: Utilizado en `QueryHostDGW` para manejar respuestas.
13. **`MESSAGE_INCOMING_BANCARD`**:
- **Descripción**: Clave para almacenar mensajes entrantes de Bancard en el contexto.
- **Uso**: Utilizado en participantes relacionados con Bancard.
14. **`TRANSACTION_TYPE`**:
- **Descripción**: Clave para almacenar el tipo de transacción en el contexto.
- **Uso**: Utilizado en varios participantes para manejar tipos de transacción.
15. **`CHIP`**:
- **Descripción**: Valor para identificar transacciones con chip.
- **Uso**: Utilizado en `EntryModeFilter` y `EntryModeSelector`.
16. **`BANDA`**:
- **Descripción**: Valor para identificar transacciones con banda magnética.
- **Uso**: Utilizado en `EntryModeFilter` y `EntryModeSelector`.
- Archivo

```java
package com.app.gateway;

public interface Constants {

public static final String ECHO_MTI="1804";
public static final String REJECTION_REASON="REJECTION_REASON";
public static final String INVALID_CRYPTOGRAM="INVALID_CRYPTOGRAM";
public static final String NOT_AUTHORIZED_BY_CEIBO="NOT_AUTHORIZED_BY_CEIBO";
public static final String CONNECTION_TIMEOUT_TO_CABAL_AR="CONNECTION_TIMEOUT_TO_CABAL_AR";
public static final String ERROR_CONNECTION_TO_CEIBO="ERROR_CONNECTION_TO_CEIBO";
public static final String ERROR_CONNECTION_TO_DB="ERROR_CONNECTION_TO_DB";
public static final String CABAL_AR_NOT_CONNECTED ="CABAL_AR_NOT_CONNECTED";
public static final String INVALID_ENTRY_MODE="INVALID_ENTRY_MODE";
public static final String INVALID_FORMAT="INVALID_FORMAT";
public static final String RESPONSE_INTERNAL_DISCOVER_ERROR="RESPONSE_INTERNAL_DISCOVER_ERROR";
public static final String RESPONSE_FROM_CABAL_AR="RESPONSE_FROM_CABAL_AR";
public static final String MESSAGE_INCOMING_BANCARD="MESSAGE_INCOMING_BANCARD";
public static final String TRANSACTION_TYPE="TRANSACTION_TYPE";
public static final String CHIP="CHIP";
public static final String BANDA="BANDA";
}

```### **IncomingListener:**

 IncomingListener:

La clase `IncomingListener` extiende la clase `org.jpos.iso.IncomingListener` y se utiliza para procesar mensajes ISO entrantes. A continuación se detalla su funcionamiento:

...

- **Flujos Impactados**: Flujos 1, 2 y 3.
- **Uso**: Este listener es utilizado para manejar mensajes de eco en todos los flujos, asegurando que los mensajes de eco sean respondidos adecuadamente.
- Archivo

```java
package com.app.gateway;

import org.jpos.iso.ISOException;
import org.jpos.iso.ISOMsg;
import org.jpos.iso.ISOSource;
import org.jpos.space.LocalSpace;
import org.jpos.space.SpaceSource;
import org.jpos.transaction.Context;

import java.io.IOException;
import java.util.Date;

import static com.app.gateway.Constants.ECHO_MTI;

public class IncomingListener extends org.jpos.iso.IncomingListener {

@Override
public boolean process(ISOSource src, ISOMsg m) {
try {
if (m.getMTI().equals(ECHO_MTI)) {
m.setResponseMTI();
src.send(m);
return true;
}
} catch (ISOException | IOException e) {
throw new RuntimeException(e);
}
return super.process(src, m);
}
}

```# Archivos

 Archivos resources

**En esta carpeta ubicada en src/main/resource y contiene ehcache.xml y hibernate.cfg.xml, son archivos de configuración relacionados con la base de datos y cómo la aplicación interactúa con ella (configuración para las bibliotecas Ehcache y Hibernate).**

...

 Ehcache.xml :

**El archivo ehcache.xml es un archivo de configuración para Ehcache, que es una biblioteca de caché en memoria para Java. Ehcache puede mejorar el rendimiento al reducir la carga en la base de datos y la red al almacenar los datos en la memoria del servidor.**

...

- `<ehcache>`: Raíz del documento de configuración de EhCache.
- `<defaultCache>`: Define la configuración predeterminada para las cachés que no tienen una configuración específica.
- `maxElementsInMemory="0"`: Número máximo de elementos en memoria. Un valor de 0 significa sin límite.
- `eternal="false"`: Si es verdadero, los elementos en caché nunca expiran. Falso significa que pueden expirar.
- `timeToIdleSeconds="1200"`: Tiempo máximo, en segundos, que un elemento puede estar inactivo en caché antes de ser considerado expirable.
- `timeToLiveSeconds="1200"`: Tiempo máximo, en segundos, que un elemento puede vivir en caché, independientemente del tiempo de inactividad.
- Archivo | ehcache.xml

```xml
<ehcache>
<defaultCache
maxElementsInMemory="0"
eternal="false"
timeToIdleSeconds="1200"
timeToLiveSeconds="1200"
/>
</ehcache>
```### **

hibernate.cfg.xml :

...

El archivo hibernate.cfg.xml es un archivo de configuración para Hibernate, que es un marco de trabajo de mapeo objeto-relacional (ORM) para Java. Hibernate permite mapear las clases Java a las tablas de la base de datos y viceversa.

...

Cada una de estas configuraciones juega un papel crucial en el rendimiento, seguridad y correcto funcionamiento de la aplicación, permitiendo una gestión eficiente de la persistencia de datos y el caché.

---# Archivos

 Archivos Gradle

...

 gradle.properties :

**Se realizaron modificaciones en el archivo `build.gradle` para gestionar dependencias, plugins y tareas de Gradle. Se agregaron dependencias necesarias para el funcionamiento del sistema, incluyendo jPOS, bibliotecas para procesamiento JSON y soporte de base de datos, entre otras.**

...

1. **Plugins**: Los plugins proporcionan funcionalidades adicionales a Gradle. En este caso, se están utilizando los plugins 'java', 'maven-publish', 'idea' y 'eclipse'. El plugin 'java' añade funcionalidades para compilar y probar proyectos Java. 'maven-publish' se utiliza para publicar artefactos en un repositorio Maven. 'idea' y 'eclipse' se utilizan para generar archivos de proyecto para los IDEs IntelliJ IDEA y Eclipse, respectivamente.
2. **Propiedades del proyecto**: Las propiedades `group`, `version`, `sourceCompatibility` y `targetCompatibility` definen la identidad y la compatibilidad del proyecto. `group` y `version` se utilizan para identificar el proyecto en un repositorio Maven. `sourceCompatibility` y `targetCompatibility` definen la versión mínima de Java requerida para construir y ejecutar el proyecto, respectivamente. En este caso, ambas están establecidas en Java 11.
3. **Repositorios**: En este caso, se están utilizando Maven Central, un repositorio personalizado en '[https://jpos.org/maven](https://jpos.org/maven)' y el repositorio local de Maven.
4. **Dependencias**: Las dependencias utilizadas son:
- `org.jpos:jpos:2.1.8-SNAPSHOT`: jPOS
- `org.jpos.ee:jposee-server-simulator:2.2.10-SNAPSHOT`: simulador de servidor para jPOS.
- `com.fasterxml.jackson.core:jackson-databind:2.17.0-rc1`: biblioteca para procesar datos JSON en Java.
- `org.jpos.ee:jposee-txn:2.2.8` y `org.jpos.ee:jposee-core:2.2.8`: bibliotecas de jPOS para el manejo de transacciones y funcionalidades básicas, respectivamente.
- `org.hibernate:hibernate-ehcache:5.5.4.Final`: Hibernate, Ehcache es una biblioteca de caché para Hibernate.
- `org.jpos.ee:jposee-dbsupport:2.2.8`: biblioteca de jPOS para el soporte de base de datos.
- `com.oracle.database.jdbc:ojdbc10:19.22.0.0`: controlador JDBC de Oracle para conectar con bases de datos Oracle.

5. **Aplicación de otro archivo Gradle**: La línea `apply from: 'jpos-app.gradle'` aplica la configuración de otro archivo Gradle llamado `jpos-app.gradle`. Esto permite separar la configuración de Gradle en varios archivos para una mejor organización.
- Archivo | gradle.properties

```xml
apply plugin: 'java'
apply plugin: 'maven-publish'
apply plugin: 'idea'
apply plugin: 'eclipse'

group = 'org.jpos.template'
version = '2.1.8-SNAPSHOT'
sourceCompatibility = 11
targetCompatibility = 11

repositories {
mavenCentral()
maven { url 'https://jpos.org/maven' }
mavenLocal()
}

dependencies {
implementation 'org.jpos:jpos:2.1.8-SNAPSHOT'
implementation 'org.jpos.ee:jposee-server-simulator:2.2.10-SNAPSHOT'
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.0-rc1'
implementation 'org.jpos.ee:jposee-txn:2.2.8'
implementation 'org.jpos.ee:jposee-core:2.2.8'
implementation 'org.hibernate:hibernate-ehcache:5.5.4.Final'
implementation 'org.jpos.ee:jposee-dbsupport:2.2.8'
implementation 'com.oracle.database.jdbc:ojdbc10:19.22.0.0'
}

apply from: 'jpos-app.gradle'
```

---### **

jpos-app.gradle :

...

**Se creo la tarea `debug` en el archivo `jpos-app.gradle` es una tarea de Gradle que se utiliza para iniciar la aplicación en modo de depuración.**

...

En resumen, el archivo `jpos-app.gradle` se utiliza para definir la configuración y las tareas específicas de la aplicación jPOS, incluyendo la compilación, la instalación, la ejecución y la depuración de la aplicación.

</aside>

---#

Archivos .properties

...

 local.properties , develop.properties y production.properties :

Los archivos develop.properties, production.properties y local.properties son archivos de configuración que se utilizan para definir las propiedades específicas del entorno para los entornos de desarrollo, producción y local, respectivamente. Estos archivos tienen información como la ip y puerto de un servidor, las credenciales de la base de datos, etc., que son específicas para cada entorno.

- Archivo | local.properties

```xml
# Cabal AR
cabal.ar.host=localhost
cabal.ar.port=7942

# DB ORACLE
dbhost=10.5.2.36
dbport=1521
dbusername=creditopy
dbpassword=desarrollo
SID=/olpy
dbname=CREDITOPY

# Env
target.name=local
```

- Archivo | develop.properties

```xml
# Cabal AR
cabal.ar.host=172.27.6.55
cabal.ar.port=9015

# DB ORACLE
dbhost=10.5.2.36
dbport=1521
dbusername=creditopy
dbpassword=desarrollo
SID=/olpy
dbname=CREDITOPY

# Env
target.name=develop
```

- Archivo | production.properties

```xml
# Env
target.name=production
```###

gradle.properties :

El archivo gradle.properties es un archivo de configuración de Gradle que se utiliza para configurar las propiedades del proyecto de Gradle. Puede contener propiedades que se aplican a todo el proyecto de Gradle. En el contexto proporcionado, este archivo se utiliza para definir el entorno objetivo (target) para el cual se está construyendo el proyecto.

...

- Archivo | gradle.properties

```xml
# ENVIRONMENT CONFIGURATION (local, develop, production)
target=develop
```

---# Docker

 Docker

...

 Dockerfile :

**En este `Dockerfile` definimos una imagen de Docker que se construye a partir de la imagen base `openjdk:11-jdk-slim`, que es una imagen de Docker que contiene el JDK 11 en una versión reducida de Debian.**

...