1. 程式人生 > >記錄自己的支付整合(微信&支付寶)

記錄自己的支付整合(微信&支付寶)

首先,作為一個菜鳥表示,雖然網上的整合框架很多,但是我要自己寫,就算被坑的死去活來也要自己寫。

1. 支付寶
這個支付寶的文件是個好東西,挺簡單,挺容易看懂的,其次還有沙箱測試,表示支付寶一次通過很開心。 支付沙箱聯調指南
首先導包,這裡使用 20170710 的包,提前說明如果使用沙箱需要在支付的activity中新增
EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
支付的賬號和密碼去沙箱中獲取,支付沙箱聯調指南頁面的最下方,點選沙箱工具,跳轉過後,頁面左側研發服務-沙箱賬號

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        EnvUtils.setEnv(EnvUtils.EnvEnum.SANDBOX);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pay);
        initView();
    }

這個直接上程式碼:ALiPay.java

public class AliPay {
    // 2017/7/20 沙盒測試通過 程式碼無誤
//支付寶支付業務:沙箱使用app_id public static final String APPID = "2016102200739552"; //支付寶支付業務:沙箱使用RSA2_PRIVATE public static final String RSA2_PRIVATE = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCTci2x3b/OPT2ESK4+yZpBP7yQxxWdpB2Ht4XL7Vxa+Oa1je3lnnGImtAPsk9Z/LcgrMdg1mrjAP0cHUUHCVs8e70NR1P0YN6zFFDjZ/I4HWYUvAHAB0b/zPCT24xHbekIuyjpvTQKTRPu1QRPyCAlscJjVf6u0aUNh8tTsekxtS5tBOvQ5cm9kHw09XYaO+XtI39ocDj7rzTOkC1uQDfed2YZLXKVbiHm3WNJ71isL9bbC0nkjkBKRDTAGvyDTecvBy/mi8OnVJr9sg270UMNAtgTLONV0eKZp5JSuLwL0PGFXKc07OPb33Na/nsX9qpl6utjETNjJw2AatKMoVEpAgMBAAECggEAZyyXYwX7aHKb8Ev3ecISqaPS7DATJRso+sXl3vv6C0JuFg75sBp98Yv7GXC5bAuVjUy5uz5uzufrNVgZ7EGU0S747CjES5XZX9BQhcA/0xpnSwz/4IJ3IRokIiKX94emcFCe4Whe2PJ0h0QR3033iMjpcB9FtsjGSUCDe85bkkbYbpzTPiv0B5BQlkuSYPR5jR4aeURLJ4qDBiLF+pJHrNy9w0cxPEgKSpY6fusi9fEfs3dKq1a0TbgOMlpHE4ZlOqkjWZld9Z6yU6AYtxfhVzYeYobhPjicmf7cTahfYPYyP4zYfR3PrFOtTCSEOthyzr5TASZeXNMWmyuOb+jnjQKBgQDP4oXSwA7gVMiAk/P7SMRbyn4TEh97CJeWIRWAFJ8r8lCzBuNOMBNbjcsC+3kRdy8rFuFH3yjfKIo2Av/zYT/b441Xm9csVlHH7NLcRD6EYV1DLvgEEc4T+k8jmwRWeri+qzkyY2fH0X8ZdUdMvE271oMViyju5rgQClYMbzVViwKBgQC1kpEsnuXCds7CT89XUqX/nLSqTIfnejm9z/CnR7sPEISI8nQvdokTtyuRm9oV1LZqtN5Zj0Qqo+j3Gvmzgb0pw0sCVAYfmn4+ldicGYNkF1PNgPk8xlLwB395P6Fu8XbyQqlXuTrFVcTz0HWGdkAUuEuhFQgeD9OrqXXCA9BSmwKBgDYe/UQe6ECTEhgXbL+Q9D4Je8UvRK7dT8mwF07fD4l7bnMNagQjFAcT5TSDj8NySf9n14LEoHloroLdSRFt0hhHJ7cVRXGvj18DUuoxgi0oxAUHp1433HTrB8t3QivZi1tobF2n747gBbz1AXkC1SH/+OSU9DUuL+FNL5XRJgt9AoGAKq1kigRfJLIgLvPrXC8E7Wu72ztZxkKoR8EUY30sroHHZRj3ziAiYAvxpavoOrFgnvwcNxjBgPQ90bb5cgPQnnxUqRtuxQbfHX7DBw3IIEKLZAYojuxemiRpBeq62wTOXGrmusPC2JcsT9JzjUNGFJiszhPPcKFvsy2FjDCxSnkCgYEAt+BPODj5cV+aRKvMWdGAapPo/GOVBmQHTtNN/Cw7Lahg9sdunpbAGvyMxfosygnRKV+Ny7VDE4k9wRbm6bIu7esEj5pZv/5fuXU5yCGDNe04GbrUlTbo5Vmw2ChjppMFZSDQDheNn5wiPkTan9Rkx9mZeaHzrZZaKs2vAskKamI="
; private static AliPay mAliPay; private Activity mContext; private PayListener mPayListener; private String orderTradeNo; private AliPay(Activity context) { mContext = context; } public static AliPay getInstance(Activity context) { if (mAliPay == null) { synchronized (AliPay.class) { if (mAliPay == null) { mAliPay = new AliPay(context); } } } return mAliPay; } public void startAliPay(final String signOrderInfo, PayListener listener) { mPayListener = listener; Runnable payRunnable = new Runnable() { @Override public void run() { PayTask alipay = new PayTask(mContext); Map<String, String> result = alipay.payV2(signOrderInfo, true); Message msg = new Message(); msg.obj = result; mHandler.sendMessage(msg); } }; Thread payThread = new Thread(payRunnable); payThread.start(); } private Handler mHandler = new Handler(Looper.getMainLooper()) { @SuppressWarnings("unchecked") public void handleMessage(Message msg) { PayResult payResult = new PayResult((Map<String, String>) msg.obj); String resultStatus = payResult.getResultStatus(); String resultInfo = payResult.getResult(); Log.i("resultinfo", resultInfo); if (mPayListener == null) { return; } // https://doc.open.alipay.com/doc2/detail.htm?spm=a219a.7629140.0.0.xN1NnL&treeId=204&articleId=105302&docType=1 if (payResult == null) { mPayListener.onPayError("結果解析錯誤"); return; } if (TextUtils.equals(resultStatus, "9000")) { //支付成功 mPayListener.onPaySuccess(); } else if (TextUtils.equals(resultStatus, "8000")) { //正在處理中,支付結果未知(有可能已經支付成功),請查詢商戶訂單列表中訂單的支付狀態 mPayListener.onPayError("正在處理結果中"); } else if (TextUtils.equals(resultStatus, "6001")) { //支付取消 mPayListener.onPayCancel(); } else if (TextUtils.equals(resultStatus, "6002")) { //網路連接出錯 mPayListener.onPayError("網路連接出錯"); } else if (TextUtils.equals(resultStatus, "4000")) { //支付錯誤 mPayListener.onPayError("訂單支付失敗"); } else { mPayListener.onPayError(resultInfo); } } }; /** * 使用支付寶建立訂單資訊 * 這個簽名工作要放在後臺伺服器中進行處理 * 前端將 付訂單引數資訊 上傳到伺服器 後臺進行簽名處理後,將簽名後的訂單資訊發回來 * * @param itemName * @param itemDescribe * @param itemPrice */ public Map createOrderParamMap(String itemName, String itemDescribe, double itemPrice, String itemTradeNo) { //這裡預設使用rsa2私鑰 Map<String, String> params = buildOrderParamMap(APPID, itemName, itemDescribe, itemPrice, itemTradeNo, true); return params; } public String createOrderInfo(Map<String, String> params) { return buildOrderParam(params); } /** * 構造支付訂單引數列表 * * @param app_id * @param itemName 商品名稱 * @param itemDecribe 商品詳情 * @param itemPrice 商品價格 * @param rsa2 是否使用rsa2 建議true; * @return */ private Map<String, String> buildOrderParamMap(String app_id, String itemName, String itemDecribe, double itemPrice, String itemTradeNo, 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\":\"" + itemPrice + "\",\"subject\":\"" + itemName + "\",\"body\":\"" + itemDecribe + "\",\"out_trade_no\":\"" + itemTradeNo + "\"}"); keyValues.put("charset", "utf-8"); keyValues.put("method", "alipay.trade.app.pay"); keyValues.put("sign_type", rsa2 ? "RSA2" : "RSA"); keyValues.put("timestamp", Comm.formatNowTime()); 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(); StringBuilder s = 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("&"); s.append(key); s.append("="); s.append(value); s.append("&"); } String tailKey = keys.get(keys.size() - 1); String tailValue = map.get(tailKey); sb.append(buildKeyValue(tailKey, tailValue, true)); s.append(tailKey); s.append("="); s.append(tailValue); Log.i("orderinfo",s.toString()); return sb.toString(); } /** * 拼接鍵值對 * * @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(); } /** * 要求外部訂單號必須唯一。 * * @return */ public 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); orderTradeNo = key; return key; } /** * 對支付引數資訊進行簽名 * * @param map 待簽名授權資訊 * @return */ public String getSign(Map<String, String> map) { 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(), RSA2_PRIVATE, true); String encodedSign = ""; try { encodedSign = URLEncoder.encode(oriSign, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return "sign=" + encodedSign; } }

然後自定義一個支付結果介面: PayListener

public interface PayListener {
    //支付成功
    void onPaySuccess();

    //支付失敗
    void onPayError(String resultStatus);

    //支付取消
    void onPayCancel();
}

然後呼叫:
注意:
1、這裡的signOrderInfo必須要從伺服器獲取,我的後臺這裡還是給力的,上傳訂單id就把signOrderInfo傳過來了,都不用自己寫,多虧了我的口才。
2、這裡的orderInfo是是進行 URLEncoder.encode() 轉碼的(鍵為原文,值進行轉碼),具體內容可以打印出來看下,然後加密sign內容是使用原文(沒有進行轉碼)進行加密
3、這裡的Base64.java PayResult.java SignUtils.java 去支付寶的demo中獲取(測試階段)
這裡的Base64.java PayResult.java SignUtils.java 去支付寶的demo中獲取(測試階段)
這裡的Base64.java PayResult.java SignUtils.java 去支付寶的demo中獲取(測試階段)

自己測試的時候可以這麼寫:

AliPay aliPay = AliPay.getInstance(this);
Map orderParamMap = aliPay.createOrderParamMap("商品名字", "商品描述", 0.01, "123123123123123");
String orderInfo = aliPay.createOrderInfo(orderParamMap);
String sign = aliPay.getSign(orderParamMap);
String signOrderInfo = orderInfo + "&" + sign;

AliPay.getInstance(PayActivity.this).startAliPay(signOrderInfo, new PayListener() {
                                @Override
                                public void onPaySuccess() {
                                    Comm.Tip(mContext, "支付成功");
                                }

                                @Override
                                public void onPayError(String resultStatus) {
                                    Comm.Tip(mContext, "支付失敗");
                                }

                                @Override
                                public void onPayCancel() {
                                    Comm.Tip(mContext, "支付取消");
                                }
                            });

2.微信支付
微信支付滿滿的都是坑,特別是官方Demo,看的我頭暈,然後還是沒弄好,很尷尬。
首先注意一個大坑:我們後臺申請接入時候簽名填寫錯誤,我就想說ios沒有簽名,安卓要簽名,然後導致了ios成功,安卓失敗,我找了一下午的原因。
注意:微信開發平臺的申請的應用簽名,如果在手機上安裝debug包獲取的簽名,那麼只能debug包才能呼叫微信支付,如果是release包獲取的簽名,那麼只有release包才能呼叫微信支付,當然,微信開發平臺上的簽名和包名都是可以更換的。
首先導個包,gradle方式也可以,然而我用AS下不下來,只好導包
簡單粗暴上程式碼:WechatPay.java

public class WechatPay {
    private static WechatPay wechatPay;
    private Context mContext;
    private IWXAPI mIWXAPI;
    private PayListener mPayListener;

    private WechatPay(Activity context) {
        mContext = context;
    }

    public static WechatPay getInstance(Activity context) {
        if (wechatPay == null) {
            synchronized (WechatPay.class) {
                if (wechatPay == null) {
                    wechatPay = new WechatPay(context);
                }
            }
        }
        return wechatPay;
    }

    /**
     * 初始化微信支付介面
     *
     * @param appId
     */
    public void init(String appId) {
        mIWXAPI = WXAPIFactory.createWXAPI(mContext, null);
        mIWXAPI.registerApp(appId);
    }

    /**
     * 獲取微信介面
     *
     * @return
     */
    public IWXAPI getWXApi() {
        return mIWXAPI;
    }

    /**
     * 調起支付
     *
     * @param appId
     * @param partnerId
     * @param prepayId
     * @param nonceStr
     * @param timeStamp
     * @param sign
     */
    public void startWeChatPay(String appId, String partnerId, String prepayId,
                               String nonceStr, String timeStamp, String sign, PayListener listener) {
        mPayListener = listener;
        init(appId);
        if (!checkWx()) {
            if (listener != null) {
                listener.onPayError("未安裝微信或者微信版本過低");
            }
            return;
        }
        PayReq request = new PayReq();
        request.appId = appId;
        request.partnerId = partnerId;
        request.prepayId = prepayId;
        request.packageValue = "Sign=WXPay";
        request.nonceStr = nonceStr;
        request.timeStamp = timeStamp;
        request.sign = sign;
        mIWXAPI.sendReq(request);
    }


    /**
     * 響應支付回撥
     *
     * @param error_code
     * @param message
     */
    public void onResp(int error_code, String message) {
        if (error_code == 0) {
            //支付成功
            mPayListener.onPaySuccess();
        } else if (error_code == -1) {
            //支付異常
            mPayListener.onPayError(message);
        } else if (error_code == -2) {
            //支付取消
            mPayListener.onPayCancel();
        }
        mPayListener = null;
    }


    //檢測微信客戶端是否支援微信支付
    private boolean checkWx() {
        return isWeixinAvilible() && mIWXAPI.isWXAppInstalled() && mIWXAPI.getWXAppSupportAPI() >= Build.PAY_SUPPORTED_SDK_INT;
    }

    /**
     * 判斷微信是否安裝
     *
     * @return
     */
    private boolean isWeixinAvilible() {
        return appIsAvilible("com.tencent.mm");
    }

    /**
     * 判斷app是否安裝
     *
     * @param packageName
     * @return
     */
    private boolean appIsAvilible(String packageName) {
        final PackageManager packageManager = mContext.getPackageManager();// 獲取packagemanager
        List<PackageInfo> pinfo = packageManager.getInstalledPackages(0);// 獲取所有已安裝程式的包資訊
        if (pinfo != null) {
            for (int i = 0; i < pinfo.size(); i++) {
                String pn = pinfo.get(i).packageName;
                if (pn.equals(packageName)) {
                    return true;
                }
            }
        }
        return false;
    }
}

其次是:WXPayEntryActivity .java

public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (WechatPay.getInstance(this) != null) {
            WechatPay.getInstance(this).getWXApi().handleIntent(getIntent(), this);
        } else {
            finish();
        }
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        if (WechatPay.getInstance(this) != null) {
            WechatPay.getInstance(this).getWXApi().handleIntent(intent, this);
        }
    }

    @Override
    public void onReq(BaseReq baseReq) {

    }

    @Override
    public void onResp(BaseResp baseResp) {
        if (baseResp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {
            if (WechatPay.getInstance(this) != null) {
                WechatPay.getInstance(this).onResp(baseResp.errCode, baseResp.errStr);
                finish();
            }
        }
    }
}

再來清單檔案中:
改下第二行和最後一行的地址

<!-- 微信 -->
        <activity
            android:name=".Controller.wxapi.WXPayEntryActivity"
            android:configChanges="orientation|keyboardHidden|navigation|screenSize"
            android:launchMode="singleTop"
            android:theme="@android:style/Theme.Translucent.NoTitleBar"/>
        <activity-alias
            android:name=".wxapi.WXPayEntryActivity"
            android:exported="true"
            android:targetActivity=".Controller.wxapi.WXPayEntryActivity"/>

好的,現在開始呼叫:
我的order類當然也是後臺發給我的嘍

 WechatPay.getInstance(PayActivity.this).startWeChatPay(order.getAppid(), order.getPartid(), order.getPrepayid(), order.getNoncestr(), order.getTimestamp(), order.getSign(), new PayListener() {
                                @Override
                                public void onPaySuccess() {
                                    Comm.Tip(mContext, "支付成功");
                                }

                                @Override
                                public void onPayError(String resultStatus) {
                                    Comm.Tip(mContext, "支付失敗");
                                }

                                @Override
                                public void onPayCancel() {
                                    Comm.Tip(mContext, "支付取消");
                                }
                            });

好的,打完收工,完成之後發現,其實也就是那麼回事。