服务端支付宝App支付对接笔记

支付宝在2016年12月份开始将以前的App支付改为移动支付,而现在的App支付则是全新的支付,而功能和以前的App支付并没有太大的差别,但申请现在的App支付需要先废除掉以前的移动支付(控制台显示的还是App支付),然后再重新申请.

对于现在的App支付对接,其实参照支付宝开发文档App支付请求参数说明就可以进行对接了,但对于新手对接时,还是可能会遇到一些问题.总结一下 
一.首先需要清楚公共参数和业务参数中标为必填的参数都必须要有,并提供给客户端.另外,自己的参数不要与支付宝给定的参数重名. 
二.主要参数的获取:

  1. app_id 创建应用,地址管理中心,就会生成对应的应用信息 
    这里写图片描述
  2. method 接口名称,这个可在App支付产品介绍 中根据自己的需求填写APP支付产品包含的接口和描述 或者翻看Api字典
  3. charset 请求使用的编码格式,一般都是使用utf-8
  4. sign_type 商户生成签名字符串所使用的签名算法类型,目前支持RSA2和RSA,推荐使用RSA2
  5. timestamp 发送请求的时间,格式”yyyy-MM-dd HH:mm:ss”
  6. version 调用的接口版本,固定为:1.0
  7. notify_url 支付宝服务器主动通知商户服务器里指定的页面http/https路径。建议商户使用https
  8. biz_content 业务请求参数的集合,最大长度不限,除公共参数外所有请求参数都必须放在这个参数中传递
  9. subject 商品的标题/交易标题/订单标题/订单关键字等.业务参数,拼接时放在biz_content中
  10. out_trade_no 商户网站唯一订单号,这个需要注意支付宝在调用支付的时候会在支付宝的后台生成一个支付订单,而我们调用支付的时候也需要生成我们自己的支付订单,这里的支付订单是我们自己生成的订单号,不要跟回参的trade_no(支付宝后台生成的订单号)搞混.业务参数,拼接时放在biz_content中
  11. total_amount 订单总金额,单位为元,精确到小数点后两位,取值范围[0.01,100000000] ,注意这里有好多说要必须格式化为.00的格式,其实不用,只要在取值范围内的整数和小数都可以.业务参数,拼接时放在biz_content中.业务参数,拼接时放在biz_content中*
  12. product_code 销售产品码,商家和支付宝签约的产品码,为固定值QUICK_MSECURITY_PAY.业务参数,拼接时放在biz_content中
  13. sign 商户请求参数的签名串,这个可参考签名 有两种方式,第一种是直接使用支付宝提供的SDK,这个比较简单,支付宝SDK已经对签名和验签做了处理,我们只需要按照参数请求,填入对应的值就行了.第二种是自行实现签名,而使用这种签名方式就有比较多的坑,在这里先讲一下签名需要注意的事项,后面的对接方式也是使用第二种签名方式实现的.根据文档上的说明”请求参数按照key=value&key=value方式拼接的未签名原始字符串”,如下所示:
app_id=2015052600090779&biz_content={"timeout_express":"30m","seller_id":"","product_code":"QUICK_MSECURITY_PAY","total_amount":"0.01","subject":"1","body":"我是测试数据","out_trade_no":"IQJZSRC1YMQB5HU"}&charset=utf-8&format=json&method=alipay.trade.app.pay&notify_url=http://domain.merchant.com/payment_notify&sign_type=RSA2&timestamp=2016-08-25 20:26:31&version=1.0
  • 1

这个文档已经做了详细的说明,但不太好找自行实现签名,公共的参数需要作为一级的key和value,而业务参数则拼接在biz_content,作为它的value,并且需要注意需要剔除值为空的参数,并按照第一个字符的键值ASCII码递增排序(字母升序排序),如果遇到相同字符则按照第二个字符的键值ASCII码递增排序,再调用加签方法 生成签名.生成签名后,可通过自行实现签名中的”工具演示签名过程”验证自己代码实现的签名的正确性

三 .拼接请求参数 
在上面的参数介绍中,已经介绍了加签方法.现在需要把签名也作为一对键值,经过ASCII编码组合后拼接到请求串中,这样就能得到类似如下的请求串

app_id=2015052600090779&biz_content={"timeout_express":"30m","seller_id":"","product_code":"QUICK_MSECURITY_PAY","total_amount":"0.01","subject":"1","body":"我是测试数据","out_trade_no":"IQJZSRC1YMQB5HU"}&charset=utf-8&format=json&method=alipay.trade.app.pay&notify_url=http://domain.merchant.com/payment_notify&sign_type=RSA2&timestamp=2016-08-25 20:26:31&version=1.0&sign=cYmuUnKi5QdBsoZEAbMXVMmRWjsuUj+y48A2DvWAVVBuYkiBj13CFDHu2vZQvmOfkjE0YqCUQE04kqm9Xg3tIX8tPeIGIFtsIyp/M45w1ZsDOiduBbduGfRo1XRsvAyVAv2hCrBLLrDI5Vi7uZZ77Lo5J0PpUUWwyQGt0M4cj8g=
  • 1

四.拼接请求参数 
最后对请求字符串的所有一级value(biz_content作为一个value)进行encode,编码格式按请求串中的charset为准,没传charset按UTF-8处理,获得最终的请求字符串:

app_id=2015052600090779&biz_content=%7B%22timeout_express%22%3A%2230m%22%2C%22seller_id%22%3A%22%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22total_amount%22%3A%220.01%22%2C%22subject%22%3A%221%22%2C%22body%22%3A%22%E6%88%91%E6%98%AF%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE%22%2C%22out_trade_no%22%3A%22IQJZSRC1YMQB5HU%22%7D&charset=utf-8&format=json&method=alipay.trade.app.pay&notify_url=http%3A%2F%2Fdomain.merchant.com%2Fpayment_notify&sign_type=RSA2&timestamp=2016-08-25%2020%3A26%3A31&version=1.0&sign=cYmuUnKi5QdBsoZEAbMXVMmRWjsuUj%2By48A2DvWAVVBuYkiBj13CFDHu2vZQvmOfkjE0YqCUQE04kqm9Xg3tIX8tPeIGIFtsIyp%2FM45w1ZsDOiduBbduGfRo1XRsvAyVAv2hCrBLLrDI5Vi7uZZ77Lo5J0PpUUWwyQGt0M4cj8g%3D
  • 1

注意,在使用UTF-8编码的时候可能会遇到空格被转为”+”的情况,这时需要将含有”+”的字符串替换为”%20” 
字符串替换

完成这些处理后,我们就可以将请求参数发送给客户端,让他们调用对应的支付接口了.

五.支付回调 
当支付完成以后,支付宝会调用我们的回调接口,在回调接口中,我们可以处理我们的业务逻辑.在回调中,我们首先需要验签:

验签方法,根据接口不同,验签时会去掉sign_type(rsaCheckV1)或者保留,sign_type(rsaCheckV2)。

/**    此方法会去掉sign_type做验签,暂时除生活号(原服务窗)激活开发者模式外都使用V1。
@param params 参数列表(包括待验签参数和签名值sign) key-参数名称 value-参数值
@param publicKey 验签公钥
@param charset 验签字符集
**/
boolean AlipaySignature.rsaCheckV1(Map<String, String> params, String publicKey, String charset, String sign_type)

/**    此方法不会去掉sign_type验签,用于生活号(原服务窗)激活开发者模式
@param params 参数列表(包括待验签参数和签名值sign) key-参数名称 value-参数值
@param publicKey 验签公钥
@param charset 验签字符集
**/
boolean AlipaySignature.rsaCheckV2(Map<String, String> params, String publicKey, String charset, String sign_type)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

验签成功后,按照支付结果异步通知中的描述,对支付结果中的业务内容进行二次校验,校验成功后在response中返回success并继续商户自身业务处理,校验失败返回failure.主要需要验证的有 
1、商户需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号; 
2、判断total_amount是否确实为该订单的实际金额(即商户订单创建时的金额); 
3、校验通知中的seller_id(或者seller_email) 是否为out_trade_no这笔单据对应的操作方(有的时候,一个商户可能有多个seller_id/seller_email); 
4、验证app_id是否为该商户本身。上述1、2、3、4有任何一个验证不通过,则表明同步校验结果是无效的,只有全部验证通过后,才可以认定买家付款成功。

当这些都通过后,就可进行自己的业务逻辑编写了.

另外,在支付对接中还有很多需要注意的事项,这些都在开发文档中有说明,这里做一下汇总: 
1. 商户在请求参数中,自己附属的一些额外参数,不要和支付宝系统中约定的key(下表中 公共请求参数\请求参数)重名,否则将可能导致未知的异常。 
比如以下示例中app_id=2014072300007148******&version=1.0&biz_content的key是公共请求参数,业务方自己的扩展参数需要放在biz_content内部,比如示例中tips属性,很显然下面total_amount属性是商户按照自己的业务属性赋值的,但是由于total_amount也是支付宝关键key,支付宝将会认为这个total_amount是支付宝业务的参数应该是金额,这个最终将导致误解析。下列请求串为了展示清晰,未进行encode并且做了格式化处理,下同。

app_id=2014072300007148&charset=UTF-8&version=1.0&timestamp=2016-07-01 08:08:08&method=alipay.trade.app.pay&notify_url=https://api.**.com/pay_receive_notify.html&sign_type=RSA2&sign=ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE&version=1.0&biz_content=
  {
    "body":"对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。",
    "subject":"大乐透",
    "out_trade_no":"70501111111S001111119",
    "timeout_express":"90m",
    "total_amount":"一共花费了10元",
    "seller_id":"2088102147948060",
    "auth_token":"appopenBb64d181d0146481ab6a762c00714cC27",
    "product_code":"QUICK_MSECURITY_PAY",
    "tips":"测试一笔支付"
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  1. 商户的请求参数中,所有的key(支付宝关键key或者商户自己的key),其对应的value中都不应该出现支付宝关键key,否则该类交易将可能被支付宝拦截禁止支付。 
    比如以下的请求中\”subject\”:\”大乐透 这个辣条不错 out_trade_no=123 total_fee=123.5\”,其value值中有支付宝关键key\”out_trade_no\”、\”total_fee\”,这样的业务请求参数支付宝将会拦截。
app_id=2014072300007148&charset=UTF-8&version=1.0&timestamp=2016-07-01 08:08:08&method=alipay.trade.app.pay&notify_url=https://api.**.com/pay_receive_notify.htm&sign_type=RSA2&sign=ERITJKEIJKJHKKKKKKKHJEREEEEEEEEEEE&version=1.0&biz_content=
  {
    "body":"对一笔交易的具体描述信息。如果是多种商品,请将商品描述字符串累加传给body。",
    "subject":"大乐透 这个辣条不错 out_trade_no=123 total_fee=123.5",
    "out_trade_no":"70501111111S001111119",
    "timeout_express":"90m",
    "total_amount":10.0,
    "seller_id":"2088102147948060",
    "auth_token":"appopenBb64d181d0146481ab6a762c00714cC27",
    "product_code":"QUICK_MSECURITY_PAY"
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  1. 商户支付请求参数的安全注意点: 
    a)请求参数的sign字段请务必在服务端完成签名生成(不要在客户端本地签名); 
    b)支付请求中的订单金额total_amount,请务必依赖服务端,不要轻信客户端上行的数据(客户端本地上行数据在用户手机环境中无法确保一定安全)。
  2. AlipayClient的实现类都是线程安全的,所以没有必要每次API请求都新建一个AlipayClient实现类;
  3. 创建AlipayClient实现类的实例时,指定format=json,相比xml格式,可以减少数据传输量,提升API请求效率。


地图大数据云平台  http://www.favxu.com

三维地球云平台    http://www.hapxu.com

地图云平台交流合作 QQ:63747667

邮箱:hui1788@163.com


阅读更多
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页