大型网上购物系统除了能让会员选择货到付款结账方式外,还应该提供一些更方便快捷的网上支付方式。如果网上商店没有足够的实力提供会员直接在网站中 建立现金账户的功能,就可以将订单信息转接到支付宝,让会员从支付宝付款。当然就算会员可以在网站上建立自己的现金账户,提供支付宝支付功能也不失为另一 种方便快捷的支付方式,这可以给客户提供更多可选的支付方式。
首先,网上购物系统必须与支付宝公司签订合作协议,以确保从本购物网站上传到
支付宝网站上的订单信息能被正确接收。
当会员于购物网站上买下一系列商品并选择支付宝付款方式后,购物系统即将会员购物的订单信息转发到支付宝,网站页面也会转到支付宝的付款页面。此 时,支付宝页面会发送一个验证信息到本网站以确认支付宝正确收到订单信息。
会员于支付宝网站付款完成后,网站页面会重新跳回本购物网站,同时支付宝会将已付款的订单信息发回本网站以便对本购物网站的数据库进行必要的修改操 作。另外本网站还需要向支付宝网站发送一个返回信息,告知支付宝本系统已正确收到付款完毕的订单信息并且已经完成对数据的处理操作。
向支付宝网站传送订单信息时主要参数的含义:
gateway :支付接口
service:识别是何接口实现何功能的表示
seller_email:商家签约时的支付宝账号,即收款的支付宝账号
key:安全校验码,与partner是一组
partner:商户ID,合作伙伴ID
sign_type:加密类型
_input_charset:编码类型
show_url:展示地址,即在支付宝页面时商品名称旁边的“详情”的链接地址
out_trade_no:会员订单编号,订单编号必须在本系统中保持唯一
subject:商品名称,也可称为订单名称,该接口并不是单一的只能买一样东西,可把一次支付当作一次下订单
body:商品描述,即备注
total_fee:商品价格,也可称为订单的总金额
源码分析(C#):
首先必须建立一个通知页面(Notify.aspx)和一个返回页面(Return.aspx)以接受并验证从支付宝返回的信息并对数据库中相应的 订单信息做修改处理操作。
Notify.aspx.cs
using System; using System.Data; using System.Configuration; using System.Collections; 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.Text; using System.Collections.Specialized; using System.IO; using Gateway; /// <summary> /// 创建该页面文件时,请留心该页面文件中无任何HTML代码及空格。 /// 该页面称作“通知页”,是异步被支付宝服务器所调用。 /// 当支付宝的订单状态改变时,支付宝服务器则会自动调用此页面,因此请做好自身网站订单信息与支付宝上的订单的同步工作 /// </summary> public partial class Alipay_Notify : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string alipayNotifyURL = "https://www.alipay.com/cooperate/gateway.do?service=notify_verify"; //string alipayNotifyURL = "http://notify.alipay.com/trade/notify_query.do?";//此路径是在上面链接地址无法起作用时替换使用。 string partner = ""; //partner合作伙伴id(必须填写) string key = ""; //partner 的对应交易安全校验码(必须填写) string _input_charset = "utf-8";//编码类型,完全根据客户自身的项目的编码格式而定,千万不要填错。否则极其容易造成MD5加密错误。 alipayNotifyURL = alipayNotifyURL + "&partner=" + partner + "¬ify_id=" + Request.Form["notify_id"]; //获取支付宝ATN返回结果,true是正确的订单信息,false 是无效的 string responseTxt = AliPay.Get_Http(alipayNotifyURL, 120000); //*******加密签名程序开始******* int i; NameValueCollection coll; //Load Form variables into NameValueCollection variable. coll = Request.Form; // Get names of all forms into a string array. String[] requestarr = coll.AllKeys; //进行排序; string[] Sortedstr = AliPay.BubbleSort(requestarr); //构造待md5摘要字符串 ; StringBuilder prestr = new StringBuilder(); for (i = 0; i < Sortedstr.Length; i++) { if (Request.Form[Sortedstr[i]] != "" && Sortedstr[i] != "sign" && Sortedstr[i] != "sign_type") { if (i == Sortedstr.Length - 1) { prestr.Append(Sortedstr[i] + "=" + Request.Form[Sortedstr[i]]); } else { prestr.Append(Sortedstr[i] + "=" + Request.Form[Sortedstr[i]] + "&"); } } } prestr.Append(key); string mysign = AliPay.GetMD5(prestr.ToString(), _input_charset); //*******加密签名程序结束******* string sign = Request.Form["sign"]; if (mysign == sign && responseTxt == "true") //验证支付发过来的消息,签名是否正确,只要成功进如这个判断里,则表示该页面已被支付宝服务器成功调用 //但判断内出现自身编写的程序相关错误导致通知给支付宝并不是发送success的消息或没有更新客户自身的数据库的情况,请自身程序编写好应对措施,否则查明原因时困难之极 { if (Request.Form["trade_status"] == "WAIT_BUYER_PAY")// 判断支付状态_等待买家付款(文档中有枚举表可以参考) { //更新自己数据库的订单语句,请自己填写一下 string strOrderNO = Request.Form["out_trade_no"];//订单号 string strPrice = Request.Form["total_fee"];//金额 如果你申请了商家购物卷功能,在返回信息里面请不要做金额的判断,否则会校验通过不了。 } else if (Request.Form["trade_status"] == "TRADE_FINISHED" || Request.Form["trade_status"] == "TRADE_SUCCESS")// 判断支付状态_交易成功结束(文档中有枚举表可以参考) { //更新自己数据库的订单语句,请自己填写一下 string strOrderNO = Request.Form["out_trade_no"];//订单号 string strPrice = Request.Form["total_fee"];//金额 } else { //更新自己数据库的订单语句,请自己填写一下 } Response.Write("success"); //返回给支付宝消息,成功,请不要改写这个success //success与fail及其他字符的区别在于,支付宝的服务器若遇到success时,则不再发送请求通知(即不再调用该页面,让该页面再次运行起来), //若不是success,则支付宝默认没有收到成功的信息,则会反复不停地调用该页面直到失效,有效调用时间是24小时以内。 //最好写TXT文件,以记录下是否异步返回记录。 ////写文本,纪录支付宝返回消息,比对md5计算结果(如网站不支持写txt文件,可改成写数据库) //string TOEXCELLR = "MD5结果:mysign=" + mysign + ",sign=" + sign + ",responseTxt=" + responseTxt; //StreamWriter fs = new StreamWriter(Server.MapPath("Notify_DATA/" + DateTime.Now.ToString().Replace(":", "")) + ".txt", false, System.Text.Encoding.Default); //fs.Write(TOEXCELLR); //fs.Close(); } else { Response.Write("fail"); //最好写TXT文件,以记录下是否异步返回记录。 //写文本,纪录支付宝返回消息,比对md5计算结果(如网站不支持写txt文件,可改成写数据库) string TOEXCELLR = "MD5结果:mysign=" + mysign + ",sign=" + sign + ",responseTxt=" + responseTxt; StreamWriter fs = new StreamWriter(Server.MapPath("Notify_DATA/" + DateTime.Now.ToString().Replace(":", "")) + ".txt", false, System.Text.Encoding.Default); fs.Write(TOEXCELLR); fs.Close(); } } }
Return.aspx.cs
using System; using System.Data; using System.Configuration; using System.Collections; 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.Text; using System.Collections.Specialized; using System.IO; using Gateway; /// <summary> /// 创建该页面文件时,请留心该页面文件是可以对其进行美工处理的,原因在于支付完成以后,当前窗口会从支付宝的页面跳转回这个页面。 /// 该页面称作“返回页”,是同步被支付宝服务器所调用,可当作是支付完成后的提示信息页,如“您的某某某订单,多少金额已支付成功”。 /// </summary> public partial class Alipay_Return : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string alipayNotifyURL = "https://www.alipay.com/cooperate/gateway.do?service=notify_verify"; //string alipayNotifyURL = "http://notify.alipay.com/trade/notify_query.do?";//此路径是在上面链接地址无法起作用时替换使用。 string key = ""; //partner 的对应交易安全校验码(必须填写) string partner = ""; //partner合作伙伴id(必须填写) string _input_charset = "utf-8";//编码类型,完全根据客户自身的项目的编码格式而定,千万不要填错。否则极其容易造成MD5加密错误。 alipayNotifyURL = alipayNotifyURL + "&partner=" + partner + "¬ify_id=" + Request.QueryString["notify_id"]; //获取支付宝ATN返回结果,true是正确的订单信息,false 是无效的 string responseTxt = AliPay.Get_Http(alipayNotifyURL, 120000); //*******加密签名程序开始//******* int i; NameValueCollection coll; //Load Form variables into NameValueCollection variable. coll = Request.QueryString; // Get names of all forms into a string array. String[] requestarr = coll.AllKeys; //进行排序; string[] Sortedstr = AliPay.BubbleSort(requestarr); //构造待md5摘要字符串 ; StringBuilder prestr = new StringBuilder(); for (i = 0; i < Sortedstr.Length; i++) { if (Request.Form[Sortedstr[i]] != "" && Sortedstr[i] != "sign" && Sortedstr[i] != "sign_type") { if (i == Sortedstr.Length - 1) { prestr.Append(Sortedstr[i] + "=" + Request.QueryString[Sortedstr[i]]); } else { prestr.Append(Sortedstr[i] + "=" + Request.QueryString[Sortedstr[i]] + "&"); } } } prestr.Append(key); //生成Md5摘要; string mysign = AliPay.GetMD5(prestr.ToString(), _input_charset); //*******加密签名程序结束******* string sign = Request.QueryString["sign"]; // Response.Write(prestr.ToString()); //调试用,支付宝服务器返回时的完整路径。 if (mysign == sign && responseTxt == "true") //验证支付发过来的消息,签名是否正确 { //更新自己数据库的订单语句,请自己填写一下 string strOrderNO = Request.QueryString["out_trade_no"];//订单号 string strPrice = Request.QueryString["total_fee"];//金额 string strTradeStatus = Request.QueryString["TRADE_STATUS"];//订单状态 Response.Write("订单号:" + strOrderNO + "<br>金额:" + strPrice); //成功,可美化该页面,提示信息 } else { Response.Write("------------------------------------------"); Response.Write("<br>Result:responseTxt=" + responseTxt); Response.Write("<br>Result:mysign=" + mysign); Response.Write("<br>Result:sign=" + sign); Response.Write("支付失败"); //支付失败,提示信息 } } }
除此之外在Notify.aspx页面和Return.aspx页面公用的一些方法,可以提取出来放在一个公共的类里面(Alipay.cs)
Alipay.cs
using System.Web; using System.Text; using System.Security.Cryptography; using System.IO; using System.Net; using System; /// <summary> /// New Interface for AliPay /// </summary> namespace Gateway { public class AliPay { /// <summary> /// 与ASP兼容的MD5加密算法 /// </summary> public static string GetMD5(string s, string _input_charset) { MD5 md5 = new MD5CryptoServiceProvider(); byte[] t = md5.ComputeHash(Encoding.GetEncoding(_input_charset).GetBytes(s)); StringBuilder sb = new StringBuilder(32); for (int i = 0; i < t.Length; i++) { sb.Append(t[i].ToString("x").PadLeft(2, '0')); } return sb.ToString(); } /// <summary> /// 冒泡排序法 /// 按照字母序列从a到z的顺序排列 /// </summary> public static string[] BubbleSort(string[] r) { int i, j; //交换标志 string temp; bool exchange; for (i = 0; i < r.Length; i++) //最多做R.Length-1趟排序 { exchange = false; //本趟排序开始前,交换标志应为假 for (j = r.Length - 2; j >= i; j--) {//交换条件 if (System.String.CompareOrdinal(r[j + 1], r[j]) < 0) { temp = r[j + 1]; r[j + 1] = r[j]; r[j] = temp; exchange = true; //发生了交换,故将交换标志置为真 } } if (!exchange) //本趟排序未发生交换,提前终止算法 { break; } } return r; } /// <summary> /// 生成URL链接或加密结果 /// </summary> /// <param name="para">参数加密数组</param> /// <param name="_input_charset">编码格式</param> /// <param name="sign_type">加密类型</param> /// <param name="key">安全校验码</param> /// <returns>字符串URL或加密结果</returns> public static string CreatUrl( //string gateway,//GET方式传递参数时请去掉注释 string[] para, string _input_charset, string sign_type, string key ) { int i; //进行排序; string[] Sortedstr = BubbleSort(para); //构造待md5摘要字符串 ; StringBuilder prestr = new StringBuilder(); for (i = 0; i < Sortedstr.Length; i++) { if (i == Sortedstr.Length - 1) { prestr.Append(Sortedstr[i]); } else { prestr.Append(Sortedstr[i] + "&"); } } prestr.Append(key); //生成Md5摘要; string sign = GetMD5(prestr.ToString(), _input_charset); //以下是POST方式传递参数 return sign; //以下是GET方式传递参数 //构造支付Url; // char[] delimiterChars = { '='}; // StringBuilder parameter = new StringBuilder(); // parameter.Append(gateway); // for (i = 0; i < Sortedstr.Length; i++) // {//UTF-8格式的编码转换 // parameter.Append(Sortedstr[i].Split(delimiterChars)[0] + "=" + HttpUtility.UrlEncode(Sortedstr[i].Split(delimiterChars)[1]) + "&"); // } // // parameter.Append("sign=" + sign + "&sign_type=" + sign_type); // // //返回支付Url; // return parameter.ToString(); } //获取远程服务器ATN结果,验证是否是支付宝服务器发来的请求 public static string Get_Http(string a_strUrl, int timeout) { string strResult; try { HttpWebRequest myReq = (HttpWebRequest)HttpWebRequest.Create(a_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; } } }
以上三个文件建之后,就可以在需要的地方对支付宝接口进行调用以完成支付宝支付的功能了(Default.aspx.cs)
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 Gateway; public partial class _Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { } protected void BtnAlipay_Click(object sender, EventArgs e) { //业务参数赋值; string gateway = "https://www.alipay.com/cooperate/gateway.do?"; //支付接口 string service = "create_direct_pay_by_user"; //服务名称,这个是识别是何接口实现何功能的标识,请勿修改 string seller_email = ""; //商家签约时的支付宝帐号,即收款的支付宝帐号 string sign_type = "MD5"; //加密类型,签名方式“不用改” string key = ""; //安全校验码,与partner是一组,获取方式是:用签约时支付宝帐号登陆支付宝网站www.alipay.com,在商家服务我的商家里即可查到。 string partner = ""; //商户ID,合作身份者ID,合作伙伴ID string _input_charset = "utf-8"; //编码类型,完全根据客户自身的项目的编码格式而定,千万不要填错。否则极其容易造成MD5加密错误。 string show_url = "http://www.alipay.com/"; //展示地址,即在支付页面时,商品名称旁边的“详情”的链接地址。 string out_trade_no = TxtOrderno.Text.Trim(); //客户自己的订单号,订单号必须在自身订单系统中保持唯一性 string subject = TxtSubject.Text.Trim(); //商品名称,也可称为订单名称,该接口并不是单一的只能买一样东西,可把一次支付当作一次下订单 string body = TxtBody.Text.Trim(); //商品描述,即备注 string total_fee = TxtTotal_fee.Text.Trim(); //商品价格,也可称为订单的总金额 //服务器通知url(Alipay_Notify.aspx文件所在路经),必须是完整的路径地址 string notify_url = "http://localhost:8978/direct_vs2005_utf/Alipay_Notify.aspx"; //服务器返回url(Alipay_Return.aspx文件所在路经),必须是完整的路径地址 string return_url = "http://localhost:8978/direct_vs2005_utf/Alipay_Return.aspx"; //构造数组; //以下数组即是参与加密的参数,若参数的值不允许为空,若该参数为空,则不要成为该数组的元素 string[] para ={ "service="+service, "partner=" + partner, "seller_email=" + seller_email, "out_trade_no=" + out_trade_no, "subject=" + subject, "body=" + body, "total_fee=" + total_fee, "show_url=" + show_url, "payment_type=1", "notify_url=" + notify_url, "return_url=" + return_url, "_input_charset="+_input_charset }; //支付URL生成 string aliay_url = AliPay.CreatUrl( //gateway,//GET方式传递参数时请去掉注释 para, _input_charset, sign_type, key ); //以下是GET方式传递参数 //Response.Redirect(aliay_url); //以下是POST方式传递参数 Response.Write("<form name='alipaysubmit' method='post' action='https://www.alipay.com/cooperate/gateway.do?_input_charset=utf-8'>"); Response.Write("<input type='hidden' name='service' value=" + service + ">"); Response.Write("<input type='hidden' name='partner' value=" + partner + ">"); Response.Write("<input type='hidden' name='seller_email' value=" + seller_email + ">"); Response.Write("<input type='hidden' name='out_trade_no' value=" + out_trade_no + ">"); Response.Write("<input type='hidden' name='subject' value=" + subject + ">"); Response.Write("<input type='hidden' name='body' value=" + body + ">"); Response.Write("<input type='hidden' name='total_fee' value=" + total_fee + ">"); Response.Write("<input type='hidden' name='show_url' value=" + show_url + ">"); Response.Write("<input type='hidden' name='return_url' value=" + return_url + ">"); Response.Write("<input type='hidden' name='notify_url' value=" + notify_url + ">"); Response.Write("<input type='hidden' name='payment_type' value=1>"); Response.Write("<input type='hidden' name='sign' value=" + aliay_url + ">"); Response.Write("<input type='hidden' name='sign_type' value=" + sign_type + ">"); Response.Write("</form>"); Response.Write("<script>"); Response.Write("document.alipaysubmit.submit()"); Response.Write("</script>"); } }
转接到支付宝的付款功能在调试过程中,必须真实的从网站够买商品并跳转到支付宝网
付款。可以将所买商品的单价设为0.01元,从支付宝付款成功并重新跳回本网站后,检查订单信息是否正确,以及对传回的 数据所做的处理操作是否正确。总之,用支付宝付款的功能在调试过程中必须进行实际的支付操作才能发现错误。