我们的支付宝悬赏项目今天上线了,整理一下开发过程中遇到的问题,以及相应的解决方法。我们线上项目地址:http://yingyong.taobao.com/show.htm?app_id=155001
测试环境的ip限制
支付宝项目大多涉及到money,所以对安全性要求较高。支付宝一般要求第三方提供固定ip的白名单,仅允许白名单ip访问其系统接口(注:测试开发阶段,访问页面接口,也需通过固定ip)。
测试开发阶段,也同样需要固定ip的服务器,并在这些服务器上配置支付宝提供的hosts。我们的测试、开发服务器出口ip不固定,但是可以通过ssh的端口转发、本地代理等方式解决。以下,将称呼该固定ip服务器为代理服务器。
为chrome安装proxy switch,并用putty建立一个到代理服务器的ssh代理通道,指定给proxy switch。在本地开发机上,将hosts配置为淘宝所需。这样就可以访问其页面接口了。下面通过linux curl函数代理解决固定ip访问系统接口的问题。
Linux curl函数代理
绑定帐号之类的页面接口访问,可以通过浏览器代理实现。但是之后的创建悬赏任务等,都是系统接口,在server端执行的,需要设置linux下curl函数的sock代理。原理一样,也是通过ssh代理实现:ssh -TnNv -D local-port -p ssh-proxy-port user@ssh-proxy-ip # 如果需要后台执行,则ssh -qTfnN 。
之后,在代码里设置代理信息:
ssh代理建立完成后,可以写一些小测试代码,检查代理是否正确。这时,有些情况会没有响应,这时可以换一个目标url试试。比如我之前用baidu测试,就没有响应,但是退出ssh代理时可以看到:
debug1: channel 2: free: direct-tcpip: listening port 7070 for 119.75.218.77 port 80, connect from 127.0.0.1 port 21856, nchannels 3 debug1: channel 3: free: direct-tcpip: listening port 7070 for 119.75.217.56 port 80, connect from 127.0.0.1 port 21857, nchannels 2 debug1: channel 4: free: direct-tcpip: listening port 7070 for 119.75.217.56 port 80, connect from 127.0.0.1 port 21864, nchannels 1
说明还是连接上了,但是baidu没给影响而已。
这样就彻底解决了固定IP的问题,以下正式开发。
绑定帐号
我们的功能需求是,定期给我们的用户打款,人和金额都不定。
由于支付宝接口中,用户都是用支付宝唯一用户号(2088开头,16位纯数字)来标识的,可以通过用户绑定的方式获取其支付宝id号(也有其他途径,比如taobao.user.get)。针对悬赏系列接口而言,仅创建任务者需要通过该接口绑定。其他如收款者,只需要获取到支付宝唯一号即可,不需要强制绑定。
例如我们的用户flykobe登录之后,希望绑定支付宝帐号,则页面会由站内跳转到alipay,flykobe在那里输入支付宝的帐号、密码手工授权,授权成功后,会跳转到return_url指定的我们的站内页面来,提示授权成功,并且该return_url会将绑定的结果(包括支付宝唯一用户号、部分屏蔽的支付宝帐号、绑定key)记录在我们的本地存储里,留待后用。
sns.account.bind参数和返回值解释:
Args:sns_user_id – 第三方站内用户id;sns_user_name – 第三方站内用户名; type – 我使用的是common,因为不清楚buyer、seller、common之后的区别。注意:之后悬赏接口的outer_account_name和outer_account_id就对应这里的sns_user_name和sns_user_id。
Returns:alipay_login_id – 支付宝帐号,可能是邮箱、手机号等;key – 在解除绑定时需要,之后的悬赏接口未见使用;alipay_card_no – 支付宝用户号,16位纯数字,目前以2088开头。
注意:在测试阶段,使用的支付宝帐号是测试帐号,需要请支付宝帮忙生成,否则就会一直报“请求参数不正确。”
注意2:对支付宝返回值进行sign verify的时候,仅需要将支付宝提供的参数作为verify的对象,而return_url等中自带的参数不需要!否则就会verify失败!
创建任务
可以在https://shang.alipay.com/qiugou/index.htm看看支付宝的悬赏任务。我的理解是,由一些商家或者组织,发布任务提供赏金(即冻结这部分钱),一旦用户完成了这些任务,并由商家人工审核通过后,就可以标记用户,并自动完成支付赏金的过程。创建任务接口,顾名思义,就是创建一个新的悬赏任务。
在支付宝提供的悬赏接口中分为页面接口和系统接口。前者实际上是带着第三方提供的url参数跳转到支付宝里,需要人工输入一些密码、验证码等信息来完成的;后者则是可以完全代码后台运行,不需要人工干预的。
创建任务是一个页面接口,解释一下参数和返回值。
Args:outer_task_id – 第三方的任务id,由第三方保证其唯一性;outer_task_freeze_no – 第三方流水号,也由第三方保证唯一性;task_amount – 赏金金额(总额);task_type – 目前测试阶段,由支付宝提供了一个字符串作为我们的任务类型,不清楚之后应该传入什么值;task_title – 标题;task_expired_time – 截止时间;outer_account_name – 绑定的第三方用户名;outer_account_id – 绑定的第三方用户id。未使用增值服务,所以增值字段都置为空了。
Returns:仅多返回了paid_time – 支付时间。其他还返回了outer_task_id、outer_task_freeze_no、task_amount、additional_profit_amount、additional_profit_transfer_no。所以,具体支付的细节,需要在调用该create接口前,第三方自己记录下来。
注意:在测试环境下,验证码都是“8888”!
注意2:线上环境里,任务的创建者必须是与支付宝签约的帐号,否则在进行后续的pay动作时,会报错:PLATFORM_AUTHORITY_ILLEGAL
注意3:线上环境,刚上线时,创建的任务截止时间不要太长、金额不要太大。如果用户有未结束的创建任务,则无法解除绑定。非签约用户可创建任务,但该任务之后无法支付赏金,也无法结束。
打款
alipay.witkey.task.pay.by.platform接口是真正付款的接口,是一个系统接口。可以针对不同的用户,支付不同的金额。需要创建者的sns_user_id或者支付宝id,以及收款用户的支付宝id和sns_user_name,收款用户可以不绑定帐号。支付宝id可以通过taobao.user.get获取(app授权后可用)。
在测试时,开始少传递了return_url和notify_url,则返回ILLEGAL_ARGUMENT错误。
该接口调用时,有以下几点需要注意的:
- 请求参数必须使用POST方式传递
- 返回分为同步和异步两种:
- 同步返回:仅表示支付宝是否成功受理该请求,若成功,第三方可以修改支付为“处理中”的状态;若失败,则代表参数有问题,或者金额不足等,需要自行处理
- 异步返回:至notify_url。表示赏金是否支付成功,第三方可修改支付为“已支付”状态。并且,第三方必须打印“success”作为响应,否则支付宝会多次调用notify_url,直至超时。
解释下参数:
Args:outer_task_id – 对应create里的outer_task_id;outer_account_id – 创建者的第三方用户id;alipay_user_id – 创建者的支付宝id;transfer_detail – 字符串,放款明细。记录数不超过1000。 多条记录之间用(*@|$)分割, 记录内部属性之间用(~*@^)分割。 具体内容: 赏金分配流水号~*@^打款金额 ~*@^支付宝用户号~*@^合作网站用户号。其中,赏金分配流水号由第三方自行指定;打款金额以元为单位,精确到分。支付宝用户号是收款人的支付宝id;合作网站用户号是收款人的第三方用户号,如果绑定的话,需要跟绑定时传入的sns_user_id一致。
异步返回给notify_url的数据是分条的。及transfer_detail中若含有N条数据,则会异步返回N次。异步返回成功后,可以在创建者绑定的支付宝帐号的账单明细中看到支付记录。
注意:失败时返回的错误提示码并不是很友好,比如,当传入的收款人不存在时,返回的是ILLEGAL_SIGN。并且如果传入的第三方用户号与支付宝用户号不一致的话,也报ILLEGAL_SIGN。
注意2:传入的notify_url中,不可以有GET参数!比如http://abc.com/ri.php?action=notify,否则支付宝会解析为:“http://abc.com/ri.php?action=notify,charset=utf-8,params={name=sign,”
最后,感谢支付宝合作方的各种帮助,我在开发过程中,提了N多的傻问题,不好意思了