2017安卓开发接入支付宝支付功能详解,真正做到完全翻译支付宝sdk开发应用
来源:互联网 发布:三菱q系列plc编程软件 编辑:程序博客网 时间:2024/04/28 06:21
对于初级开发者而言,吗德支付宝的开发文档最新版本和老版本写的一样狗屎的狠,网络上很多专家写的博客虽然有一点多余解释,但一个比一个惜字如金,来回调用的方法都看不懂有木有!更有甚者,有些大神完全脱离开发文档demo,自由发挥新篇章,浏览诸多,心有猛火。老子自己重新翻译支付表的开发文档!本文就是我怒火烧心后的产物,绝对完全剖析,Android在线支付Alipay(支付宝)开发,废话少说,上demo
package com.example.alipayinstense;import java.util.Map;import com.alipay.sdk.app.AuthTask;import com.alipay.sdk.app.PayTask;import android.annotation.SuppressLint;import android.app.Activity;import android.app.AlertDialog;import android.content.DialogInterface;import android.content.Intent;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.support.v7.app.ActionBarActivity;import android.text.TextUtils;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;//这里的mainactivity就是官方的PayDemoActivity ;//里面的方法都在这里,就是类名不一样而已,大可不必纠结public class MainActivity extends ActionBarActivity implements OnClickListener { private TextView tv; private Button buy; /** 支付宝支付业务:入参app_id */ public static final String APPID = "2016083000128338"; /** 支付宝账户登录授权业务:入参pid值 */ public static final String PID = "2088502954592834"; /** 支付宝账户登录授权业务:入参target_id值 商户收款账号*///此处应该不起任何作用,下文无调用 public static final String TARGET_ID = ""; /** 商户私钥,pkcs8格式 */ /** 如下私钥,RSA2_PRIVATE 或者 RSA_PRIVATE 只需要填入一个 */ /** 如果商户两个都设置了,优先使用 RSA2_PRIVATE */ /** RSA2_PRIVATE 可以保证商户交易在更加安全的环境下进行,建议使用 RSA2_PRIVATE */ /** 获取 RSA2_PRIVATE,建议使用支付宝提供的公私钥生成工具生成, */ /** 工具地址:https://doc.open.alipay.com/docs/doc.htm?treeId=291&articleId=106097&docType=1 */ //公钥已上传至支付宝平台,私钥已保存本地,这是本应用私钥 public static final String RSA2_PRIVATE = "MIIEvasefDA.....9w0BAQEFAASCBKYwgg....RUJCt7xt+EaJRCX/mGktAi5BQ4iwKTQea8PPDK9m+DzyfVECgYAtKMhyH...86CocnJrNqbtyoq......0pDStHa98LrihY+XeiyXUME6.....HBjey/..kBjmUb30PYdHU1OfTL3qHvy9MAE1U6GyOJgahZCPYHeqg=="; //rsa没用 public static final String RSA_PRIVATE = ""; private static final int SDK_PAY_FLAG = 1;//sdk正常运行了吗? private static final int SDK_AUTH_FLAG = 2;//sdk确认了吗? private Activity activity;//这里声明一个活动是干什么的? private String orderNo;//订单详情 @SuppressLint("HandlerLeak") private Handler mHandler = new Handler() { @SuppressWarnings("unused") public void handleMessage(Message msg) { switch (msg.what) { case SDK_PAY_FLAG: {//如果消息是 SDK正常运行 @SuppressWarnings("unchecked") //将随该消息附带的msg.obj强转回map中,建立新的payresult支付结果 PayResult payResult = new PayResult((Map<String, String>) msg.obj); /** 对于支付结果,请商户依赖服务端的异步通知结果。同步通知结果,仅作为支付结束的通知。 */ String resultInfo = payResult.getResult();// 同步返回需要验证的信息。从支付结果中取到resultinfo String resultStatus = payResult.getResultStatus();//得到resultstatus // 判断resultStatus 为9000则代表支付成功 if (TextUtils.equals(resultStatus, "9000")) { // 该笔订单是否真实支付成功,需要依赖服务端的异步通知。 Toast.makeText(MainActivity.this, "支付成功", Toast.LENGTH_SHORT).show(); } else { // 该笔订单真实的支付结果,需要依赖服务端的异步通知。 Toast.makeText(MainActivity.this, "支付失败", Toast.LENGTH_SHORT).show(); } break; } case SDK_AUTH_FLAG: {//如果消息是SDK已经确认 @SuppressWarnings("unchecked") //将随该消息附带的msg.obj强转回MAP,第二个参数为“去除消息中包含的括号吗?(boolean)” AuthResult authResult = new AuthResult((Map<String, String>) msg.obj, true);//新建自定义的authResult对象 //利用Authresult中的自定义方法的到resultstatus String resultStatus = authResult.getResultStatus(); // 判断resultStatus 为“9000”且result_code // 为“200”则代表授权成功,具体状态码代表含义可参考授权接口文档 if (TextUtils.equals(resultStatus, "9000") && TextUtils.equals(authResult.getResultCode(), "200")) { // 获取alipay_open_id,调支付时作为参数extern_token 的value // 传入,则支付账户为该授权账户 Toast.makeText(MainActivity.this, "授权成功\n" + String.format("authCode:%s", authResult.getAuthCode()), Toast.LENGTH_SHORT) .show(); } else { // 其他状态值则为授权失败 Toast.makeText(MainActivity.this, "授权失败" + String.format("authCode:%s", authResult.getAuthCode()), Toast.LENGTH_SHORT).show(); } break; } default: break; } }; }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);//当前活动界面赋值 initView();//初始化控件 } private void initView() { // TODO Auto-generated method stub } /** * 支付宝pay方法 * 支付宝支付业务 * * @param v */ public void payV2(View v) { /** * 如果APPID是空值或私钥两个全是空,则弹出警告对话框告诉开发者“需要配置APPID|RSA_PRIVATE” */ if (TextUtils.isEmpty(APPID) || (TextUtils.isEmpty(RSA2_PRIVATE) && TextUtils.isEmpty(RSA_PRIVATE))) { new AlertDialog.Builder(this).setTitle("警告").setMessage("需要配置APPID | RSA_PRIVATE") .setPositiveButton("确定", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialoginterface, int i) { // finish(); } }).show(); return; } /** * 这里只是为了方便直接向商户展示支付宝的整个支付流程;所以Demo中加签过程直接放在客户端完成; * 真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成; * 防止商户私密数据泄露,造成不必要的资金损失,及面临各种安全风险; * * orderInfo的获取必须来自服务端;防止本地orderInfo被认为更改,例如买个冰箱改成买个鸡蛋 */ boolean rsa2 = (RSA2_PRIVATE.length() > 0);//rsa2私钥已经被赋值 //在这里,传入APPID和私钥,得到请求map,即包含支付订单信息的map Map<String, String> params = OrderInfoUtil2_0.buildOrderParamMap(APPID, rsa2); //将map解析成一个String类型的支付订单 String orderParam = OrderInfoUtil2_0.buildOrderParam(params); //是rsa2类型的私钥吗?如果rsa2私钥已经被赋值,那么privateKey就被设置成rsa2,否则就被设置成rsa。这就是支付宝说的“如果你有俩私钥,优先使用RSa2私钥,靠!” String privateKey = rsa2 ? RSA2_PRIVATE : RSA_PRIVATE; //对,privateKey就是商户私钥!通过此方法对商户的私钥进行“签名”处理,处理后就会生成(返回)一个sign=GKHJL%……&*的一大串字串 String sign = OrderInfoUtil2_0.getSign(params, privateKey, rsa2); //最终,结合orderparam参数与sign签名字串,搞成orderInfo字串; final String orderInfo = orderParam + "&" + sign; //新开一个线程,将orderInfo字串传入到PayTask任务中去 Runnable payRunnable = new Runnable() { @Override public void run() { //新建一个PAyTask对象 PayTask alipay = new PayTask(MainActivity.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(); } /** * 支付宝账户授权业务 * * @param v */ public void authV2(View v) { if (TextUtils.isEmpty(PID) || TextUtils.isEmpty(APPID) || (TextUtils.isEmpty(RSA2_PRIVATE) && TextUtils.isEmpty(RSA_PRIVATE)) || TextUtils.isEmpty(TARGET_ID)) { new AlertDialog.Builder(this).setTitle("警告").setMessage("需要配置PARTNER |APP_ID| RSA_PRIVATE| TARGET_ID") .setPositiveButton("确定", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialoginterface, int i) { } }).show(); return; } /** * 这里只是为了方便直接向商户展示支付宝的整个支付流程;所以Demo中加签过程直接放在客户端完成; * 真实App里,privateKey等数据严禁放在客户端,加签过程务必要放在服务端完成; * 防止商户私密数据泄露,造成不必要的资金损失,及面临各种安全风险; * * authInfo的获取必须来自服务端; */ boolean rsa2 = (RSA2_PRIVATE.length() > 0); Map<String, String> authInfoMap = OrderInfoUtil2_0.buildAuthInfoMap(PID, APPID, TARGET_ID, rsa2); //利用这个方法,我可以将map解析成一个String类型的支付订单,即得到info String info = OrderInfoUtil2_0.buildOrderParam(authInfoMap); String privateKey = rsa2 ? RSA2_PRIVATE : RSA_PRIVATE; //对支付参数信息进行签名 传入进来一个Map,一个rsaKey, //并且询问是否是rsa2格式的私钥 这个rsaKey是商户的私钥 就是说通过此方法对商户的私钥进行“签名”处理, //处理后就会生成(返回)一个sign=GKHJL%……&*的一大串字串 String sign = OrderInfoUtil2_0.getSign(authInfoMap, privateKey, rsa2); final String authInfo = info + "&" + sign; //得到orderInfo后,我们开启新线程,发送相关信息 Runnable authRunnable = new Runnable() { @Override public void run() { // 构造AuthTask 对象 AuthTask authTask = new AuthTask(MainActivity.this); // 调用授权接口,获取授权结果 Map<String, String> result = authTask.authV2(authInfo, true); Message msg = new Message(); msg.what = SDK_AUTH_FLAG; msg.obj = result; mHandler.sendMessage(msg); } }; // 必须异步调用 Thread authThread = new Thread(authRunnable); authThread.start(); } /** * get the sdk version. 获取SDK版本号 * */ public void getSDKVersion() { PayTask payTask = new PayTask(this); String version = payTask.getVersion(); Toast.makeText(this, version, Toast.LENGTH_SHORT).show(); } /** * 原生的H5(手机网页版支付切natvie支付) 【对应页面网页支付按钮】 * 意思就是我应用里面有一个h5的页面,页面里面有好多 * 商品售卖,当我点击h5中的末一个商品支付时,支付表就会在此时调出来 * 如果app中没有webView实现h5,那完全用不到此方法 * * @param v */ public void h5Pay(View v) { Intent intent = new Intent(this, H5PayDemoActivity.class); Bundle extras = new Bundle(); /** * url是测试的网站,在app内部打开页面是基于webview打开的,demo中的webview是H5PayDemoActivity, * demo中拦截url进行支付的逻辑是在H5PayDemoActivity中shouldOverrideUrlLoading方法实现, * 商户可以根据自己的需求来实现 */ String url = "http://m.taobao.com"; // url可以是一号店或者淘宝等第三方的购物wap站点,在该网站的支付过程中,支付宝sdk完成拦截支付 extras.putString("url", url); intent.putExtras(extras); startActivity(intent); } @Override public void onClick(View v) { // TODO Auto-generated method stub }}
接下来是AuthResult类
package com.alipay.sdk.pay.demo;import java.util.Map;import android.text.TextUtils;/** * 确定结果 * @author Administrator * */public class AuthResult { private String resultStatus;//结果状态 private String result;//结果 private String memo;//备忘录 private String resultCode;//结果码 private String authCode;//确认码 private String alipayOpenId;//支付宝开放ID /** * 确认结果,参数为键值对全是string型的’结果行‘和一个boolean类型的参数'去除所有括号了吗' * @param rawResult * @param removeBrackets */ public AuthResult(Map<String, String> rawResult, boolean removeBrackets) { if (rawResult == null) {//传入的结果行map不为空才往下进行 return; } for (String key : rawResult.keySet()) { if (TextUtils.equals(key, "resultStatus")) {//获取传入map中的键值对为resultStatus的值放入到resultStatus中(结果状态信息) resultStatus = rawResult.get(key); } else if (TextUtils.equals(key, "result")) {//获取传入map中的键值对的结果 result = rawResult.get(key); } else if (TextUtils.equals(key, "memo")) {////获取传入map中的备注(备忘录) memo = rawResult.get(key); } } String[] resultValue = result.split("&");//将结果用&符号拆分 //对传入数组进行遍历 for (String value : resultValue) { if (value.startsWith("alipay_open_id")) {//如果以alipay_open_id(支付宝开放id)开始的 //此时的value是以alipay_open_id开头的值;支付宝的开放id=截取到的alipay_open_id字串去除括号 alipayOpenId = removeBrackets(getValue("alipay_open_id=", value), removeBrackets); continue; } if (value.startsWith("auth_code")) {//如果循环进行到确认码"auth_code" authCode = removeBrackets(getValue("auth_code=", value), removeBrackets);//处理得到确认码 continue; } if (value.startsWith("result_code")) {//如果解析到结果码 resultCode = removeBrackets(getValue("result_code=", value), removeBrackets);//处理得到结果码 continue; } } } /** * 移除括号的方法 * @param str 传入的字符串 * @param remove 移除吗? * @return 返回处理过后的字符串 */ private String removeBrackets(String str, boolean remove) { if (remove) {//如果是移除的话 if (!TextUtils.isEmpty(str)) {//如果传入的字符串不是空的话,则将对字串进行一下操作 if (str.startsWith("\"")) { str = str.replaceFirst("\"", "");//移除第一个斜杠 } if (str.endsWith("\"")) { str = str.substring(0, str.length() - 1);//移除最后的斜杠 } } } return str;//返回处理过后的字符串 } @Override public String toString() { return "resultStatus={" + resultStatus + "};memo={" + memo + "};result={" + result + "}"; } /** * 获取值得方法 * @param header 头部字符串 * @param data 数据字符串 * @return */ private String getValue(String header, String data) { return data.substring(header.length(), data.length());//将data从截取头部的长度处截取到传入data的末尾 } /** * 获取String类型的结果状态 * @return the resultStatus */ public String getResultStatus() { return resultStatus; } /** * 获取备注 * @return the memo */ public String getMemo() { return memo; } /** * 获取结果 * @return the result */ public String getResult() { return result; } /** * 获取结果码 * @return the resultCode */ public String getResultCode() { return resultCode; } /** * 获取确认码 * @return the authCode */ public String getAuthCode() { return authCode; } /** * 获取支付宝开放支付ID * @return the alipayOpenId */ public String getAlipayOpenId() { return alipayOpenId; }}
接下来是OrderInfoUtil2_0
package com.example.alipayinstense;import java.io.UnsupportedEncodingException;import java.net.URLEncoder;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Collections;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Locale;import java.util.Map;import java.util.Random;public class OrderInfoUtil2_0 { /** * 自定义orderInfo请求信息对象 * 构造授权参数列表 * 列表里面的参数有PID,APPID,TARGETID,和一个boolean值“是否设置了rsa2私钥” * 就是说我这个支付参数map中要有这些参数,并且给他们赋值 * @param pid * @param app_id * @param target_id * @return */ public static Map<String, String> buildAuthInfoMap(String pid, String app_id, String target_id, boolean rsa2) { Map<String, String> keyValues = new HashMap<String, String>(); // 商户签约拿到的app_id,如:2013081700024223 keyValues.put("app_id", app_id); // 商户签约拿到的pid,如:2088102123816631 keyValues.put("pid", pid); // 服务接口名称, 固定值 keyValues.put("apiname", "com.alipay.account.auth"); // 商户类型标识, 固定值 keyValues.put("app_name", "mc"); // 业务类型, 固定值 keyValues.put("biz_type", "openservice"); // 产品码, 固定值 keyValues.put("product_id", "APP_FAST_LOGIN"); // 授权范围, 固定值 keyValues.put("scope", "kuaijie"); // 商户唯一标识,如:kkkkk091125//这个是干什么用的???????? keyValues.put("target_id", target_id); // 授权类型, 固定值 keyValues.put("auth_type", "AUTHACCOUNT"); // 签名类型 keyValues.put("sign_type", rsa2 ? "RSA2" : "RSA"); return keyValues; } /** * 构造支付订单参数列表 * 就是说我这个支付订单是由这个map组成的,map中要有这些几个参数,并且给他们赋值 * @param pid * @param app_id * @param target_id * @return */ public static Map<String, String> buildOrderParamMap(String app_id, boolean rsa2) { Map<String, String> keyValues = new HashMap<String, String>(); keyValues.put("app_id", app_id); keyValues.put("biz_content", "{\"timeout_express\":\"30m\",\"product_code\":\"QUICK_MSECURITY_PAY\",\"total_amount\":\"0.01\",\"subject\":\"1\",\"body\":\"我是测试数据\",\"out_trade_no\":\"" + getOutTradeNo() + "\"}"); keyValues.put("charset", "utf-8"); keyValues.put("method", "alipay.trade.app.pay"); keyValues.put("sign_type", rsa2 ? "RSA2" : "RSA");//是rsa2私钥还是rsa私钥 keyValues.put("timestamp", "2016-07-29 16:55:53"); keyValues.put("version", "1.0"); return keyValues; } /** * 构造支付订单参数信息 * 传入进来一个Map参数,就是说利用这个方法,我可以将map解析成一个String类型的支付订单 * @param map * 支付订单参数 * @return */ public static String buildOrderParam(Map<String, String> map) { List<String> keys = new ArrayList<String>(map.keySet()); StringBuilder sb = new StringBuilder(); for (int i = 0; i < keys.size() - 1; i++) { String key = keys.get(i); String value = map.get(key); sb.append(buildKeyValue(key, value, true)); sb.append("&"); } String tailKey = keys.get(keys.size() - 1); String tailValue = map.get(tailKey); sb.append(buildKeyValue(tailKey, tailValue, true)); return sb.toString(); } /** * 拼接键值对 * 将传入进来的两个字符串利用此方法拼接成key=value的形式 * 该方法咨询是否转码(isEncode),如果需要对value进行转码的话,将会将其转为UTF—8格式; * @param key * @param value * @param isEncode * @return */ private static String buildKeyValue(String key, String value, boolean isEncode) { StringBuilder sb = new StringBuilder(); sb.append(key); sb.append("="); if (isEncode) { try { sb.append(URLEncoder.encode(value, "UTF-8")); } catch (UnsupportedEncodingException e) { sb.append(value); } } else { sb.append(value); } return sb.toString(); } /** * 对支付参数信息进行签名 * 传入进来一个Map,一个rsaKey,并且询问是否是rsa2格式的私钥 * 这个rsaKey是商户的私钥 * 就是说通过此方法对商户的私钥进行“签名”处理,处理后就会生成(返回)一个sign=GKHJL%……&*的一大串字串 * @param map * 待签名授权信息 * * @return */ public static String getSign(Map<String, String> map, String rsaKey, boolean rsa2) { List<String> keys = new ArrayList<String>(map.keySet()); // key排序 Collections.sort(keys); StringBuilder authInfo = new StringBuilder(); for (int i = 0; i < keys.size() - 1; i++) { String key = keys.get(i); String value = map.get(key); authInfo.append(buildKeyValue(key, value, false)); authInfo.append("&"); } String tailKey = keys.get(keys.size() - 1); String tailValue = map.get(tailKey); authInfo.append(buildKeyValue(tailKey, tailValue, false)); String oriSign = SignUtils.sign(authInfo.toString(), rsaKey, rsa2); String encodedSign = ""; try { encodedSign = URLEncoder.encode(oriSign, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return "sign=" + encodedSign; } /** * 利用此方法获取到一个外部订单号"OutTradeNo" * 要求外部订单号必须唯一。 * @return */ private static String getOutTradeNo() { SimpleDateFormat format = new SimpleDateFormat("MMddHHmmss", Locale.getDefault()); Date date = new Date(); String key = format.format(date); Random r = new Random(); key = key + r.nextInt(); key = key.substring(0, 15); return key; }}
利用最后两个类直接替换掉官方demo,会有意想不到的效果,你会发现PayDemoActivity的思路是那么的清晰。
看完这些全新的注释,是不是发现原来安卓应用支付宝开发原来很简单?
shirt!
1 0
- 2017安卓开发接入支付宝支付功能详解,真正做到完全翻译支付宝sdk开发应用
- 2017安卓开发接入支付宝支付功能详解,真正做到完全翻译支付宝sdk开发应用
- Android开发 接入支付宝移动支付
- 接入支付宝SDK
- Android接入支付宝开发
- Android开发接入支付宝
- 支付宝支付iOS开发官方SDK
- 安卓集成支付宝支付功能
- android 支付宝sdk接入详解
- [Android开发]cocos2dx工程中接入支付宝sdk
- 支付宝开发详解
- 接入支付宝支付功能思路总结
- ThinkPHP接入支付宝支付功能
- Android接入支付宝实现支付功能
- Android接入支付宝实现支付功能
- ThinkPHP接入支付宝支付功能
- Android接入支付宝实现支付功能
- Android接入支付宝实现支付功能
- Socket通信实现
- java实现排序算法之插入排序(直接插入排序,折半插入排序,希尔排序)
- ogg Bad record found at RBA 48104849, format 5.50 Trailer token)
- 互联网广告作弊方法
- hdu 2018 母牛的故事
- 2017安卓开发接入支付宝支付功能详解,真正做到完全翻译支付宝sdk开发应用
- Errors occurred during the build. Errors running builder 'Maven Project Builder' on project
- 过滤器
- 监听器
- UE4 用Touch的方法实现物体的旋转
- UVa133 - The Dole Queue
- Skip List 实现思路
- Java中的equals和hashCode方法详解
- 【面试】Liveramp 面试题 面经 猴子过河问题