农行银企直联Java

农行银企直联

前段时间项目中接入了农行的银企直联来完成代发的功能,当我拿到银行方面给过来的文档和资料后,发现和招行的银企直联模式差不多,大概就是:在window机器上开一个类似于前置机的小程序,作为我们和银行服务器直联数据连接的中介,我们发送xml数据给前置机,前置机再将数据加密后发送给银行服务器。但是万万没想到农行这个银企直联给我搞了不小的麻烦,他们的文档写的简直是不忍直视,接口返回码也模糊不清,没有明确说明。现在我把踩过的坑给分享一下。

  1. 准备开始
    在开始之前我们会拿到2个东西,一个是中国农业银行银企通平台(4.70版).msi安装包,另一个是现金管理银企直连接入开发手册V1.2.1.wps接口文档说明
    开始安装前置机程序,完成之后是这个样子:
    农行银企直联Java_第1张图片
    测试用的客户号、操作员代码、操作员密码都会一并提供过来。
    注意:正式环境下是需要插入一个key宝,由于现在是测试环境,在安装目录的etc路径下,用记事本编辑 etc\LoginSet.xml,将 IsKey 节点中的内容改成0,就可以不用KEY登录了。
  2. 系统设置
    点击系统设置,在里面配置我们要用的模式:ERP公网接入、本地服务器地址、通讯协议、监听的端口等等。
    直接上几张图吧:
    农行银企直联Java_第2张图片
    农行银企直联Java_第3张图片
    农行银企直联Java_第4张图片
    农行银企直联Java_第5张图片
    农行银企直联Java_第6张图片
    注意:这里有一个坑需要说明一下:农行的这个程序是不支持http 协议(虽然他上面写着可以选择,无语)所以我们要用tcp协议。

  3. 组装XML数据
    看到上图,已经成功的监听到了15999端口,现在我们就要向前置机所在的机器的ip+port的这个URL上推送XML数据,例如:192.168.1.111:15999
    农行银企直联Java_第7张图片
    这里举一个范例:汇兑-单笔对似,xml数据报文是这样要求的。
    农行银企直联Java_第8张图片
    java代码实例:

    //5.汇兑-单笔对私
        ApRoot root = new ApRoot();
        root.setCCTransCode("CFRT21");
        root.setAmt("9.10");
        builder.setRoot(root);
        ApCmp cmp = new ApCmp();
        cmp.setDbAccNo("361101040010679");
        cmp.setDbProv("05");
        cmp.setDbCur("01");
        cmp.setDbLogAccNo("");
        cmp.setCrAccNo("6228453296002816764");
        cmp.setCrProv("");
        cmp.setCrCur("01");
        cmp.setCrLogAccNo("");
        cmp.setConFlag("1");
        builder.setCmp(cmp);
        ApCorp corp = new ApCorp();
        corp.setPsFlag("");
        corp.setBookingFlag("0");
        corp.setBookingDate("");
        corp.setBookingTime("");
        corp.setUrgencyFlag("0");
        corp.setOthBankFlag("0");
        corp.setCrAccName("郑春广");
        corp.setDbAccName("内猛关仪太彩悟佑慊古丝");
        corp.setWhyUse("测试");
        corp.setPostscript("测试");
        builder.setCorp(corp);
    安装要求组装数据:
    
    String s = builder.toXmlString(builder);
    /**
     * 请求数据:加密标识(1加密,0不加密) + 请求xml数据的长度(默认7位,不够补空格) + 请求的xml
     * @param s 请求的xml
     * @author jieYW
     * @date 2018/5/29
     * @return java.lang.String
     */
    public String genRequestData(String s)throws Exception{
        return "1" + String.format("%1$-6s", s.getBytes("gbk").length) + s;
    }

    注意:这里也有一个大坑,字符编码必须通过gbk的编码。前置机内部是通过gbk来解码的。这里如果不设置gbk,你在报文中有汉字的时候就gg了,会一直报这个错误:接收请求报文失败 -接收报体失败 - POLL失败退出 - 偏移量 = 750,当时这个问题困扰了我一段时间,因为我是知道他字符编码是gbk的,所以我只在socket的输出流里设置了字符的编码,经过血一般经历后终于想到了这里,然后彻底解决中文乱码的问题。

  4. socket发送数据
    将上述组装好的数据通过tcp协议发送给前置机:

    String s = builder.toXmlString(builder);
    ApHttpRequest request = new ApHttpRequest();
    String s1 = request.socketSendAndReceive("192.168.1.111", 15999, builder.genRequestData(s));
    System.out.println("接受数据:" + s1);

    测试结果:
    农行银企直联Java_第9张图片

  5. 补充代码
    XML组装:

    /**
     * 将ApXmlBuilder格式的数据转化为xml形式
     *
     * @param builder
     * @author jieYW
     * @date 2018/5/29
     * @return java.lang.String
     */
    public String toXmlString(ApXmlBuilder builder)throws Exception{
        StringBuffer sb = new StringBuffer("");
        Field[] fields = builder.getClass().getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            Field field = fields[i];
            field.setAccessible(true);
            Object item = field.get(builder);
            if(item == null){
                continue;
            }
            String name = DaoSupport.toFirstUpperCase(field.getName());
            if(!name.equals("Root")){
                sb.append("<" + name + ">");
            }
            Field[] itemFields = item.getClass().getDeclaredFields();
            for (int j = 0; j < itemFields.length; j++) {
                Field itemField = itemFields[j];
                itemField.setAccessible(true);
                Object itemObject = itemField.get(item);
                if(itemObject == null){
                    continue;
                }
                String itemFieldName = itemField.getName();
                sb.append("<" + itemFieldName + ">");
                sb.append(itemField.get(item).toString());
                sb.append(" + itemFieldName + ">");
            }
            if(!name.equals("Root")){
                sb.append(" + name + ">");
            }
        }
        sb.append("");
        return sb.toString();
    }

    发送报文:

     public String socketSendAndReceive(String url, int port,String data)throws Exception{
        System.out.println("请求数据:" + data);
        Socket socket = new Socket(url, port);
        OutputStream bw = socket.getOutputStream();
        bw.write(data.getBytes("gbk"));
        bw.flush();
        InputStream ips = socket.getInputStream();
        StringBuffer sb = new StringBuffer();
        int len = 0;
        byte[] buf = new byte[1024];
        while((len=ips.read(buf))!=-1){
            sb.append(new String(buf,0,len,"gbk"));
        }
        bw.close();
        ips.close();
        socket.close();
        return sb.toString();
    }

    解析XML:

    /**
     * 将xml数据解析为ApXmlBuilde格式的数据
     *
     * @param msg
     * @author jieYW
     * @date 2018/5/29
     * @return com.mind.pay.abc.ap.ApXmlBuilder
     */
    public static ApXmlBuilder parseXml(String msg)throws Exception {
        ApXmlBuilder builder = new ApXmlBuilder();
        Method[] methods  = builder.getClass().getMethods();
        ApRoot apRoot = new ApRoot();
        Method[] rootMethods = apRoot.getClass().getMethods();
    
        Map methodMap = new HashMap<>();
        for (Method method : methods) {
            methodMap.put(method.getName(),method);
        }
        InputStream inputStream = new ByteArrayInputStream(msg.getBytes("UTF-8"));
        SAXReader reader = new SAXReader();
        Document document = reader.read(inputStream);
        Element root = document.getRootElement();
        List elementList = root.elements();
        // 遍历所有子节点
        for (Element e : elementList){
            if(methodMap.keySet().contains("set" + e.getName())){
                Class aClass = Class.forName("com.mind.pay.abc.ap.Ap" + e.getName());
                Object itemObject = aClass.newInstance();
                List  items = e.elements();
                for (Element itemElement : items) {
                    Method[] itemMethods = itemObject.getClass().getMethods();
                    for (Method itemMethod : itemMethods) {
                        //如果字段存在,invoke方法赋值
                        if(itemMethod.getName().contains("set"+itemElement.getName())){
                            itemMethod.invoke(itemObject,itemElement.getText());
                        }
                    }
                }
                methodMap.get("set" + e.getName()).invoke(builder,itemObject);
            }else{
                //根目录下的参数,封装到apRoot中
                for (Method rootMethod : rootMethods) {
                    //如果字段存在,invoke方法赋值
                    if(rootMethod.getName().contains("set"+e.getName())){
                        rootMethod.invoke(apRoot,e.getText());
                    }
                }
                builder.setRoot(apRoot);
            }
         }
        // 释放资源
        inputStream.close();
        inputStream = null;
        return builder;
    }
  6. 代码下载地址
    https://download.csdn.net/download/wei389083222/10498109


结语:反手支持一下吧

你可能感兴趣的:(java,银行支付,农行银企直联,Java,银企直联)