今天总算把网站的财付通集成也搞定了。长吁一口气,很不轻松的。之前我希望让我的网站能同时集成支付宝和财付通。 支付宝集成我一个月前就搞定了。之前已经有“情留蚊子”在他自己的博客里放出他开发的支付宝集成工具。虽然他写的程序不能直接搬用到我的网站上来,但是至少的,我只需要改写不多的内容就能把它搞定了。 然后我从昨天开始做财付通集成功的。要做网站财付通集成首先得去它们的官方网站上申请企业版用户,地址是:http://mch.tenpay.com/market/index.shtml,申请右边的“即时到帐交易”就可以了,中介担保交易太麻烦了。 申请的时候各种资料请如实填写。注意的是那个邮箱地址千万不要填写企业邮局的邮箱地址,也不要填写QQ域名邮箱的邮箱地址,一定要填写163邮箱或者qq邮箱这些邮箱地址。据说是企业邮局的邮箱和QQ域名邮箱是不能收证书邮件的。碍,郁闷啊。我之前申请过一个QQ域名邮箱,webmaster@asprain.com,这次我在申请财付通企业版的时候就填写了这个邮箱了。谁知,整整等了四天都没有收到证书邮件。去联系财付通客服,才知道原来这邮箱是不能收证书邮件的。于是只好申请修改邮箱——非常费事。本来申请的时候只要在网上填写就可了了,谁知修改邮箱地址居然必须用传真把你的亲笔函件传真过去,还要附上身份证复印件或者敲上公章。没办法,最后我写好写了一个请求修改邮箱的函件,到街上找文印店传真了过去,真的很费事啊。 不过话说回来,财付通的客服MM真的很尽心尽力的。 你如果是用qq邮箱或者163邮箱申请财付通的话,一般一天功夫就能收到通过申请的邮件了,在邮件里,包含着你的商家编号(10位数字)、登录密码(初始登录密码为111111)还有一个32位密钥。另外还有一个附件是浏览器证书。可惜,我照着它们的要求做了一遍:把证书导进自己的IE浏览器里,然后到https://mch.tenpay.com/里去登录,选择“证书登录”,居然浏览器连连报错,说证书不对。没办法,我只好选择“非证书登录”,这下倒没问题了。登录进去之后第一件事你得同意两个用户协议,都同意了之后财付通企业版就能用了。 这个页面的下边有开发文档:http://mch.tenpay.com/market/index.shtml 自己去下载吧! 开发文档里有一个《腾讯公司财付通支付网关商户开发指南.doc》,里面有支付协议的详细说明——虽然是详细了,但是并不太好懂,我琢磨了一个晚上才基本理解了它的意思。还有四个版本的demo:asp、C++、jsp、php这四个版本。asp的那个版本我研究了一下,觉得它们写得真不好,代码糟糕得就像是一个只学asp三个月的年轻人写的。而且我仔细看了之后,才发现这四个demo里全都只包含支付协议,没有包含查询协议。而且支付协议是以get方式通知的。我觉得以get方式通知的话,而且没有经过查询确认的话,这个md5签名密钥是很容易伪造的呀。幸好,虽然demo里没有包含查询协议,但是那个《指南.doc》里有详细的查询协议说明,我就照着这个指南写出了这个包含查询确认的网站财付通集成组件。 那个《指南.doc》里说,财付通支付的过程是:买家在商家的网站上下单,然后点“财付通支付”,跳转到财付通的网站里,通过财付通的网站把钱付到商家的帐上,财付通的服务器会用xmlhttp的方式向return_url所指定的地址发送支付成功能的get通知,在收到return_url里吐出的“”之后返回。 我问过财付通的技术人员:万一没有收到return_url吐出的这句代码的话会怎么样?对方告诉我:财付通的服务器会连续向这个return_url发送三次支付成功通知的。(居然要连续发送三次啊?!我记得支付宝的支付成功通知是只发送一次的,之前我在开发支付宝集成功的的时候,用数据库记录下了所有的支付宝网站发来的通知内容,发现每支付一次只得到一次post通知,这次我在开发财付通集成功能的时候,用数据库记录下了财付通网站发来的所有的通知内容,确实发现支付一次会在不到三秒内连续通知三次的,有时候甚至通知了四次)。最终,买家支付成功之后,买家的浏览器又会跳转到return_url这个地址上的,而且后面还带着一长串包含着各种信息的querystring。所以要求商家在return_url这个地址里具有这些特点:包含这句代码,并能告诉买家支付是否成功了。验证是否成功取决于querystring里的所有的参数(除了sign这个参数之外)连缀起来,再连上一个商家密钥,再经过md5加密,看看能否和sign这个参数对得上号,对上号了就表明验证成功了,对不上号就表明验证没有成功。刚才我说:我觉得以get方式通知的话,而且没有经过查询确认的话,这个md5签名密钥是很容易伪造的。是的,因为既然sign的编码方式是公开的,get通知方式只需要在浏览器地址栏里加一些内容就能做到,那对黑客来说,恐怕是有空可钻的。但是如果再加上一个查询确认的话,黑客就没空可钻了。 那个《指南.doc》里说,财付通的查询协议是这样的:商家的网站向财付通的网址http://mch.tenpay.com/cgi-bin/cfbi_query_order_v3.cgi发出一个查询请求,查询请求里必须包含cmdno、date、bargainor_id、transaction_id、sp_billno、attach、output_xml、charset、sign这九个查询字段,然后财付通的网站会验证这前八个查询字段产生的md5编码和sign字段对不对得牢,如果对得牢的话就返回一个包含支付信息的xml,如果对不牢的话就会返回一个包含出错信息的xml。 这xml的样式可以查看以下这个地址: http://mch.tenpay.com/cgi-bin/cfbi_query_order_v3.cgi?cmdno=2&date=20091224&bargainor_id=1205466501&transaction_id=1205466501200912241433025205&sp_billno=27&attach=1&output_xml=1&charset=GB2312&sign=E91C661D8CBF2A0E3D20BC5C3238DF57 如果支付失败的话,或者支付不存在的话,retcode这个标签的值不是00,而是其它的一些值。因此这个标签可以鉴别是否支付成功。 为了保证只要买家支付成功商家的数据库就立即能把支付状态更新到成功状态,而不是买家的浏览器地址从财付通的网站上跳转到商家的网站上来之后才更新数据库,商家的网站里必须对财付通的网站发来的get通知作出响应。但是怎样区分财付通网站发来的get通知和买家的浏览器的跳转呢?我的办法很简单,就是查询request.ServerVariables("http_user_agent")的值,我发现如果是财付通网站发来的get通知的话,request.ServerVariables("http_user_agent")的值为空,如果是买家的浏览器的跳转的话,request.ServerVariables("http_user_agent")的值是各式各样的浏览器的http_user_agent值,总而言之不会是空的。而且还可以通过验证cookies来区分,那买家在商家的网站上买东西肯定是要登录的喽,而财付通的网站发来的get通知,cookies肯定也是空的。不过为了保险,我采用验证request.ServerVariables("http_user_agent")是否为空的办法来区分请求来自浏览器跳转还是来自财付通网站的通知。