公司最近有一個網站商城項目要開始開發了,這幾天老闆和幾個同事一塊兒開着需求會議,javascript
討論了接下來的業務規劃和需求策略,等技術需求一下來還要討論技術需求,html
確認後再慢慢的進入開發階段,趁着閒暇時間新造的人想總結一下進入公司不久java
接觸過的一個關於銀行支付API接口的調用,咱是第一次接觸這類東西。程序員
之後仍是儘可能養成寫技術生活博客的習慣,工做了4年多,今年纔開始想起來應該把本身的工做經歷記錄成編程
文,造成經驗積累和技術共享,之前不少經歷都淡忘了,但願之後可以往往有點思緒就記錄下來,時間長了c#
也是一筆不小的積累和總結(好記性不如爛筆頭),總不能工做這麼多年一點技術經驗積累記錄都木有,實瀏覽器
爲缺憾哉!(語言組織能力欠佳,還望海涵)dom
廢話有點多,下面是正題:socket
1、API調用環境與相關配置詳細說明;
post
要在網上支持客戶(或商城會員)使用交通銀行(BOCOM,交行國際)支付方式買東西,首先公司得與交行
合做,要求其提供支付接口API(通常程序員都知道),等公司拿到API以後須要按照銀行API要求調用的環
境安裝一些軟件(通常是由銀行提供API安裝包)以及配置各類參數:
從銀行拿到的API安裝包:
圖1(圖中start.bat文件是後來加的,具體做用後面會作說明)
各文件夾簡要說明(我直接從doc文件夾裏的技術開發說明文檔拷貝過來的):
裏面會有一個安裝說明(如上圖的簡要說明.txt),打開後內有詳細API安裝及環境參數配置說明:
圖2
相信以上圖片中白紙黑字你們都能看懂,我爲你們更詳細介紹下
(上圖所示文本中提到的 文檔 是指由交行提供的另外一個技術開發指導文檔,放在doc文件夾裏):
注:如下各類安裝配置是配置的通用版的測試環境,網上有下載的,正式調用只需修改相關配置參數便可;
1.首先在網上下載最新版本jdk,安裝java運行環境:
圖3
(根據本身電腦的狀況選擇合適版本的java運行環境,我電腦是64位系統);
2.C盤新建文件夾commjava
(可自定義,但要和後面相關參數的配置一致,不知道可不能夠裝在別的盤,待我後期測試再看看補起來),
將上圖1中ini、cert文件夾複製進去;
3.將已經拷過去的文件夾cert中的證書文件(PFX文件)打開進行安裝導入到瀏覽器
(支付的時候須要驗證是否安裝了交行提供的證書,不然沒法支付,交行也會返回相關驗證信息):
圖4
一直點「下一步」直到填寫密碼處,默認密碼是:12345678,再繼續點「下一步」直到完成,
導入成功之後能夠在瀏覽器中看到(Internet選項→內容→證書):
圖5
4.將以前安裝包裏的lib文件夾下全部的文件都拷到以前安裝的jdk目錄 Java\jre7\lib\ext 下,
同時也要複製一份拷到以前commjava文件夾下(需先在commjava文件夾下新建lib目錄),
或者乾脆把整個lib文件夾拷進去,
並在commjava文件夾新建名爲log和settlement的文件夾
(其中log用來存放下面提到的bat文件執行日誌);
5.在任何一個文件夾新建一個.bat 批處理文件並執行;
(圖中我是新建在安裝包目錄下,其實只要內容編輯正確放哪裏均可以,內容編輯按照你以前安裝的目錄自行修改),
編輯內容以下:
java -jar C:\bocommjava\lib\socket_c#.jar 8080 C:\bocommjava\ini\B2CMerchant.xml C:\bocommjava\log\socket.log
這裏採用8080端口,命令大體意思是:執行該批處理命令會調用jar包,讀取xml配置信息,
返回執行結果日誌並在log目錄下生成日誌文件(與執行結果日誌一致)。
注:該批處理文件打開後就不要關閉,之後測試接口調用就是以這個爲基礎,關掉後會沒法調用;
筆者注:這麼一路配置下來總感受網上銀行支付接口的調用環境配置都是銀行本身定義死了
(下面的頁面調用不少配置也是定死的。。。),
只要有一個地方配置錯誤後面調用就會有問題。
2、頁面調用詳細說明;
以上的準備工做作好後,就能夠在頁面先後臺代碼中進行相關調用了。
1.前臺配置:交行支付接口報文驗證很嚴格,報文中不能有其餘任何規定以外的參數存在,否則就會因驗籤失敗而出錯,
因此頁面提交的時候,一個form是不夠的,一個form用來放除支付接口所需參數外的全部頁面控件HTML代碼,
另外一個form用來專門提交支付接口所需參數:
(1)第一個form:
1 <form id="form1" runat="server"> 2 <!--除支付接口所需參數外的全部頁面控件HTML代碼好比選擇銀行的控件,確認支付按鈕等--> 3 </form>
(2)第二個form:
(注:如下各個參數安裝包的開發文檔中都有說明。每一個參數具體註釋請見後面的後臺代碼註釋)
1 <form id="form2" name="form2" method="post" action="<%=orderUrl %>"> 2 <input type="hidden" name="interfaceVersion" value="<%=interfaceVersion%>" /> 3 <input type="hidden" name="merID" value="<%=merID%>" /> 4 <input type="hidden" name="orderid" value="<%=orderid%>" /> 5 <input type="hidden" name="orderDate" value="<%=orderDate%>" /> 6 <input type="hidden" name="orderTime" value="<%=orderTime%>" /> 7 <input type="hidden" name="tranType" value="<%=tranType%>" /> 8 <input type="hidden" name="amount" value="<%=amount%>" /> 9 <input type="hidden" name="curType" value="<%=curType%>" /> 10 <input type="hidden" name="orderContent" value="<%=orderContent%>" /> 11 <input type="hidden" name="orderMono" value="<%=orderMono%>" /> 12 <input type="hidden" name="phdFlag" value="<%=phdFlag%>" /> 13 <input type="hidden" name="notifyType" value="<%=notifyType%>" /> 14 <input type="hidden" name="merURL" value="<%=merURL%>" /> 15 <input type="hidden" name="goodsURL" value="<%=goodsURL%>" /> 16 <input type="hidden" name="jumpSeconds" value="<%=jumpSeconds%>" /> 17 <input type="hidden" name="payBatchNo" value="<%=payBatchNo%>" /> 18 <input type="hidden" name="proxyMerName" value="<%=proxyMerName%>" /> 19 <input type="hidden" name="proxyMerType" value="<%=proxyMerType%>" /> 20 <input type="hidden" name="proxyMerCredentials" value="<%=proxyMercredentials%>" /> 21 <input type="hidden" name="netType" value="<%=netType%>" /> 22 <input type="hidden" name="merSignMsg" value="<%=merSignMsg%>" /> 23 <input type="hidden" name="issBankNo" value="<%=issBankNo%>" /> 24 </form>
(3).表單提交的js:
<script language="javascript" type="text/javascript"> function submitForm(form) { setTimeout(function () { $(form).submit(); }, 0); } </script>
2.後臺代碼:
(1)網關傳輸參數初始化:
1 #region 交行網關傳輸參數 2 public string interfaceVersion = "1.0.0.0"; /*消息版本號,固定爲1.0.0.0*/ 3 public string orderid = DateTime.Now.ToString("yyyyMMddHHmmss"); /*訂單號,商戶應保證3個月以上的惟一性*/ 4 public string orderDate = DateTime.Now.ToString("yyyyMMdd"); /*商戶訂單日期,格式:yyyyMMdd*/ 5 public string orderTime = DateTime.Now.ToString("HHmmss"); /*商戶訂單時間,格式:HHmmss*/ 6 public string tranType = "0"; /*交易類別 0 B2C*/ 7 public string amount = "1"; /*訂單金額,單位:元並帶兩位小數15位整數+2位小數*/ 8 public string curType = "CNY"; /*訂單幣種, 人民幣 CNY*/ 9 public string orderContent = string.Empty; /*商家填寫的其餘訂單信息,在我的客戶頁面顯示*/ 10 public string orderMono = "6222600110030037084"; /*不在我的客戶頁面顯示的備註,但可在商戶管理頁面上顯示*/ 11 public string phdFlag = string.Empty; /*物流配送標誌:0-非物流 ,1-物流配送*/ 12 public string notifyType = "1"; /*通知方式:0-不通知,1-通知,2-轉頁面*/ 13 public string jumpSeconds = string.Empty; /*自動跳轉時間,等待n秒後自動跳轉取貨URL;若不填寫則表示不自動跳轉*/ 14 public string payBatchNo = string.Empty; /*商戶批次號,商家可填入本身的批次號,對帳使用*/ 15 public string proxyMerName = string.Empty; /*代理商家名稱,二級商戶編號/或證件號碼*/ 16 public string proxyMerType = string.Empty; /*代理商家證件類型*/ 17 public string proxyMercredentials = string.Empty; /*代理商家證件號碼*/ 18 public string netType = "0"; /*渠道編號,固定填0:(html渠道)*/ 19 public string issBankNo = "BOCOM"; /*發行卡機構號*/ 20 public string merURL = ""; /*主動通知URL,爲空則不發通知*/ 21 public string goodsURL = "../PayRuslut/COMMPayReslut.aspx"; /*取貨URL,顯示商戶最終訂單支付結果信息,爲空則不顯示按鈕,不自動跳轉*/ 22 public string merSignMsg = string.Empty; /*發行卡機構號*/ 23 public string merID = "301310063009501"; /*網上支付受權碼,也就是上面導入的那個證書編號*/ 24 public string tranCode = "cb2200_sign"; /*交易編號*/ 25 public string orderUrl = string.Empty; /*訂單最終的提交地址,須要從xml配置文件裏獲取*/ 26 #endregion
(2).把安裝包裏的demo文件下:C#\netpay\App_Code 的 config.cs 文件拷貝到系統界面層,
修改其命名空間及其類名便可,或者在你本身的代碼中添加也能夠,只要可以供後面調用便可;
這個類的完整代碼以下:
1 using System; 2 using System.Data; 3 using System.Configuration; 4 5 using System.Web; 6 using System.Web.Security; 7 using System.Web.UI; 8 using System.Web.UI.HtmlControls; 9 using System.Web.UI.WebControls; 10 using System.Web.UI.WebControls.WebParts; 11 12 using System.Net.Sockets; 13 14 /// <summary> 15 ///config 的摘要說明 16 ///配置的系統參數和通信方法示例 17 /// 18 /// </summary> 19 public class config 20 { 21 //商戶號,就是前面導入進去的那個證書編號 22 public static string merchantID = "301310063009501"; 23 //socket bridge通信ip,測試環境通常是本地,正式生產環境中須要修改 24 public static string ip = "127.0.0.1"; 25 //socket bridge端口 26 public static int port = 8080; 27 28 public config() 29 { 30 31 } 32 33 //與socket bridge通信的方法示例 34 public string sendAndReceive(string sendMsg) 35 { 36 TcpClient client = new TcpClient(config.ip, config.port); 37 NetworkStream stream = client.GetStream(); 38 39 Byte[] data = System.Text.Encoding.UTF8.GetBytes(sendMsg.ToString()); 40 stream.Write(data, 0, data.Length); 41 data = new Byte[50 * 1024]; 42 String responseData = String.Empty; 43 Int32 bytes = stream.Read(data, 0, data.Length); 44 responseData = System.Text.Encoding.UTF8.GetString(data, 0, bytes); 45 stream.Close(); 46 client.Close(); 47 return responseData; 48 } 49 }
(3).在支付提交的方法里加入以下代碼:
#region 交行支付網關 orderid = DateTime.Now.ToString("yyyyMMddHHmmss"); /*訂單號,商戶應保證3個月以上的惟一性*/ amount = _CountPayMoney.ToString("F2"); /*訂單金額,單位:元並帶兩位小數15位整數+2位小數*/ merID = config.merchantID;/*獲取證書編號*/ string issuerId = IssUserID;/*銀行代碼,交行爲bocom*/ Random ro = new Random(); string orderDatetime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); int orderAmount = Convert.ToInt32(Convert.ToDouble(_CountPayMoney.ToString("F2")) * 100); string ext1 = OrderID + "&" + "0"; string ext2 = VIPID.ToString();//會員賬號 //拼接商戶訂單支付所需信息字符串 orderMono = _payType + "_" + issuerId + "_" + orderAmount + "_" + ext1 + "_" + ext2 + "_" + orderDatetime; string sourceMsg = interfaceVersion + "|" + merID + "|" + orderid + "|" + orderDate + "|" + orderTime + "|" + tranType + "|" + amount + "|" + curType + "|" + orderContent + "|" + orderMono + "|" + phdFlag + "|" + notifyType + "|" + merURL + "|" + goodsURL + "|" + jumpSeconds + "|" + payBatchNo + "|" + proxyMerName + "|" + proxyMerType + "|" + proxyMercredentials + "|" + netType; StringBuilder sendMsg = new StringBuilder(""); //組織申請報文 sendMsg.Append("<Message>") .Append("<TranCode>").Append(tranCode).Append("</TranCode>") .Append("<MsgContent>") .Append(sourceMsg) .Append("</MsgContent></Message>"); string responseData = new config().sendAndReceive(sendMsg.ToString()); //解析返回報文 XmlDocument xmlDoc = new XmlDocument(); xmlDoc.LoadXml(responseData); XmlNodeList list = xmlDoc.GetElementsByTagName("retCode"); string retCode = list.Item(0).InnerText.Trim(); list = xmlDoc.GetElementsByTagName("errMsg"); string errMsg = list.Item(0).InnerText.Trim(); list = xmlDoc.GetElementsByTagName("signMsg"); merSignMsg = list.Item(0).InnerText.Trim(); list = xmlDoc.GetElementsByTagName("orderUrl"); orderUrl = list.Item(0).InnerText.Trim(); if (!retCode.Equals("0")) { Response.Write("交易返回碼:" + retCode + "<br>"); Response.Write("交易錯誤信息:" + errMsg + "<br>"); } else { //提交 ClientScript.RegisterStartupScript("".GetType(), "", "<script language=\"javascript\" type=\"text/javascript\">submitForm('#form2');</script>"); } #endregion
(4)銀行返回支付結果後系統進行處理的代碼:
需新建一個支付結果接收頁面,也就是上面配置的取貨URL參數goodsURL裏的aspx頁面。
在頁面加載的時候調用:
protected void Page_Load(object sender, EventArgs e) { PayResult(); }
1 /// <summary> 2 /// 支付返回結果 3 /// </summary> 4 private void PayReslut() 5 { 6 string tranCode = "cb2200_verify"; 7 string notifyMsg = Request.Params.Get("notifyMsg"); 8 9 StringBuilder sendMsg = new StringBuilder(""); 10 //sendMsg.Append("<?xml version='1.0' encoding='UTF-8'?>") 11 //組織申請報文 12 sendMsg.Append("<Message>") 13 .Append("<TranCode>").Append(tranCode).Append("</TranCode>") 14 .Append("<MsgContent>") 15 .Append(notifyMsg) 16 .Append("</MsgContent></Message>"); 17 18 TcpClient client = new TcpClient(config.ip, config.port); 19 NetworkStream stream = client.GetStream(); 20 21 Byte[] data = System.Text.Encoding.UTF8.GetBytes(sendMsg.ToString()); 22 stream.Write(data, 0, data.Length); 23 data = new Byte[50 * 1024]; 24 String responseData = String.Empty; 25 Int32 bytes = stream.Read(data, 0, data.Length); 26 responseData = System.Text.Encoding.UTF8.GetString(data, 0, bytes); 27 stream.Close(); 28 client.Close(); 29 30 //解析返回報文 31 XmlDocument xmlDoc = new XmlDocument(); 32 xmlDoc.LoadXml(responseData); 33 XmlNodeList list = xmlDoc.GetElementsByTagName("retCode"); 34 string retCode = list.Item(0).InnerText.Trim(); 35 list = xmlDoc.GetElementsByTagName("errMsg"); 36 string errMsg = list.Item(0).InnerText.Trim(); 37 38 if (!retCode.Equals("0")) 39 { 40 //支付失敗 41 PayReslutShowH3.InnerHtml = "當前訂單本次支付失敗!"; 42 PayReslutShowH3.Attributes.Add("class", "paySuccess_p1F"); 43 } 44 else 45 { 46 //支付成功 47 string[] strs = notifyMsg.Split('|'); 48 string[] orderMono = Encoding.GetEncoding("utf-8").GetString(Convert.FromBase64String(strs[16])).Split('_'); 49 decimal PayMoney = Convert.ToDecimal(strs[2]);//得到支付的錢 50 decimal OrderMoney = (Convert.ToDecimal(orderMono[2]) / 100);//得到訂單錢 51 orderIDSpan.InnerHtml = strs[1];//顯示交行支付的訂單號 52 PayMoneySpan.InnerHtml = PayMoney.ToString("F2");//顯示本次支付的錢 53 string[] _ext1 = orderMono[3].Split('&'); 54 string PayType = _ext1[1];//得到支付類型 0=訂單,1=充值,2=還款 3=團購訂單 4=續費 55 string OrderID = _ext1[0];//訂單號:訂單支付的時候纔會有 56 int VipID = int.Parse(orderMono[4]);//會員ID號碼 57 //BLL.HSSM_LinPayLog.Exists(paymentResult.getPaymentOrderId()) 58 if (HSSM_Public_DB.IsRecord("HSSM_LinPayLog", "paymentOrderId='" + OrderID + "'"))/*判斷是否重複支付,根據支付的訂單號進行判斷*/ 59 { 60 PayReslutShowH3.InnerHtml = "當前訂單已經支付成功!"; 61 return; 62 } 63 if (PayMoney <= 0) 64 { 65 Response.Redirect("~/NullData.html"); 66 return; 67 }
#region 系統接收支付結果返回成功結果進行扣款操做
//相關代碼略,依據系統需求而定,可能調用發送訂單回執短信、郵件等
#endregion 256 } 257 }
好了,至此,全部的相關配置以及代碼就介紹完了。
以上全部的過程都是按照成功運行以後回頭總結的,其實在配置API調用環境和調試支付接口的調用時遇到了一些問題,
經過技術主管跟銀行方面溝通以及主管和本身的不斷調試運行,最終支付接口的調用才成功,銀行那邊也返回了各類消息。
我想之後往往有點東西都會記錄成文,望堅持下去。。
分享就是快樂,你們一塊兒學習進步,一天進步一點,日積月累。。。
附:
我最近剛收到一位主內弟兄送個人一本書,名叫《工做是一份禮物》
是主內一位靈脩大師寫的: