第三方支付指的是第三方平台与各银行签约,在买方与卖方之间实现中介担保,从而增强了支付交易的安全性。国内常用的支付平台主要是支付宝和微信支付,其中支付宝的市场份额为71.5%,微信支付的市场份额为15.99%,也就是说这两家垄断了八分之七的支付市场(2015年数据)。除此之外,还有几个app开发会用到的支付平台,包括:银联支付,主要用于公共事业缴费,如水电煤、有线电视、移动电信等等的充值;易宝支付,主要用于各种报名考试的缴费,特别是公务员与事业单位招考;快钱,被万达收购,主要用于航空旅行、教育培训、游戏娱乐等网站的支付;京东支付,主要用于京东商城的支付;百度钱包,主要用于百度系的电商平台。
因为第三方支付只是个中介,交易流程要多次确认,所以app若要集成支付sdk,得进行以下处理:
1、除了作为买方的用户自己拥有支付账号,开发者还得申请作为卖方的商户账号。
2、支付过程中,虽然允许app直接与第三方支付平台通信,但是最好app要有自己的后台服务器,由自己的后台与第三方平台进行通信。这样做的好处是,一方面自己后台掌握了用户交易记录,做账有依据,管理也方便;另一方面,关键交易在自己后台处理,也减少了恶意篡改的风险。
3、为保证信息安全,需对关键数据进行加密处理,如支付宝采用RSA+BASE64算法,微信支付采用MD5算法,银联支付采用RSA算法。
支付宝支付
交易流程
支付宝支付的交易流程大致如下:
1、按照指定格式封装好交易信息;
2、对交易信息进行RSA加密与URL编码;
3、调用支付接口,传入加密好的信息串;(这步要另开线程处理,不能放在UI线程中)
4、支付宝sdk在界面下方弹出支付窗口,用户输入支付帐号信息,提交支付;
5、收到支付完成的结果,判断支付状态是成功还是失败;
集成步骤
支付宝sdk的集成比较简单,除了必要的权限外,无需再修改AndroidManifest.xml,jar包也只要导入alipaySdk-20160516.jar。
代码方面,支付宝官方给的demo采用了Thread+Handler的异步处理模式,不过该模式要把代码写在Activity中,不便管理和维护,而本文的测试代码将它改为Android自带的异步任务处理即AsyncTask方式
测试帐号
支付宝官方demo没有给出测试的商户账号,下面是我在网上找到的测试帐号:
[java]view plaincopy
1.// 商户PID
2.publicstaticfinalString PARTNER ="2088811977704990";
3.// 商户收款账号
4.publicstaticfinalString SELLER ="dev@ifensi.com";
5.// 商户私钥,pkcs8格式
6.publicstaticfinalString RSA_PRIVATE =
7."MIICXQIBAAKBgQDlQ468L1A7Q+GG80/Z8f3IsSiiFIluSxfTTSuJ/XSPzvYS+bMZ"
8.+"AQLMqq/nGhkp+1Q5pHF9LAQtQS3gL2pqzbKdtvZSsy/tNFFQcGCsgK2ygMl+MW/F"
9.+"g/ufx7c1jy1kZAeDyl1m302dnRrtSgDalkgH7FKRcmDxbXPTnFGHbg9zMQIDAQAB"
10.+"AoGAa28wGQF28H7L1Yh5V+FtkrlqGCHVkQjBfnRAPea205kheRzoD4SIwk4OJhb1"
11.+"ydWLz4M+53BT+Lz9eXveu3PvCdQe9zMIVC5dKUNVYCvvcHZ+Ot8HriiuwGPb3Quu"
12.+"twbnLGM5gxxPDo0yUyWrfaVn/qR35mS6TDfmgowVG8CmBpECQQDzuhodR/Jgxrtn"
13.+"tka+88alyy+BfjUZqNloPuE7JfXrpOxH5lodk7Y4lTki/dlo5BrK+hrismLFr9Du"
14.+"ueAJ7G9dAkEA8M8C6VnpUMAK5+rYcjKnQssDqcMfurKYEil1BD/TUdSbLI6v8p02"
15.+"mv1ApuTVtQQypZJKIFfurGk0g0QlvzLZ5QJAGfY38+iHDAH/UnPbI1oKTfzPyaZs"
16.+"95fB2NXh3hAUGw7NUHdcIAxs+6gBlxWdRAwQQpDTrlQ8KzyoL9XC5Ku3zQJBALO8"
17.+"j5vEtFTFQl6f9zYlgJpmFTHcpg4fx0mnD+RAD2aAneHADquzlFJSvLLVEn2tyG+0"
18.+"pQdHGqotTDi94L65IdECQQDb1h+5kugCu47IxsDkrLRsKVcr8dSDMORyeT1L0HWR"
19.+"ctramBu+2PBz2UKC6+9dQ+ZQH4XTKpBSvkyZH4mYi1de";
20.// // 支付宝公钥
21.// public static final String RSA_PUBLIC = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCnxj/9qwVfgoUh/y2W89L6BkRAFljhNhgPdyPuBV64bfQNN1PjbCzkIM6qRdKBoLPXmKKMiFYnkd6rAoprih3/PrQEB/VsW8OoM8fxn67UDYuyBTqA23MML9q1+ilIZwBC2AQ2UBVOrFXfFl75p6/B5KsiNG9zpgmLCUYuLkxpLQIDAQAB";
下面是支付宝支付的示例代码:
[java]view plaincopy
1.importjava.io.UnsupportedEncodingException;
2.importjava.net.URLEncoder;
3.importjava.text.SimpleDateFormat;
4.importjava.util.Date;
5.importjava.util.Locale;
6.importjava.util.Random;
7.
8.importcom.alipay.sdk.app.PayTask;
9.importcom.example.exmpay.alipay.bean.AlipayConstants;
10.importcom.example.exmpay.alipay.bean.PayResult;
11.importcom.example.exmpay.alipay.util.SignUtils;
12.
13.importandroid.app.Activity;
14.importandroid.app.AlertDialog;
15.importandroid.app.ProgressDialog;
16.importandroid.content.Context;
17.importandroid.content.DialogInterface;
18.importandroid.os.AsyncTask;
19.importandroid.text.TextUtils;
20.importandroid.widget.Toast;
21.
22.publicclassAlipayTaskextendsAsyncTask {
23.
24.privatestaticfinalString TAG ="AlipayTask";
25.privateContext context;
26.privateProgressDialog dialog;
27.
28.publicAlipayTask(Context context) {
29.this.context = context;
30.}
31.
32.@Override
33.protectedvoidonPreExecute() {
34.if(TextUtils.isEmpty(AlipayConstants.PARTNER)
35.|| TextUtils.isEmpty(AlipayConstants.RSA_PRIVATE)
36.|| TextUtils.isEmpty(AlipayConstants.SELLER)) {
37.newAlertDialog.Builder(context).setTitle("警告").setMessage("需要配置PARTNER | RSA_PRIVATE| SELLER")
38..setPositiveButton("确定",newDialogInterface.OnClickListener() {
39.publicvoidonClick(DialogInterface dialoginterface,inti) {
40.}
41.}).show();
42.cancel(true);
43.}else{
44.dialog = ProgressDialog.show(context,"提示","正在启动支付宝...");
45.}
46.}
47.
48.@Override
49.protectedString doInBackground(Void... params) {
50.String orderInfo = getOrderInfo("测试的商品","该测试商品的详细描述","0.01");
51.
52.//特别注意,这里的签名逻辑需要放在服务端,切勿将私钥泄露在代码中!
53.String sign = sign(orderInfo);
54.try{
55.//仅需对sign 做URL编码
56.sign = URLEncoder.encode(sign,"UTF-8");
57.}catch(UnsupportedEncodingException e) {
58.e.printStackTrace();
59.}
60.
61.//完整的符合支付宝参数规范的订单信息
62.finalString payInfo = orderInfo +"&sign=\""+ sign +"\"&"+ getSignType();
63.// 构造PayTask 对象
64.PayTask alipay =newPayTask((Activity) context);
65.// 调用支付接口,获取支付结果
66.String result = alipay.pay(payInfo,false);
67.returnresult;
68.}
69.
70.@Override
71.protectedvoidonPostExecute(String result) {
72.if(dialog !=null) {
73.dialog.dismiss();
74.}
75.
76.PayResult payResult =newPayResult(result);
77.
78.// 支付宝返回此次支付结果及加签,建议对支付宝签名信息拿签约时支付宝提供的公钥做验签
79.String resultInfo = payResult.getResult();
80.Toast.makeText(context,"resultInfo="+resultInfo, Toast.LENGTH_SHORT).show();
81.
82.String resultStatus = payResult.getResultStatus();
83.// 判断resultStatus 为“9000”则代表支付成功,具体状态码代表含义可参考接口文档
84.if(TextUtils.equals(resultStatus,"9000")) {
85.Toast.makeText(context,"支付宝缴费成功", Toast.LENGTH_SHORT).show();
86.}else{
87.// 判断resultStatus 为非“9000”则代表可能支付失败
88.// “8000”代表支付结果因为支付渠道原因或者系统原因还在等待支付结果确认,最终交易是否成功以服务端异步通知为准(小概率状态)
89.if(TextUtils.equals(resultStatus,"8000")) {
90.Toast.makeText(context,"支付宝缴费结果确认中", Toast.LENGTH_SHORT).show();
91.}else{
92.// 其他值就可以判断为支付失败,包括用户主动取消支付,或者系统返回的错误
93.Toast.makeText(context,"支付宝缴费失败"+payResult.getResult(), Toast.LENGTH_SHORT).show();
94.}
95.}
96.}
97.privateString getOrderInfo(String subject, String body, String price) {
98.// 签约合作者身份ID
99.String orderInfo ="partner="+"\""+ AlipayConstants.PARTNER +"\"";
100.// 签约卖家支付宝账号
101.orderInfo +="&seller_id="+"\""+ AlipayConstants.SELLER +"\"";
102.// 商户网站唯一订单号
103.orderInfo +="&out_trade_no="+"\""+ getOutTradeNo() +"\"";
104.// 商品名称
105.orderInfo +="&subject="+"\""+ subject +"\"";
106.// 商品详情
107.orderInfo +="&body="+"\""+ body +"\"";
108.// 商品金额
109.orderInfo +="&total_fee="+"\""+ price +"\"";
110.// 服务器异步通知页面路径
111.orderInfo +="¬ify_url="+"\""+"http://notify.msp.hk/notify.htm"+"\"";
112.// 服务接口名称, 固定值
113.orderInfo +="&service=\"mobile.securitypay.pay\"";
114.// 支付类型, 固定值
115.orderInfo +="&payment_type=\"1\"";
116.// 参数编码, 固定值
117.orderInfo +="&_input_charset=\"utf-8\"";
118.
119.// 设置未付款交易的超时时间,默认30分钟,一旦超时,该笔交易就会自动被关闭。
120.// 取值范围:1m~15d。m-分钟,h-小时,d-天,1c-当天(无论交易何时创建,都在0点关闭)。
121.// 该参数数值不接受小数点,如1.5h,可转换为90m。
122.orderInfo +="&it_b_pay=\"30m\"";
123.
124.// extern_token为经过快登授权获取到的alipay_open_id,带上此参数用户将使用授权的账户进行支付
125.// orderInfo += "&extern_token=" + "\"" + extern_token + "\"";
126.
127.// 支付宝处理完请求后,当前页面跳转到商户指定页面的路径,可空
128.orderInfo +="&return_url=\"m.alipay.com\"";
129.
130.// 调用银行卡支付,需配置此参数,参与签名, 固定值 (需要签约《无线银行卡快捷支付》才能使用)
131.// orderInfo += "&paymethod=\"expressGateway\"";
132.
133.returnorderInfo;
134.}
135.
136.privateString getOutTradeNo() {
137.SimpleDateFormat format =newSimpleDateFormat("MMddHHmmss", Locale.getDefault());
138.Date date =newDate();
139.String key = format.format(date);
140.
141.Random r =newRandom();
142.key = key + r.nextInt();
143.key = key.substring(0,15);
144.returnkey;
145.}
146.
147.privateString sign(String content) {
148.returnSignUtils.sign(content, AlipayConstants.RSA_PRIVATE);
149.}
150.
151.privateString getSignType() {
152.return"sign_type=\"RSA\"";
153.}
154.
155.}