“ 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 + port
String 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];// data
while (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";//报文类型固定为2
protected 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字节左补0
string 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/
版权归作者所有,转载请注明出处