前段时间项目中接入了农行的银企直联来完成代发的功能,当我拿到银行方面给过来的文档和资料后,发现和招行的银企直联模式差不多,大概就是:在window机器上开一个类似于前置机的小程序,作为我们和银行服务器直联数据连接的中介,我们发送xml数据给前置机,前置机再将数据加密后发送给银行服务器。但是万万没想到农行这个银企直联给我搞了不小的麻烦,他们的文档写的简直是不忍直视,接口返回码也模糊不清,没有明确说明。现在我把踩过的坑给分享一下。
准备开始
在开始之前我们会拿到2个东西,一个是中国农业银行银企通平台(4.70版).msi安装包,另一个是现金管理银企直连接入开发手册V1.2.1.wps接口文档说明
开始安装前置机程序,完成之后是这个样子:
测试用的客户号、操作员代码、操作员密码都会一并提供过来。
注意:正式环境下是需要插入一个key宝,由于现在是测试环境,在安装目录的etc路径下,用记事本编辑 etc\LoginSet.xml,将 IsKey 节点中的内容改成0,就可以不用KEY登录了。
系统设置
点击系统设置,在里面配置我们要用的模式:ERP公网接入、本地服务器地址、通讯协议、监听的端口等等。
直接上几张图吧:
注意:这里有一个坑需要说明一下:农行的这个程序是不支持http 协议(虽然他上面写着可以选择,无语)所以我们要用tcp协议。
组装XML数据
看到上图,已经成功的监听到了15999端口,现在我们就要向前置机所在的机器的ip+port的这个URL上推送XML数据,例如:192.168.1.111:15999
这里举一个范例:汇兑-单笔对似,xml数据报文是这样要求的。
java代码实例:
1 //5.汇兑-单笔对私
2 ApRoot root = new ApRoot();
3 root.setCCTransCode("CFRT21");
4 root.setAmt("9.10");
5 builder.setRoot(root);
6 ApCmp cmp = new ApCmp();
7 cmp.setDbAccNo("361101040010679");
8 cmp.setDbProv("05");
9 cmp.setDbCur("01");
10 cmp.setDbLogAccNo("");
11 cmp.setCrAccNo("6228453296002816764");
12 cmp.setCrProv("");
13 cmp.setCrCur("01");
14 cmp.setCrLogAccNo("");
15 cmp.setConFlag("1");
16 builder.setCmp(cmp);
17 ApCorp corp = new ApCorp();
18 corp.setPsFlag("");
19 corp.setBookingFlag("0");
20 corp.setBookingDate("");
21 corp.setBookingTime("");
22 corp.setUrgencyFlag("0");
23 corp.setOthBankFlag("0");
24 corp.setCrAccName("郑春广");
25 corp.setDbAccName("内猛关仪太彩悟佑慊古丝");
26 corp.setWhyUse("测试");
27 corp.setPostscript("测试");
28 builder.setCorp(corp);
安装要求组装数据:
1 1
2 String s = builder.toXmlString(builder);
3 /**
4 * 请求数据:加密标识(1加密,0不加密) + 请求xml数据的长度(默认7位,不够补空格) + 请求的xml
5 * @param s 请求的xml
6 * @author jieYW
7 * @date 2018/5/29
8 * @return java.lang.String
9 */
10 public String genRequestData(String s)throws Exception{
11 return "1" + String.format("%1$-6s", s.getBytes("gbk").length) + s;
12 }
注意:这里也有一个大坑,字符编码必须通过gbk的编码。前置机内部是通过gbk来解码的。这里如果不设置gbk,你在报文中有汉字的时候就gg了,会一直报这个错误:接收请求报文失败 -接收报体失败 - POLL失败退出 - 偏移量 = 750,当时这个问题困扰了我一段时间,因为我是知道他字符编码是gbk的,所以我只在socket的输出流里设置了字符的编码,经过血一般经历后终于想到了这里,然后彻底解决中文乱码的问题。
【讲xml转换成socket套接字段】
socket发送数据
将上述组装好的数据通过tcp协议发送给前置机:
1 String s = builder.toXmlString(builder);
2 ApHttpRequest request = new ApHttpRequest();
3 String s1 = request.socketSendAndReceive("192.168.1.111", 15999, builder.genRequestData(s));
4 System.out.println("接受数据:" + s1);
测试结果:
补充代码
XML组装:
1 /**
2 * 将ApXmlBuilder格式的数据转化为xml形式
3 *
4 * @param builder
5 * @author jieYW
6 * @date 2018/5/29
7 * @return java.lang.String
8 */
9 public String toXmlString(ApXmlBuilder builder)throws Exception{
10 StringBuffer sb = new StringBuffer("<ap>");
11 Field[] fields = builder.getClass().getDeclaredFields();
12 for (int i = 0; i < fields.length; i++) {
13 Field field = fields[i];
14 field.setAccessible(true);
15 Object item = field.get(builder);
16 if(item == null){
17 continue;
18 }
19 String name = DaoSupport.toFirstUpperCase(field.getName());
20 if(!name.equals("Root")){
21 sb.append("<" + name + ">");
22 }
23 Field[] itemFields = item.getClass().getDeclaredFields();
24 for (int j = 0; j < itemFields.length; j++) {
25 Field itemField = itemFields[j];
26 itemField.setAccessible(true);
27 Object itemObject = itemField.get(item);
28 if(itemObject == null){
29 continue;
30 }
31 String itemFieldName = itemField.getName();
32 sb.append("<" + itemFieldName + ">");
33 sb.append(itemField.get(item).toString());
34 sb.append("</" + itemFieldName + ">");
35 }
36 if(!name.equals("Root")){
37 sb.append("</" + name + ">");
38 }
39 }
40 sb.append("</ap>");
41 return sb.toString();
42 }
43 ————————————————
发送报文:
1 public String socketSendAndReceive(String url, int port,String data)throws Exception{
2 System.out.println("请求数据:" + data);
3 Socket socket = new Socket(url, port);
4 OutputStream bw = socket.getOutputStream();
5 bw.write(data.getBytes("gbk"));
6 bw.flush();
7 InputStream ips = socket.getInputStream();
8 StringBuffer sb = new StringBuffer();
9 int len = 0;
10 byte[] buf = new byte[1024];
11 while((len=ips.read(buf))!=-1){
12 sb.append(new String(buf,0,len,"gbk"));
13 }
14 bw.close();
15 ips.close();
16 socket.close();
17 return sb.toString();
18 }
解析XML:
1 /**
2 * 将xml数据解析为ApXmlBuilde格式的数据
3 *
4 * @param msg
5 * @author jieYW
6 * @date 2018/5/29
7 * @return com.mind.pay.abc.ap.ApXmlBuilder
8 */
9 public static ApXmlBuilder parseXml(String msg)throws Exception {
10 ApXmlBuilder builder = new ApXmlBuilder();
11 Method[] methods = builder.getClass().getMethods();
12 ApRoot apRoot = new ApRoot();
13 Method[] rootMethods = apRoot.getClass().getMethods();
14
15 Map<String,Method> methodMap = new HashMap<>();
16 for (Method method : methods) {
17 methodMap.put(method.getName(),method);
18 }
19 InputStream inputStream = new ByteArrayInputStream(msg.getBytes("UTF-8"));
20 SAXReader reader = new SAXReader();
21 Document document = reader.read(inputStream);
22 Element root = document.getRootElement();
23 List<Element> elementList = root.elements();
24 // 遍历所有子节点
25 for (Element e : elementList){
26 if(methodMap.keySet().contains("set" + e.getName())){
27 Class<?> aClass = Class.forName("com.mind.pay.abc.ap.Ap" + e.getName());
28 Object itemObject = aClass.newInstance();
29 List<Element> items = e.elements();
30 for (Element itemElement : items) {
31 Method[] itemMethods = itemObject.getClass().getMethods();
32 for (Method itemMethod : itemMethods) {
33 //如果字段存在,invoke方法赋值
34 if(itemMethod.getName().contains("set"+itemElement.getName())){
35 itemMethod.invoke(itemObject,itemElement.getText());
36 }
37 }
38 }
39 methodMap.get("set" + e.getName()).invoke(builder,itemObject);
40 }else{
41 //根目录下的参数,封装到apRoot中
42 for (Method rootMethod : rootMethods) {
43 //如果字段存在,invoke方法赋值
44 if(rootMethod.getName().contains("set"+e.getName())){
45 rootMethod.invoke(apRoot,e.getText());
46 }
47 }
48 builder.setRoot(apRoot);
49 }
50 }
51 // 释放资源
52 inputStream.close();
53 inputStream = null;
54 return builder;
55 }
原文链接:https://blog.csdn.net/wei389083222/article/details/80802839
来源:oschina
链接:https://my.oschina.net/u/4343260/blog/4296379