Android开发支付集成——支付宝集成

微信支付传送门:https://www.cnblogs.com/dingxiansen/p/9209159.html

 

 一、支付宝支付

1. 支付宝支付流程图

支付宝支付流程

2. 集成前准备

  1. 去蚂蚁金服注册应用获取appKey等信息
  2. 创建应用,添加APP支付功能
  3. 找到APP支付开发文档,下载 SDK&Demo

3. 开始集成

  1、导入Demo中需要用到的Jar包

    

  2、配置AndroidManifest.xml(这里直接放常用的权限)

  

 <!--所需权限-->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_LOGS" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />

    <!--广播跳转-->
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

    <!--百度地图-->
    <!--百度地图权限-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />

    <!--Mob分享-->
    <uses-permission android:name="android.permission.GET_TASKS" />
    <!-- 短信验证登陆功能需要添加次权限来自动填充验证码,用不到可以去掉 -->
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.READ_SMS" />


    <!--微信支付权限-->
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />

    <!--支付宝支付权限-->
    <!-- 安卓读写sd权限 -->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

支付回调

<!--支付宝支付-->
        <!-- 支付宝H5页面支付用的 -->
        <activity
            android:name="com.alipay.sdk.app.H5PayActivity"
            android:configChanges="orientation|keyboardHidden|navigation"
            android:exported="false"
            android:screenOrientation="behind" />
        <!-- 支付宝App支付页面用的 -->
        <activity
            android:name="com.alipay.sdk.auth.AuthActivity"
            android:configChanges="orientation|keyboardHidden|navigation"
            android:exported="false"
            android:screenOrientation="behind" />

 

3、请求后台接口拿到签过名的信息(appKey,订单信息等等),这里我获取数据,是直接给后台传递的商品id,如果有的需要商品数量直接给传给后台,后台根据id查询出商品的信息,然后返回给前台所需要的支付参数。

  支付宝返回参数用例(开始集成的时候看过好多博客都没有返回参数数据结构,结果一脸懵逼,在这里贴出,未加密之前的0.0):

  

格式化后的:

{
    "msg":"success",
    "code":200,
    "data":"alipay_sdk=alipay-sdk-java-dynamicVersionNo&app_id=你们申请的app_id&biz_content=%7B%22out_trade_no%22%3A%22HY201806210002%22%2C%22product_code%22%3A%22QUICK_MSECURITY_PAY%22%2C%22subject%22%3A%22%E4%BC%98%E9%93%BA%E4%BC%9A%E5%91%98%E6%9C%8D%E5%8A%A1%E8%B4%B9%22%2C%22total_amount%22%3A%220.01%22%7D&charset=UTF-8&format=json&method=alipay.trade.app.pay&notify_url=http%3A%2F%2F你们的回调地址%2FaliPayCallBack%2FcallBack&sign=buipaoj2F8xl5XCAUVoJz%2Fbh8dHbaoRmdzoAEzqKRJqtZATT4bfFdzSHurAAL5C5gvntFrDTGHgNRGw%2BNZBtG4DfetOzcpHMAyjslrmUIMIr1YGC7Qya04mFBCh%2B0UIa1E7RISZWSbIVCHpZISknNgF2oTuTixNosXvDXzkGYGBUoaxdh1f6%2F%2Bw9lqKz7mkhsUc0x8lCeJHw4MnTS4gSLU%2BDmOCk6Tkiwb4Yv4Mz%2F6j7XReeagfX7qxs5qbObnnPX%2FFmu9T%2BF0LwJaPxr5Xys8kr8E4bhd4f7Y5FimXiw%2BG7EFkY0I69boiRob7zo%2BbWQ%2F53TAMeTXX5RJybEdXhrA%3D%3D&sign_type=RSA2&timestamp=2018-06-21+14%3A11%3A40&version=1.0",
    "status":"0"
}

后台返回信息之后接下来就是我们的事情了,调起支付宝进行支付

 /*支付宝测试*/
    private void testZfbPay(final String key, final String value) {
        StringRequest stringRequest = new StringRequest(Request.Method.POST, NetWorkUrl.ZFBPAY, new Response.Listener<String>() {
            @RequiresApi(api = Build.VERSION_CODES.M)
            @Override
            public void onResponse(String s) {
                Log.e("GoPayOrderActivity", "-------getJson2-------" + s.toString());

                /*判断code*/
                String code = (JSONObject.parseObject(s.toString()).getString("code"));

                if (code.equals("200")) {
                    String orderInfo = (JSONObject.parseObject(s.toString()).getString("data"));//返回的信息
                    MyALipayUtils.ALiPayBuilder builder = new MyALipayUtils.ALiPayBuilder();


                    builder.build().toALiPay(GoPayOrderActivity.this, orderInfo);
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError volleyError) {

            }
        }) {
            @Override
            public Map<String, String> getParams() throws AuthFailureError {
                Log.e("GoPayOrderActivity", "getParams:-----------------> " + userEntity.getPhone());

                Map<String, String> map = new HashMap<String, String>();
                map.put("account", userEntity.getPhone());
                map.put(key, value);
                map.put("token", userEntity.getToken());
                return map;
            }

            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                HashMap<String, String> headers = new HashMap<String, String>();
                if (userEntity.getToken().equals("") && userEntity != null) {
                    headers.put("Authorization", userEntity.getToken());
                }
                return headers;
            }

        };
        /*设置请求一次*/
        stringRequest.setRetryPolicy(
                new DefaultRetryPolicy(
                        5000,//默认超时时间,应设置一个稍微大点儿的,例如本处的500000
                        DefaultRetryPolicy.DEFAULT_MAX_RETRIES,//默认最大尝试次数
                        DefaultRetryPolicy.DEFAULT_BACKOFF_MULT
                )
        );
        AppApplication.getHttpQueues().add(stringRequest);/*请求数据*/
    }

MyALipayUtils.java这个类直接copy使用就可以

/**
 * Created by dingchao on 2018/3/20.
 */

public class MyALipayUtils {
    private static final int SDK_PAY_FLAG = 1;
    private Activity context;
    private ALiPayBuilder builder;

    private MyALipayUtils(ALiPayBuilder builder) {
        this.builder = builder;
    }

    private Handler mHandler = new Handler() {
        public void handleMessage(Message msg) {

//            返回码    含义
//            9000    订单支付成功
//            8000    正在处理中,支付结果未知(有可能已经支付成功),请查询商户订单列表中订单的支付状态
//            4000    订单支付失败
//            5000    重复请求
//            6001    用户中途取消
//            6002    网络连接出错
//            6004    支付结果未知(有可能已经支付成功),请查询商户订单列表中订单的支付状态
//            其它    其它支付错误
            PayResult payResult = new PayResult((Map<String, String>) msg.obj);
            switch (payResult.getResultStatus()) {
                case "9000":
                    MobclickAgent.onEvent(context, "payment_success", "付款成功");
                    Toast.makeText(context, "支付成功", Toast.LENGTH_SHORT).show();
                    AppApplication.finishActivity();
                    context.finish();
//                    Toast.makeText(this, "支付成功", Toast.LENGTH_SHORT).show();
                    /*跳转我的会员页面*/
//                    Intent intent = new Intent(context, MyVipActivity.class);
//                    context.startActivity(intent);
                    break;
                case "8000":
                    Toast.makeText(context, "正在处理中", Toast.LENGTH_SHORT).show();
                    break;
                case "4000":
                    MobclickAgent.onEvent(context, "payment_fali", "付款失败");
                    Toast.makeText(context, "订单支付失败", Toast.LENGTH_SHORT).show();
                    break;
                case "5000":
                    Toast.makeText(context, "重复请求", Toast.LENGTH_SHORT).show();
                    break;
                case "6001":
                    Toast.makeText(context, "已取消支付", Toast.LENGTH_SHORT).show();
                    break;
                case "6002":
                    Toast.makeText(context, "网络连接出错", Toast.LENGTH_SHORT).show();
                    break;
                case "6004":
                    Toast.makeText(context, "正在处理中", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    MobclickAgent.onEvent(context, "payment_fali", "付款失败");
                    Toast.makeText(context, "支付失败", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    };

    /**
     * 签名发在客户端来做。
     *
     * @param context
     */
    public void toALiPay(final Activity context) {
        this.context = context;
        boolean rsa2 = (builder.getRsa2().length() > 0);
        Map<String, String> params = buildOrderParamMap(rsa2);
        String orderParam = buildOrderParam(params);
        String privateKey = rsa2 ? builder.getRsa2() : builder.getRsa();
        String sign = getSign(params, privateKey, rsa2);
        final String orderInfo = orderParam + "&" + sign;
        Log.e("chx", "toALiPay: " + orderInfo);
        Runnable payRunnable = new Runnable() {

            @Override
            public void run() {
                PayTask alipay = new PayTask(context);
                Map<String, String> result = alipay.payV2
                        (orderInfo, true);
                Message msg = new Message();
                msg.what = SDK_PAY_FLAG;
                msg.obj = result;
                mHandler.sendMessage(msg);
            }
        };

        Thread payThread = new Thread(payRunnable);
        payThread.start();
    }

    /**
     * 签名在服务端来做
     *
     * @param context
     * @param orderInfo
     */
    public void toALiPay(final Activity context, final String orderInfo) {
        this.context = context;
        Runnable payRunnable = new Runnable() {

            @Override
            public void run() {
                PayTask alipay = new PayTask(context);
                Map<String, String> result = alipay.payV2
                        (orderInfo, true);
                Message msg = new Message();
                msg.what = SDK_PAY_FLAG;
                msg.obj = result;
                mHandler.sendMessage(msg);
            }
        };

        Thread payThread = new Thread(payRunnable);
        payThread.start();
    }

    /**
     * 构造支付订单参数列表
     *
     * @param
     * @param
     * @return
     */
    private Map<String, String> buildOrderParamMap(boolean rsa2) {
        Map<String, String> keyValues = new HashMap<String, String>();

        keyValues.put("app_id", builder.appid);

        keyValues.put("biz_content", "{\"timeout_express\":\"30m\",\"product_code\":\"QUICK_MSECURITY_PAY\",\"total_amount\":\"" + builder.money + "\",\"subject\":\"" + builder.title + "\",\"out_trade_no\":\"" + builder.orderTradeId + "\"}");

        keyValues.put("charset", "utf-8");

        keyValues.put("method", "alipay.trade.app.pay");
        //回调接口
        keyValues.put("notify_url", builder.getNotifyUrl());

        keyValues.put("sign_type", rsa2 ? "RSA2" : "RSA");

//        keyValues.put("timestamp", "2016-07-29 16:55:53");
        keyValues.put("timestamp", getCurrentTimeString());

        keyValues.put("version", "1.0");


        return keyValues;
    }

    /**
     * 构造支付订单参数信息
     *
     * @param map 支付订单参数
     * @return
     */
    private 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("&");
            Log.e("chx", "buildOrderParam: " + buildKeyValue(key, value, true));
        }

        String tailKey = keys.get(keys.size() - 1);
        String tailValue = map.get(tailKey);
        sb.append(buildKeyValue(tailKey, tailValue, true));

        return sb.toString();
    }

    /**
     * 对支付参数信息进行签名
     *
     * @param map 待签名授权信息
     * @return
     */
    private 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 = sign(authInfo.toString(), rsaKey, rsa2);
        String encodedSign = "";

        try {
            encodedSign = URLEncoder.encode(oriSign, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return "sign=" + encodedSign;
    }

    private static final String ALGORITHM = "RSA";

    private static final String SIGN_ALGORITHMS = "SHA1WithRSA";

    private static final String SIGN_SHA256RSA_ALGORITHMS = "SHA256WithRSA";

    private static final String DEFAULT_CHARSET = "UTF-8";

    private String getAlgorithms(boolean rsa2) {
        return rsa2 ? SIGN_SHA256RSA_ALGORITHMS : SIGN_ALGORITHMS;
    }

    private String sign(String content, String privateKey, boolean rsa2) {
        try {
            PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
                    Base64.decode(privateKey));
            KeyFactory keyf = KeyFactory.getInstance(ALGORITHM);
            PrivateKey priKey = keyf.generatePrivate(priPKCS8);

            java.security.Signature signature = java.security.Signature
                    .getInstance(getAlgorithms(rsa2));

            signature.initSign(priKey);
            signature.update(content.getBytes(DEFAULT_CHARSET));

            byte[] signed = signature.sign();

            return Base64.encode(signed);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 拼接键值对
     *
     * @param key
     * @param value
     * @param isEncode
     * @return
     */
    private 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();
    }

    /**
     * 获取当前日期字符串
     *
     * @return
     */
    private String getCurrentTimeString() {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return df.format(new Date());
    }

    public static class ALiPayBuilder {
        private String rsa2 = "";
        private String rsa = "";
        private String appid;
        private String money;
        private String title;
        private String notifyUrl;
        private String orderTradeId;

        public MyALipayUtils build() {
            return new MyALipayUtils(this);
        }

        public String getOrderTradeId() {
            return orderTradeId;
        }

        public ALiPayBuilder setOrderTradeId(String orderTradeId) {
            this.orderTradeId = orderTradeId;
            return this;
        }

        public String getRsa2() {
            return rsa2;
        }

        public ALiPayBuilder setRsa2(String rsa2) {
            this.rsa2 = rsa2;
            return this;
        }

        public String getRsa() {
            return rsa;
        }

        public ALiPayBuilder setRsa(String rsa) {
            this.rsa = rsa;
            return this;
        }

        public String getAppid() {
            return appid;
        }

        public ALiPayBuilder setAppid(String appid) {
            this.appid = appid;
            return this;
        }

        public String getMoney() {
            return money;
        }

        public ALiPayBuilder setMoney(String money) {
            this.money = money;
            return this;
        }

        public String getTitle() {
            return title;
        }

        public ALiPayBuilder setTitle(String title) {
            this.title = title;
            return this;
        }

        public String getNotifyUrl() {
            return notifyUrl;
        }

        public ALiPayBuilder setNotifyUrl(String notifyUrl) {
            this.notifyUrl = notifyUrl;
            return this;
        }
    }
}

完成上述操作,你的app在蚂蚁金服后台应用上线之后,就完全可以调用支付了,但是在开发阶段,应用没有上线,你是不能进行调试的,所以支付宝有沙箱模式可以进行调试,ios没有哟

进行沙箱调试在Activity的的onCreate()方法中添加

EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);//支付宝沙箱环境,正式需注释

别忘了让你们后台把appKey等信息换成沙箱的,然后测试支付的时候,需要下载一个沙箱支付宝,这个你可以随意支付,附上链接

沙箱首页:https://sandbox.alipaydev.com/user/accountDetails.htm?currentBar=1

App支付接入文档:https://docs.open.alipay.com/204/105051

沙箱钱包下载:

调试都没有问题之后,就可以在蚂蚁金服开发者平台进行应用上线,然后沙箱代码就可以注释了,这样支付宝支付就接入完成

 如果问题或建议请发送到我的邮箱:dingchao7323@qq.com

 

 

    

 

posted @ 2018-06-21 14:35  丁先森  阅读(5258)  评论(0编辑  收藏  举报