公司最近有一個網站商城項目要開始開發了,這幾天老板和幾個同事一起開着需求會議,
討論了接下來的業務規划和需求策略,等技術需求一下來還要討論技術需求,
確認后再慢慢的進入開發階段,趁着閑暇時間新造的人想總結一下進入公司不久
接觸過的一個關於銀行支付API接口的調用,咱是第一次接觸這類東西。
以后還是盡量養成寫技術生活博客的習慣,工作了4年多,今年才開始想起來應該把自己的工作經歷記錄成
文,形成經驗積累和技術共享,以前很多經歷都淡忘了,希望以后能夠每每有點思緒就記錄下來,時間長了
也是一筆不小的積累和總結(好記性不如爛筆頭),總不能工作這么多年一點技術經驗積累記錄都木有,實
為缺憾哉!(語言組織能力欠佳,還望海涵)
廢話有點多,下面是正題:
一、API調用環境與相關配置詳細說明;
要在網上支持客戶(或商城會員)使用交通銀行(BOCOM,交行國際)支付方式買東西,首先公司得與交行
合作,要求其提供支付接口API(一般程序員都知道),等公司拿到API之后需要按照銀行API要求調用的環
境安裝一些軟件(一般是由銀行提供API安裝包)以及配置各種參數:
從銀行拿到的API安裝包:
圖1(圖中start.bat文件是后來加的,具體作用后面會做說明)
各文件夾簡要說明(我直接從doc文件夾里的技術開發說明文檔拷貝過來的):
- cert 提供商戶端測試環境的商戶測試證書、銀行端測試環境測試根證書及 銀行端生產環境根證書;
- demo 存放交易演示Asp頁面文件,商戶可參照demo中的頁面進行編程開發;起始頁面:Index.htm;
- doc 存放開發編程說明文檔;
- icon 交通銀行logo徽標
- ini 商戶端API配置文件,API初始化需指定該配置文件,配置文件內容包含地址的指定、證書的指定及日志存放目錄
- 指定等。
- setup 存放API的安裝文件。
- lib 提供商戶編程API所需全部 DLL 文件 ;
里面會有一個安裝說明(如上圖的簡要說明.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目錄下生成日志文件(與執行結果日志一致)。
注:該批處理文件打開后就不要關閉,以后測試接口調用就是以這個為基礎,關掉后會無法調用;
筆者注:這么一路配置下來總感覺網上銀行支付接口的調用環境配置都是銀行自己定義死了
(下面的頁面調用很多配置也是定死的。。。),
只要有一個地方配置錯誤后面調用就會有問題。
二、頁面調用詳細說明;
以上的准備工作做好后,就可以在頁面前后台代碼中進行相關調用了。
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調用環境和調試支付接口的調用時遇到了一些問題,
通過技術主管跟銀行方面溝通以及主管和自己的不斷調試運行,最終支付接口的調用才成功,銀行那邊也返回了各種消息。
我想以后每每有點東西都會記錄成文,望堅持下去。。
分享就是快樂,大家一起學習進步,一天進步一點,日積月累。。。
附:
我最近剛收到一位主內弟兄送我的一本書,名叫《工作是一份禮物》
是主內一位靈修大師寫的: