統一下單入口 調用該方法入口:sql
public void WxPayAPI() { //string PayPrice ="99.9"; ////訂單號 //string Payorder =GenerateOutTradeNo(); //支付金額 string PayPrice = Request.GetString("payprice").ToString("filtersql"); //支付單號 string Payorder = Request.GetString("payorder").ToString("filtersql"); //公衆號jsapi支付 H5支付傳參數 string Openid =Request.GetString("openid").ToString("filtersql");
string data = "";
try { WechatPayHelper wxpay = new WechatPayHelper(); int total_fee = (Convert.ToInt32(decimal.Parse(PayPrice) * 100)); ; //////////////////////////////////////////////////////////////////////////////////////////// //發起統一下單請求APP支付 // string return_response = wxpay.GetUnifiedOrderResult(Payorder, total_fee, "財政專費"); //////////////////////////////////////////////////////////////////////////////////////////// //發起統一下單請求JASPI H5支付 傳遞參數 訂單號 金額 body說明 公衆號ID string return_response = wxpay.GetUnifiedOrderResultJSAPI(Payorder, total_fee, "財政專費", "rsR8y885552oooo8DH0"); //寫日誌 Utils.WriteLog("統一下單:" + return_response, "微信下單日誌"); //讀取返回xml文件信息 XmlDocument doc = new XmlDocument(); doc.LoadXml(return_response);//讀取xml字符串 XmlElement root = doc.DocumentElement; //轉化類 WeUnifiedorder weun = GetExmlMsg(root); if (null!=weun) { if (weun.return_code.Trim().ToLower() == "fail") { //簽名失敗 接口問題 Utils.WriteLogFile("[WechatPay]簽名失敗:", "異常日誌"); ReturnData(API.ReturnStatus.失敗, "SYSTEMERROR", "系統超時", ""); } else if (weun.result_code.Trim().ToLower() == "fail") { string error = ErrorCode(weun.err_code); Utils.WriteLog("[WechatPay]" + weun.err_code + ":" + error + "", "異常日誌"); ReturnData(API.ReturnStatus.失敗, weun.err_code, error, ""); } else { //統一下單接口返回正常的prepay_id,再按簽名規範從新生成簽名後,將數據傳輸給APP或者公衆號客戶端。
//參與簽名的字段名爲appId,partnerId,prepayId,nonceStr,timeStamp,package。
//注意:package的值格式爲Sign=WXPay string noncestr = WechatPayHelper.StrRodamNo(16); string timestamp = Common.TimeSpanValue.getTimestamp(); //把請求參數打包成數組 Dictionary<string, string> sParaTempToken = new Dictionary<string, string>(); sParaTempToken.Add("appid", weun.appid); sParaTempToken.Add("partnerid", weun.mch_id); sParaTempToken.Add("noncestr", noncestr); sParaTempToken.Add("prepayid", weun.prepay_id); sParaTempToken.Add("package", "Sign=WXPay"); sParaTempToken.Add("timestamp", timestamp); //請求參數體 string strRequestData = ""; //加密 string strSign = wxpay.MakeSignData(sParaTempToken, ref strRequestData); data = "{\"appid\":\"" + weun.appid + "\",\"partnerid\":\"" + weun.mch_id + "\",\"noncestr\":\"" + noncestr + "\",\"sign\":\"" + strSign + "\",\"Package\":\"Sign=WXPay\",\"prepayid\":\"" + weun.prepay_id + "\",\"timestamp\":\"" + timestamp + "\"}"; ReturnData(API.ReturnStatus.成功, "success", "下單支付成功", data); } } else { ReturnData(API.ReturnStatus.失敗, "error", "下單支付失敗", ""); } } catch (System.Threading.ThreadAbortException) { } catch (Exception e) { Utils.WriteLog("統一下單:" + e.Message, "異常日誌"); ReturnData(API.ReturnStatus.失敗, "error", "下單支付失敗", data); } } /// <summary> /// 統一下單返回類 /// </summary> /// <param name="root"></param> /// <returns></returns> public WeUnifiedorder GetExmlMsg(XmlElement root) { WeUnifiedorder xmlMsg = new WeUnifiedorder() { return_code = root.SelectSingleNode("return_code").InnerText }; //返回狀態碼爲SUCCESS if (xmlMsg.return_code.Trim().ToLower() == "fail") { xmlMsg.return_msg = root.SelectSingleNode("return_msg").InnerText; } else { xmlMsg.appid = root.SelectSingleNode("appid").InnerText; xmlMsg.mch_id = root.SelectSingleNode("mch_id").InnerText; xmlMsg.nonce_str = root.SelectSingleNode("nonce_str").InnerText; xmlMsg.sign = root.SelectSingleNode("sign").InnerText; xmlMsg.result_code = root.SelectSingleNode("result_code").InnerText; //業務結果 if (xmlMsg.result_code.Trim().ToLower() == "fail") { xmlMsg.err_code = root.SelectSingleNode("err_code").InnerText; xmlMsg.err_code_des = root.SelectSingleNode("err_code_des").InnerText; } else { xmlMsg.trade_type = root.SelectSingleNode("trade_type").InnerText; xmlMsg.prepay_id = root.SelectSingleNode("prepay_id").InnerText; } } return xmlMsg; } /// <summary> /// 統一下單返回錯誤碼 /// </summary> /// <param name="code"></param> /// <returns></returns> public string ErrorCode(string code) { string message = "錯誤信息:"; switch (code) { case "NOAUTH": message += "商戶未開通此接口權限"; break; case "NOTENOUGH": message += "用戶賬號餘額不足"; break; case "ORDERPAID": message += "商戶訂單已支付"; break; case "ORDERCLOSED": message += "當前訂單已關閉,沒法支付"; break; case "SYSTEMERROR": message += "系統超時"; break; case "APPID_NOT_EXIST": message += "請檢查APPID是否正確"; break; case "MCHID_NOT_EXIST": message += "請檢查MCHID是否正確"; break; case "APPID_MCHID_NOT_MATCH": message += "appid和mch_id不匹配"; break; case "LACK_PARAMS": message += "缺乏必要的請求參數"; break; case "OUT_TRADE_NO_USED": message += "商戶訂單號重複"; break; case "SIGNERROR": message += "簽名錯誤"; break; case "XML_FORMAT_ERROR": message += "XML格式錯誤"; break; case "REQUIRE_POST_METHOD": message += "未使用post傳遞參數 "; break; case "POST_DATA_EMPTY": message += "post數據不能爲空"; break; case "NOT_UTF8": message += "編碼格式錯誤"; break; } return message; }
WechatPayHelper 代碼:api
public class WechatPayHelper { private string RequestUrl = "";//接口調用地址 //交易安全檢驗碼,由數字和字母組成的32位字符串 private string key = ""; private string appid = "";//應用ID private string mch_id = "";//商戶號 private string nonce_str = "";//隨機字符串 private string sign = "";//簽名 private string spbill_create_ip = "";//終端IP private string notify_url = "";//通知地址 private string trade_type = "";//交易類型 private string pay_url = ""; //字符編碼格式 目前支持 utf-8 private string input_charset = "utf-8"; //簽名方式RSA MD5 private string sign_type = "MD5"; public WechatPayHelper() { HttpContext Context = System.Web.HttpContext.Current; DataTable dt = null; ////////////////////////////////////////////////////// //APP支付配置 讀取配置文件XML ///////////////////////////////////////////////////// // string strXML = "Wechat_Pay_APP.xml"; ///////////////////////////////////////////////// //jsapi h5支付配置 讀取配置XML文件 string strXML = "Wechat_Pay_JSAPI.xml"; object objValue =GetCache(strXML); if (objValue == null) { dt = GetConfigXml("//Config//PayConfig/" + strXML); SetCache(strXML, dt); } else { dt = (DataTable)objValue; } if (dt != null) { appid = dt.Rows[0]["appid"].ToString(); mch_id = dt.Rows[0]["mch_id"].ToString(); notify_url = dt.Rows[0]["notify_url"].ToString(); pay_url = dt.Rows[0]["pay_url"].ToString(); spbill_create_ip = GetUserIp(); nonce_str=StrRodamNo(16); trade_type = dt.Rows[0]["trade_type"].ToString(); key = dt.Rows[0]["key"].ToString(); } }
/// <summary> /// 調用統一下單,得到下單結果 APP /// </summary> /// <param name="out_trade_no"></param> /// <param name="total_fee"></param> /// <param name="body"></param> /// <returns></returns> public string GetUnifiedOrderResult(string out_trade_no, int total_fee, string body) { //請求業務參數詳細 StringBuilder sb = new StringBuilder(); sb.AppendFormat("<xml><appid>{0}</appid><mch_id>{1}</mch_id> <body>{2}</body><nonce_str>{3}</nonce_str>", appid, mch_id, body, nonce_str); sb.AppendFormat("<out_trade_no>{0}</out_trade_no><total_fee>{1}</total_fee> <spbill_create_ip>{2}</spbill_create_ip><trade_type>{3}</trade_type>", out_trade_no, total_fee.ToString(), spbill_create_ip, trade_type); sb.AppendFormat("<notify_url>{0}</notify_url>", notify_url); //把請求參數打包成數組 Dictionary<string, string> softdic = new Dictionary<string, string>(); softdic.Add("appid", appid); softdic.Add("mch_id", mch_id); softdic.Add("nonce_str", nonce_str); softdic.Add("body", body); softdic.Add("out_trade_no", out_trade_no); softdic.Add("total_fee", total_fee.ToString()); softdic.Add("spbill_create_ip", spbill_create_ip); softdic.Add("trade_type", trade_type); softdic.Add("notify_url", notify_url); //請求參數體 string strRequest = ""; //加密簽名 string strSign = MakeSignData(softdic, ref strRequest); strRequest += "&sign=" + strSign; //打包xml sb.AppendFormat("<sign>{0}</sign></xml>", strSign); //發送請求 string strResponse = RequestWechatpay(sb.ToString()); //URLDECODE返回的信息 Encoding code = Encoding.GetEncoding(input_charset); strResponse = HttpUtility.UrlDecode(strResponse, code); return strResponse; } /// <summary> /// 調用統一下單,得到下單結果 JSAPI /// </summary> /// <param name="out_trade_no"></param> /// <param name="total_fee"></param> /// <param name="body"></param> /// <returns></returns> public string GetUnifiedOrderResultJSAPI(string out_trade_no, int total_fee, string body, string openid) { //請求業務參數詳細 StringBuilder sb = new StringBuilder(); sb.AppendFormat("<xml><appid>{0}</appid><mch_id>{1}</mch_id> <body>{2}</body><nonce_str>{3}</nonce_str>", appid, mch_id, body, nonce_str); sb.AppendFormat("<out_trade_no>{0}</out_trade_no><total_fee>{1}</total_fee> <spbill_create_ip>{2}</spbill_create_ip><trade_type>{3}</trade_type>", out_trade_no, total_fee.ToString(), spbill_create_ip, trade_type); sb.AppendFormat("<notify_url>{0}</notify_url>", notify_url); sb.AppendFormat("<openid>{0}</openid>", openid); //把請求參數打包成數組 Dictionary<string, string> softdic = new Dictionary<string, string>(); softdic.Add("appid", appid); softdic.Add("mch_id", mch_id); softdic.Add("nonce_str", nonce_str); softdic.Add("body", body); softdic.Add("out_trade_no", out_trade_no); softdic.Add("total_fee", total_fee.ToString()); softdic.Add("spbill_create_ip", spbill_create_ip); softdic.Add("trade_type", trade_type); softdic.Add("notify_url", notify_url); softdic.Add("openid", openid); //請求參數體 string strRequest = ""; //加密簽名 string strSign = MakeSignData(softdic, ref strRequest); strRequest += "&sign=" + strSign; //打包xml sb.AppendFormat("<sign>{0}</sign></xml>", strSign); //發送請求 string strResponse = RequestWechatpay(sb.ToString()); //URLDECODE返回的信息 Encoding code = Encoding.GetEncoding(input_charset); strResponse = HttpUtility.UrlDecode(strResponse, code); return strResponse; } /// <summary>
/// 設置當前應用程序指定CacheKey的Cache值
/// </summary>
/// <param name="CacheKey"></param>
/// <param name="objObject"></param>
public static void SetCache(string CacheKey, object objObject)
{
System.Web.Caching.Cache objCache = HttpRuntime.Cache;
if (objObject!=null)
objCache.Insert(CacheKey, objObject, null, DateTime.UtcNow.AddDays(7), TimeSpan.Zero);
}
/// <summary>
/// 獲取當前應用程序指定CacheKey的Cache值
/// </summary>
/// <param name="CacheKey"></param>
/// <returns></returns>
public static object GetCache(string CacheKey)
{
System.Web.Caching.Cache objCache = HttpRuntime.Cache;
return objCache[CacheKey];
} /// <summary> /// 簽名原始串 /// </summary> /// <param name="dicParm">全部非空參數</param> /// <param name="strQueryString">請求串</param> /// <returns>簽名串</returns> public string MakeSignData(Dictionary<string, string> dicParm, ref string strQueryString) { //先排序 Dictionary<string, string> dicSort = dicParm.OrderBy(o => o.Key).ToDictionary(o => o.Key, p => p.Value); StringBuilder sb = new StringBuilder(); //再轉換成URL字符串 foreach (KeyValuePair<string, string> kvParm in dicSort) { if (null != kvParm.Value && "".CompareTo(kvParm.Value) != 0 && "sign".CompareTo(kvParm.Key) != 0 && "key".CompareTo(kvParm.Key) != 0 && "sign_type".CompareTo(kvParm.Key) != 0) { if (sb.Length > 0) { sb.Append("&"); strQueryString += "&"; } sb.Append(kvParm.Key + "=" + HttpUtility.UrlDecode(kvParm.Value)); strQueryString += kvParm.Key + "=" + HttpUtility.UrlEncode(kvParm.Value); } } //再和key拼裝成字符串 sb.Append("&key=" + key); //再進行MD5加密轉成大寫 return MD5Create(sb.ToString(), input_charset).ToUpper(); } public static string MD5Create(string str, string charset) { string retStr; MD5CryptoServiceProvider m5 = new MD5CryptoServiceProvider(); //建立md5對象 byte[] inputBye; byte[] outputBye; //使用GB2312編碼方式把字符串轉化爲字節數組. try { inputBye = Encoding.GetEncoding(charset).GetBytes(str); } catch (Exception ex) { inputBye = Encoding.GetEncoding("GB2312").GetBytes(str); } outputBye = m5.ComputeHash(inputBye); retStr = System.BitConverter.ToString(outputBye); retStr = retStr.Replace("-", ""); return retStr; } /// <summary> ///把請求參數信息打包發送請求微信支付地址 /// </summary> /// <param name="strRequestData">請求參數字符串(QueryString)</param> /// <returns></returns> private string RequestWechatpay(string strRequestData) { Encoding code = Encoding.GetEncoding(input_charset); //把數組轉換成流中所需字節數組類型 byte[] bytesRequestData = code.GetBytes(strRequestData); //請求遠程HTTP string strResult = ""; try { //設置HttpWebRequest基本信息 HttpWebRequest myReq = (HttpWebRequest)HttpWebRequest.Create(pay_url); myReq.Method = "post"; myReq.ContentType = "text/xml"; //填充POST數據 myReq.ContentLength = bytesRequestData.Length; Stream requestStream = myReq.GetRequestStream(); requestStream.Write(bytesRequestData, 0, bytesRequestData.Length); requestStream.Close(); //發送POST數據請求服務器 HttpWebResponse HttpWResp = (HttpWebResponse)myReq.GetResponse(); Stream myStream = HttpWResp.GetResponseStream(); //獲取服務器返回信息 StreamReader reader = new StreamReader(myStream, code); StringBuilder responseData = new StringBuilder(); String line; while ((line = reader.ReadLine()) != null) { responseData.Append(line); } //釋放 myStream.Close(); strResult = responseData.ToString(); } catch (Exception exp) { strResult = "報錯:" + exp.Message; } return strResult; } /// <summary> /// 得到客戶端的IP /// </summary> /// <returns>當前頁面客戶端的IP</returns> public static string GetUserIp() { string userHostAddress = HttpContext.Current.Request.UserHostAddress; if (string.IsNullOrEmpty(userHostAddress)) { userHostAddress = HttpContext.Current.Request.ServerVariables["REMOTE_ADDR"]; } //最後判斷獲取是否成功,並檢查IP地址的格式(檢查其格式很是重要) if (!string.IsNullOrEmpty(userHostAddress) && IsIP(userHostAddress)) { return userHostAddress; } return "127.0.0.1"; } /// <summary> /// 檢查IP地址格式 /// </summary> /// <param name="ip"></param> /// <returns></returns> public static bool IsIP(string ip) { return System.Text.RegularExpressions.Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$"); } /// <summary> /// 生成隨機字母與數字 /// </summary> /// <param name="IntStr">生成長度</param> /// <returns></returns> public static string StrRodamNo(int Length) { return StrRodam(Length, false); } /// <summary> /// 生成隨機字母與數字 /// </summary> /// <param name="Length">生成長度</param> /// <param name="Sleep">是否要在生成前將當前線程阻止以免重複</param> /// <returns></returns> public static string StrRodam(int Length, bool Sleep) { if (Sleep) System.Threading.Thread.Sleep(3); char[] Pattern = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' }; string result = ""; int n = Pattern.Length; System.Random random = new Random(~unchecked((int)DateTime.Now.Ticks)); for (int i = 0; i < Length; i++) { int rnd = random.Next(0, n); result += Pattern[rnd]; } return result; } #region 讀取xml中的指定節點的值 /// <summary> /// 讀取xml中的指定節點的值 /// </summary> private string ReadXmlNode(string filename) { string result = "調用微信服務異常"; XmlDocument xmlDoc = new XmlDocument(); try { xmlDoc.LoadXml(filename); XmlNode root = xmlDoc.SelectSingleNode("xml"); if (root != null) result = (root.SelectSingleNode("code_url")).InnerText; } catch (Exception e) { SZRPP.Common.Utils.WriteLogFile("ReadXmlNode:" + e.Message,""); } return result; } #endregion }
微信返回結果異步通知地址數組
wechatpay_notify.aspx安全
protected void Page_Load(object sender, EventArgs e) { StreamReader reader = new StreamReader(Request.InputStream); String xmlData = reader.ReadToEnd(); Utils.WriteLog("微信異步回調:" + xmlData, "微信異步回調"); //序列化xml Dictionary<string, string> dicParam = GetInfoFromXml(xmlData); string data = ""; try { //當收到通知進行處理時,首先檢查對應業務數據的狀態,判斷該通知是否已經處理過,若是沒有處理過再進行處理,若是處理過直接返回結果成功。在對業務數據進行狀態檢查和處理以前,要採用數據鎖進行併發控制,以免函數重入形成的數據混亂。 if (dicParam.ContainsKey("return_code") && dicParam["return_code"] == "SUCCESS") { //通信成功 WechatPayHelper wcHelper = new WechatPayHelper(); string strRequestData = ""; //計算加密 string strSign = wcHelper.MakeSignData(dicParam, ref strRequestData); //判斷簽名 if (strSign == dicParam["sign"]) { //判斷業務結果 if ("SUCCESS" == dicParam["result_code"]) { //判斷業務是否處理過 string out_trade_no = dicParam["out_trade_no"];//訂單編號 if (out_trade_no != null) { OrderPayment payment = payBll.Query(out_trade_no,""); if (payment != null) {//商戶系統對於支付結果通知的內容必定要作簽名驗證,並校驗返回的訂單金額是否與商戶側的訂單金額一致,防止數據泄漏致使出現「假通知」,形成資金損失。 if (payment.PayPrice.Equals(decimal.Parse(dicParam["total_fee"])/100)) { data = "<xml><return_code><![CDATA[FAIL]]></return_code> <return_msg><![CDATA[金額不一致]]></return_msg></xml>"; Response.Write(data); } if (payment.PayStatus == 1) { //已經支付 視爲處理過 直接返回 data = "<xml><return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg></xml>"; Response.Write(data); } else { //修改支付狀態 if (payBll.UpdatePayStatus(out_trade_no, "1",1) > 0) { data = "<xml><return_code><![CDATA[SUCCESS]]></return_code> <return_msg><![CDATA[OK]]></return_msg></xml>"; Response.Write(data); } } } else { data = "<xml><return_code><![CDATA[FAIL]]></return_code> <return_msg><![CDATA[系統調用超時]]></return_msg></xml>"; Response.Write(data); } } } else { //錯誤信息 string error = dicParam["err_code"] + ":" + dicParam["err_code_des"]; data = "<xml><return_code><![CDATA[FAIL]]></return_code> <return_msg><![CDATA[系統調用超時]]></return_msg></xml>"; Response.Write(data); } } else { data="<xml><return_code><![CDATA[FAIL]]></return_code> <return_msg><![CDATA[系統調用超時]]></return_msg></xml>"; Response.Write(data); } } } catch (Exception ex) { Utils.WriteLog("微信異步回調異常:" + ex.Message, "異常日誌"); data = "<xml><return_code><![CDATA[FAIL]]></return_code> <return_msg><![CDATA[系統調用超時]]></return_msg></xml>"; Response.Write(data); } } /// <summary> /// 把XML數據轉換爲Sorted<string, string>集合 /// </summary> /// <param name="strxml"></param> /// <returns></returns> public Dictionary<string, string> GetInfoFromXml(string xmlstring) { Dictionary<string, string> sParams = new Dictionary<string, string>(); try { XmlDocument doc = new XmlDocument(); doc.LoadXml(xmlstring); XmlElement root = doc.DocumentElement; int len = root.ChildNodes.Count; for (int i = 0; i < len; i++) { string name = root.ChildNodes[i].Name; if (!sParams.ContainsKey(name)) { sParams.Add(name.Trim(), root.ChildNodes[i].InnerText.Trim()); } } } catch (Exception ex) { } return sParams; }
xml配置文件: Wechat_Pay_JSAPI.xml 服務器
<?xml version="1.0" encoding="utf-8" ?> <data> <!--接收微信支付異步通知回調地址,通知url必須爲直接可訪問的url,不能攜帶參數--> <notify_url>http://8.20.7.8:300/PayNotifyPage/PayNotify/wechatpay_notify.aspx</notify_url> <pay_url>https://api.mch.weixin.qq.com/pay/unifiedorder</pay_url> <!--微信開放平臺審覈經過的應用APPID--> <appid>wxf99999999999</appid> <!--微信支付分配的商戶號--> <mch_id>148888888882</mch_id> <key>16ce9ghjjjjjj5555552ee19d</key> <subject>財政專費</subject> <trade_type>JSAPI</trade_type> </data>
Wechat_Pay_APP.xml微信
<?xml version="1.0" encoding="utf-8" ?> <data> <!--接收微信支付異步通知回調地址,通知url必須爲直接可訪問的url,不能攜帶參數--> <notify_url>http://8.20.7.8:300/PayNotifyPage/PayNotify/wechatpay_notify.aspx</notify_url> <pay_url>https://api.mch.weixin.qq.com/pay/unifiedorder</pay_url> <!--微信開放平臺審覈經過的應用APPID--> <appid>wxf99999999999</appid> <!--微信支付分配的商戶號--> <mch_id>148888888882</mch_id> <key>16ce9ghjjjjjj5555552ee19d</key> <subject>財政專費</subject> <trade_type>APP</trade_type> </data>
</data>
稍加對接口的調整 加一個參數類型 直接使用一個接口便可完成對APP 公衆號 jsapi H5 的統一調用便可 當哪個渠道發起支付請求就喚起哪個渠道的支付
至此進行傳遞參數進行發起下單既可完成整個支付請求了。在這裏分開寫 主要起到一個牽針引線的做用併發