Java 杂谈

工商银行银企互联Java开发

2019-05-20  本文已影响141人  Helloword_Cc

由于微信转账限额(大概是每个月20万的样子),公司业务上需要将公账的金额转出到用户被金额限制,所以准备接入工商银行的银企互联(公司银行公账——>用户账户),开发中发现工商的工作人员丢过来的开发文档不是最新的,导致折腾了半天,今天折腾出来了做个笔记,也让大家少走弯路。。

  1. 安装工商银行银企互联工具(NSAE_NC),这个东西必须安装在和调用服务的功能同一台服务器。因为工商限制本机调用访问,为了提高安全性。

    安装完成打开127.0.0.1,默认80端口,如果80端口跑了其他服务器,则修改config.xml文件的端口号。 image.png
    好了,startup启动。访问127.0.0.1:8081(由于作者端口号改为8081)
    image.png

    启动成功,修改客户端,服务端,签名的配置,


    image.png
    本地监听IP为本地IP,端口自己定,不被占用就好。后台服务IP填写directbank.icbc.com.cn端口号为 446,再其次就是导入证书跟秘钥了。 image.png

三个端都显示运行中则证明运行成功。如果有报错可以看日志文件再自己分析解决。日志路径如下图:


image.png

好了,终于把东西安装好了,抽根烟继续。😄

  1. 我们先来看看这个***开发文档怎么说。


    image.png
1.  **结算类:**

1.  企业按照工行提供的xml包格式进行打包,在局域网内与NetSafe Client的签名端口建立Socket连接,通过此连接向签名端口发送http数据包。http包头中需包含“**Content-Length**”和“**Content-Type**”两个属性。其中“**Content-Length:**”后面是需要签名的二进制数据包的长度,“**Content-Type:**”后面是需要签名的标记,为**INFOSEC_SIGN/1.0**。(注意大小写)

**http****请求格式:****action=**”[http://客户端NetSafe Client的地址和签名端口号](http://%E5%AE%A2%E6%88%B7%E7%AB%AFNetSafe%20Client%E7%9A%84%E5%9C%B0%E5%9D%80%E5%92%8C%E7%AD%BE%E5%90%8D%E7%AB%AF%E5%8F%A3%E5%8F%B7)**”**

**请求数据格式:**结算类请求提交的xml包

NetSafe Client对xml包进行签名后,通过http协议将签名结果返回给企业系统。如签名成功<sign>标签与</sign>标签之间的部分为签名结果。

NetSafe Client返回的签名包如下:

<html>

<head>

<title>签名结果</title>

<result>0</result>

</head>

<body>

<sign>MIIIXAYJKovcNAQcCo…………. 0BlLdSgw=</sign>

</body>

</html>

2. 企业按照工行提供的xml包格式进行打包,在局域网内通过http协议以POST方式将交易包发送到NetSafe Client的安全http协议服务器。
http请求格式:action=”http://客户端NetSafe Client的地址和加密端口号/servlet/ICBCCMPAPIReqServlet?userID=证书ID&PackageID=包序列ID &SendTime=请求时间” 
请求数据格式(post方式):Version=版本号(区分版本时间,暂定0.0.0.1) &TransCode=交易代码(区分交易类型,每个交易固定)&BankCode=客户的归属单位&GroupCIS=客户的归属编码&ID=客户的证书ID(无证书客户可空)&PackageID=客户的指令包序列号(由客户ERP系统产生,不可重复)&Cert=客户的证书公钥信息(进行BASE64编码;NC客户送空) &reqData=客户的xml请求数据
其中:包序列ID、证书ID应根据实际情况进行更改,请求时间为企业发出该交易请求包的当前系统时间。post方式最后不允许有回车等其他乱字符,TransCode交易名称应与xml包内标签<TransCode></TransCode>中的值一致,action中的证书ID、PackageID与请求数据格式中的证书ID、PackageID、xml包中的证书ID、PackageID的值三者相一致。

3.  NetSafe Client将企业送来的签名包加密后按照https协议,通过互联网/专线发送到工行端的NetSafe Server,再发往工行网银进行处理。(本步由NetSafe Client完成,企业无需处理)。

额,看懂了吗,看不懂听我说。把xml报文请求到刚才安装的签名端进行签名,签名完再http请求到刚才安装的客户端,客户端再帮你请求服务端,服务端再帮你请求工行系统。你觉得很绕吗,你的感觉是对的,😄
其实这些都是小事情,主要还是xml报文,工行丢过来的文档是旧的,导致总是报错xml解析包失败。作者自己在网上找了好几天,拼拼凑凑,现在给个成功的xml大家,让大家可以绕坑。

 // 明文组包
        xmlcontent = "<?xml version=\"1.0\" encoding = \"GBK\"?>" +
                "<CMS><eb>" +
                "<pub>" +
                "<TransCode>PAYENT</TransCode>" +
                "<CIS></CIS>" + //你的cis,自己找工商获取
                "<BankCode>102</BankCode>" +
                "<ID></ID>" + // 证书id
                "<TranDate>"+DateFormatUtil.formatDate(new Date(),DateFormatUtil.PT_YYYYMMDD)+"</TranDate>" +//日期,格式自己看
                "<TranTime>"+DateFormatUtil.formatDate(new Date(),"hhmmssSSS")+"</TranTime>" +
                "<fSeqno>"+fSeqno+"</fSeqno>" +  //FDK随机字符 跟下面post到客户端的PackageID一致
                "</pub>" +
                "<in>" +
                "<OnlBatF>1</OnlBatF>" +
                "<SettleMode>0</SettleMode>" +
                "<TotalNum>1</TotalNum>" + //这个为此次转账的发起数量(一个xml报文可以发起对多个账号提现)
                "<TotalAmt>10</TotalAmt>" +//所有转账的总金额,注意这个单位为分
                "<SignTime>"+time+"</SignTime>" +
                "<ReqReserved1></ReqReserved1>" +
                "<ReqReserved2></ReqReserved2>" +
                "<rd>" +
                "<iSeqno>1</iSeqno>" +
                "<ReimburseNo></ReimburseNo>" +
                "<ReimburseNum></ReimburseNum>" +
                "<StartDate></StartDate>" +
                "<StartTime></StartTime>" +
                "<PayType>2</PayType>" +
                "<PayAccNo></PayAccNo>" +// 付款方的银行卡账号
                "<PayAccNameCN></PayAccNameCN>" + //支付的账户名称,需要跟刚才填写的账号对应,实名的,否则报错
                "<PayAccNameEN></PayAccNameEN>" +
                "<RecAccNo></RecAccNo>" +//收款方的账号
                "<RecAccNameCN></RecAccNameCN>" +//收款方的账户名称,也需要跟账号对应
                "<RecAccNameEN></RecAccNameEN>" +
                "<SysIOFlg>1</SysIOFlg>" +//第一笔发起
                "<IsSameCity></IsSameCity>" +
                "<Prop>1</Prop>" +
                "<RecICBCCode></RecICBCCode>" +
                "<RecCityName></RecCityName>" +
                "<RecBankNo></RecBankNo>" +
                "<RecBankName></RecBankName>" +
                "<CurrType>001</CurrType>" +
                "<PayAmt>10</PayAmt>" +//第一笔发起的金额
                "<UseCode></UseCode>" +
                "<UseCN>上线测试</UseCN>" +
                "<EnSummary></EnSummary>" +
                "<PostScript></PostScript>" +
                "<Summary></Summary>" +
                "<Ref>"+fSeqno+"</Ref>" +
                "<Oref></Oref>" +
                "<ERPSqn></ERPSqn>" +
                "<BusCode></BusCode>" +
                "<ERPcheckno></ERPcheckno>" +
                "<CrvouhType></CrvouhType>" +
                "<CrvouhName></CrvouhName>" +
                "<CrvouhNo></CrvouhNo>" +
                "<ReqReserved3></ReqReserved3>" +
                "<ReqReserved4></ReqReserved4>" +
                "</rd>" +
                "</in></eb></CMS>";

xml的报文需要注意的是,文档写着的:xml包内标签<TransCode></TransCode>中的值一致,action中的证书ID、PackageID与请求数据格式中的证书ID、PackageID、xml包中的证书ID、PackageID的值三者相一致。

基本的必填项目我已经备注,其他的可以跟着填写。
现在上整个请求代码

public Map<String,Object> settleAccounts(HttpServletRequest request) {
       Map result  =  new HashMap();
       String xmlcontent = ""; // xml报文格式的字符串
       String signcontent = ""; // 签名返回的信息
       String repcontent = ""; // 工行返回的信息
       String NCIp = ""; //你安装工行系统服务器的ip
       // String NCIp = "127.0.0.1";
       String NCPort = ""; // 加密端口号
       String NCPort2 = ""; // 客户端端口号
       String fSeqno ="FRK"+ DateFormatUtil.formatDate(new Date(),"yyyyMMddHHmmss");
       String time =DateFormatUtil.formatDate(new Date(),"yyyyMMddHHmmssSSS");



       // 明文组包
       xmlcontent = "<?xml version=\"1.0\" encoding = \"GBK\"?>" +
               "<CMS><eb>" +
               "<pub>" +
               "<TransCode>PAYENT</TransCode>" +
               "<CIS></CIS>" +
               "<BankCode>102</BankCode>" +
               "<ID></ID>" +
               "<TranDate>"+DateFormatUtil.formatDate(new Date(),DateFormatUtil.PT_YYYYMMDD)+"</TranDate>" +
               "<TranTime>"+DateFormatUtil.formatDate(new Date(),"hhmmssSSS")+"</TranTime>" +
               "<fSeqno>"+fSeqno+"</fSeqno>" +
               "</pub>" +
               "<in>" +
               "<OnlBatF>1</OnlBatF>" +
               "<SettleMode>0</SettleMode>" +
               "<TotalNum>1</TotalNum>" +
               "<TotalAmt>10</TotalAmt>" +
               "<SignTime>"+time+"</SignTime>" +
               "<ReqReserved1></ReqReserved1>" +
               "<ReqReserved2></ReqReserved2>" +
               "<rd>" +
               "<iSeqno>1</iSeqno>" +
               "<ReimburseNo></ReimburseNo>" +
               "<ReimburseNum></ReimburseNum>" +
               "<StartDate></StartDate>" +
               "<StartTime></StartTime>" +
               "<PayType>2</PayType>" +
               "<PayAccNo></PayAccNo>" +
               "<PayAccNameCN></PayAccNameCN>" +
               "<PayAccNameEN></PayAccNameEN>" +
               "<RecAccNo></RecAccNo>" +
               "<RecAccNameCN></RecAccNameCN>" +
               "<RecAccNameEN></RecAccNameEN>" +
               "<SysIOFlg>1</SysIOFlg>" +
               "<IsSameCity></IsSameCity>" +
               "<Prop>1</Prop>" +
               "<RecICBCCode></RecICBCCode>" +
               "<RecCityName></RecCityName>" +
               "<RecBankNo></RecBankNo>" +
               "<RecBankName></RecBankName>" +
               "<CurrType>001</CurrType>" +
               "<PayAmt>10</PayAmt>" +
               "<UseCode></UseCode>" +
               "<UseCN>上线测试</UseCN>" +
               "<EnSummary></EnSummary>" +
               "<PostScript></PostScript>" +
               "<Summary></Summary>" +
               "<Ref>"+fSeqno+"</Ref>" +
               "<Oref></Oref>" +
               "<ERPSqn></ERPSqn>" +
               "<BusCode></BusCode>" +
               "<ERPcheckno></ERPcheckno>" +
               "<CrvouhType></CrvouhType>" +
               "<CrvouhName></CrvouhName>" +
               "<CrvouhNo></CrvouhNo>" +
               "<ReqReserved3></ReqReserved3>" +
               "<ReqReserved4></ReqReserved4>" +
               "</rd>" +
               "</in></eb></CMS>";

       String dataTime =DateFormatUtil.formatDate(new Date(),"yyyyMMddHHmmss");


       logger.error("xml报文明文组包:" + xmlcontent);
       HttpClient myclient = null;
       PostMethod httppost = null;


       // 如果明文xmlcontent中包括SignTime节点,即该交易需要签名
       if (xmlcontent.indexOf("<SignTime>") > -1) {
           try {
               myclient = new HttpClient(); // 构建http客户端
               httppost = new PostMethod("http://" + NCIp + ":" + NCPort2); // 加密端口
               httppost.addRequestHeader("Content-Type", "INFOSEC_SIGN/1.0");
               InputStream in = new ByteArrayInputStream(xmlcontent.getBytes());
               httppost.setRequestBody(in);
               int returnFlag = myclient.executeMethod(httppost); // 获得http返回码
               String postResult = httppost.getResponseBodyAsString();
               signcontent = new String(postResult.getBytes("ISO8859-1"),"gb2312");
               System.out.println("NC签名返回数据如下:" + signcontent);
              // String is ="";
               logger.error("NC签名返回数据如下:" + signcontent);
           } catch (Exception e) {
               logger.error("签名出错:"+ e.toString());
               e.printStackTrace();
           } finally {
               try {
                   httppost.releaseConnection(); // 释放http连接
                   myclient.getHttpConnectionManager().closeIdleConnections(0);
               } catch (Exception e2) {
                   logger.error("签名释放出错:"+ e2.toString());
               }
               myclient = null;
               httppost = null;
           }

           signcontent.replaceAll("\n", "");
           int beginSign = signcontent.indexOf("<sign>") + 6;
           int endSign = signcontent.indexOf("</sign>");
           signcontent = signcontent.substring(beginSign, endSign);
           logger.error("签名:"+ signcontent);
       } else {
           //如果不需要签名直接放明文
           signcontent = xmlcontent;
       }

       String urlStr1 = "http://" + NCIp + ":" + NCPort+ "/servlet/ICBCCMPAPIReqServlet";

       try {
           myclient = new HttpClient(); // 构建http客户端
           httppost = new PostMethod(urlStr1); // 加密端口
           httppost.addRequestHeader("Content-Type","application/x-www-form-urlencoded");
          /* httppost.addParameter("Version", "0.1");
           ;*/
           httppost.addParameter("PackageID", fSeqno);
           httppost.addParameter("Version", "1.0");
           httppost.addParameter("TransCode", "PAYENT");
           httppost.addParameter("BankCode", "102");
           httppost.addParameter("Cert", "");
           httppost.addParameter("ID", ");//证书id
           httppost.addParameter("GroupCIS", "");//CIS
           httppost.addParameter("reqData", signcontent);



      

           int returnFlag = myclient.executeMethod(httppost); // 获得http返回码
           String postResult = httppost.getResponseBodyAsString();
           repcontent = new String(postResult.getBytes("ISO8859-1"), "gb2312");
           System.out.println("工行返回数据如下:" + repcontent);
           logger.error("工行返回数据如下:" + repcontent);
           logger.error("http返回码:" + returnFlag);
           result.put("httpCode",returnFlag);
       } catch (Exception e) {
           e.printStackTrace();
           logger.error("银企互联报错:" + e.toString());
           ExceptionUntil.setLogger(logger,e);
       } finally {
           try {
               httppost.releaseConnection(); // 释放http连接
               myclient.getHttpConnectionManager().closeIdleConnections(0);
           } catch (Exception e2) {
               logger.error("银企互联释放http连接报错:" + e2.toString());
               ExceptionUntil.setLogger(logger,e2);
           }
           myclient = null;
           httppost = null;
       }
       try {
           repcontent = repcontent.substring(8);
           logger.error("银企互联返回----"+repcontent);
           byte[] decodeResult = TesterpTrans.getFromBASE64(repcontent);
           repcontent = new String(decodeResult);
           System.out.println("base64解码如下:" + repcontent);
           logger.error("base64解码如下:" + repcontent);
           result.put("xml",repcontent);
           result.put("result","true");
       } catch (Exception e) {
           e.printStackTrace();
           logger.error("银企互联返回base64报错:" + e.toString());
           result.put("result","false");
           result.put("message",e.toString());
           ExceptionUntil.setLogger(logger,e);
       }
       return  result;
 }

基本看了作者的代码demo也可以实现了。接口成功后iRetMsg返回成功,查询提现的账户。有一笔提现,终于成功接通。😄


image.png
上一篇 下一篇

猜你喜欢

热点阅读