SPRING BOOT
SERVER
USING JPOS
First, what is Jpos? Jpos is a framework for creating ISO8583-based applications. Then what is iso8583? Iso8583 is an internasional standart for financial transaction card originated messaging.
So here we wan't to make ISO8583 service using Jpos.
Prerequired
- check_circle Girlfriend (not found)
- check_circle Most beautifull IDE (IntelIj Idea)
- check_circle Java 17
- check_circle Maven
Good to be prepared
- check_circle Good mood
- check_circle Snacks
- check_circle Cup of tea
Initiate Project
Go to Spring Initialzr Spring Initializr then create new project.

Dependencies.
Add this dependency: jpos:2.1.7
Write Code
Create folder deploy, this folder contains the config of this service.
Add folder log, to save log file.
Add folder packager, to save xml field data element.
Add these config xml inside folder deploy
file 00_logger.xml
<?xml version="1.0" encoding="UTF-8"?>
<logger name="Q2" class="org.jpos.q2.qbean.LoggerAdaptor">
<log-listener class="org.jpos.util.SimpleLogListener" />
<log-listener class="org.jpos.util.DailyLogListener">
<property name="window" value="86400" />
<property name="prefix" value="log/server" />
<property name="suffix" value=".log"/>
<property name="date-format" value="-yyyy-MM-dd-HH"/>
<property name="compression-format" value="gzip"/>
</log-listener>
</logger>file 11_server.xml
<server class="org.jpos.q2.iso.QServer" logger="Q2" name="iso-channel">
<attr name="port" type="java.lang.Integer">10001</attr>
<attr name="maxSessions" type="java.lang.Integer">500</attr>
<channel class="org.jpos.iso.channel.ASCIIChannel" packager="org.jpos.iso.packager.GenericPackager"
logger="Q2" name="server-channel">
<property name="packager-config" value="packager/iso87ascii.xml" />
</channel>
<request-listener class="id.co.pandz.iso_server.service.RequestListener" logger="Q2">
</request-listener>
</server>file 99_sysmon.xml
<sysmon logger="Q2">
<attr name="sleepTime" type="java.lang.Long">3600000</attr>
<attr name="detailRequired" type="java.lang.Boolean">true</attr>
</sysmon>Add xml data element inside folder packager
iso87ascii.xml, download it here iso87ascii.xml or you can create your own.
Create folder service di src/main/java/id/co/pandz/iso_server
Create class RequestListener inside folder service, here we write the logic when server take the request.
public class RequestListener implements ISORequestListener {
@Override
public boolean process(ISOSource isoSrc, ISOMsg isoMsg) {
try {
System.out.println("ISO8583 Message received on host ["+ ((BaseChannel) isoSrc).getSocket().getInetAddress().getHostAddress() + "]");
receiveMessage(isoSrc, isoMsg);
} catch (Exception ex) {
ex.printStackTrace();
}
return true;
}
private void receiveMessage(ISOSource isoSrc, ISOMsg isoMsg) throws ISOException, IOException {
ISOMsg reply = (ISOMsg) isoMsg.clone();
GenericPackager packager = new GenericPackager("./packager/iso87ascii.xml");
isoMsg.setPackager(packager);
reply.setMTI("0210");
reply.set(39, "00");
isoSrc.send(reply);
}
}Create class SendRequest inside folder service, this class is used for simulate send request message ISO to the server.
public class SendRequest {
public static void main(String[] args) throws IOException, ISOException {
GenericPackager packager = new GenericPackager("./packager/iso87ascii.xml");
ISOMsg isoMsg = new ISOMsg();
isoMsg.setPackager(packager);
isoMsg.setMTI("0200");
isoMsg.set(2, "0000000000628567782");
isoMsg.set(3, "301000");
isoMsg.set(4, "000025000000");
isoMsg.set(7, "0726025012");
isoMsg.set(11, "123456");
isoMsg.set(12, "025012");
isoMsg.set(13, "0726");
isoMsg.set(14, "0726");
isoMsg.set(15, "0726");
isoMsg.set(30, "000650000");
isoMsg.set(32, "3020");
isoMsg.set(37, "ESB1000009");
isoMsg.set(41, "1002");
isoMsg.set(42, "123002");
isoMsg.set(43, "BTN ID JKT 01211");
isoMsg.set(48, "123123123123 TRX12309787234");
isoMsg.set(49, "360");
isoMsg.set(56, "08555555555");
isoMsg.set(62, "001002003");
isoMsg.set(102, "0000000008555555555");
ASCIIChannel channel = new ASCIIChannel("127.0.0.1", 10001, packager);
logISOMsg(isoMsg);
byte[] rawMessage = isoMsg.pack();
System.out.println(ISOUtil.hexdump(rawMessage));
channel.connect();
channel.send(isoMsg);
ISOMsg response = channel.receive();
System.out.println("**************** Response *********************");
logISOMsg(response);
}
private static void logISOMsg(ISOMsg msg) {
System.out.println("----ISO MESSAGE-----");
try {
System.out.println(" MTI : " + msg.getMTI());
for (int i = 1; i <= msg.getMaxField(); i++) {
if (msg.hasField(i)) {
System.out.println(" Field-" + i + " : "
+ msg.getString(i));
}
}
} catch (ISOException e) {
e.printStackTrace();
} finally {
System.out.println("--------------------");
}
}
}Next, in the main class IsoServerApplication, start server using Q2.
@SpringBootApplication
public class IsoServerApplication {
public static void main(String[] args) {
Q2 appServer = new Q2(new String[]{"deploy", "-r"});
appServer.start();
}
}Run server, IsoServerApplication
<log realm="iso-channel.server" at="2024-08-07T16:02:36.660636">
<iso-server>
listening on port 10001
</iso-server>
</log>Send request, run class SendRequest
<log realm="channel/127.0.0.1:61678" at="2024-08-07T16:03:37.415909">
<send>
<isomsg direction="outgoing">
<field id="0" value="0210"/>
<field id="2" value="0000000000628567782"/>
<field id="3" value="301000"/>
<field id="4" value="000025000000"/>
<field id="7" value="0726025012"/>
<field id="11" value="123456"/>
<field id="12" value="025012"/>
<field id="13" value="0726"/>
<field id="14" value="0726"/>
<field id="15" value="0726"/>
<field id="30" value="000650000"/>
<field id="32" value="3020"/>
<field id="37" value="ESB1000009 "/>
<field id="39" value="00"/>
<field id="41" value="1002 "/>
<field id="42" value="123002 "/>
<field id="43" value="BTN ID JKT 01211"/>
<field id="49" value="360"/>
<field id="56" value="08555555555"/>
<field id="62" value="001002003"/>
<field id="102" value="0000000008555555555"/>
</isomsg>
</send>
</log>Request goes to server and server returns success response.
Then what if we want to get data from API rest?
Add more dependencies ini: spring-boot-starter-web and lombok
Add folder dto inside folder iso_server. Create class ParamReq iniside folder dto
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ParamNameReq {
private String paramName;
}Enhance code at RequestListener, we will retrieve rest service to get value then use it to the response.
String mti = reply.getMTI();
if ("0200".equalsIgnoreCase(mti)) {
RestTemplate httpClient = new RestTemplate();
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.set(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
ParamNameReq request = ParamNameReq.builder()
.paramName("NEW_SIM:bal-inq:httpCode")
.build();
HttpEntity<Object> entity = new HttpEntity<>(request, httpHeaders);
String result = httpClient.postForObject("http://localhost:8081/simulator/parameter/value", entity, String.class);
System.out.println("Response param from API: " + result); // 200
reply.set(48, result);
}Let's test it!
<log realm="channel/127.0.0.1:61678" at="2024-08-07T16:03:37.415909">
<send>
<isomsg direction="outgoing">
<field id="0" value="0210"/>
<field id="2" value="0000000000628567782"/>
<field id="3" value="301000"/>
<field id="4" value="000025000000"/>
<field id="7" value="0726025012"/>
<field id="11" value="123456"/>
<field id="12" value="025012"/>
<field id="13" value="0726"/>
<field id="14" value="0726"/>
<field id="15" value="0726"/>
<field id="30" value="000650000"/>
<field id="32" value="3020"/>
<field id="37" value="ESB1000009 "/>
<field id="39" value="00"/>
<field id="41" value="1002 "/>
<field id="42" value="123002 "/>
<field id="43" value="BTN ID JKT 01211"/>
<field id="48" value="200"/>
<field id="49" value="360"/>
<field id="56" value="08555555555"/>
<field id="62" value="001002003"/>
<field id="102" value="0000000008555555555"/>
</isomsg>
</send>
</log>Nice!
How about getting the data from the database?
Okee… First add the following dependencies: jakarta.persistence-api:3.0.0, hibernate-core:6.1.7.Final, hibernate-hikaricp:6.1.7.Final, oracle ojdbc11:23.2.0.0.
Create folder dao at src/main/java/id/co/pandz/iso_server. Then create class ParameterDao.
@Setter
@Getter
@Entity
@Builder
@AllArgsConstructor
@NoArgsConstructor
@Table(name = "PARAMETER", schema = "CONFIG")
public class ParameterDao {
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name = "ID")
private Long id;
@Column(name = "NAME")
private String name;
@Column(name = "VALUE")
private String value;
}Create config connection to database. Add file hibernate.cfg.xml at src/main/resources
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.dialect">org.hibernate.dialect.OracleDialect</property>
<property name="hibernate.connection.driver_class">oracle.jdbc.OracleDriver</property>
<property name="hibernate.connection.url">jdbc:oracle:thin:@//localhost:1521/dbdev</property>
<property name="hibernate.connection.username">admin</property>
<property name="hibernate.connection.password">admin</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<property name="hibernate.show_sql">true</property>
<property name="hibernate.format_sql">false</property>
<property name="hibernate.hikari.minimumIdle">10</property>
<property name="hibernate.hikari.maximumPoolSize">50</property>
<property name="hibernate.hikari.idleTimeout">30000</property>
<property name="hibernate.hikari.connectionTimeout">20000</property>
<property name="hibernate.hikari.maxLifetime">1800000</property>
<mapping class="id.co.pandz.iso_server.dao.ParameterDao"/>
</session-factory>
</hibernate-configuration>Create folder repository at src/main/java/id/co/pandz/iso_server. Then create interface GenericRepository.
public interface GenericRepository<T, ID> {
T save(T entity);
T findById(ID id);
List<T> findAll();
}Create interface ParameterRepository
public interface ParameterRepository extends GenericRepository<ParameterDao, Long> {
ParameterDao findByName(String paramName);
}
Next create folder impl at src/main/java/id/co/pandz/iso_server/repository.
Create class implementation ParameterRepositoryImpl.
public class ParameterRepositoryImpl implements ParameterRepository {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction transaction = null;
@Override
public ParameterDao save(ParameterDao entity) {
session.getTransaction().begin();
session.persist(entity);
session.getTransaction().commit();
session.close();
return entity;
}
@Override
public ParameterDao findById(Long id) {
ParameterDao entity = session.find(ParameterDao.class, id);
session.close();
return entity;
}
@Override
public List<ParameterDao> findAll() {
List<ParameterDao> entities = session.createQuery("SELECT lp FROM ParameterDao lp ", ParameterDao.class).getResultList();
session.close();
return entities;
}
@Override
public ParameterDao findByName(String paramName) {
transaction = session.beginTransaction();
ParameterDao result = null;
try {
// This query should refer to your entity because hibernate read your code
result = session.createQuery("FROM ParameterDao lp WHERE lp.name = :paramName", ParameterDao.class)
.setParameter("paramName", paramName)
.getSingleResult();
} catch (Exception e) {
if (transaction != null) {
transaction.rollback();
}
e.printStackTrace();
} finally {
if (session != null) {
session.close();
}
}
return result;
}
}
We will create class connection for the database.
Create folder util at src/main/java/id/co/pandz/iso_server. Then create class HibernateUtil.
public class HibernateUtil {
private static final SessionFactory sessionFactory = buildSessionFactory();
private static SessionFactory buildSessionFactory() {
try {
// Create the SessionFactory from hibernate.cfg.xml
return new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Initial SessionFactory creation failed." + ex);
throw new ExceptionInInitializerError(ex);
}
}
public static SessionFactory getSessionFactory() {
return sessionFactory;
}
public static void shutdown() {
// Close caches and connection pools
getSessionFactory().close();
}
}So now we can use it to connect database. Enhance class RequestListerner.
ParamNameReq request = ParamNameReq.builder()
.paramName("NEW_SIM:bal-inq:httpCode")
.build();
HttpEntity<Object> entity = new HttpEntity<>(request, httpHeaders);
String result = httpClient.postForObject("http://localhost:8081/simulator/parameter/value", entity, String.class);
System.out.println("Response param from API: " + result); // 200
ParameterRepository repository = new ParameterRepositoryImpl();
ParameterDao responseCode = repository.findByName(NEW_SIM_REFUND_CASECODE_CACHE);
System.out.println("Response param from DB: " + responseCode.getValue());
reply.set(48, result + responseCode.getValue());Test run SendRequest again...
But wait…
After re run service, then SendRequest, it take time too long to get response. This is happen because Hibernate just starting create connection to database when service receive request.
So, let's make Hibernate start initiate connection when server startup, So when a request comes in, you can get data from the database more quickly.
Create folder qbean at src/main/java/id/co/pandz/iso_server. Create class HibernateQBean.
public class HibernateQBean extends QBeanSupport {
@Override
protected void startService() {
// Initialize Hibernate SessionFactory
HibernateUtil.getSessionFactory();
log.info("Hibernate SessionFactory initialized");
}
}Then add more config so that qbean will be loaded at server startup. Add 07_deploy.xml at folder deploy.
<qbean name="hibernate-init" class="id.co.pandz.iso_server.qbean.HibernateQBean" logger="Q2">
<property name="enabled" value="true"/>
</qbean>Re run server then retest.
Yup... give me a clap if you succeed!
You can find complete source code here: https://github.com/pandz3rd/jpos-iso8583-server