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