“ Yuema约吗?一起学技术,一起成长!学海无涯 高人带路系列”
程序的世界,就是有坑的地方!分享踩坑的心得与体验!每天分享一点点!
关注公众号,进入学海无涯,高人带路模式!!银企直连再难,有人带路,轻松搞定
招商银行银企直连怎么收费
您好,银企直联主要是可以将企业网银与企业财务软件连接的方式,一般企业申请银企“直联”会收取服务费:开通10000元,每月基本服务费200元,或按协议价格收取。通过银企直联转账手续费同企业网银支付功能收费。
这个收费标准是否已经让一波小伙伴放弃了银企直连,如果每家银行都这个标准来算的话,全部接下来,成本也不少。这就是第三方支付的生存意义了。结合之前写的平安银行的银企直连,再来看招商银行的,会发现,基本是一相通的,莫非出自一个外包公司之手?
温故而知新
平安银行对接|银企对接扫雷排坑实战经验分享 感受一下财大气粗的银行对接 感受一下等长报文的痛苦 未曾经历无以感受他人在坑中崩溃状
熟悉的前置机
对比平安银行的对接玩法,再来看招商银行的银企直连,又看到了这么一个熟悉的前置机”神兽”,这跟支付宝对接、微信对接的玩法,出入相当的大。
demo
java版demo就4个文件HttpRequest.java、SaxHandler.java、SocketRequest.java、XmlPacket.java
import java.io.*;import java.net.*;import java.util.Map;import java.util.Properties;/**** HTTP通讯范例: 直接支付**/public class HttpRequest {/*** 生成请求报文** @return*/private String getRequestStr() {// 构造支付的请求报文XmlPacket xmlPkt = new XmlPacket("Payment", "USRA01");Map mpPodInfo = new Properties();mpPodInfo.put("BUSCOD", "N02031");xmlPkt.putProperty("SDKPAYRQX", mpPodInfo);Map mpPayInfo = new Properties();mpPayInfo.put("YURREF", "201009270001");mpPayInfo.put("DBTACC", "571905400910411");mpPayInfo.put("DBTBBK", "57");mpPayInfo.put("DBTBNK", "招商银行杭州分行营业部");mpPayInfo.put("DBTNAM", "NEXT TEST");mpPayInfo.put("DBTREL", "0000007715");mpPayInfo.put("TRSAMT", "1.01");mpPayInfo.put("CCYNBR", "10");mpPayInfo.put("STLCHN", "N");mpPayInfo.put("NUSAGE", "费用报销款");mpPayInfo.put("CRTACC", "571905400810812");mpPayInfo.put("CRTNAM", "测试收款户");mpPayInfo.put("CRTBNK", "招商银行");mpPayInfo.put("CTYCOD", "ZJHZ");mpPayInfo.put("CRTSQN", "摘要信息:[1.01]");xmlPkt.putProperty("SDKPAYDTX", mpPayInfo);return xmlPkt.toXmlString();}/*** 连接前置机,发送请求报文,获得返回报文** @param data* @return* @throws MalformedURLException*/private String sendRequest(String data) {String result = "";try {URL url;url = new URL("http://localhost:8080");HttpURLConnection conn;conn = (HttpURLConnection) url.openConnection();conn.setRequestMethod("POST");conn.setDoInput(true);conn.setDoOutput(true);OutputStream os;os = conn.getOutputStream();os.write(data.toString().getBytes("gbk"));os.close();BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));String line;while ((line = br.readLine()) != null) {result += line;}System.out.println(result);br.close();} catch (MalformedURLException e) {e.printStackTrace();} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (ProtocolException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return result;}/*** 处理返回的结果** @param result*/private void processResult(String result) {if (result != null && result.length() > 0) {XmlPacket pktRsp = XmlPacket.valueOf(result);if (pktRsp != null) {String sRetCod = pktRsp.getRETCOD();if (sRetCod.equals("0")) {Map propPayResult = pktRsp.getProperty("NTQPAYRQZ", 0);String sREQSTS = (String) propPayResult.get("REQSTS");String sRTNFLG = (String) propPayResult.get("RTNFLG");if (sREQSTS.equals("FIN") && sRTNFLG.equals("F")) {System.out.println("支付失败:"+ propPayResult.get("ERRTXT"));} else {System.out.println("支付已被银行受理(支付状态:" + sREQSTS + ")");}} else if (sRetCod.equals("-9")) {System.out.println("支付未知异常,请查询支付结果确认支付状态,错误信息:"+ pktRsp.getERRMSG());} else {System.out.println("支付失败:" + pktRsp.getERRMSG());}} else {System.out.println("响应报文解析失败");}}}public static void main(String[] args) {try {HttpRequest request = new HttpRequest();// 生成请求报文String data = request.getRequestStr();// 连接前置机,发送请求报文,获得返回报文String result = request.sendRequest(data);// 处理返回的结果request.processResult(result);} catch (Exception e) {System.out.println(e.getMessage());}}}
import org.xml.sax.Attributes;import org.xml.sax.SAXException;import org.xml.sax.helpers.DefaultHandler;import java.util.*;/**** 招行XML报文解析类**/public class SaxHandler extends DefaultHandler {int layer=0;String curSectionName;String curKey;String curValue;XmlPacket pktData;Map mpRecord;public SaxHandler(XmlPacket data){curSectionName = "";curKey = "";curValue = "";pktData = data;mpRecord = new Properties();}public void startElement(String uri, String localName, String qName,Attributes attributes) throws SAXException {layer++;if(layer==2){curSectionName = qName;}else if(layer==3){curKey = qName;}}public void endElement(String uri, String localName, String qName)throws SAXException {if(layer==2){pktData.putProperty(curSectionName, mpRecord);mpRecord = new Properties();}else if(layer==3){mpRecord.put(curKey, curValue);if(curSectionName.equals("INFO")){if(curKey.equals("FUNNAM")){pktData.setFUNNAM(curValue);}else if(curKey.equals("LGNNAM")){pktData.setLGNNAM(curValue);}else if(curKey.equals("RETCOD")){pktData.setRETCOD(curValue);}else if(curKey.equals("ERRMSG")){pktData.setERRMSG(curValue);}}}curValue = "";layer--;}public void characters(char[] ch, int start, int length)throws SAXException {if(layer==3){String value = new String(ch, start, length);if(ch.equals("\n")){curValue += "\r\n";}else{curValue += value;}}}}
import java.io.*;import java.net.*;import java.util.*;/*** SOCKET通讯范例:查询账户信息*/public class SocketRequest {/*** 生成请求报文** @return*/private String getRequestStr() {// 构造查询余额的请求报文XmlPacket xmlPkt = new XmlPacket("GetAccInfo", "USRA01");Map mpAccInfo = new Properties();mpAccInfo.put("BBKNBR", "57");mpAccInfo.put("ACCNBR", "571905400610301");xmlPkt.putProperty("SDKACINFX", mpAccInfo);return xmlPkt.toXmlString();}/*** 连接前置机,发送请求报文,获得返回报文** @param data* @return*/private String sendRequest(String data) {// 连接前置机:Ip + portString hostname = "localhost";int port = 8080;String result = null;try {InetAddress addr = InetAddress.getByName(hostname);Socket socket = new Socket(addr, port);// 设置2分钟通讯超时时间socket.setSoTimeout(120 * 1000);DataOutputStream wr = new DataOutputStream(socket.getOutputStream());// 通讯头为8位长度,右补空格:先补充8位空格,再取前8位作为报文头String strLen = String.valueOf(data.getBytes().length) + " ";wr.write((strLen.substring(0, 8) + data).getBytes());wr.flush();DataInputStream rd = new DataInputStream(socket.getInputStream());// 接收返回报文的长度byte rcvLen[] = new byte[8];rd.read(rcvLen);String sLen = new String(rcvLen);int iSum = 0;try {iSum = Integer.valueOf(sLen.trim());} catch (NumberFormatException e) {System.out.println("报文头格式错误:" + sLen);}if (iSum > 0) {System.out.println("响应报文长度:" + iSum);// 接收返回报文的内容int nRecv = 0, nOffset = 0;byte[] rcvData = new byte[iSum];// datawhile (iSum > 0) {nRecv = rd.read(rcvData, nOffset, iSum);if (nRecv < 0)break;nOffset += nRecv;iSum -= nRecv;}result = new String(rcvData);System.out.println("响应报文内容:" + result);}wr.close();rd.close();} catch (java.net.SocketTimeoutException e) {System.out.println("通讯超时:" + e.getMessage());} catch (IOException e) {System.out.println(e.getMessage());}return result;}/*** 处理返回的结果** @param result*/private void processResult(String result) {if (result != null && result.length() > 0) {XmlPacket pktRsp = XmlPacket.valueOf(result);if (pktRsp != null) {if (pktRsp.isError()) {System.out.println("取账户信息失败:" + pktRsp.getERRMSG());} else {Map propAcc = pktRsp.getProperty("NTQACINFZ", 0);System.out.println("账户" + propAcc.get("ACCNBR") + "的联机余额:"+ propAcc.get("ONLBLV"));}} else {System.out.println("响应报文解析失败");}}}public static void main(String[] args) {try {SocketRequest request = new SocketRequest();// 生成请求报文String data = request.getRequestStr();// 连接前置机,发送请求报文,获得返回报文String result = request.sendRequest(data);// 处理返回的结果request.processResult(result);} catch (Exception e) {System.out.println(e.getMessage());}}}
import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStream;import java.util.*;import javax.xml.parsers.ParserConfigurationException;import javax.xml.parsers.SAXParser;import javax.xml.parsers.SAXParserFactory;import java.io.ByteArrayInputStream;import org.xml.sax.SAXException;/**** 招行XML通讯报文类**/public class XmlPacket{protected String FUNNAM;protected final String DATTYP="2";//报文类型固定为2protected String LGNNAM;protected String RETCOD;protected String ERRMSG;protected Map data; //<String,Vector>public XmlPacket(){data = new Properties();}public XmlPacket(String sFUNNAM){FUNNAM = sFUNNAM;data = new Properties();}public XmlPacket(String sFUNNAM, String sLGNNAM){FUNNAM = sFUNNAM;LGNNAM = sLGNNAM;data = new Properties();}public String getFUNNAM() {return FUNNAM;}public void setFUNNAM(String fUNNAM) {FUNNAM = fUNNAM;}public String getLGNNAM() {return LGNNAM;}public void setLGNNAM(String lGNNAM) {LGNNAM = lGNNAM;}public String getRETCOD() {return RETCOD;}public void setRETCOD(String rETCOD) {RETCOD = rETCOD;}public String getERRMSG() {return ERRMSG;}public void setERRMSG(String eRRMSG) {ERRMSG = eRRMSG;}/*** XML报文返回头中内容是否表示成功* @return*/public boolean isError(){if(RETCOD.equals("0")){return false;}else{return true;}}/*** 插入数据记录* @param sSectionName* @param mpData <String, String>*/public void putProperty(String sSectionName, Map mpData){if(data.containsKey(sSectionName)){Vector vt = (Vector)data.get(sSectionName);vt.add(mpData);}else{Vector vt = new Vector();vt.add(mpData);data.put(sSectionName, vt);}}/*** 取得指定接口的数据记录* @param sSectionName* @param index 索引,从0开始* @return Map<String,String>*/public Map getProperty(String sSectionName, int index){if(data.containsKey(sSectionName)){return (Map)((Vector)data.get(sSectionName)).get(index);}else{return null;}}/*** 取得制定接口数据记录数* @param sSectionName* @return*/public int getSectionSize(String sSectionName){if(data.containsKey(sSectionName)){Vector sec = (Vector)data.get(sSectionName);return sec.size();}return 0;}/*** 把报文转换成XML字符串* @return*/public String toXmlString(){StringBuffer sfData = new StringBuffer("<?xml version='1.0' encoding = 'GBK'?>");sfData.append("<CMBSDKPGK>");sfData.append("<INFO><FUNNAM>"+FUNNAM+"</FUNNAM><DATTYP>"+DATTYP+"</DATTYP><LGNNAM>"+LGNNAM+"</LGNNAM></INFO>");int secSize = data.size();Iterator itr = data.keySet().iterator();while(itr.hasNext()){String secName = (String)itr.next();Vector vt = (Vector)data.get(secName);for(int i=0; i<vt.size(); i++){Map record = (Map)vt.get(i);Iterator itr2 = record.keySet().iterator();sfData.append("<"+secName+">");while(itr2.hasNext()){String datakey = (String)itr2.next();String dataValue = (String)record.get(datakey);sfData.append("<"+datakey+">");sfData.append(dataValue);sfData.append("</"+datakey+">");}sfData.append("</"+secName+">");}}sfData.append("</CMBSDKPGK>");return sfData.toString();}/*** 解析xml字符串,并转换为报文对象* @param message*/public static XmlPacket valueOf(String message) {SAXParserFactory saxfac = SAXParserFactory.newInstance();try {SAXParser saxparser = saxfac.newSAXParser();ByteArrayInputStream is = new ByteArrayInputStream(message.getBytes());XmlPacket xmlPkt= new XmlPacket();saxparser.parse(is, new SaxHandler(xmlPkt));is.close();return xmlPkt;} catch (ParserConfigurationException e) {e.printStackTrace();} catch (SAXException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}}
C#版demo
相对java版,C#版的demo好歹是一个工程,用vs可以直接打开。
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;using System.Net;using System.Net.Sockets;using System.IO;namespace CSharpTest{class Program{static void Main(){//报文内容,用户自己根据接口组装string strSendData = "<?xml version=\"1.0\" encoding = \"GBK\"?><CMBSDKPGK><INFO><FUNNAM>SDKCSFDFBRTIMG</FUNNAM><DATTYP>2</DATTYP><LGNNAM>QGZ01</LGNNAM></INFO><CSRRCFDFY0><EACNBR>216082647110001</EACNBR><BEGDAT>20170426</BEGDAT><ENDDAT>20170526</ENDDAT><RRCFLG>1</RRCFLG><RRCCOD>HD002016</RRCCOD></CSRRCFDFY0></CMBSDKPGK>";//前置机部署的IP地址string strIP = "127.0.0.1";//Socket传输方式前置机的监听端口int iSocketPort = 1080;//Http传输方式前置机的监听端口int iHttpPort = 8080;string strReciveData = string.Empty;Console.WriteLine("请选择通讯方式:1.Socket 2.HTTP");string strType = Console.ReadLine();if (strType.Equals("1")){strReciveData = Send_Socket(strSendData, strIP, iSocketPort);}else if (strType.Equals("2")){strReciveData = Send_Http(strSendData, strIP, iHttpPort);}else{Console.WriteLine("输入有误!");}Console.WriteLine(strReciveData);Console.WriteLine("按Enter键退出!");Console.ReadLine();}static private string Send_Socket(string strSendData, string strIP, int iPort){string strDataLen = Convert.ToString(Encoding.Default.GetBytes(strSendData).Length).PadLeft(8, '0'); //长度8字节左补0string sReturn = string.Empty;IPAddress ip = IPAddress.Parse(strIP); //前置机IP地址Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 30 * 1000);socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, 30 * 1000);try{socket.Connect(new IPEndPoint(ip, iPort));byte[] dataToBeSend = Encoding.Default.GetBytes(strDataLen + strSendData);socket.Send(dataToBeSend);byte[] receivedData = new byte[3000];int recvLen = socket.Receive(receivedData);if (recvLen == 0){throw new Exception("返回数据为空");}sReturn = Encoding.Default.GetString(receivedData, 0, recvLen);}catch (Exception ex){sReturn = ex.Message;}if (socket != null){socket.Close();}return sReturn;}static private string Send_Http(string strSendData, string strIP, int iPort){string strUrl = "http://" + strIP + ":" + iPort.ToString(); //前置机的地址和监听端口string sReturn = string.Empty;try{byte[] byteArray = Encoding.Default.GetBytes(strSendData);HttpWebRequest webReq = (HttpWebRequest)WebRequest.Create(new Uri(strUrl));webReq.Method = "POST";webReq.ContentType = "application/x-www-form-urlencoded";webReq.Timeout = 30 * 1000;webReq.ContentLength = byteArray.Length;Stream newStream = webReq.GetRequestStream();newStream.Write(byteArray, 0, byteArray.Length);newStream.Close();HttpWebResponse response = (HttpWebResponse)webReq.GetResponse();StreamReader sr = new StreamReader(response.GetResponseStream(), Encoding.Default);sReturn = sr.ReadToEnd();sr.Close();response.Close();newStream.Close();}catch (Exception ex){sReturn = ex.Message;}return sReturn;}}}
作者:钟代麒
出处:http://www.jishudao.com/
版权归作者所有,转载请注明出处