背景
当下最流行的第三方支付平台就是支付宝和微信了,这种第三方支付的方式以其极大的方便快捷性,被广泛的使用,如今各大app中都接入了这种模式。
官方地址
https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.SAJ1pf&treeId=204&articleId=106079&docType=1
前期准备
在支付宝平台注册账号
在开发者中心模块创建新的应用,这里需要进行线上签约,才能获得pid。签约需要填写一些公司的资料信息,这项工作一般都是运营或者是产品人员去申请。
设置基础环境,填写应用网关以及授权回调地址等信息
生成秘钥,有私钥和公钥,私钥进行加密,公钥负责解密(设置到支付宝平台),最新版的支付宝有两种公钥形式, RSA2(SHA256) RSA(SHA1) 推荐使用前者。下载秘钥生成工具,下载地址:
[https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.Kfqj5c&treeId=291&articleId=105971&docType=1](https://doc.open.alipay.com/docs/doc.htm?spm=a219a.7629140.0.0.Kfqj5c&treeId=291&articleId=105971&docType=1)
生成公钥后配置到支付宝后台。
系统架构
在介绍具体的接入流程之前先来看看支付宝支付系统的架构以及支付的流程图
支付系统架构
支付主流程
接入流程
导入开发资源
下载sdk资源并将sdk目录下的alipaySdk-20170407.jar包导入到项目中
重新编译工程,就会把引用加入的jar包,引用成功后如图,如果没有成功就需要手动添加了
手动添加jar包
修改Manifest
在工程中的清单文件中添加声明:
这里写代码片
<activity android:name="com.alipay.sdk.app.H5PayActivity" android:configChanges="orientation|keyboardHidden|navigation" android:exported="false" android:screenOrientation="behind" > </activity> <activity android:name="com.alipay.sdk.auth.AuthActivity" android:configChanges="orientation|keyboardHidden|navigation" android:exported="false" android:screenOrientation="behind" > </activity>
权限声明:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
添加混淆规则:
``` -libraryjars libs/alipaySDK-20150602.jar -keep class com.alipay.android.app.IAlixPay{*;} -keep class com.alipay.android.app.IAlixPay$Stub{*;} -keep class com.alipay.android.app.IRemoteServiceCallback{*;} -keep class com.alipay.android.app.IRemoteServiceCallback$Stub{*;} -keep class com.alipay.sdk.app.PayTask{ public *;} -keep class com.alipay.sdk.app.AuthTask{ public *;} ```
至此,开发包资源导入已经完成,下面就是具体的接口调用
生成订单数据
- 如demo中,已经演示了怎样生成订单数据,以及订单的格式。但是实际上为了安全,生成订单数据的操作要放在服务器去做。具体代码参考demo。
支付接口调用
获取 PayTask实例,并在子线程执行。代码示例
Runnable payRunnable = new Runnable() { @Override public void run() { PayTask alipay = new PayTask(PayDemoActivity.this); Map<String, String> result = alipay.payV2(orderInfo, true); Log.i("msp", result.toString()); Message msg = new Message(); msg.what = SDK_PAY_FLAG; msg.obj = result; mHandler.sendMessage(msg); } }; //必须一异步调用 Thread payThread = new Thread(payRunnable); payThread.start();
支付结果获取和处理,调用pay方法支付后,将通过2种途径获得支付结果:
1. 同步返回
商户应用客户端通过当前调用支付的Activity的Handler对象,通过它的回调函数获取支付结果。(可参考alipay_demo实现)
代码示例:private Handler mHandler = new Handler() { public void handleMessage(Message msg) { Result result = new Result((String) msg.obj); Toast.makeText(DemoActivity.this, result.getResult(), Toast.LENGTH_LONG).show(); }; };
同步通知结果参数说明:
结果码代表的含义:
同步验签:
在返回数据resultStatus为9000的情况下,解析result结果,提取验证签名的相关核心数据:
第一步: 提取alipay_trade_app_pay_response字段值,其代表签名原始字符串,上述示例格式如下:
{"code":"10000","msg":"Success","total_amount":"9.00","app_id":"2014072300007148","trade_no":"2014112400001000340011111118","seller_id":"2088111111116894","out_trade_no":"70501111111S001111119"}
第二步: 提取sign_type字段值,其代表签名类型,上述示例格式如下:
RSA2
第三步: 提取sign字段值,其代表签名结果,上述示例格式如下:
NGfStJf3i3ooWBuCDIQSumOpaGBcQz+aoAqyGh3W6EqA/gmyPYwLJ2REFijY9XPTApI9YglZyMw+ZMhd3kb0mh4RAXMrb6mekX4Zu8Nf6geOwIa9kLOnw0IMCjxi4abDIfXhxrXyj********
第四步: 验证签名是否合法:
使用各自语言对应的SHA256WithRSA签名验证函数,传入签名的原始字符串、支付宝公钥、签名类型RSA、签名字符进行合法性验证。
第五步: 在第四步签名验证通过后,必须严格按照如下的描述校验通知参数的合法性:
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有任何一个验证不通过,则表明同步校验结果是无效的,只有全部验证通过后,才可以认定买家付款成功。
这里的验签可以不做,用异步验签的方式代替
2.异步通知
需要提供一个http协议的接口,包含在请求支付的入参中,其key对应notify_url。支付宝服务器在支付完成后,会以POST方式调用notify_url传输数据。参数说明
支付宝定义了很多参数,是商户在与支付宝进行数据交互时,提供给支付宝的请求数据,以便支付宝根据这些数据进一步处理。公共参数
业务参数
业务扩展参数说明
特殊说明(很重要)
商户在请求参数中,自己附属的一些额外参数,不要和支付宝系统中约定的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×tamp=2016-07-01 08:08:08&method=alipay.trade.app.pay¬ify_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":"测试一笔支付" }
商户的请求参数中,所有的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×tamp=2016-07-01 08:08:08&method=alipay.trade.app.pay¬ify_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" }
- 商户支付请求参数的安全注意点:
a)请求参数的sign字段请务必在服务端完成签名生成(不要在客户端本地签名);
b)支付请求中的订单金额total_amount,请务必依赖服务端,不要轻信客户端上行的数据(客户端本地上行数据在用户手机环境中无法确保一定安全)。