自定义8583模板,打包解包,使用j8583包

j8583_boss.xml


<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE j8583-config PUBLIC "-//J8583//DTD CONFIG 1.0//EN"
 "C:/tvpay/config/j8583.dtd">
<j8583-config>

	<!-- These are the ISO headers to be prepended to the message types specified -->
	<header type="1027">1027</header>
	<header type="1028">1028</header>
	<header type="1005">1005</header>
	<header type="1006">1006</header>

	<!-- The server example uses this to read the requests -->
	<parse type="1027">
		<field num="2" type="LLVAR" />
		<field num="3" type="LLVAR" />
		<field num="4" type="NUMERIC" length="4" />	
		<field num="5" type="NUMERIC" length="4" />
		<field num="6" type="NUMERIC" length="4" />
		<field num="7" type="NUMERIC" length="1" />
		<field num="8" type="NUMERIC" length="1" />
		<field num="43" type="LLVAR" />
		<field num="44" type="LLVAR" />
		<field num="45" type="LLVAR" />
	</parse>

	<parse type="1005">
		<field num="2" type="LLVAR" />
		<field num="3" type="LLVAR" />
		<field num="4" type="NUMERIC" length="4" />
		<field num="5" type="NUMERIC" length="4" />
		<field num="6" type="NUMERIC" length="4" />
		<field num="7" type="NUMERIC" length="1" />
		<field num="8" type="NUMERIC" length="1" />
		<field num="19" type="LLVAR" />
		<field num="21" type="LLVAR" />
		<field num="23" type="ALPHA" length="2" />
		<field num="24" type="LLVAR" />
		<field num="27" type="LLVAR" />
		<!-- <field num="39" type="NUMERIC" length="6" /> -->
	</parse>

	<!-- The client example uses this to read the responses -->
	<parse type="1028">
		<field num="2" type="LLVAR" />
		<field num="3" type="LLVAR" />
		<field num="4" type="NUMERIC" length="4" />
		<field num="5" type="NUMERIC" length="4" />
		<field num="6" type="NUMERIC" length="4" />
		<field num="7" type="NUMERIC" length="1" />
		<field num="8" type="NUMERIC" length="1" />
		<field num="41" type="LLLVAR" />
		<field num="43" type="LLVAR" />
		<field num="44" type="LLVAR" />
		<field num="45" type="LLVAR" />
	</parse>

	<parse type="1006">
		<field num="2" type="LLVAR" />
		<field num="3" type="LLVAR" />
		<field num="4" type="NUMERIC" length="4" />
		<field num="5" type="NUMERIC" length="4" />
		<field num="6" type="NUMERIC" length="4" />
		<field num="7" type="NUMERIC" length="1" />
		<field num="8" type="NUMERIC" length="1" />
		<field num="19" type="LLVAR" />
		<field num="20" type="LLVAR" />
		<field num="21" type="LLVAR" />
		<field num="22" type="LLVAR" />
		<field num="23" type="ALPHA" length="2" />
		<field num="24" type="LLVAR" />
		<field num="27" type="LLVAR" />
		<field num="29" type="NUMERIC" length="10" />
	</parse>

</j8583-config>
PacketDataOperateService.java

package com.sumavision.connector.iso8583.parse;

import java.io.*;
import java.util.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.io.Resource;

import com.sumavision.connector.iso8583.util.ConfigParser;
import com.sumavision.connector.iso8583.util.IsoValue;
import com.sumavision.connector.iso8583.util.MessageFactory;
import com.sumavision.connector.iso8583.util.SimpleTraceGenerator;
import com.sumavision.connector.iso8583.util.IsoMessage;
import com.sumavision.connector.iso8583.util.IsoType;
import com.sumavision.connector.util.DesEncrypt;
import com.sumavision.connector.util.Hex2GBKStringUtil;
import com.sumavision.connector.util.PacketIdGenerator;
import com.sumavision.connector.util.RequestIdGenerator;
import com.sumavision.crpt.MessageDigestMD5;

/**
 * <p>
 * 数据打包解包服务
 * </p>
 * 
 * @author Administrator
 */
public class PacketDataOperateService {

	private final Logger log = LoggerFactory.getLogger(this.getClass());
	// 模板文件配置路径
	private final String OPERATE_TEMPLATE_PATH = "com/sumavision/connector/iso8583/parse/j8583_boss.xml";

	private Resource templatePath = null;

	// 获取用户信息(终端号) 支付平台传来的交易代码
	private final String GET_TERMINALNO_TRANSCODE = "BT000201";
	// 获取用户缴费账单(BOSS缴费单) 支付平台传来的交易代码
	private final String GET_USERFEE_TRANSCODE = "BT000202";
	// 获取用户信息(终端号) 功能码
	private final int GET_TERMINALNO_FUNCNO = 1027;
	// 获取用户信息(终端号) 返回响应功能码
	private final int GET_TERMINALNO_RESPONSE_FUNCNO = 1028;
	// 获取用户缴费账单(BOSS缴费单) 功能码
	private final int GET_USERFEE_FUNCNO = 1005;
	// 获取用户缴费账单(BOSS缴费单) 返回响应功能码
	private final int GET_USERFEE_RESPONSE_FUNCNO = 1006;

	// 机顶盒内部编码
	private final String STB_INSIDE_CODE = "STB_INSIDE_CODE";
	// 厂商编号
	private final String MANUFACTURERS_NO = "MANUFACTURERS_NO";
	// 硬件版本号
	private final String HARDWARE_VERSION_NO = "HARDWARE_VERSION_NO";
	// 客户编码
	private final String CUSTOMER_NO = "CUSTOMER_NO";
	// 账户编码
	private final String ACCOUNT_NO = "ACCOUNT_NO";
	// 业务编码
	private final String SERVICE_TYPE = "SERVICE_TYPE";
	// 服务号码
	private final String SUBSCRIBER_NO = "SUBSCRIBER_NO";
	// 账务周期
	private final String BILLING_CYCLE = "BILLING_CYCLE";

	// 开始标识+包长度+包体+校验字段+结束标识
	// 报文开始标识
	private final String PACKET_HEADER = "|*!#";
	// 报文结束标识
	private final String PACKET_TAILER = "#@&^";

	private static final Map<String, String> retCodeMsgMap = new HashMap<String, String>();

	/* 初始化错误代码表 */
	static {
		retCodeMsgMap.put("0000", "交易成功");
		retCodeMsgMap.put("0001", "服务器忙");
		retCodeMsgMap.put("0002", "无法解析字符串");
		retCodeMsgMap.put("0003", "SMS系统内部错误");
		retCodeMsgMap.put("0004", "版本号不支持");
		retCodeMsgMap.put("0005", "数据库没有该银行的记录");
		retCodeMsgMap.put("0006", "银行代码与银行认证码不符");
		retCodeMsgMap.put("0007", "客户编码不存在");
		retCodeMsgMap.put("0008", "帐户编码不存在");
		retCodeMsgMap.put("0009", "业务编码不存在");
		retCodeMsgMap.put("0010", "服务号码不存在");
		retCodeMsgMap.put("0011", "账务周期不存在");
		retCodeMsgMap.put("0012", "银行流水号不存在");
		retCodeMsgMap.put("0013", "余额账本不存在");
		retCodeMsgMap.put("0014", "存在多个余额账本,请按余额账本编号查询");
		retCodeMsgMap.put("0015", "存在一个服务号码由多个客户的帐户付费");
		retCodeMsgMap.put("0016", "已缴费");
		retCodeMsgMap.put("0017", "已签订代扣协议");
		retCodeMsgMap.put("0018", "此帐户未使用该流水号签订代扣协议");
		retCodeMsgMap.put("0019", "此客户有多个帐户,请使用帐户编号");
		retCodeMsgMap.put("0020", "预存失败,预存超过余额上限");
		retCodeMsgMap.put("0021", "实缴金额不等于实际费用加滞纳金");
		retCodeMsgMap.put("0022", "冲正失败");
		retCodeMsgMap.put("0023", "已经对账,不能进行冲正");
		retCodeMsgMap.put("0024", "已经冲正,不能再次冲正");
		retCodeMsgMap.put("0025", "必填数据未填充");
		retCodeMsgMap.put("0026", "代扣失败");
		retCodeMsgMap.put("0027", "发票号码不存在");
		retCodeMsgMap.put("0028", "发票号码不可使用");
		retCodeMsgMap.put("0029", "附加信息域格式错误");
		retCodeMsgMap.put("0030", "产品信息域格式错误");
		retCodeMsgMap.put("0032", "找不到对应用户");
	}

	/**
	 * <p>
	 * 打包数据
	 * </p>
	 * 
	 * @param map
	 * @return
	 * @throws Exception
	 */
	public String packetData(Map<String, Object> map) throws Exception {

		log.info("打包数据准备...Map >>{}", map);
		if (null == map || map.size() < 1) {
			log.error("请求参数域为空,打包失败");
			return null;
		}

		if (!map.containsKey("STB_ID")) {
			log.error("请求参数STB_ID域为空,打包失败");
			return null;
		}
		String stb = map.get("STB_ID").toString();
		map.put("MANUFACTURERS_NO", stb.substring(0, 3));
		map.put("HARDWARE_VERSION_NO", stb.substring(3, 18));
		map.put("STB_INSIDE_CODE", stb.substring(18, 25));

		MessageFactory mfact = ConfigParser.createFromStream(templatePath
				.getInputStream());
		// MessageFactory mfact =
		// ConfigParser.createFromClasspathConfig(templatePath.);
		// mfact.setAssignDate(true);
		// mfact.setTraceNumberGenerator(new
		// SimpleTraceGenerator((int)(System.currentTimeMillis() % 100000)));

		String transcode = (String) map.get("TRANSCODE");
		IsoMessage m = null;
		if (null != transcode && transcode.equals(GET_TERMINALNO_TRANSCODE)) {
			m = _cardNumberForMachinePacket(map, mfact);
		} else if (null != transcode && transcode.equals(GET_USERFEE_TRANSCODE)) {
			m = _accessToUserFeesBillPacket(map, mfact);
		} else {
			log.error("交易代码错误,打包失败");
			return null;
		}
		String packetStr = new String(m.writeData());
		String packetLenStr = String.valueOf(packetStr.length() + 32);
		StringBuffer sbuff = new StringBuffer();
		sbuff.append(PACKET_HEADER);
		if (packetLenStr.length() < 8) {
			sbuff.append("00000000".substring(packetLenStr.length()));
		}
		sbuff.append(packetStr.length() + 32);
		sbuff.append(packetStr);
		sbuff.append(generateValidateCode(packetStr).toLowerCase());
		sbuff.append(PACKET_TAILER);
		log.info("打包数据完成...报文 >>{}", sbuff.toString());
		return sbuff.toString();
	}

	/**
	 * <p>
	 * 解析报文字符串
	 * </p>
	 * 
	 * @param pstr
	 *            报文体
	 * @param vstr
	 *            校验字段
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("rawtypes")
	public Map parseData(String pstr, String vstr, int funcCode)
			throws Exception {
		log.info("解包数据准备...报文体 >>{}", pstr);
		log.info("解包数据准备...报文校验字段 >>{}", vstr);
		MessageFactory mfact = ConfigParser.createFromStream(templatePath
				.getInputStream());
		// MessageFactory mfact =
		// ConfigParser.createFromClasspathConfig(templatePath);
		// mfact.setAssignDate(true);
		// mfact.setTraceNumberGenerator(new
		// SimpleTraceGenerator((int)(System.currentTimeMillis() % 100000)));
		mfact.setUseBinaryMessages(false);
		// 可能有中文
		// String bstr = new String(bytes,"GBK");
		// int functionCode = Integer.valueOf(pstr.substring(68, 72));
		// String packetDataStr = bstr.substring(12, bstr.length()-4-32);
		// String validateStr = bstr.substring(bstr.length()-4-32,
		// bstr.length()-4);
		Map map = null;
		byte[] packetBytes = pstr.getBytes("GBK");
		if (funcCode == GET_TERMINALNO_RESPONSE_FUNCNO) {
			if (!validateResponseData(pstr, vstr)) {
				throw new Exception("数据包校验失败,无效数据包");
			}
			map = cardNumberForMachinePacket_(mfact.parseMessage(packetBytes,
					4136));
		} else if (funcCode == GET_USERFEE_RESPONSE_FUNCNO) {
			// 查询单用户余额账本的 返回报文中含有中文字符,返回的校验码与对报文做MD5生成的校验码不一致
			// 未找到原因: 暂时不做校验
			map = accessToUserFeesBillPacket_(mfact.parseMessage(packetBytes,
					4102));
		} else {
			log.error("无法识别的功能码,解包失败");
			return null;
		}

		log.info("解包数据完成...Map >>{}", map);
		return map;
	}

	/**
	 * 打包
	 * 
	 * @param mf
	 * @param input
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private IsoMessage _cardNumberForMachinePacket(Map input, MessageFactory mf) {
		// IsoMessage m = mf.newMessage(GET_TERMINALNO_FUNCNO);
		IsoMessage m = new IsoMessage();
		m.setBinary(false);
		// m.setBinary(true);
		// 会话ID 对来自银行前置机接入SMS的请求数据,此处填写由银行前置机产生的会话ID,在传输过程中应保持一对请求/响应的数据中
		// SessionID 保持不变
		m.setValue(2, RequestIdGenerator.genRequestId(), IsoType.LLVAR, 16);
		// 数据包序号 从 0 开始,顺序累加,步长为 1,循环使用
		m.setValue(3, PacketIdGenerator.genPacketId(), IsoType.LLVAR, 32);
		// 银行代码 暂时不知道,先写1111
		m.setValue(4, 2000, IsoType.NUMERIC, 4);
		// 功能码 业务标识,用于标识请求/响应数据包代表的业务操作
		m.setValue(5, GET_TERMINALNO_FUNCNO, IsoType.NUMERIC, 4);
		// 响应吗 标识业务处理结果
		m.setValue(6, 0000, IsoType.NUMERIC, 4);
		// 重发标识 0 标识普通数据包;1:标识重发数据包
		m.setValue(7, 0, IsoType.NUMERIC, 1);
		// 加密标识 0 标识包体数据为明文,1:标识包体数据为加密数据
		m.setValue(8, 0, IsoType.NUMERIC, 1);
		// 机顶盒内部编码 StbInsideCode
		m.setValue(43, input.get(STB_INSIDE_CODE), IsoType.LLVAR, 50);
		// 厂商编号 ManufacturersNo
		m.setValue(44, input.get(MANUFACTURERS_NO), IsoType.LLVAR, 20);
		// 硬件版本号 HardwareVersionNo
		m.setValue(45, input.get(HARDWARE_VERSION_NO), IsoType.LLVAR, 20);
		return m;
	}

	/**
	 * 打包
	 * 
	 * @param mf
	 * @param input
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	private IsoMessage _accessToUserFeesBillPacket(Map input, MessageFactory mf) {
		IsoMessage m = mf.newMessage(GET_USERFEE_FUNCNO);
		m.setBinary(false);
		// 会话ID 对来自银行前置机接入SMS的请求数据,此处填写由银行前置机产生的会话ID,在传输过程中应保持一对请求/响应的数据中
		// SessionID 保持不变
		m.setValue(2, RequestIdGenerator.genRequestId(), IsoType.LLVAR, 16);
		// 数据包序号 从 0 开始,顺序累加,步长为 1,循环使用
		m.setValue(3, PacketIdGenerator.genPacketId(), IsoType.LLVAR, 32);
		// 银行代码 暂时不知道,先写1111
		m.setValue(4, 2000, IsoType.NUMERIC, 4);
		// 功能码 业务标识,用于标识请求/响应数据包代表的业务操作
		m.setValue(5, GET_USERFEE_FUNCNO, IsoType.NUMERIC, 4);
		// 响应吗 标识业务处理结果
		m.setValue(6, 0000, IsoType.NUMERIC, 4);
		// 重发标识 0 标识普通数据包;1:标识重发数据包
		m.setValue(7, 0, IsoType.NUMERIC, 1);
		// 加密标识 0 标识包体数据为明文,1:标识包体数据为加密数据
		m.setValue(8, 0, IsoType.NUMERIC, 1);
		// 客户编码 CustomerNo 广电客户的编码
		m.setValue(19, input.get(CUSTOMER_NO), IsoType.LLVAR, 20);
		// 下面3项必填一项
		if (null != input.get(ACCOUNT_NO)) {
			// 账户编码 AccountNo 广电客户的帐户编码
			m.setValue(21, input.get(ACCOUNT_NO), IsoType.LLVAR, 20);
		}
		if (null != input.get(SERVICE_TYPE)) {
			// 业务编码 ServiceType 公共业务00 数字电视业务02
			m.setValue(23, input.get(SERVICE_TYPE), IsoType.ALPHA, 2);
		}
		if (null != input.get(SUBSCRIBER_NO)) {
			// 服务号码 SubscriberNo 智能卡号、上网帐号等
			m.setValue(24, input.get(SUBSCRIBER_NO), IsoType.LLVAR, 20);
		}
		// 账务周期 BillingCycle 可选
		// m.setValue(39, input.get(BILLING_CYCLE), IsoType.LLVAR, 20);
		return m;
	}

	/**
	 * 解包
	 * 
	 * @param im
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private Map cardNumberForMachinePacket_(IsoMessage im) {
		log.info("cardNumberForMachinePacket_ : receive Msg :"
				+ printIsoMessage(im));
		// 银行代码
		IsoValue bankCode = im.getAt(4);
		// 功能码
		IsoValue functionCode = im.getAt(5);
		// boss响应吗
		IsoValue retCode = im.getAt(6);
		// 加密标识
		IsoValue isEncrpt = im.getAt(8);
		// 机顶盒内部编码
		IsoValue stbInsideCode = im.getAt(43);
		// 厂商编码
		IsoValue manufacturersNo = im.getAt(44);
		// 硬件版本号
		IsoValue hardwareVersionNo = im.getAt(45);
		// 附件信息 响应中的“附加信息”域经过加密处理,加密方式会通过其他方式告知。其中的内容如下:
		// 智能卡号/t 客户ID/t 用户ID/t 客户姓名/t 客户地址例如:
		// 1234567890 1521 1598 张三 天津市河西区气象台路卫星里
		IsoValue additionalData = im.getAt(41);

		if (!String.valueOf(functionCode.getValue()).equals(
				String.valueOf(GET_TERMINALNO_RESPONSE_FUNCNO))) {
			log.error("功能码不正确");
		}

		Map map = new HashMap();
		map.put("RETCODE", retCode.getValue());
		map.put("TRANSMSG",
				getTransMsg(paddingRetCode(String.valueOf(retCode.getValue()))));
		// map.put("BANK_CODE", bankCode.getValue());
		map.put("IS_ENCRPT", isEncrpt.getValue());
		map.put("STB_INSIDE_CODE", stbInsideCode.getValue());
		map.put("MANUFACTURERS_NO", manufacturersNo.getValue());
		map.put("HARDWARE_VERSION_NO", hardwareVersionNo.getValue());
		map.put("ADDITIONAL_DATA", additionalData.getValue());
		Object addData = additionalData.getValue();
		// additionalData解密
		String additional_Str = null;
		if (null != addData) {
			String raw_additional_Str = decrptDesString(String.valueOf(addData));
			if (null != raw_additional_Str) {
				additional_Str = Hex2GBKStringUtil
						.toStringHex(raw_additional_Str);
				log.info("HEX字符串转为GBK文本串为 /n {}", additional_Str);
			}
		}

		try {
			String[] infos = parseAdditionalInfo(additional_Str);
			// 智能卡号
			map.put("ICCARD_ID", infos[0]);
			// 客户ID
			map.put("CUSTOMER_ID", infos[1]);
			// 用户ID
			map.put("USER_ID", infos[2]);
			// 客户姓名
			map.put("CUSTOMER_NAME", infos[3]);
			// 客户地址
			map.put("USER_ADDRESS", infos[4]);
		} catch (Exception e) {
			log.error("解析附加信息失败 , 原因: {}", e);
			// 智能卡号
			map.put("ICCARD_ID", null);
			// 客户ID
			map.put("CUSTOMER_ID", null);
			// 用户ID
			map.put("USER_ID", null);
			// 客户姓名
			map.put("CUSTOMER_NAME", null);
			// 客户地址
			map.put("USER_ADDRESS", null);
		}
		return map;
	}

	/**
	 * 解包
	 * 
	 * @param im
	 * @return
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	private Map accessToUserFeesBillPacket_(IsoMessage im) {
		log.info("accessToUserFeesBillPacket_ : receive Msg :"
				+ printIsoMessage(im));
		// 银行代码
		IsoValue bankCode = im.getAt(4);
		// 功能码
		IsoValue functionCode = im.getAt(5);
		// boss响应吗
		IsoValue retCode = im.getAt(6);
		// 加密标识
		IsoValue isEncrpt = im.getAt(8);
		// 客户编码
		IsoValue customerNo = im.getAt(19);
		// 客户名称
		IsoValue customerName = im.getAt(20);
		// 账户编码
		IsoValue accountNo = im.getAt(21);
		// 帐户名称
		IsoValue accountName = im.getAt(22);
		// 业务编码
		IsoValue serviceType = im.getAt(23);
		// 服务号码
		IsoValue subscriNo = im.getAt(24);

		// 余额
		IsoValue balance = im.getAt(29);

		if (!String.valueOf(functionCode).equals(
				String.valueOf(GET_USERFEE_RESPONSE_FUNCNO))) {
			log.error("功能码不正确");
		}

		Map map = new HashMap();
		map.put("RETCODE", retCode.getValue());
		map.put("TRANSMSG",
				getTransMsg(paddingRetCode(String.valueOf(retCode.getValue()))));
		// map.put("BANK_CODE", bankCode.getValue());
		map.put("IS_ENCRPT", isEncrpt.getValue());
		map.put("CUSTOMER_NO", customerNo.getValue());
		map.put("CUSTOMER_NAME", customerName.getValue());
		map.put("BALACNE", balance == null ? null : balance.getValue());

		return map;
	}

	/**
	 * <p>
	 * 校验响应数据包的校验码是否正确
	 * </p>
	 * 
	 * @param rawData
	 *            待校验数据串
	 * @param validateCode
	 *            校验码
	 * @return
	 */
	private boolean validateResponseData(String rawData, String validateCode) {
		return validateCode.equals(MessageDigestMD5.keyedEncode(rawData, ""));
	}

	/**
	 * <p>
	 * 获取返回码对应的消息
	 * </p>
	 * 
	 * @param code
	 * @return
	 */
	private String getTransMsg(String code) {
		if (retCodeMsgMap.containsKey(code)) {
			return retCodeMsgMap.get(code);
		} else {
			return "交易失败";
		}
	}

	private String paddingRetCode(String rawcode) {
		if (rawcode.length() < 4) {
			return "0000".substring(rawcode.length()) + rawcode;
		} else {
			return rawcode;
		}
	}

	/**
	 * <p>
	 * 解密DES加密串
	 * </p>
	 * 
	 * @param rawData
	 * @return
	 * @throws Exception
	 */
	private String decrptDesString(String rawData) {
		String rstr = null;
		log.info("DES解密算法解密源字符串:       /n {}", rawData);
		if (null == rawData) {
			log.error("源加密字符串为空!");
			return null;
		}
		try {
			// 设置密钥 静态,设置一次即可 密钥是固定的,写死即可
			DesEncrypt.setKey("A000000000000000");
			rstr = DesEncrypt.desDecrypt(rawData);
		} catch (Exception e) {
			log.error("DES解密算法 解密密码串失败, 原因: {}", e);
		}
		log.info("DES解密算法解密结果:       /n {}", rstr);
		return rstr;
	}

	private String printIsoMessage(IsoMessage m) {
		StringBuffer sbuff = new StringBuffer();
		sbuff.append("TYPE: " + Integer.toHexString(m.getType()) + " /n");
		for (int i = 2; i < 128; i++) {
			if (m.hasField(i)) {
				sbuff.append("F " + i + "(" + m.getField(i).getType() + "): "
						+ m.getObjectValue(i) + " -> '"
						+ m.getField(i).toString() + "' /n");
			}
		}
		return sbuff.toString();
	}

	/**
	 * <p>
	 * 解密附加信息 用于 解析 根据机顶盒号查询用户响应
	 * </p>
	 * 
	 * @param s
	 * @return [0] 智能卡号 [1] 客户ID [2] 用户ID [3] 客户姓名 [4] 客户地址
	 * @throws Exception
	 */
	private String[] parseAdditionalInfo(String s) throws Exception {
		if (null != s && !"".equals(s)) {
			String[] ss = s.trim().split("//t");
			if (ss.length != 5) {
				throw new Exception("解析附加信息失败,信息格式不正确!");
			} else {
				return new String[] { ss[0].trim(), ss[1].trim(), ss[2].trim(),
						ss[3].trim(), ss[4].trim() };
			}
		} else {
			throw new Exception("解析附加信息失败,信息格式不正确!");
		}
	}

	/**
	 * <p>
	 * 发送报文时生成校验码
	 * </p>
	 * 
	 * @param rawData
	 * @return
	 */
	private String generateValidateCode(String rawData) {
		return MessageDigestMD5.keyedEncode(rawData, "");
	}

	public static void main(String[] strs) throws Exception {
		PacketDataOperateService pd = new PacketDataOperateService();
		// System.out.println(pd.generateValidateCode("7F003E28000000001613039575047501073200000000000000000000000000000006200010060000001210600001287906傅雪晶1200000301582511傅雪晶-帐户00120000030158250000067062"));
		// System.out.println(pd.generateValidateCode("12345678901234567890").length());
		String r = pd
				.decrptDesString("3EE1B1F40175DC92701588F6F40A7762B05859FACE94BA7CD7EC5513E4DBE3C519CD9BFF085642C765BDAD8F06D719F5A75161E54ACF5230BBD811E91E00A6A1C7FE957F19AEF1F6");
		String additional_Str = Hex2GBKStringUtil.toStringHex(r);
		String[] ss = pd.parseAdditionalInfo(additional_Str);
		for (String s : ss) {
			System.out.println(s);
		}
		// System.out.println(pd.paddingRetCode("3"));
	}

	public Resource getTemplatePath() {
		return templatePath;
	}

	public void setTemplatePath(Resource templatePath) {
		this.templatePath = templatePath;
	}

}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
最近在做中国银行的一个快捷支付渠道,使用的是 ISO8583 协议,一开始用的是JPOS框架,但是感觉框架比较臃肿,而且文档也比较少。在等待银行专线的过程中,自己闭门造车做了一个简单的8583报文解析框架 —— Simple8583,将程序重写了一遍,渠道中的代码量少了不少,这几天中行的接口在测试环境终于调试完成了。抽空分享一下这段时间自己学到的知识。 数据类型与编码格式: 根据接触到的数据类型将数据分为如下几种类型:          CHAR(asc编码,直接使用字符串的getBytes(ENCODING)方法获取字节数组)   BINARY(二进制编码,在打包时将8位01值组装为一个字节),             NUMERIC(BCD编码,即8421码),                LLVAR(变长域,采用ASC编码,每个LLVAR类型的域前会有1字节的域字节长度,表示长度的字节用BCD编码表示)                LLLVAR(变长域,与LLVAR域类似,不同之处在于每个LLLVAR域前会有2字节的域字节长度,长度同样以BCD编码表示)             LLVAR_NUMERIC(变长域,采用BCD编码,前有1字节的长度,长度为域值的长度,而非字节长,如域值为123456,编码后长度为3字节,但是表示域长的字节值为6)       如果用到其它数据类型可以在IsoType中进行添加,并在IsoField中添加处理操作 BitMap:        BitMap是ISO8583报文的精髓所在,ISO8583报文支持64域和128域两种,但是并非每次请求都会将所有域都请求过去,BItMap就起到了标识哪些域是有效的请求域,接收方也会根据BitMap中约定的值对域进行解析。   那么BitMap又是如何工作的呢?          首先,BItMap分为8字节和16字节两种情况,分别表示支持64域和128域,其第一位值为1,表示BitMap为16字节,否则为8字节。       其次,BitMap中的每一位对应数据域的第几域,有效域会置为1,比如01001000表示第二域和第5域为有效位。 在Simple8583中具体的实现是通过BitMap类实现的,具体可参考源码。 mti:            mti即 message type identifier消息类型标识,为4位bcd编码的数字标识符,用于描述信息的类型。 同一个mti可以用于标识多个不同的交易,比如一般常用的0200可以用来表示消费交易,消费撤销,分期付款消费和分期付款撤销,但是对于同一个mti标识的数据域类型定义是类似的。           具体的实现,我将Simple8583的xml文件设置为了两部分,一部分为公用的报文头,如msgLength,tpdu,bitmap等,另外一部分分按照mti的不同分为多个package体。 粗略的实现流程:          1)组装请求的Map数据(只组装需要的数据域,key值为对应的数据域或头的值)          2)请求数据进入SimpleClient代理,SimpleClient根据传入的值解析xml文件(jaxb实现,做了缓存)          3)根据传入值的mti寻找对应的IsoPackage类,对找到的IsoPackage类进行clone(避免污染),对clone值中的域进行值处理和格式化         4)生成BitMap,计算Mac值(如有)          5)使用ByteArrayOutputStream将组装成的IsoPackage域值进行拼装成为一个大的byte数组,在byte前拼装两个字节的长度          6)通过Socket将数据发送并接受响应(读取前两个字节长度,根据长度获取其剩余报文),根据IsoPackage解析报文域,解析得到BitMap后根据BitMap对数据域进行解析,并将值都放入到对应的field中          7)将数据都放在Map中返回,并进行MAC校验(如有) 标签:Simple8583
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值