支付宝接口程序、文档及解读(ASP.NET)
最近需要为网站加入支付宝的充值接口,而目前关于支付宝接口开发的资料比较杂乱,这里就我此次开发所用到的资料进行汇总整理,希望能够帮助需要的朋友。
开发步骤:
1. 确定签约类型
支付宝的接口有多种类型,所以首先你要确定签约的合同类型,确定使用的是哪个接口,不至于走错了道道。
此步骤中,可以参考文档:支付宝接口操作教程
2. 下载开发文档
如果是即时到账的开发,请先下载并仔细阅读:标准快速付款接口文档
文档中比较详细的介绍了逻辑流程,时序图以及枚举定义等,最好在看示例代码之前通读之
3. 获取签约账号的支付宝安全校验码(key)和合作id:partner
支付宝示例程序的调试需要商户号和相应的密钥,否则无法进行程序的调试,具体获取方法请查看官方帮助,不再赘述
4. 下载支付宝示例程序
asp 即时到账接口代码实例:点此下载 (31.62 KB)
php_GBK 即时到账接口代码实例:点此下载 (22.02 KB)
php_UTF-8 即时到账接口代码实例:点此下载 (22.02 KB)
net_05_GBK(post方式) 即时到账接口代码实例:点此下载 (12.03 KB)
net_05_UTF-8(post方式) 即时到账接口代码实例: 点此下载 (10.72 KB)
java_GBK(get方式) 即时到账接口代码实例:点此下载 (136.54 KB)
java_UTF-8 (get方式)即时到账接口代码实例:点此下载 (112.67 KB)
5. 支付宝示例程序的解读
下面内容转自http://dev.alipay.com/devclub/mvnforum/viewthread_thread,161_offset,0
大家想必都有这种困惑——拿到支付宝的接口代码后,尽管里面的程序有注释,接口代码包中也附有开发说明,但还是不知道该如何入手。这不难想象是什么原因,因为自己并不了解这个接口的工作原理是什么?
那么这篇文章就是要向大家全面展示关于支付宝接口的所有东西,以便大家能快速上手把接口接入自己的项目中,也能帮助那些已经对支付宝接口有所了解的程序开发者们更了解支付宝的一些通用规则、特殊用途等。
正题开始——
一、 结构
a) 一般由两部分组成,接入部分与通知返回部分。接入部分即为传递参数等信息组合成超级链接,并用该链接来进行跳转。通知返回部分则是支付宝服务器对该笔订单处理完毕后,通知与返回该笔订单的详细信息到商户服务器,商
户服务器接收到后,并对其进行数据处理。
b) 以实物标准双接口ASP代码中的程序为例。
i. 接入部分的页面文件包含:配置页alipay_Config.asp、方法详细页alipay/Alipay_Payto.asp、程序入口页index.asp以及MD5加密方法类页alipayto/Alipay_md5.asp。
ii. 通知返回部分的页面文件包含:方法详细页alipay/Alipay_Payto.asp、MD5加密方法类页alipayto/Alipay_md5.asp、支付完成后(支付宝处理完毕后)自动跳转回的自定义页面return_Alipay_Notify.asp、两方服务器间相互交互(肉眼无法见到的)通知页Alipay_Notif
y.asp。
这里大家可以一目了然,MD5加密方法类与方法详细页不论是哪部分都有被调用,因此这两个文件可视为核心代码部分,若想理解接口的工作原理则要从该部分入手。
c) 以实物标准双接口的ASP.NET C#语言代码程序为例:
i. 接入部分的页面文件包含:无需更改的ALIPAY的类文件App_Code/AliPay.cs及入口页面文件Default.aspx
ii. 通知返回部分的页面文件包含:两方服务器间相互交互(肉眼无法见到的)通知页Alipay_Notify.aspx、支付完成后(支付宝处理完毕后)自动跳转回的自定义页面Alipay_Return.aspx、
无需更改的ALIPAY的类文件App_Code/AliPay.cs
这个架构是否更容易理解了?没错,核心部分的运算过程就在AliPay.cs这个文件中。
二、 工作原理
大家对结构部分已经有所了解,那么我们就开始分析具体的这个接口是如何运作的。
a) 接入部分原理
i. 第一步——选定参数信息:
结合技术文档以及接口代码DEMO,选定传递给支付宝服务器的参数,以实物标准双接口为例。如必传项service、partner、seller_email、sign、sign_type、out_trade
_no、price、subject、quantity、payment_type以及最少一组的物流信息参数三个logistics_type、logistics_fee、logistics_payment
等,选填项body、discount、show_url等。
以ASP.NET C#语言代码程序为例:string service = "trade_create_by_buyer";
string seller_email = "aaaa@126.com";
string sign_type = "MD5";
string key = "********************************";
string partner = "2088************";
string _input_charset = "utf-8";
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 price = TxtPrice.Text.Trim();
string quantity = TxtQua.Text.Trim();
string logistics_type = "POST";
string logistics_fee = TxtPost.Text.Trim();
string logistics_payment = "BUYER_PAY";
string notify_url = "http://www.xxx.com/swnet05utf8/Alipay_Notify.aspx";
string return_url = "http://www.xxx.com/swnet05utf8/Alipay_Return.aspx";
ii. 第二步——排序:
把这些参数的变量名(即技术文档里给出的变量名,以这种方式组合:service=”trade_create_by_buyer”作为一串字符串)按从a到z的顺序依次排序。以ASP.NET C#语言代码程序为例,该功能在ALIPAY.CS类中;以ASP代码中的程序为例,该功能在alipayto/Alipay_Payto.asp文件中。
iii. 第三步——加密:
目前一般的加密方式是MD5,不论是哪种加密方式,要加密的信息是要传给支付宝的信息,且存在于技术文档中,而非自定义的变量名。对以上排序好的所有参数(不包括网关参数即:string gateway = "https://www.alipay.com/cooperate/gateway.do?" ;)以循环的方式,用‘&’字符拼接成一长串字符串(这里需要注意,所有的参数都是&字符来拼接的,拼接后直接再拼接安全校验码Key,在程序中大家可看到,这个key是直接加到该字符串后面而没有用&字符 ),之后进行加密。得出的加密字符串集存储于sign这个参数中。
iv. 第四步——拼接字符串成URL链接
我们已经拿到了各个参数、参数所属的值以及加密得出的加密字符串,那么手上现在的所有参数信息的格式,应当都是一组一组的service=”trade_create_by_buyer”这种格式的字符串,拼接的话,则依靠循环的方式遍历所有的这种字符串,因为这次的拼接是要成URL链接,所以之前排除在外的网关gaetway和加密类型参数sig
n_type也都会被拼接进来,那么,连接的字符则用大家所熟知的字符’&’,就这样得出一个完整的URL链接地址,如:
该链接来自支付宝官方的技术文档“标准实物双接口技术文档”
v. 第五步——自动跳转
第四步中已经运算得出的URL链接字符串,我们则要让其活起来,那么活起来的方式就是——用程序调用它,也就是所谓的页面自动跳转。这样就能跳到支付宝的官方收银台页面。
可以说,现在已经成功的把支付宝接口融合进了大家自己的网站中,且能够使用支付宝来进行付款了。
b) 通知返回部分原理
i. 专业术语
通知返回是两个页面,即传递给支付宝时的notify_url参数所对应的页面文件(asp.net的是Alipay_Notify.aspx、asp的是Alipay_Notify.asp)称之为通知页,传递
给支付宝时的return_url参数所对应的页面文件(asp.net的是Alipay_Return.aspx、asp的是return_Alipay_Notify.asp)称之为返回页。
ii. 通知返回原理
1. 第一步——验证是否是支付宝服务器发来的请求:
a) 以asp程序代码为例:alipayNotifyURL = "http://notify.alipay.com/trade/notify_query.do?"
alipayNotifyURL = alipayNotifyURL &"partner=" & partner & "¬ify_id=" & request("notify_id")
Set Retrieval = Server.CreateObject("Msxml2.ServerXMLHTTP.3.0")
Retrieval.setOption 2, 13056
Retrieval.open "GET", alipayNotifyURL, False, "", ""
Retrieval.send()
ResponseTxt = Retrieval.ResponseText
Set Retrieval = Nothing
得到的便是ResponseTxt的值,这是下面的步骤要用到的。
b) 以asp.net C#程序代码为例://获取远程服务器ATN结果,验证是否是支付宝服务器发来的请求
public 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;
}
调用部分:string alipayNotifyURL = "https://www.alipay.com/cooperate/gateway.do?service=notify_verify";
string partner = "2088************";
alipayNotifyURL = alipayNotifyURL + "&partner=" + partner + "¬ify_id=" + Request.Form["notify_id"];
//获取支付宝ATN返回结果,true是正确的订单信息,false 是无效的
string responseTxt = Get_Http(alipayNotifyURL, 120000);
得到的便是ResponseTxt的值,这是下面的步骤要用到的。
2. 第二步——排序:
该部分的排序的原理与“接入部分”的原理“排序步骤”一样,值得注意的是 ,这里的参数是支付宝通知返回时,传回来的订单信息的各种参数以及值。
3. 第三步——加密:
该部分的加密原理与“接入部分”的原理“加密步骤”一样,依然值得注意的部分是加密的参数信息,这些参数信息是来源于上面一步骤排序好后的参数拼接起来的字符串来加密的。
4. 第四步——判断:
上面我们有得到加密的结果(命名为mysign吧)、检验是否是支付宝发来的消息的正确性ResponseTxt、以及通过POST或GET的方式得到的sign参数的值,那么这个判断的含义便是通知返回里最重要
的部分了,因为它是来检验下面的程序是否执行我们的数据处理的。如何判断呢?各语言程序代码中,都是把加密得出的结果mysign与从支付宝那或得到的sign的值进行比较,并且还要让reponseTxt这个的
值要等于true,这样才达到验证成功。值得注意的是 ,大家都有遇到过这种事,支付部分即接入部分的确是做好了,但为什么无法与支付宝的交易信息同步,出现的问题就在这个判断上没有成功,下面的第五部分则会详细说明。
5. 第五步——自身网站的数据处理
终于判断成功了,程序已经执行到了这里。各语言程序代码的这块地方的注释都写着“更新自己数据库的订单语句”或是“这里可以指定你需要显示的内容”。如字面上的意思,这块地方就是要我们大家来对这笔交易信息进行数
据处理,即编写程序。这个说法大概专业了点,简单易懂的讲法便是,支付宝的交易成功的信息和其他的一切交易状态,自己的网站也能够对这笔订单同步起来,即支付宝里这笔订单的交易状态是“买家已付款等待卖家发货”,
那么自己网站里显示的这笔状态也因如此,那么就应当在这里面写下诸如:if (Request.Form["trade_status"] == "WAIT_SELLER_SEND_GOODS")// 判断支付状态_买家付款成功,等待卖家发货(文档中有枚举表可以参考)
{
//更新自己数据库的订单语句,请自己填写一下
string strOrderNO = Request.Form["out_trade_no"];//订单号
string strPrice = Request.Form["price"];//金额
string sql = "update order_table set order_status = ‘买家已付款,等待卖家发货’ where order_no = " + strOrderNO;
Update(sql);
}
等数据库处理代码。
iii. 存在的区别
1. 大家仔细阅读代码不难发现,在通知页中程序运行时,获取参数的方法是用POST方式,而返回页中程序运行时,获取参数的方法是用GET方式。由此可知一些基本的信息——返回页传递回来的参数信息是储存在URL链接
里的,而通知页的参数信息是不在URL链接里,也能从中推断出二者在功能上的差异。
2. 大家可看到通知页面比返回页中多一个环节,那就是Response.Write("success");
作用上不同的详细说明,大家可以看下面的第四部分。
三、 参数
首先大家有个疑问,技术文档中的输入参数列表中给出了诸多参数,而手上拿到的代码里只写了一部分参数来进行传递信息,这究竟是为什么?那么我们先带着这个疑问往下看。
以下讨论的参数不涵盖网关gateway、加密参数sign、加密类型sign_type,因为这些都是必须的。
以实物标准双接口为例,可把参数看做几个功能部分组成
a) 不可缺少的参数
i. service服务参数,这个是用来区别这个接口是用的什么接口,所以绝对不能修改。
ii. partner合作身份者ID、key安全校验码或称私钥这一组参数是签约合同生效后才能拿的到,partner是来鉴别是哪个商家与支付宝签约,而这个Key它如同钥匙般相当重要。
iii. seller_email收款人支付宝账号,支付宝中有手机类型、电子邮件类型的支付宝账号是都可以用这个参数的。
iv. subject在支付宝的收银台里是直接与商品名称关联在一起的,但是说的更准确些的话,这个参数是这笔交易的名称,因为这笔交易不一定只买一件商品。它的作用不仅是在收银台里可以清晰的显示出来,而且在支付宝的账
户的交易明细的列表里,它也是排在第一列,由此可推测出,它有财务对账、作为交易查询的筛选条件等诸多作用。非常重要。
v. out_trade_no技术文档中给出的是商户交易号(确保在商户系统中唯一),顾名思义这个就是我们大家自己网站的订单系统里的唯一订单号,而非支付宝的。这里需要强调的,这个订单号必须得是唯一的,如何唯一法?自己网站
里订单系统的订单号是绝对唯一的吧,支付宝要求的唯一就是这个,为什么非要唯一?支付宝会根据订单号来判定这笔订单对于这个商家的所有交易中是否是唯一的。
vi. price金额、quantity数量,这里设置有两种方式一种商品的单价金额,多个数量(即大于等于1)。另种是数量为1,金额代表总额,甚至是包含了运费。为什么大部分的客户要这么做?原因很简单,第一,购物车里的东西不一定是单纯的
一件或者多件相同的商品,那么为商品设置金额时就有困难了,因此这里用总额是最好的,而数量就默认为1。第二,运费的设置很多客户是与各家快递公司签约、每件物品的快递费用也不尽相同,为了省去麻烦,在程序计算的
时候干脆把运费也加进去。因此我们只需要记住一件事,这个price的金额就是所谓的总额了。
vii. payment_type支付类型,没什么可说的直接写成1,无需改动。
viii. 物流信息logistics_type、logistics_fee、logistics_payment这是一组物流信息,实物标准双接口中必须得至少有一组物流信息,也就是指这三个参数了,最多可有三组,哪三组呢?logistics_type_1、logistics_fee_1、logistics_paymen
t_1(第二组);logistics_type_2、logistics_fee_2、logistics_payment_2(第三组)。后两组为可选项。一般前面有说Price已经是总额了且包含了运费,那
么这里物流运费就直接设置成0即可,即logistics_fee=”0”,其他两个的信息可参考技术文档来填写,因为要从技术文档中的枚举列表里来选择,所以绝不可乱填写。
b) 可增加的有用参数
i. 物流信息最多三组,最少一组,这已经在前部分有所提及,这里就不再细说。
ii. _input_charset,当是UTF-8的编码格式时必须得用到且不允许为空的,即_input_charset=”utf-8”
iii. notify_url、return_url,return_url代表支付完毕后可以自动从支付宝的官方页面跳转回来,notify_url这个是防止调单的首选最佳工具。
iv. body,在支付宝收银台中的商品描述里显示,如果subject是订单名称的话,那么这个body则最准确的称之为订单描述,其实个人认为它作为备注之类的更为恰当。很多人都很郁闷支付宝为何不能像其他公司
的接口有个自定义的参数来存放客户想要的东西,其实body也具有类似的这种功能,它不仅容纳的信息是所有参数里最大的,而且还是以字符串的形式储存,个人认为它其实也是非常重要的不可缺少的参数之一呢。
v. discount折扣,顾名思义如果小于0,则是用原金额Price*quantity+(discount),实际金额便比原总额小了。现在有些商户有支付宝的优惠卷,而优惠卷的用途也是在这个参数中体现,具体做法与前
面无异。
vi. show_url商品展示地址,这个链接的作用是在支付宝收银台的商品链接旁边有个下划线“详情”的链接,而点链接弹出的一个新页面便是这个商品展示地址的页面。
vii. 收货信息receive_name、receive_address、receive_zip、receive_phone、receive_mobile,这些信息若也设置为传递给支付宝的参数之一的话,那么在支付宝收银台点选下一步的时候,本该出现的填写收货信息页面不见踪影,而直接跳到了收货信息页面的下一个页面去了。很多商户在自己的网站的购物
流程中都有一个填写收货信息的选项卡,为了省去到支付宝收银台中还要填写一次收货信息的麻烦,那么这些收货信息的参数就派上用场了。值得注意的是,收货人姓名和地址是必填项,不然还是会出现收货信息填写页。
viii. buyer_email买家支付宝账号,这个设置好后呈现的效果便是,原本是空的支付宝账号的输入框此时已经有个支付宝账号在里面放置。
c) 剩下的参数无需理会
整个实物标准双接口的参数介绍完毕,那么其他接口的参数还要介绍吗?大家从上面分析得出的东西对比技术文档的参数列表是否看出什么来了?
1、 参数列表的最后一列叫“可空”,N代表不允许为空,Y代表允许为空,结合上面的不可缺少参数与增加的有用参数来进行比较,不难发现,不可缺少的参数全是为N的。
2、 有些为Y的参数有一组,例如buyer_email、buyer_id,凡是遇到这种的一般都是二可选一也可都不选,或是二必选一。举例说明:二必选一的是seller_email、seller_id,二选一的
是buyer_email、buyer_id。
所有的接口的参数如此分析就能判断出哪些是重要参数哪些可不要,结合技术文档与程序接口来研究就能一目了然。
四、 通知返回
a) 返回页
传递给支付宝时的return_url参数所对应的页面文件。
具备的属性:
1、支付接口中买家的购买流程已经走到支付宝里且支付宝提示支付成功时,页面会自动跳转回自身网站的这个页面里来。
2、同步的,无时差
3、获得参数的方法是用get方式获取。
4、不论跳转回来程序判断是真还是假(if(sign = mysign and responseTxt = true))只跳转回来一次,不重复。
5、这个并不是支付宝服务器调用了该页面,而是通过与组合拼接各参数形成的URL链接原理等同,拼接出来的URL链接,之后程序上做自动跳转。
6、基于5的原因,该页面的程序调试可不必在服务器上而是本机上调试、运行。
b) 通知页
传递给支付宝时的notify_url参数所对应的页面文件
具备的属性:
1、这个通知页就是被支付宝调用才能启动的。
2、服务器间的互动,不像返回页肉眼可以看到,这个是看不到的。
3、获得参数的方法是用POST方式获取。
4、支付宝中的该笔交易存在,且该笔交易状态发生了变更,就会被调用。
5、被调用程序判断(if(sign = mysign and responseTxt = true)),若我们自己在该判断中有做程序编写,成功则不再被调用,不成功则会反复被调用。
6、异步的,第一次收到订单信息(以下都称之为“通知”)是与返回页近乎等同或等同的同步时间,在判断不成功的情况下,会收到第二次第三次等次数的通知,时间间隔从最先的一两分钟,到后面的几个小时。失效时间是4
8小时。
7、基于6的原因,该页面的程序调试必须在服务器上调试、运行。
8、程序编写时必须采用程序执行成功,才写页面response.Write(“success”);,不成功则写页面response.Write(“fail”); 支付宝根据success来判定是否要重新再次发送通知。
9、该页面的Html页面中必须是空白、无任何Html标签、无任何空格。
以C# ASP.NET实物标准双接口代码为例:if (mysign == sign && responseTxt == "true")
{
if (Request.Form["trade_status"] == "WAIT_BUYER_PAY")// 判断支付状态_等待买家付款(文档中有枚举表可以参考)
{
//更新自己数据库的订单语句,请自己填写一下
}
else if (Request.Form["trade_status"] == "WAIT_SELLER_SEND_GOODS")// 判断支付状态_买家付款成功,等待卖家发货(文档中有枚举表可以参考)
{
//更新自己数据库的订单语句,请自己填写一下
string strOrderNO = Request.Form["out_trade_no"];//订单号
string strPrice = Request.Form["price"];//金额
string sql = "update order_table set order_status = '买家已付款,等待卖家发货' where order_no = @out_trade_no";
Update(sql,para);
}
else if (Request.Form["trade_status"] == "WAIT_BUYER_CONFIRM_GOODS")// 判断支付状态_卖家已发货等待买家确认(文档中有枚举表可以参考)
{
//更新自己数据库的订单语句,请自己填写一下
string strOrderNO = Request.Form["out_trade_no"];//订单号
string strPrice = Request.Form["price"];//金额
string sql = "update order_table set order_status = '卖家已发货,等待买家确认收货' where order_no = @out_trade_no";
Update(sql, para);
}
else if (Request.Form["trade_status"] == "TRADE_FINISHED")// 判断支付状态_交易成功结束(文档中有枚举表可以参考)
{
//更新自己数据库的订单语句,请自己填写一下
string strOrderNO = Request.Form["out_trade_no"];//订单号
string strPrice = Request.Form["price"];//金额
string sql = "update order_table set order_status = '交易成功' where order_no = @out_trade_no";
Update(sql, para);
}
else
{
//更新自己数据库的订单语句,请自己填写一下
}
Response.Write("success");
}
else
{
Response.