asp.net core 3.1开发交通银行支付接口笔记二支付
asp.net core 3.1开发交通银行支付接口笔记三退款、查询和收尾
例行吐槽
这个接口上一年很早时候客户就和银行对接了。知道元旦后才给的接口。这效率。。。没谁了。所以,我回老家过完元旦赶紧火急火燎的赶回来写码。按照经验,这
个接口只要写得好没歧义、完整难度不大。如果有源码接口DEMO提供,速度可以控制在半天内。可是呢提供过来有助于开发的只有2个
1:交通银行“慧智付”报文交换规范(商户正式版)文档(只有接口说明连流程都没有)
2:签名验签方法代码(java版一份,socket版其它语言一份)
当时没说明这个socket是干嘛的,java的还是看使用配置说明看出是java版。socket我看了是java的包,顿时懵逼结果一顿很久的沟通操作后回复说其它语言用socket,我去,在说明包里面加个说明文档就那么难?好了,接下来开始
开始开发前的选择
因为我这个项目要求手机公众号支付,当时因为不懂交行支付是啥流程,文档也没写流程。
于是自己按照经验设计是跳转交行他们的H5页面输入卡号信息支付完成后再跳转回来
文档没有写流程只好看文字盲猜选定跳转支付扣款,不过后来又折腾了一顿客户和交行开发人员一起建了个群,通过咨询得知,这个这个跳转支付是在本地网页点击支付>跳转交行服务器>输入微信密码>完成支付。唉在文档加个流程就这么难?
配置:原来SOCKET启动后发送加签信息返回00000000联系了交行技术人员,然后提供了2个商户号。一个加签用一个交易用。。。。这操作。。。
按照说明书放置电子证书。写配置商户号和密码。日志路径即可。文档这个倒是写的明白。
开始开发
第一步:写底层代码。这里代码比较多。就放入有代表性的一些意思意思即可
第一点:先是生成xml信息的
其它的生成只要按照这个生成即可
/// <summary>
/// 生成支付报文
/// </summary>
/// <param name="orderNo">订单号</param>
/// <param name="pay">支付金额</param>
/// <param name="tranContent">客户看的内容</param>
/// <param name="remark">后台看的备注</param>
/// <returns></returns>
public static string CreatePayMessage(string orderNo, decimal pay, string tranContent, string remark)
{
XmlDocument xmldoc = new XmlDocument();
//加入XML的声明段落:<?xmlversion="1.0" encoding="utf-8"?>
XmlDeclaration xmldecl = xmldoc.CreateXmlDeclaration("1.0", "utf-8", null);
xmldoc.AppendChild(xmldecl);
//root节点
XmlElement rootNode = xmldoc.CreateElement("Document");
rootNode.AppendChild(createHeadXML(xmldoc, "接口标识号"));
rootNode.AppendChild(createPayBodyXML(xmldoc, orderNo, pay, tranContent, remark));
xmldoc.AppendChild(rootNode);
return signMessage(xmldoc);
}
/// <summary>
/// 生成支付内容XML
/// </summary>
/// <param name="xmldoc">主XML</param>
/// <param name="orderNo">订单号</param>
/// <param name="pay">支付金额</param>
/// <param name="tranContentStr">客户看的内容</param>
/// <param name="remark">后台看的备注</param>
/// <returns></returns>
private static XmlElement createPayBodyXML(XmlDocument xmldoc, string orderNo, decimal pay, string tranContentStr, string remark)
{
XmlElement bodyNode = xmldoc.CreateElement("Body");
//商户交易编号
XmlElement payMerTranNo = xmldoc.CreateElement("PayMerTranNo");
payMerTranNo.InnerText = orderNo;
bodyNode.AppendChild(payMerTranNo);
//线上或线下
XmlElement location = xmldoc.CreateElement("Location");
location.InnerText = "ONLINE";
bodyNode.AppendChild(location);
//交易场景
XmlElement tranScene = xmldoc.CreateElement("TranScene");
tranScene.InnerText = "B2C-JSAPI-WECHAT";
bodyNode.AppendChild(tranScene);
//金额
XmlElement amount = xmldoc.CreateElement("Amount");
amount.InnerText = pay.ToString("0.00");
bodyNode.AppendChild(amount);
//币种
XmlElement currency = xmldoc.CreateElement("Currency");
currency.InnerText = "CNY";
bodyNode.AppendChild(currency);
//交易内容
XmlElement tranContent = xmldoc.CreateElement("TranContent");
tranContent.InnerText = tranContentStr;
bodyNode.AppendChild(tranContent);
//商户内部备注
XmlElement merMemo = xmldoc.CreateElement("MerMemo");
merMemo.InnerText = remark;
bodyNode.AppendChild(merMemo);
//交易选项(暂时不用。留着)
//交易失效时间
XmlElement validPeriod = xmldoc.CreateElement("ValidPeriod");
validPeriod.InnerText = DateTime.Now.AddMinutes(30).ToString("yyyyMMddHHmmss");
bodyNode.AppendChild(validPeriod);
//后台通知地址
XmlElement notifyURL = xmldoc.CreateElement("NotifyURL");
notifyURL.InnerText = GlobalData.TranInfo["notifyurl"];
bodyNode.AppendChild(notifyURL);
//前台通知地址(暂时不用微信公众号无法通知,员工后台让操作员自己刷新页面即可)
//需要通知中返回的字段(暂时不用)
return bodyNode;
}
第二点:加签解签。
要吐槽一下。文档给的这段极为有歧义,这个AttachedSign和AttachedVerify没说明是啥。就写这个英文单词在这里,因为看了java代码,当时我理解成是加签后的字符串,或者调用java里面的一个加签方法。,然后可想而知,一顿操作猛如虎,返回加签0000000,问了技术人员才知道正确意思。又浪费N多时间
加签,这里注意。是要AttachedSign开始到末尾的字节长度。不是字符长度。这里文档没仔细看。。。。可是呢?如果长度<文档字节长度才出现问题,当然。。这个长度是从单词AttachedSign或AttachedVerify开始算起。。这个文档没说。。这里继续浪费时间。。。。。。
.
/// <summary>
/// 给报文签名
/// </summary>
/// <param name="xmldoc">xml报文</param>
/// <returns></returns>
private static string signMessage(XmlDocument xmldoc)
{
string temp = "AttachedSign" + GlobalData.TranInfo["商户号"] + xmldoc.OuterXml;
int strLength = Encoding.UTF8.GetByteCount(temp);
string tempSign = BCMSocket.GetData(strLength.ToString().PadLeft(8, '0') + temp);
return tempSign.Substring(8);
}
解签的。同样道理是要AttachedVerify开始到末尾的字节长度。不是字符长度。。这里文档没仔细看。。。。
/// <summary>
/// 解签报文
/// </summary>
/// <param name="signStr"></param>
/// <returns></returns>
public static XmlDocument UnSignMessage(string signStr)
{
string temp = "AttachedVerify" + signStr;
int strLength = Encoding.UTF8.GetByteCount(temp);
//int strLength = temp.Length;
string tempSign = BCMSocket.GetData(strLength.ToString().PadLeft(8, '0') + temp);
tempSign= tempSign.Substring(8);
StringReader Reader = new StringReader(tempSign);
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(Reader);
return xmlDoc;
}
顺便附上获得指定标签值的代码。
private string getXMLInnerText(Dictionary<string, string> xmlNodeDic, string key)
{
if (xmlNodeDic.ContainsKey(key))
{
return xmlNodeDic[key];
}
return "";
}
第三点:服务器发送接收代码
就写一段代表性的,注意这里用TCL连接。而且发送和接收都用UTF-8编码。我就是因为没注意导致接收到的中文全是 “?” 号,当时还以为SOCKET出问题了。。。唉
public class BCMSocket
{
public static string GetData(string sendStr)
{
string url = "127.0.0.1"; // URL也可以是(http://www.baidu.com/)这种形式
int port = Convert.ToInt32(GlobalData.TranInfo["signPoint"]); //端口
BCMSocketHelper o = new BCMSocketHelper(); //创建新对象
o.Connection(url, port); //打开远程端口
o.Send(sendStr); //发送数据
string rs = o.Recev();
o.Dispose(); //销毁对象
return rs;
}
}
这是SOCKET的资料:https://www.cnblogs.com/feifeiwzh/articles/1267895.html