公衆號支付有2種支付方式:javascript
JS API 支付:是指用戶打開圖文消息或者掃描二維碼,在微信內置瀏覽器打開網頁進行的支付。商戶網頁前端經過使用微信提供的 JS API,調用微信支付模塊。這種方式,適合
須要在商戶網頁進行選購下單的購買流程。
Native(原生)支付:是指商戶組成符合 Native(原生)支付規則的 URL 連接,用戶可經過點擊該連接或者掃描對應的二維碼直接進入微信支付模塊(微信客戶端界面),便可
進行支付。這種方式,適合無需選購直接支付的購買流程。css
以上兩種方式最大的區別是:是否須要通過網頁調用!html
交互原理:前端
客戶是想作一個手機端的商城,因此選擇JS API支付,經過網頁調取微信支付。。java
樓主第一次看過文檔後,什麼「支付簽名(paySign)」啊,什麼「package包」啊等等一些參數,反正似懂非懂的樣子。樓主這人比較懶,否則也不 會註冊3年了,也就寫了這麼篇不入流的文章,樓主想啊像這些個大公司開放個啥接口的確定會有demo。樓主的運氣還真不錯(PS:樓主但是爲福利事業貢獻 了很多哦,咋就沒這運氣?),還真讓樓主找到了,那心情確定是比不上中500W的,嘿嘿。jquery
<!DOCTYPE html> <html> <head> <title>公衆號支付測試網頁</title> <script language="javascript" src="http://res.mail.qq.com/mmr/static/lib/js/jquery.js"></script> <script language="javascript" src="http://res.mail.qq.com/mmr/static/lib/js/lazyloadv3.js"></script> <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/md5.js"></script> <script src="http://crypto-js.googlecode.com/svn/tags/3.1.2/build/rollups/sha1.js"></script> <script Language="javascript"> //輔助函數 function Trim(str,is_global) { var result; result = str.replace(/(^\s+)|(\s+$)/g,""); if(is_global.toLowerCase()=="g") result = result.replace(/\s/g,""); return result; } function clearBr(key) { key = Trim(key,"g"); key = key.replace(/<\/?.+?>/g,""); key = key.replace(/[\r\n]/g, ""); return key; } //獲取隨機數 function getANumber() { var date = new Date(); var times1970 = date.getTime(); var times = date.getDate() + "" + date.getHours() + "" + date.getMinutes() + "" + date.getSeconds(); var encrypt = times * times1970; if(arguments.length == 1){ return arguments[0] + encrypt; }else{ return encrypt; } } //如下是package組包過程: var oldPackageString;//記住package,方便最後進行總體簽名時取用 function getPartnerId() { return document.form1.partnerId.value; } function getPartnerKey() { return "8934e7d15453e97507ef794cf7b0519d"; } function getPackage() { var banktype = "WX"; var body = document.form1.body.value;//商品名稱信息,這裏由測試網頁填入。 var fee_type = "1";//費用類型,這裏1爲默認的人民幣 var input_charset = "GBK";//字符集,這裏將統一使用GBK var notify_url = "http://www.qq.com";//支付成功後將通知該地址 var out_trade_no = ""+getANumber();//訂單號,商戶須要保證該字段對於本商戶的惟一性 var partner = getPartnerId();//測試商戶號 var spbill_create_ip = "127.0.0.1";//用戶瀏覽器的ip,這個須要在前端獲取。這裏使用127.0.0.1測試值 var total_fee = document.form1.totalFee.value;//總金額。 var partnerKey = getPartnerKey();//這個值和以上其餘值不同是:簽名須要它,而最後組成的傳輸字符串不能含有它。這個key是須要商戶好好保存的。 //首先第一步:對原串進行簽名,注意這裏不要對任何字段進行編碼。這裏是將參數按照key=value進行字典排序後組成下面的字符串,在這個字符串最後拼接上key=XXXX。因爲這裏的字段固定,所以只須要按照這個順序進行排序便可。 var signString = "bank_type="+banktype+"&body="+body+"&fee_type="+fee_type+"&input_charset="+input_charset+"¬ify_url="+notify_url+"&out_trade_no="+out_trade_no+"&partner="+partner+"&spbill_create_ip="+spbill_create_ip+"&total_fee="+total_fee+"&key="+partnerKey; var md5SignValue = ("" + CryptoJS.MD5(signString)).toUpperCase(); //而後第二步,對每一個參數進行url轉碼,若是您的程序是用js,那麼須要使用encodeURIComponent函數進行編碼。 banktype = encodeURIComponent(banktype); body=encodeURIComponent(body); fee_type=encodeURIComponent(fee_type); input_charset = encodeURIComponent(input_charset); notify_url = encodeURIComponent(notify_url); out_trade_no = encodeURIComponent(out_trade_no); partner = encodeURIComponent(partner); spbill_create_ip = encodeURIComponent(spbill_create_ip); total_fee = encodeURIComponent(total_fee); //而後進行最後一步,這裏按照key=value除了sign外進行字典序排序後組成下列的字符串,最後再串接sign=value var completeString = "bank_type="+banktype+"&body="+body+"&fee_type="+fee_type+"&input_charset="+input_charset+"¬ify_url="+notify_url+"&out_trade_no="+out_trade_no+"&partner="+partner+"&spbill_create_ip="+spbill_create_ip+"&total_fee="+total_fee; completeString = completeString + "&sign="+md5SignValue; oldPackageString = completeString;//記住package,方便最後進行總體簽名時取用 return completeString; } //下面是app進行簽名的操做: var oldTimeStamp ;//記住timestamp,避免簽名時的timestamp與傳入的timestamp時不一致 var oldNonceStr ; //記住nonceStr,避免簽名時的nonceStr與傳入的nonceStr不一致 function getAppId() { return document.form1.appId.value; } function getAppKey() { return "2Wozy2aksie1puXUBpWD8oZxiD1DfQuEaiC7KcRATv1Ino3mdopKaPGQQ7TtkNySuAmCaDCrw4xhPY5qKTBl7Fzm0RgR3c0WaVYIXZARsxzHV2x7iwPPzOz94dnwPWSn"; } function getTimeStamp() { var timestamp=new Date().getTime(); var timestampstring = timestamp.toString();//必定要轉換字符串 oldTimeStamp = timestampstring; return timestampstring; } function getNonceStr() { var $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; var maxPos = $chars.length; var noceStr = ""; for (i = 0; i < 32; i++) { noceStr += $chars.charAt(Math.floor(Math.random() * maxPos)); } oldNonceStr = noceStr; return noceStr; } function getSignType() { return "SHA1"; } function getSign() { var app_id = getAppId().toString(); var app_key = getAppKey().toString(); var nonce_str = oldNonceStr; var package_string = oldPackageString; var time_stamp = oldTimeStamp; //第一步,對全部須要傳入的參數加上appkey做一次key=value字典序的排序 var keyvaluestring = "appid="+app_id+"&appkey="+app_key+"&noncestr="+nonce_str+"&package="+package_string+"×tamp="+time_stamp; sign = CryptoJS.SHA1(keyvaluestring).toString(); return sign; } </script> <meta http-equiv="content-type" content="text/html;charset=utf-8"/> <meta id="viewport" name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1; user-scalable=no;" /> <style> body { margin:0;padding:0;background:#eae9e6; } body,p,table,td,th { font-size:14px;font-family:helvetica,Arial,Tahoma; } h1 { font-family:Baskerville,HelveticaNeue-Bold,helvetica,Arial,Tahoma; } a { text-decoration:none;color:#385487;} .container { } .title { } #content {padding:30px 20px 20px;color:#111;box-shadow:0 1px 4px #ccc; background:#f7f2ed; } .seeAlso { padding:15px 20px 30px; } .headpic div { margin:20px 0 0;} .headpic img { display:block;} .title h1 { font-size:22px;font-weight:bold;padding:0;margin:0;line-height:1.2;color:#1f1f1f; } .title p { color:#aaa;font-size:12px;margin:5px 0 0;padding:0;font-weight:bold;} .pic { margin:20px 0; } .articlecontent img { display:block;clear:both;box-shadow:0px 1px 3px #999; margin:5px auto;} .articlecontent p { text-indent: 2em; font-family:Georgia,helvetica,Arial,Tahoma;line-height:1.4; font-size:16px; margin:20px 0; } .seeAlso h3 { font-size:16px;color:#a5a5a5;} .seeAlso ul { margin:0;padding:0; } .seeAlso li { font-size:16px;list-style-type:none;border-top:1px solid #ccc;padding:2px 0;} .seeAlso li a { border-bottom:none;display:block;line-height:1.1; padding:13px 0; } .clr{ clear:both;height:1px;overflow:hidden;} .fontSize1 .title h1 { font-size:20px; } .fontSize1 .articlecontent p { font-size:14px; } .fontSize1 .weibo .nickname,.fontSize1 .weibo .comment { font-size:11px; } .fontSize1 .moreOperator { font-size:14px; } .fontSize2 .title h1 { font-size:22px; } .fontSize2 .articlecontent p { font-size:16px; } .fontSize2 .weibo .nickname,.fontSize2 .weibo .comment { font-size:13px; } .fontSize2 .moreOperator { font-size:16px; } .fontSize3 .title h1 { font-size:24px; } .fontSize3 .articlecontent p { font-size:18px; } .fontSize3 .weibo .nickname,.fontSize3 .weibo .comment { font-size:15px; } .fontSize3 .moreOperator { font-size:18px; } .fontSize4 .title h1 { font-size:26px; } .fontSize4 .articlecontent p { font-size:20px; } .fontSize4 .weibo .nickname,.fontSize4 .weibo .comment { font-size:16px; } .fontSize4 .moreOperator { font-size:20px; } .jumptoorg { display:block;margin:16px 0 16px; } .jumptoorg a { } .moreOperator a { color:#385487; } .moreOperator .share{ border-top:1px solid #ddd; } .moreOperator .share a{ display:block;border:1px solid #ccc;border-radius:4px;margin:20px 0;border-bottom-style:solid;background:#f8f7f1;color:#000; } .moreOperator .share a span{ display:block;padding:10px 10px;border-radius:4px;text-align:center;border-top:1px solid #eee;border-bottom:1px solid #eae9e3;font-weight:bold; } .moreOperator .share a:hover, .moreOperator .share a:active { background:#efedea; } @media only screen and (-webkit-min-device-pixel-ratio: 2) { } </style> <script language="javascript"> function auto_remove(img){ div=img.parentNode.parentNode;div.parentNode.removeChild(div); img.onerror=""; return true; } function changefont(fontsize){ if(fontsize < 1 || fontsize > 4)return; $('#content').removeClass().addClass('fontSize' + fontsize); } // 當微信內置瀏覽器完成內部初始化後會觸發WeixinJSBridgeReady事件。 document.addEventListener('WeixinJSBridgeReady', function onBridgeReady() { //公衆號支付 jQuery('a#getBrandWCPayRequest').click(function(e){ WeixinJSBridge.invoke('getBrandWCPayRequest',{ "appId" : getAppId(), //公衆號名稱,由商戶傳入 "timeStamp" : getTimeStamp(), //時間戳 "nonceStr" : getNonceStr(), //隨機串 "package" : getPackage(),//擴展包 "signType" : getSignType(), //微信簽名方式:1.sha1 "paySign" : getSign() //微信簽名 },function(res){ if(res.err_msg == "get_brand_wcpay_request:ok" ) {} // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功後返回ok,但並不保證它絕對可靠。 //所以微信團隊建議,當收到ok返回時,向商戶後臺詢問是否收到交易成功的通知,若收到通知,前端展現交易成功的界面;若此時未收到通知,商戶後臺主動調用查詢訂單接口,查詢訂單的當前狀態,並反饋給前端展現相應的界面。 }); }); WeixinJSBridge.log('yo~ ready.'); }, false) if(jQuery){ jQuery(function(){ var width = jQuery('body').width() * 0.87; jQuery('img').error(function(){ var self = jQuery(this); var org = self.attr('data-original1'); self.attr("src", org); self.error(function(){ auto_remove(this); }); }); jQuery('img').each(function(){ var self = jQuery(this); var w = self.css('width'); var h = self.css('height'); w = w.replace('px', ''); h = h.replace('px', ''); if(w <= width){ return; } var new_w = width; var new_h = Math.round(h * width / w); self.css({'width' : new_w + 'px', 'height' : new_h + 'px'}); self.parents('div.pic').css({'width' : new_w + 'px', 'height' : new_h + 'px'}); }); }); } </script> </head> <body> <form name="form1" target="_blank"> <table border="1"> <TR><th>公衆號ID</th> <th><INPUT value="wxf8b4f85f3a794e77" name="appId" id="1"></th> <tr><th>商戶ID</th><th><INPUT value="1900000109" name="partnerId" id="2"></th> <TR><th>總金額</th><th><INPUT value=1 name="totalFee" id="3"></th> <TR><th>商品名</th><th><INPUT value="江詩丹頓" name="body" id="4"></th> </table> </form> <div class="WCPay"> <a id="getBrandWCPayRequest" href="javascript:void(0);"><h1 class="title">提交</h1></a> </div> </body> </html>
打開一看(樓主內心想TX你是多缺美工!),「提交」兩字這麼大,點一下試試,沒反應?這不科學啊!立馬找緣由,當看到這句:「微信支付,是基於微信客戶端提供的支付服務功能」 ,樓主**(已和諧)了!放網站上用微信打開,此次有反應了!微信友情的提示我:「功能未受權」。。 此次知道是啥緣由了,找客戶溝通,原來合同快遞還沒到,保證金5W沒交(如今只要2W了)。。web
N天以後。。。。api
這下測試能成功了,該怎麼跟網站結合呢?不會直接把參數改了,而後讓客戶點提交吧?樓主想一想這用戶體驗也太差了吧,客戶確定不認同。這問題糾結了樓主幾 天,百撕不得騎姐。有一天忽然企業QQ一個討論組的圖標老閃,我就納悶我沒加過討論組啊,打開正想噴(實際也真沒噴過,能拉進討論組都是好友,不過問題解 不出來的時候確實很煩)的時候,人物列表那個頭像怎麼那麼熟呢?那不我客戶嗎?瞬間一盆涼水從頭到腳完全沒脾氣了!A(TX商務人員):「大家的微信支付 作好了沒?」,我:「還沒呢。」,A:」有什麼困難嗎?」,我:「有,有一些地方不明白。」,A:」我幫你安排個技術支持吧?」,樓主欣喜若狂啊,從看到 這行信息到打出「好的」發出去絕對是沒有超過1秒。。數組
以後樓主一路順風了,如魚得水了,同事妹紙也變的水靈多了。。瀏覽器
<——————————————————————-吐 槽結束,華麗的分割線 —————————————————————–>
如下是.NET版本的微信支付:
MD5Util類:
using System; using System.Security.Cryptography; using System.Text; namespace tenpayApp { /// <summary> /// MD5Util 的摘要說明。 /// </summary> public class MD5Util { public MD5Util() { // // TODO: 在此處添加構造函數邏輯 // } /** 獲取大寫的MD5簽名結果 */ public static string GetMD5(string encypStr, string charset) { string retStr; MD5CryptoServiceProvider m5 = new MD5CryptoServiceProvider(); //建立md5對象 byte[] inputBye; byte[] outputBye; //使用GB2312編碼方式把字符串轉化爲字節數組. try { inputBye = Encoding.GetEncoding(charset).GetBytes(encypStr); } catch (Exception ex) { inputBye = Encoding.GetEncoding("GB2312").GetBytes(encypStr); } outputBye = m5.ComputeHash(inputBye); retStr = System.BitConverter.ToString(outputBye); retStr = retStr.Replace("-", "").ToUpper(); return retStr; } } }
RequestHandler類:
using System; using System.Collections; using System.Text; using System.Web; using System.Xml; using System.Security.Cryptography; using System.Collections.Generic; using System.Text.RegularExpressions; namespace tenpayApp { /** '簽名工具類 ============================================================================/// <summary> 'api說明: 'init(); '初始化函數,默認給一些參數賦值。 'setKey(key_)'設置商戶密鑰 'createMd5Sign(signParams);字典生成Md5簽名 'genPackage(packageParams);獲取package包 'createSHA1Sign(signParams);建立簽名SHA1 'parseXML();輸出xml 'getDebugInfo(),獲取debug信息 * * ============================================================================ */ public class RequestHandler { public RequestHandler(HttpContext httpContext) { parameters = new Hashtable(); this.httpContext = httpContext; } /** 密鑰 */ private string key; protected HttpContext httpContext; /** 請求的參數 */ protected Hashtable parameters; /** debug信息 */ private string debugInfo; /** 初始化函數 */ public virtual void init() { } /** 獲取debug信息 */ public String getDebugInfo() { return debugInfo; } /** 獲取密鑰 */ public String getKey() { return key; } /** 設置密鑰 */ public void setKey(string key) { this.key = key; } /** 設置參數值 */ public void setParameter(string parameter, string parameterValue) { if (parameter != null && parameter != "") { if (parameters.Contains(parameter)) { parameters.Remove(parameter); } parameters.Add(parameter, parameterValue); } } //獲取package帶參數的簽名包 public string getRequestURL() { this.createSign(); StringBuilder sb = new StringBuilder(); ArrayList akeys=new ArrayList(parameters.Keys); akeys.Sort(); foreach(string k in akeys) { string v = (string)parameters[k]; if(null != v && "key".CompareTo(k) != 0) { sb.Append(k + "=" + TenpayUtil.UrlEncode(v, getCharset()) + "&"); } } //去掉最後一個& if(sb.Length > 0) { sb.Remove(sb.Length-1, 1); } return sb.ToString(); } //建立md5摘要,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。 protected virtual void createSign() { StringBuilder sb = new StringBuilder(); ArrayList akeys=new ArrayList(parameters.Keys); akeys.Sort(); foreach(string k in akeys) { string v = (string)parameters[k]; if(null != v && "".CompareTo(v) != 0 && "sign".CompareTo(k) != 0 && "key".CompareTo(k) != 0) { sb.Append(k + "=" + v + "&"); } } sb.Append("key=" + this.getKey()); string sign = MD5Util.GetMD5(sb.ToString(), getCharset()).ToUpper(); this.setParameter("sign", sign); //debug信息 this.setDebugInfo(sb.ToString() + " => sign:" + sign); } //建立package簽名 public virtual string createMd5Sign() { StringBuilder sb = new StringBuilder(); ArrayList akeys=new ArrayList(parameters.Keys); akeys.Sort(); foreach(string k in akeys) { string v = (string)parameters[k]; if(null != v && "".CompareTo(v) != 0 && "sign".CompareTo(k) != 0 && "".CompareTo(v) != 0) { sb.Append(k + "=" + v + "&"); } } string sign = MD5Util.GetMD5(sb.ToString(), getCharset()).ToLower(); this.setParameter("sign", sign); return sign; } //建立sha1簽名 public string createSHA1Sign() { StringBuilder sb = new StringBuilder(); ArrayList akeys = new ArrayList(parameters.Keys); akeys.Sort(); foreach (string k in akeys) { string v = (string)parameters[k]; if (null != v && "".CompareTo(v) != 0 && "sign".CompareTo(k) != 0 && "key".CompareTo(k) != 0) { if(sb.Length==0) { sb.Append(k + "=" + v); } else{ sb.Append("&" + k + "=" + v); } } } string paySign = SHA1Util.getSha1(sb.ToString()).ToString().ToLower(); //debug信息 this.setDebugInfo(sb.ToString() + " => sign:" + paySign); return paySign; } //輸出XML public string parseXML() { StringBuilder sb = new StringBuilder(); sb.Append("<xml>"); foreach (string k in parameters.Keys) { string v = (string)parameters[k]; if (Regex.IsMatch(v, @"^[0-9.]$")) { sb.Append("<" + k + ">" + v + "</" + k + ">"); } else { sb.Append("<" + k + "><![CDATA[" + v + "]]></" + k + ">"); } } sb.Append("</xml>"); return sb.ToString(); } /** 設置debug信息 */ public void setDebugInfo(String debugInfo) { this.debugInfo = debugInfo; } public Hashtable getAllParameters() { return this.parameters; } protected virtual string getCharset() { return this.httpContext.Request.ContentEncoding.BodyName; } } }
ResponseHandler類:
using System; using System.Collections; using System.Collections.Specialized; using System.Text; using System.Web; using System.Xml; namespace tenpayApp { /** '============================================================================ 'api說明: 'getKey()/setKey(),獲取/設置密鑰 'getParameter()/setParameter(),獲取/設置參數值 'getAllParameters(),獲取全部參數 'isTenpaySign(),是否正確的簽名,true:是 false:否 'isWXsign(),是否正確的簽名,true:是 false:否 ' * isWXsignfeedback判斷微信維權簽名 ' *getDebugInfo(),獲取debug信息 '============================================================================ */ public class ResponseHandler { // 密鑰 private string key; // appkey private string appkey; //xmlMap private Hashtable xmlMap; // 應答的參數 protected Hashtable parameters; //debug信息 private string debugInfo; //原始內容 protected string content; private string charset = "gb2312"; //參與簽名的參數列表 private static string SignField = "appid,appkey,timestamp,openid,noncestr,issubscribe"; protected HttpContext httpContext; //初始化函數 public virtual void init() { } //獲取頁面提交的get和post參數 public ResponseHandler(HttpContext httpContext) { parameters = new Hashtable(); xmlMap = new Hashtable(); this.httpContext = httpContext; NameValueCollection collection; //post data if (this.httpContext.Request.HttpMethod == "POST") { collection = this.httpContext.Request.Form; foreach (string k in collection) { string v = (string)collection[k]; this.setParameter(k, v); } } //query string collection = this.httpContext.Request.QueryString; foreach (string k in collection) { string v = (string)collection[k]; this.setParameter(k, v); } if (this.httpContext.Request.InputStream.Length > 0) { XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(this.httpContext.Request.InputStream); XmlNode root = xmlDoc.SelectSingleNode("xml"); XmlNodeList xnl = root.ChildNodes; foreach (XmlNode xnf in xnl) { xmlMap.Add(xnf.Name, xnf.InnerText); } } } /** 獲取密鑰 */ public string getKey() { return key;} /** 設置密鑰 */ public void setKey(string key, string appkey) { this.key = key; this.appkey = appkey; } /** 獲取參數值 */ public string getParameter(string parameter) { string s = (string)parameters[parameter]; return (null == s) ? "" : s; } /** 設置參數值 */ public void setParameter(string parameter,string parameterValue) { if(parameter != null && parameter != "") { if(parameters.Contains(parameter)) { parameters.Remove(parameter); } parameters.Add(parameter,parameterValue); } } /** 是否財付通簽名,規則是:按參數名稱a-z排序,遇到空值的參數不參加簽名。 * @return boolean */ public virtual Boolean isTenpaySign() { StringBuilder sb = new StringBuilder(); ArrayList akeys=new ArrayList(parameters.Keys); akeys.Sort(); foreach(string k in akeys) { string v = (string)parameters[k]; if(null != v && "".CompareTo(v) != 0 && "sign".CompareTo(k) != 0 && "key".CompareTo(k) != 0) { sb.Append(k + "=" + v + "&"); } } sb.Append("key=" + this.getKey()); string sign = MD5Util.GetMD5(sb.ToString(), getCharset()).ToLower(); this.setDebugInfo(sb.ToString() + " => sign:" + sign); //debug信息 return getParameter("sign").ToLower().Equals(sign); } //判斷微信簽名 public virtual Boolean isWXsign() { StringBuilder sb = new StringBuilder(); Hashtable signMap = new Hashtable(); foreach (string k in xmlMap.Keys) { if (k != "SignMethod" && k != "AppSignature") { signMap.Add(k.ToLower(), xmlMap[k]); } } signMap.Add("appkey", this.appkey); ArrayList akeys = new ArrayList(signMap.Keys); akeys.Sort(); foreach (string k in akeys) { string v = (string)signMap[k]; if (sb.Length == 0) { sb.Append(k + "=" + v); } else { sb.Append("&" + k + "=" + v); } } string sign = SHA1Util.getSha1(sb.ToString()).ToString().ToLower(); this.setDebugInfo(sb.ToString() + " => SHA1 sign:" + sign); return sign.Equals(xmlMap["AppSignature"]); } //判斷微信維權簽名 public virtual Boolean isWXsignfeedback() { StringBuilder sb = new StringBuilder(); Hashtable signMap = new Hashtable(); foreach (string k in xmlMap.Keys) { if (SignField.IndexOf(k.ToLower()) != -1) { signMap.Add(k.ToLower(), xmlMap[k]); } } signMap.Add("appkey", this.appkey); ArrayList akeys = new ArrayList(signMap.Keys); akeys.Sort(); foreach (string k in akeys) { string v = (string)signMap[k]; if ( sb.Length == 0 ) { sb.Append(k + "=" + v); } else { sb.Append("&" + k + "=" + v); } } string sign = SHA1Util.getSha1(sb.ToString()).ToString().ToLower(); this.setDebugInfo(sb.ToString() + " => SHA1 sign:" + sign); return sign.Equals( xmlMap["AppSignature"] ); } /** 獲取debug信息 */ public string getDebugInfo() { return debugInfo;} /** 設置debug信息 */ protected void setDebugInfo(String debugInfo) { this.debugInfo = debugInfo;} protected virtual string getCharset() { return this.httpContext.Request.ContentEncoding.BodyName; } } }
SHA1Util:
using System; using System.Collections.Generic; using System.Text; using System.Security.Cryptography; namespace tenpayApp { class SHA1Util { public static String getSha1(String str) { //創建SHA1對象 SHA1 sha = new SHA1CryptoServiceProvider(); //將mystr轉換成byte[] ASCIIEncoding enc = new ASCIIEncoding(); byte[] dataToHash = enc.GetBytes(str); //Hash運算 byte[] dataHashed = sha.ComputeHash(dataToHash); //將運算結果轉換成string string hash = BitConverter.ToString(dataHashed).Replace("-", ""); return hash; } } }
TenpayUtil:
using System; using System.Text; using System.Web; namespace tenpayApp { /// <summary> /// TenpayUtil 的摘要說明。 /// 配置文件 /// </summary> public class TenpayUtil { public static string tenpay = "1"; public static string partner = ""; //商戶號 public static string key = ""; //密鑰 public static string appid = "";//appid public static string appkey = "";//paysignkey(非appkey) public static string tenpay_notify = "http://localhost/payNotifyUrl.aspx"; //支付完成後的回調處理頁面,*替換成notify_url.asp所在路徑 public TenpayUtil() { } public static string getNoncestr() { Random random = new Random(); return MD5Util.GetMD5(random.Next(1000).ToString(), "GBK"); } public static string getTimestamp() { TimeSpan ts = DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, 0); return Convert.ToInt64(ts.TotalSeconds).ToString(); } /** 對字符串進行URL編碼 */ public static string UrlEncode(string instr, string charset) { //return instr; if(instr == null || instr.Trim() == "") return ""; else { string res; try { res = HttpUtility.UrlEncode(instr,Encoding.GetEncoding(charset)); } catch (Exception ex) { res = HttpUtility.UrlEncode(instr,Encoding.GetEncoding("GB2312")); } return res; } } /** 對字符串進行URL解碼 */ public static string UrlDecode(string instr, string charset) { if(instr == null || instr.Trim() == "") return ""; else { string res; try { res = HttpUtility.UrlDecode(instr,Encoding.GetEncoding(charset)); } catch (Exception ex) { res = HttpUtility.UrlDecode(instr,Encoding.GetEncoding("GB2312")); } return res; } } /** 取時間戳生成隨即數,替換交易單號中的後10位流水號 */ public static UInt32 UnixStamp() { TimeSpan ts = DateTime.Now - TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); return Convert.ToUInt32(ts.TotalSeconds); } /** 取隨機數 */ public static string BuildRandomStr(int length) { Random rand = new Random(); int num = rand.Next(); string str = num.ToString(); if(str.Length > length) { str = str.Substring(0,length); } else if(str.Length < length) { int n = length - str.Length; while(n > 0) { str.Insert(0, "0"); n--; } } return str; } } }
頁面代碼:
using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using System.Collections; using tenpayApp; //================================= //JSAPI支付 //================================= public partial class _Default : System.Web.UI.Page { public String appId = TenpayUtil.appid; public String timeStamp = ""; public String nonceStr = ""; public String packageValue = ""; public String paySign = ""; protected void Page_Load(object sender, EventArgs e) { string sp_billno = Request["order_no"]; //當前時間 yyyyMMdd string date = DateTime.Now.ToString("yyyyMMdd"); if (null == sp_billno) { //生成訂單10位序列號,此處用時間和隨機數生成,商戶根據本身調整,保證惟一 sp_billno = DateTime.Now.ToString("HHmmss") + TenpayUtil.BuildRandomStr(4); } else { sp_billno = Request["order_no"].ToString(); } sp_billno = TenpayUtil.partner + sp_billno; //建立支付應答對象 RequestHandler packageReqHandler = new RequestHandler(Context); //初始化 packageReqHandler.init(); //設置package訂單參數 packageReqHandler.setParameter("partner", TenpayUtil.partner); //商戶號 packageReqHandler.setParameter("fee_type", "1"); //幣種,1人民幣 packageReqHandler.setParameter("input_charset", "GBK"); packageReqHandler.setParameter("out_trade_no", sp_billno); //商家訂單號 packageReqHandler.setParameter("total_fee", "1"); //商品金額,以分爲單位(money * 100).ToString() packageReqHandler.setParameter("notify_url", TenpayUtil.tenpay_notify); //接收財付統統知的URL packageReqHandler.setParameter("body", "JSAPIdemo"); //商品描述 packageReqHandler.setParameter("spbill_create_ip", Page.Request.UserHostAddress); //用戶的公網ip,不是商戶服務器IP //獲取package包 packageValue = packageReqHandler.getRequestURL(); //調起微信支付簽名 timeStamp = TenpayUtil.getTimestamp(); nonceStr = TenpayUtil.getNoncestr(); //設置支付參數 RequestHandler paySignReqHandler = new RequestHandler(Context); paySignReqHandler.setParameter("appid", appId); paySignReqHandler.setParameter("appkey", TenpayUtil.appkey); paySignReqHandler.setParameter("noncestr", nonceStr); paySignReqHandler.setParameter("timestamp", timeStamp); paySignReqHandler.setParameter("package", packageValue); paySign = paySignReqHandler.createSHA1Sign(); //獲取debug信息,建議把請求和debug信息寫入日誌,方便定位問題 //string pakcageDebuginfo = packageReqHandler.getDebugInfo(); //Response.Write("<br/>pakcageDebuginfo:" + pakcageDebuginfo + "<br/>"); //string paySignDebuginfo = paySignReqHandler.getDebugInfo(); //Response.Write("<br/>paySignDebuginfo:" + paySignDebuginfo + "<br/>"); } }