/**
* Copyright (C) 2011 ArtiVisi Intermedia <info@artivisi.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.artivisi.biller.simulator.gateway.pln;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.apache.commons.lang.StringUtils;
import org.joda.time.format.DateTimeFormat;
import org.jpos.iso.BaseChannel;
import org.jpos.iso.ISOException;
import org.jpos.iso.ISOFilter.VetoException;
import org.jpos.iso.ISOMsg;
import org.jpos.iso.ISORequestListener;
import org.jpos.iso.ISOServer;
import org.jpos.iso.ISOSource;
import org.jpos.util.Log4JListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import com.artivisi.biller.simulator.entity.Pelanggan;
import com.artivisi.biller.simulator.entity.PembayaranPascabayar;
import com.artivisi.biller.simulator.entity.TagihanPascabayar;
import com.artivisi.biller.simulator.gateway.pln.constants.MTIConstants;
import com.artivisi.biller.simulator.gateway.pln.constants.ResponseCode;
import com.artivisi.biller.simulator.gateway.pln.entity.InquiryPostpaidResponse;
import com.artivisi.biller.simulator.gateway.pln.entity.InquiryPostpaidResponseDetail;
import com.artivisi.biller.simulator.gateway.pln.jpos.PlnChannel;
import com.artivisi.biller.simulator.gateway.pln.jpos.PlnPackager;
import com.artivisi.biller.simulator.gateway.pln.service.PlnService;
import com.artivisi.biller.simulator.service.BillerSimulatorService;
import com.artivisi.biller.simulator.service.PlnSimulatorService;
public class PlnGateway implements ISORequestListener {
private static final Logger logger = LoggerFactory.getLogger(PlnGateway.class);
@Autowired private PlnSimulatorService plnSimulatorService;
@Autowired private BillerSimulatorService billerSimulatorService;
@Autowired private PlnService plnService;
private Integer port = 11111;
private ISOServer server;
public PlnGateway(Integer port) {
this.port = port;
}
@PostConstruct
public void init(){
try {
PlnChannel channel = new PlnChannel(new PlnPackager());
server = new ISOServer(port, channel, null);
server.addISORequestListener(this);
org.jpos.util.Logger jposLogger = new org.jpos.util.Logger();
Log4JListener log4JListener = new Log4JListener();
log4JListener.setLevel("info");
jposLogger.addListener(log4JListener);
server.setLogger(jposLogger, "pln-server");
channel.setLogger(jposLogger, "pln-channel");
new Thread(server).start();
logger.info("Pln Gateway started at port [{}]", port);
} catch (Exception err){
logger.error(err.getMessage(), err);
}
}
@PreDestroy
public void shutdown(){
try {
server.shutdown();
} catch (Exception err) {
logger.error(err.getMessage(), err);
}
}
@Override
public boolean process(ISOSource src, ISOMsg msg) {
try {
logger.info("Incoming message from [{}]",((BaseChannel) src).getSocket().getInetAddress().getHostAddress());
logger.info("Message Stream [{}]",new String(msg.pack()));
if(MTIConstants.NETWORK_MANAGEMENT_REQUEST.equals(msg.getMTI())) {
return handleNetworkManagementRequest(src, msg);
}
if(MTIConstants.INQUIRY_REQUEST.equals(msg.getMTI())) {
return handleInquiryRequest(src, msg);
}
if(MTIConstants.PAYMENT_REQUEST.equals(msg.getMTI())) {
return handlePaymentRequest(src, msg);
}
} catch (Exception err){
logger.error(err.getMessage(), err);
}
return false;
}
private boolean handlePaymentRequest(ISOSource src, ISOMsg msg) throws Exception {
ISOMsg response = (ISOMsg) msg.clone();
response.setMTI(MTIConstants.PAYMENT_RESPONSE);
String bank = msg.getString(32);
if(billerSimulatorService.findBankByKode(bank) == null){
logger.debug("[POSTPAID] - [PAY-REQ] - Bit 32 [{}]", bank);
logger.error("[POSTPAID] - [PAY-REQ] - Invalid bit 32 [{}]", bank);
response.set(39, ResponseCode.ERROR_UNREGISTERED_BANK_CODE);
src.send(response);
return true;
}
String pan = msg.getString(2);
if(pan.length() != 5) {
logger.error("[POSTPAID] - [PAY-REQ] - Invalid bit 2 [{}]", pan);
response.set(39, ResponseCode.ERROR_INVALID_MESSAGE);
src.send(response);
return true;
}
String produk = pan.substring(2);
if("501".equals(produk)) {
return handlePaymentPostpaid(src, msg, response);
} else {
logger.error("[POSTPAID] - [PAY-REQ] - Invalid produk [{}]", produk);
response.set(39, ResponseCode.ERROR_UNREGISTERED_PRODUCT);
src.send(response);
return true;
}
}
private boolean handleInquiryRequest(ISOSource src, ISOMsg msg) throws Exception {
ISOMsg response = (ISOMsg) msg.clone();
response.setMTI(MTIConstants.INQUIRY_RESPONSE);
String bank = msg.getString(32);
if(billerSimulatorService.findBankByKode(bank) == null){
logger.debug("[POSTPAID] - [INQ-REQ] - Bit 32 [{}]", bank);
logger.error("[POSTPAID] - [INQ-REQ] - Invalid bit 32 [{}]", bank);
response.set(39, ResponseCode.ERROR_UNREGISTERED_BANK_CODE);
src.send(response);
return true;
}
String pan = msg.getString(2);
if(pan.length() != 5) {
logger.error("[POSTPAID] - [INQ-REQ] - Invalid bit 2 [{}]", pan);
response.set(39, ResponseCode.ERROR_INVALID_MESSAGE);
src.send(response);
return true;
}
String produk = pan.substring(2);
if("501".equals(produk)) {
return handleInquiryPostpaid(src, msg, response);
} else {
logger.error("[POSTPAID] - [INQ-REQ] - Invalid produk [{}]", produk);
response.set(39, ResponseCode.ERROR_UNREGISTERED_PRODUCT);
src.send(response);
return true;
}
}
private boolean handlePaymentPostpaid(ISOSource src, ISOMsg msg,ISOMsg response) throws Exception {
String bit48Request = msg.getString(48);
if(bit48Request.length() < 55) {
logger.error("[POSTPAID] - [PAY-REQ] - Invalid bit 48 [{}]", bit48Request);
response.set(39, ResponseCode.ERROR_INVALID_MESSAGE);
src.send(response);
return true;
}
String plnRef = bit48Request.substring(23,55).toLowerCase();
InquiryPostpaidResponse daftarTagihan = plnService.findInquiryPostpaidResponse(plnRef);
if(daftarTagihan == null) {
logger.error("[POSTPAID] - [PAY-REQ] - Invalid PLN REF [{}]", plnRef);
response.set(39, ResponseCode.ERROR_PLN_REFNUM_NOT_VALID);
src.send(response);
return true;
}
String switcher = bit48Request.substring(0,7);
Integer hold = 0;
for (InquiryPostpaidResponseDetail detail : daftarTagihan.getDetails()) {
PembayaranPascabayar payment = new PembayaranPascabayar();
payment.setBank(msg.getString(32));
payment.setLoket("");
payment.setMerchantCategory(msg.getString(26));
payment.setOperator("");
payment.setSwitcher(switcher);
payment.setTagihanPascabayar(detail.getTagihanPascabayar());
plnSimulatorService.save(payment);
hold = detail.getTagihanPascabayar().getPelanggan().getHoldResponse();
}
response.set(39, ResponseCode.SUCCESSFUL);
try {
logger.info("[POSTPAID] - [PAY-REQ] - Pelanggan diset untuk hold [{}]", hold);
Thread.sleep(hold);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
src.send(response);
return true;
}
private boolean handleInquiryPostpaid(ISOSource src, ISOMsg msg,ISOMsg response) throws ISOException, IOException, VetoException {
String bit48Request = msg.getString(48);
if(bit48Request.length() != 19) {
logger.error("[POSTPAID] - [INQ-REQ] - Invalid bit 48 [{}]", bit48Request);
response.set(39, ResponseCode.ERROR_INVALID_MESSAGE);
src.send(response);
return true;
}
String mitra = bit48Request.substring(0,7);
if(billerSimulatorService.findMitraByKode(mitra.trim()) == null){
logger.debug("[POSTPAID] - [INQ-REQ] - Mitra [{}]", mitra);
logger.error("[POSTPAID] - [INQ-REQ] - Kode mitra tidak ditemukan [{}]", mitra);
response.set(39, ResponseCode.ERROR_UNREGISTERED_SWITCHING);
src.send(response);
return true;
}
String idpel = bit48Request.substring(7);
Pelanggan p = plnSimulatorService.findPelangganByIdpel(idpel);
if(p == null) {
logger.error("[POSTPAID] - [INQ-REQ] - IDPEL tidak ditemukan [{}]", idpel);
response.set(39, ResponseCode.ERROR_UNKNOWN_SUBSCRIBER);
src.send(response);
return true;
}
if(!ResponseCode.SUCCESSFUL.equals(p.getResponseCode())) {
logger.error("[POSTPAID] - [INQ-REQ] - Pelanggan diset untuk RC [{}]", p.getResponseCode());
response.set(39, p.getResponseCode());
src.send(response);
return true;
}
List<TagihanPascabayar> daftarTagihan = plnSimulatorService.findTagihan(p);
if(daftarTagihan.size() < 1){
logger.error("[POSTPAID] - [INQ-REQ] - Tagihan untuk idpel [{}] tidak ada", idpel);
response.set(39, ResponseCode.ERROR_CURRENT_BILL_IS_NOT_AVAILABLE);
src.send(response);
return true;
}
List<TagihanPascabayar> tagihanDikirim;
if(daftarTagihan.size() > 4) {
tagihanDikirim = daftarTagihan.subList(0, 4);
} else {
tagihanDikirim = daftarTagihan;
}
BigDecimal amount = BigDecimal.ZERO;
for (TagihanPascabayar tagihanPascabayar : tagihanDikirim) {
amount = amount.add(tagihanPascabayar.getBill());
amount = amount.add(tagihanPascabayar.getDenda());
}
response.set(4, "360"+"0"+StringUtils.leftPad(amount.setScale(0, RoundingMode.HALF_EVEN).toString(), 12, "0"));
InquiryPostpaidResponse ipr = new InquiryPostpaidResponse();
ipr.setBank(msg.getString(32));
ipr.setSwitcher(mitra);
ipr.setStan(msg.getString(11));
for (TagihanPascabayar tagihanPascabayar : tagihanDikirim) {
InquiryPostpaidResponseDetail detail = new InquiryPostpaidResponseDetail();
detail.setTagihanPascabayar(tagihanPascabayar);
detail.setInquiryPostpaidResponse(ipr);
ipr.getDetails().add(detail);
}
plnService.save(ipr);
StringBuffer bit48Response = createBit48InquiryPostpaidResponse(bit48Request, p, daftarTagihan, tagihanDikirim, ipr);
response.set(39, ResponseCode.SUCCESSFUL);
response.set(48, bit48Response.toString());
try {
logger.info("[POSTPAID] - [INQ-REQ] - Pelanggan diset untuk hold [{}]", p.getHoldResponse());
Thread.sleep(p.getHoldResponse());
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
src.send(response);
return true;
}
private StringBuffer createBit48InquiryPostpaidResponse(
String bit48Request, Pelanggan p,
List<TagihanPascabayar> daftarTagihan,
List<TagihanPascabayar> tagihanDikirim, InquiryPostpaidResponse ipr) {
StringBuffer bit48Response = new StringBuffer();
bit48Response.append(bit48Request);
bit48Response.append(tagihanDikirim.size());
bit48Response.append(StringUtils.leftPad(String.valueOf(daftarTagihan.size()), 2, "0"));
bit48Response.append(ipr.getId().toUpperCase());
bit48Response.append(StringUtils.rightPad(p.getNama(), 25, " "));
bit48Response.append(StringUtils.rightPad(p.getServiceUnit(), 5, " "));
bit48Response.append(StringUtils.rightPad(p.getServiceUnitPhone(), 15, " "));
bit48Response.append(StringUtils.rightPad(p.getSubscriberSegmentation(), 4, " "));
bit48Response.append(StringUtils.leftPad(p.getPowerConsumingCategory(), 9, "0"));
bit48Response.append(StringUtils.leftPad("", 9, "0")); // total admin charges
for (TagihanPascabayar t : tagihanDikirim) {
bit48Response.append(DateTimeFormat.forPattern("yyyyMM").print(t.getBillPeriod().getTime()));
bit48Response.append(DateTimeFormat.forPattern("ddMMyyyy").print(t.getDueDate().getTime()));
bit48Response.append(DateTimeFormat.forPattern("ddMMyyyy").print(t.getMeterReadDate().getTime()));
bit48Response.append(StringUtils.leftPad(t.getBill().setScale(0, RoundingMode.HALF_EVEN).toString(), 11, "0"));
if(BigDecimal.ZERO.compareTo(t.getInsentif()) > 0){
bit48Response.append("D");
} else {
bit48Response.append("C");
}
bit48Response.append(StringUtils.leftPad(t.getInsentif().abs().setScale(0, RoundingMode.HALF_EVEN).toString(), 10, "0"));
bit48Response.append(StringUtils.leftPad(t.getVat().setScale(0, RoundingMode.HALF_EVEN).toString(), 10, "0"));
bit48Response.append(StringUtils.leftPad(t.getDenda().setScale(0, RoundingMode.HALF_EVEN).toString(), 10, "0"));
bit48Response.append(StringUtils.leftPad(t.getPreviousMeterRead1(), 8, "0"));
bit48Response.append(StringUtils.leftPad(t.getCurrentMeterRead1(), 8, "0"));
bit48Response.append(StringUtils.leftPad(t.getPreviousMeterRead2(), 8, "0"));
bit48Response.append(StringUtils.leftPad(t.getCurrentMeterRead2(), 8, "0"));
bit48Response.append(StringUtils.leftPad(t.getPreviousMeterRead3(), 8, "0"));
bit48Response.append(StringUtils.leftPad(t.getCurrentMeterRead3(), 8, "0"));
}
return bit48Response;
}
private boolean handleNetworkManagementRequest(ISOSource src, ISOMsg msg) throws Exception {
ISOMsg response = (ISOMsg) msg.clone();
response.setMTI(MTIConstants.NETWORK_MANAGEMENT_RESPONSE);
response.set(39, ResponseCode.SUCCESSFUL);
src.send(response);
return true;
}
}