支付寶支付
1,參數准備
/// <summary> /// 類名:Config /// 功能:基礎配置類 /// 詳細:設置帳戶有關信息及返回路徑 /// 版本:3.4 /// 修改日期:2016-03-08 /// 說明: /// 以下代碼只是為了方便商戶測試而提供的樣例代碼,商戶可以根據自己網站的需要,按照技術文檔編寫,並非一定要使用該代碼。 /// 該代碼僅供學習和研究支付寶接口使用,只是提供一個參考。 /// </summary> public class AlipayConfig { //↓↓↓↓↓↓↓↓↓↓請在這里配置您的基本信息↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ // 合作身份者ID,簽約賬號,以2088開頭由16位純數字組成的字符串,查看地址:https://b.alipay.com/order/pidAndKey.htm public static string partner = YardPadConfig.Partner; // 收款支付寶賬號,以2088開頭由16位純數字組成的字符串,一般情況下收款賬號就是簽約賬號 public static string seller_id = partner; //商戶的私鑰,原始格式,RSA公私鑰生成:https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.nBDxfy&treeId=58&articleId=103242&docType=1 public static string private_key = YardPadConfig.PrivateKey; //支付寶的公鑰,查看地址:https://b.alipay.com/order/pidAndKey.htm public static string alipay_public_key = YardPadConfig.AlipayPublicKey; // 服務器異步通知頁面路徑,需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常訪問 public static string notify_url = YardPadConfig.AlipayNotifyUrl; // 頁面跳轉同步通知頁面路徑,需http://格式的完整路徑,不能加?id=123這類自定義參數,必須外網可以正常訪問 public static string return_url = YardPadConfig.AlipayReturnUrl; // 簽名方式 public static string sign_type = "RSA"; // 調試用,創建TXT日志文件夾路徑,見AlipayCore.cs類中的LogResult(string sWord)打印方法。 public static string log_path = HttpRuntime.AppDomainAppPath.ToString() + "log/"; // 字符編碼格式 目前支持utf-8 public static string input_charset = "utf-8"; // 支付類型 ,無需修改 public static string payment_type = "1"; // 調用的接口名,無需修改 public static string service = "alipay.wap.create.direct.pay.by.user"; //↑↑↑↑↑↑↑↑↑↑請在這里配置您的基本信息↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ }
2.支付代碼撰寫
public string doAlipayPay(string outTradeNo) { PayOrderDomain payOrderDomain = new PayOrderDomain(); PAY_Order parOrder = new PAY_Order(); List<PAY_Order> payOrderList = payOrderDomain.GetByOrderNoList(outTradeNo); if (payOrderList != null && payOrderList.Count > 0) { parOrder = payOrderList.First(); } else { return "Order does not exist"; }////////////////////////////////////////////請求參數//////////////////////////////////////////// //商戶訂單號,商戶網站訂單系統中唯一訂單號,必填 string out_trade_no = newOrderNo; //訂單名稱,必填
string subject = project.ChineseProjectName; byte[] buffer = Encoding.UTF8.GetBytes(subject); subject = Encoding.UTF8.GetString(buffer, 0, buffer.Length); //付款金額,必填 string total_fee = parOrder.Amount.ToString(); //收銀台頁面上,商品展示的超鏈接,必填 string show_url = ""; //商品描述,可空 byte[] buffer1 = Encoding.UTF8.GetBytes(parOrder.Order_Desc); string body = Encoding.UTF8.GetString(buffer1, 0, buffer1.Length); //////////////////////////////////////////////////////////////////////////////////////////////// //把請求參數打包成數組 SortedDictionary<string, string> sParaTemp = new SortedDictionary<string, string>(); sParaTemp.Add("partner", AlipayConfig.partner); sParaTemp.Add("seller_id", AlipayConfig.seller_id); sParaTemp.Add("_input_charset", AlipayConfig.input_charset.ToLower()); sParaTemp.Add("service", AlipayConfig.service); sParaTemp.Add("payment_type", AlipayConfig.payment_type); sParaTemp.Add("notify_url", AlipayConfig.notify_url); sParaTemp.Add("return_url", AlipayConfig.return_url); sParaTemp.Add("out_trade_no", out_trade_no); sParaTemp.Add("subject", subject); sParaTemp.Add("total_fee", total_fee); sParaTemp.Add("show_url", show_url); //sParaTemp.Add("app_pay","Y");//啟用此參數可喚起錢包APP支付。 sParaTemp.Add("body", body); //其他業務參數根據在線開發文檔,添加參數.文檔地址:https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.2Z6TSk&treeId=60&articleId=103693&docType=1 //如sParaTemp.Add("參數名","參數值"); // 建立請求 string sHtmlText = AlipaySubmit.BuildRequest(sParaTemp, "get", "確認"); return sHtmlText; //Response.Write(sHtmlText); throw new NotImplementedException(); }
public class AlipaySubmit { #region 字段 //支付寶網關地址(新) private static string GATEWAY_NEW = "https://mapi.alipay.com/gateway.do?"; //商戶的私鑰 private static string _private_key = ""; //編碼格式 private static string _input_charset = ""; //簽名方式 private static string _sign_type = ""; #endregion static AlipaySubmit() { _private_key = AlipayConfig.private_key; _input_charset = AlipayConfig.input_charset.Trim().ToLower(); _sign_type = AlipayConfig.sign_type.Trim().ToUpper(); } /// <summary> /// 生成請求時的簽名 /// </summary> /// <param name="sPara">請求給支付寶的參數數組</param> /// <returns>簽名結果</returns> private static string BuildRequestMysign(Dictionary<string, string> sPara) { //把數組所有元素,按照“參數=參數值”的模式用“&”字符拼接成字符串 string prestr = Core.CreateLinkString(sPara); //把最終的字符串簽名,獲得簽名結果 string mysign = ""; switch (_sign_type) { case "RSA": mysign = RSAFromPkcs8.sign(prestr, _private_key, _input_charset); break; default: mysign = ""; break; } return mysign; } /// <summary> /// 生成要請求給支付寶的參數數組 /// </summary> /// <param name="sParaTemp">請求前的參數數組</param> /// <returns>要請求的參數數組</returns> private static Dictionary<string, string> BuildRequestPara(SortedDictionary<string, string> sParaTemp) { //待簽名請求參數數組 Dictionary<string, string> sPara = new Dictionary<string, string>(); //簽名結果 string mysign = ""; //過濾簽名參數數組 sPara = Core.FilterPara(sParaTemp); //獲得簽名結果 mysign = BuildRequestMysign(sPara); //簽名結果與簽名方式加入請求提交參數組中 sPara.Add("sign", mysign); sPara.Add("sign_type", _sign_type); return sPara; } /// <summary> /// 生成要請求給支付寶的參數數組 /// </summary> /// <param name="sParaTemp">請求前的參數數組</param> /// <param name="code">字符編碼</param> /// <returns>要請求的參數數組字符串</returns> private static string BuildRequestParaToString(SortedDictionary<string, string> sParaTemp, Encoding code) { //待簽名請求參數數組 Dictionary<string, string> sPara = new Dictionary<string, string>(); sPara = BuildRequestPara(sParaTemp); //把參數組中所有元素,按照“參數=參數值”的模式用“&”字符拼接成字符串,並對參數值做urlencode string strRequestData = Core.CreateLinkStringUrlencode(sPara, code); return strRequestData; } /// <summary> /// 建立請求,以表單HTML形式構造(默認) /// </summary> /// <param name="sParaTemp">請求參數數組</param> /// <param name="strMethod">提交方式。兩個值可選:post、get</param> /// <param name="strButtonValue">確認按鈕顯示文字</param> /// <returns>提交表單HTML文本</returns> public static string BuildRequest(SortedDictionary<string, string> sParaTemp, string strMethod, string strButtonValue) { //待請求參數數組 Dictionary<string, string> dicPara = new Dictionary<string, string>(); dicPara = BuildRequestPara(sParaTemp); StringBuilder sbHtml = new StringBuilder(); sbHtml.Append("<form id='alipaysubmit' name='alipaysubmit' action='" + GATEWAY_NEW + "_input_charset=" + _input_charset + "' method='" + strMethod.ToLower().Trim() + "' accept-charset='utf-8'>"); foreach (KeyValuePair<string, string> temp in dicPara) { sbHtml.Append("<input type='hidden' name='" + temp.Key + "' value='" + temp.Value + "'/>"); } //submit按鈕控件請不要含有name屬性 sbHtml.Append("<input type='submit' value='" + strButtonValue + "' style='display:none;'></form>"); sbHtml.Append("<script>document.forms['alipaysubmit'].submit();</script>"); return sbHtml.ToString(); } /// <summary> /// 用於防釣魚,調用接口query_timestamp來獲取時間戳的處理函數 /// 注意:遠程解析XML出錯,與IIS服務器配置有關 /// </summary> /// <returns>時間戳字符串</returns> public static string Query_timestamp() { string url = GATEWAY_NEW + "service=query_timestamp&partner=" + AlipayConfig.partner + "&_input_charset=" + AlipayConfig.input_charset; string encrypt_key = ""; XmlTextReader Reader = new XmlTextReader(url); XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(Reader); encrypt_key = xmlDoc.SelectSingleNode("/alipay/response/timestamp/encrypt_key").InnerText; return encrypt_key; } }
/// <summary> /// 除去數組中的空值和簽名參數並以字母a到z的順序排序 /// </summary> /// <param name="dicArrayPre">過濾前的參數組</param> /// <returns>過濾后的參數組</returns> public static Dictionary<string, string> FilterPara(SortedDictionary<string, string> dicArrayPre) { Dictionary<string, string> dicArray = new Dictionary<string, string>(); foreach (KeyValuePair<string, string> temp in dicArrayPre) { if (temp.Key.ToLower() != "sign" && temp.Key.ToLower() != "sign_type" && temp.Value != "" && temp.Value != null) { dicArray.Add(temp.Key, temp.Value); } } return dicArray; } /// <summary> /// 把數組所有元素,按照“參數=參數值”的模式用“&”字符拼接成字符串 /// </summary> /// <param name="sArray">需要拼接的數組</param> /// <returns>拼接完成以后的字符串</returns> public static string CreateLinkString(Dictionary<string, string> dicArray) { StringBuilder prestr = new StringBuilder(); foreach (KeyValuePair<string, string> temp in dicArray) { prestr.Append(temp.Key + "=" + temp.Value + "&"); } //去掉最後一個&字符 int nLen = prestr.Length; prestr.Remove(nLen-1,1); return prestr.ToString(); } /// <summary> /// 把數組所有元素,按照“參數=參數值”的模式用“&”字符拼接成字符串,並對參數值做urlencode /// </summary> /// <param name="sArray">需要拼接的數組</param> /// <param name="code">字符編碼</param> /// <returns>拼接完成以后的字符串</returns> public static string CreateLinkStringUrlencode(Dictionary<string, string> dicArray, Encoding code) { StringBuilder prestr = new StringBuilder(); foreach (KeyValuePair<string, string> temp in dicArray) { prestr.Append(temp.Key + "=" + HttpUtility.UrlEncode(temp.Value, code) + "&"); } //去掉最後一個&字符 int nLen = prestr.Length; prestr.Remove(nLen - 1, 1); return prestr.ToString(); } /// <summary> /// 寫日志,方便測試(看網站需求,也可以改成把記錄存入數據庫) /// </summary> /// <param name="sWord">要寫入日志里的文本內容</param> public static void LogResult(string sWord) { string strPath = AlipayConfig.log_path + "\\" + "alipay_log_" + DateTime.Now.ToString("yyyyMMddHHmmss") + ".txt"; StreamWriter fs = new StreamWriter(strPath, false, System.Text.Encoding.Default); fs.Write(sWord); fs.Close(); } /// <summary> /// 獲取文件的md5摘要 /// </summary> /// <param name="sFile">文件流</param> /// <returns>MD5摘要結果</returns> public static string GetAbstractToMD5(Stream sFile) { MD5 md5 = new MD5CryptoServiceProvider(); byte[] result = md5.ComputeHash(sFile); StringBuilder sb = new StringBuilder(32); for (int i = 0; i < result.Length; i++) { sb.Append(result[i].ToString("x").PadLeft(2, '0')); } return sb.ToString(); } /// <summary> /// 獲取文件的md5摘要 /// </summary> /// <param name="dataFile">文件流</param> /// <returns>MD5摘要結果</returns> public static string GetAbstractToMD5(byte[] dataFile) { MD5 md5 = new MD5CryptoServiceProvider(); byte[] result = md5.ComputeHash(dataFile); StringBuilder sb = new StringBuilder(32); for (int i = 0; i < result.Length; i++) { sb.Append(result[i].ToString("x").PadLeft(2, '0')); } return sb.ToString(); }
public class Notify { #region 字段 private string _partner = ""; //合作身份者ID private string alipay_public_key = ""; //支付寶的公鑰 private string _input_charset = ""; //編碼格式 private string _sign_type = ""; //簽名方式 //支付寶消息驗證地址 private string Https_veryfy_url = "https://mapi.alipay.com/gateway.do?service=notify_verify&"; #endregion /// <summary> /// 構造函數 /// 從配置文件中初始化變量 /// </summary> /// <param name="inputPara">通知返回參數數組</param> /// <param name="notify_id">通知驗證ID</param> public Notify() { //初始化基礎配置信息 _partner = AlipayConfig.partner.Trim(); alipay_public_key = AlipayConfig.alipay_public_key.Trim(); _input_charset = AlipayConfig.input_charset.Trim().ToLower(); _sign_type = AlipayConfig.sign_type.Trim().ToUpper(); } /// <summary> /// 從文件讀取公鑰轉公鑰字符串 /// </summary> /// <param name="Path">公鑰文件路徑</param> public static string getPublicKeyStr(string Path) { StreamReader sr = new StreamReader(Path); string pubkey = sr.ReadToEnd(); sr.Close(); if (pubkey != null) { pubkey = pubkey.Replace("-----BEGIN PUBLIC KEY-----", ""); pubkey = pubkey.Replace("-----END PUBLIC KEY-----", ""); pubkey = pubkey.Replace("\r", ""); pubkey = pubkey.Replace("\n", ""); } return pubkey; } /// <summary> /// 驗證消息是否是支付寶發出的合法消息 /// </summary> /// <param name="inputPara">通知返回參數數組</param> /// <param name="notify_id">通知驗證ID</param> /// <param name="sign">支付寶生成的簽名結果</param> /// <returns>驗證結果</returns> public bool Verify(SortedDictionary<string, string> inputPara, string notify_id, string sign) { //獲取返回時的簽名驗證結果 bool isSign = GetSignVeryfy(inputPara, sign); //獲取是否是支付寶服務器發來的請求的驗證結果 string responseTxt = "false"; if (notify_id != null && notify_id != "") { responseTxt = GetResponseTxt(notify_id); } //寫日志記錄(若要調試,請取消下面兩行注釋) //string sWord = "responseTxt=" + responseTxt + "\n isSign=" + isSign.ToString() + "\n 返回回來的參數:" + GetPreSignStr(inputPara) + "\n "; //Core.LogResult(sWord); //判斷responsetTxt是否為true,isSign是否為true //responsetTxt的結果不是true,與服務器設置問題、合作身份者ID、notify_id一分鍾失效有關 //isSign不是true,與安全校驗碼、請求時的參數格式(如:帶自定義參數等)、編碼格式有關 if (responseTxt == "true" && isSign)//驗證成功 { return true; } else//驗證失敗 { return false; } } /// <summary> /// 獲取待簽名字符串(調試用) /// </summary> /// <param name="inputPara">通知返回參數數組</param> /// <returns>待簽名字符串</returns> private string GetPreSignStr(SortedDictionary<string, string> inputPara) { Dictionary<string, string> sPara = new Dictionary<string, string>(); //過濾空值、sign與sign_type參數 sPara = Core.FilterPara(inputPara); //獲取待簽名字符串 string preSignStr = Core.CreateLinkString(sPara); return preSignStr; } /// <summary> /// 獲取返回時的簽名驗證結果 /// </summary> /// <param name="inputPara">通知返回參數數組</param> /// <param name="sign">對比的簽名結果</param> /// <returns>簽名驗證結果</returns> private bool GetSignVeryfy(SortedDictionary<string, string> inputPara, string sign) { Dictionary<string, string> sPara = new Dictionary<string, string>(); //過濾空值、sign與sign_type參數 sPara = Core.FilterPara(inputPara); //獲取待簽名字符串 string preSignStr = Core.CreateLinkString(sPara); //獲得簽名驗證結果 bool isSgin = false; if (sign != null && sign != "") { switch (_sign_type) { case "RSA": isSgin = RSAFromPkcs8.verify(preSignStr, sign, alipay_public_key, _input_charset); break; default: break; } } return isSgin; } /// <summary> /// 獲取是否是支付寶服務器發來的請求的驗證結果 /// </summary> /// <param name="notify_id">通知驗證ID</param> /// <returns>驗證結果</returns> private string GetResponseTxt(string notify_id) { string veryfy_url = Https_veryfy_url + "partner=" + _partner + "¬ify_id=" + notify_id; //獲取遠程服務器ATN結果,驗證是否是支付寶服務器發來的請求 string responseTxt = Get_Http(veryfy_url, 120000); return responseTxt; } /// <summary> /// 獲取遠程服務器ATN結果 /// </summary> /// <param name="strUrl">指定URL路徑地址</param> /// <param name="timeout">超時時間設置</param> /// <returns>服務器ATN結果</returns> private string Get_Http(string strUrl, int timeout) { string strResult; try { HttpWebRequest myReq = (HttpWebRequest)HttpWebRequest.Create(strUrl); myReq.Timeout = timeout; HttpWebResponse HttpWResp = (HttpWebResponse)myReq.GetResponse(); Stream myStream = HttpWResp.GetResponseStream(); StreamReader sr = new StreamReader(myStream, Encoding.Default); StringBuilder strBuilder = new StringBuilder(); while (-1 != sr.Peek()) { strBuilder.Append(sr.ReadLine()); } strResult = strBuilder.ToString(); } catch (Exception exp) { strResult = "錯誤:" + exp.Message; } return strResult; } }
/// <summary> /// 支付寶支付回調 /// </summary> /// <returns></returns> [HttpPost] public ActionResult AlipayNotify() { string status = string.Empty; SortedDictionary<string, string> sPara = GetRequestPost(); if (sPara.Count > 0)//判斷是否有帶返回參數 { Notify aliNotify = new Notify(); bool verifyResult = aliNotify.Verify(sPara, Request.Form["notify_id"], Request.Form["sign"]); if (verifyResult)//驗證成功 { ///////////////////////////////////////////////////////////////////////////////////////////////////////////// //請在這里加上商戶的業務邏輯程序代碼 //——請根據您的業務邏輯來編寫程序(以下代碼僅作參考)—— //獲取支付寶的通知返回參數,可參考技術文檔中服務器異步通知參數列表 //商戶訂單號 string out_trade_no = Request.Form["out_trade_no"]; //支付寶交易號 string trade_no = Request.Form["trade_no"]; // 支付金額 string amount = Request.Form["total_fee"]; //交易狀態 string trade_status = Request.Form["trade_status"];if (Request.Form["trade_status"] == "TRADE_SUCCESS") { //判斷該筆訂單是否在商戶網站中已經做過處理 //如果沒有做過處理,根據訂單號(out_trade_no)在商戶網站的訂單系統中查到該筆訂單的詳細,並執行商戶的業務程序 //請務必判斷請求時的total_fee、seller_id與通知時獲取的total_fee、seller_id為一致的 //如果有做過處理,不執行商戶的業務程序 //注意: //付款完成后,支付寶系統發送該交易狀態通知 //驗簽成功,更新訂單表狀態 PayOrderRelationDomain payOrderRelationDomain = new PayOrderRelationDomain(); List<PAY_Order_Relation> payOrderRelationList = payOrderRelationDomain.GetByOrderNoList(out_trade_no); if (payOrderRelationList != null && payOrderRelationList.Count > 0) { PAY_Order_Relation payOrderRelation = payOrderRelationList.First(); PayOrderDomain payOrderDomain = new PayOrderDomain(); PAY_Order payOrder = new PAY_Order(); List<PAY_Order> payOrderList = payOrderDomain.GetByOrderNoList(payOrderRelation.Original_Order_No); if (payOrderList != null && payOrderList.Count > 0) { payOrder = payOrderList.First(); payOrder.Status = "Paied"; payOrder.UpdateDate = DateTime.Now; payOrder.NotifyNum = 1; payOrder.NotifyTime = DateTime.Now.AddMinutes(5); payOrderDomain.Modify(payOrder.ID, payOrder); } status = "success"; //請不要修改或刪除 } else { status = "fail"; } } else { status = "fail"; } //——請根據您的業務邏輯來編寫程序(以上代碼僅作參考)—— ///////////////////////////////////////////////////////////////////////////////////////////////////////////// } else//驗證失敗 { status = "fail"; } } else { status = "無通知參數"; } // Response.Write(""); return Content(status); //return Content("success"); } /// <summary> /// 獲取支付寶POST過來通知消息,並以“參數名=參數值”的形式組成數組 /// </summary> /// <returns>request回來的信息組成的數組</returns> public SortedDictionary<string, string> GetRequestPost() { int i = 0; SortedDictionary<string, string> sArray = new SortedDictionary<string, string>(); NameValueCollection coll; //Load Form variables into NameValueCollection variable. coll = Request.Form; // Get names of all forms into a string array. String[] requestItem = coll.AllKeys; for (i = 0; i < requestItem.Length; i++) { sArray.Add(requestItem[i], Request.Form[requestItem[i]]); } return sArray; }