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

 

 

    

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM