見識2分鐘集成微信和支付寶支付的威力

在開發(fā)的APP中項目集成了微信跟支付寶支付,分別是在訂單確認頁面,訂單列表以及訂單詳情里面都需要進行支付,并且需要在當(dāng)前界面處理支付結(jié)果。

之前的處理是將代碼拷貝了三份,后來隨著項目越來越大,感覺比較low。

于是打算重構(gòu)一下支付模塊,下面分享一下重構(gòu)的整個過程。

支付寶支付

支付寶的集成比較簡單,按照官方文檔來集成,一般是比較順利的。

不過當(dāng)時做的時候簽名一直不成功,但是跟文檔是一模一樣的,后來無意中發(fā)現(xiàn)是orderInfo的拼接順序跟簽名的順序不一樣導(dǎo)致的。

所以要么在本地簽名,要么在服務(wù)端簽名,千萬不要一邊拼接另一邊簽名,這樣會導(dǎo)致orderInfo的拼接順序跟簽名的順序不一樣失敗。

▍特點:

1.返回的結(jié)果可以實時通知當(dāng)前頁面
因為是在一個類中,我們只需要在修改AlipayActivity的構(gòu)造方法的時候傳入一個接口,在支付寶支付返回給當(dāng)前調(diào)用的界面就OK了,不管是Activity,F(xiàn)ragment,或者是Adapter都是可以的。

2.不需要簽名
在調(diào)試的時候不管是debug模式還是release模式,都可以進行調(diào)試。

3.用戶不需要安裝微信客戶端
支付寶有自帶的H5頁面,不安裝客戶端也可以正常支付

微信支付

相對于支付寶支付,微信支付就稍微麻煩一下,而且文檔資料比較少,一定要嚴格按照文檔的規(guī)范,不然出問題了,比較麻煩。

▍特點:

1.返回的結(jié)果在固定的頁面
微信支付比較坑的一點就是,他的回調(diào)必須是在固定的包名:項目包名+wxapi,而且名稱是WXPayEntryActivity。

2.必須簽名
所以開發(fā)的時候即使是debug模式也必須帶上簽名

3.需要檢測用戶是否安裝微信以及微信的版本
必須進行校驗,不然他只會返回給你一個code,然后并不會告訴你為什么失敗,就是這么任性。

封裝之路

下面借用stay大神的What-How-Why的思路對這個支付方式進行了重構(gòu)

▍what

我們希望在支付的時候傳入一個支付的對象,支付需要的參數(shù)就能夠進行支付,并且能夠返回給我們支付的結(jié)果。

▍how

1.傳參不一樣
定義一個基類,讓支付參數(shù)對象繼承這個基類。

支付參數(shù)基類

public class BasePayBean implements Serializable {
}

支付寶支付參數(shù)

public class AliPayBean extends BasePayBean {
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    private String code;
    private String message;
    private DataBean data;
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public DataBean getData() {
        return data;
    }
    public void setData(DataBean data) {
        this.data = data;
    }
    public static class DataBean {
        private String _input_charset;
        private String body;
        private String notify_url;
        private String out_trade_no;
        private String partner;
        private String payment_type;
        private String return_url;
        private String seller_id;
        private String service;
        private String subject;
        private String total_fee;
        private String sign_string;
        private String sign_type;
        private String sign;
        public String get_input_charset() {
            return _input_charset;
        }
        public void set_input_charset(String _input_charset) {
            this._input_charset = _input_charset;
        }
        public String getBody() {
            return body;
        }
        public void setBody(String body) {
            this.body = body;
        }
        public String getNotify_url() {
            return notify_url;
        }
        public void setNotify_url(String notify_url) {
            this.notify_url = notify_url;
        }
        public String getOut_trade_no() {
            return out_trade_no;
        }
        public void setOut_trade_no(String out_trade_no) {
            this.out_trade_no = out_trade_no;
        }
        public String getPartner() {
            return partner;
        }
        public void setPartner(String partner) {
            this.partner = partner;
        }
        public String getPayment_type() {
            return payment_type;
        }
        public void setPayment_type(String payment_type) {
            this.payment_type = payment_type;
        }
        public String getReturn_url() {
            return return_url;
        }
        public void setReturn_url(String return_url) {
            this.return_url = return_url;
        }
        public String getSeller_id() {
            return seller_id;
        }
        public void setSeller_id(String seller_id) {
            this.seller_id = seller_id;
        }
        public String getService() {
            return service;
        }
        public void setService(String service) {
            this.service = service;
        }
        public String getSubject() {
            return subject;
        }
        public void setSubject(String subject) {
            this.subject = subject;
        }
        public String getTotal_fee() {
            return total_fee;
        }
        public void setTotal_fee(String total_fee) {
            this.total_fee = total_fee;
        }
        public String getSign_string() {
            return sign_string;
        }
        public void setSign_string(String sign_string) {
            this.sign_string = sign_string;
        }
        public String getSign_type() {
            return sign_type;
        }
        public void setSign_type(String sign_type) {
            this.sign_type = sign_type;
        }
        public String getSign() {
            return sign;
        }
        public void setSign(String sign) {
            this.sign = sign;
        }
    }
}

微信支付參數(shù)

public class WeChatBean extends BasePayBean {
    private String code;
    private String message;
    private DataBean data;
    public String getCode() {
        return code;
    }
    public void setCode(String code) {
        this.code = code;
    }
    public String getMessage() {
        return message;
    }
    public void setMessage(String message) {
        this.message = message;
    }
    public DataBean getData() {
        return data;
    }
    public void setData(DataBean data) {
        this.data = data;
    }
    public static class DataBean {
        private String appid;
        private String partnerid;
        private String prepayid;
        private String package_;
        private String noncestr;
        private String timestamp;
        private String sign;
        public String getAppid() {
            return appid;
        }
        public void setAppid(String appid) {
            this.appid = appid;
        }
        public String getPartnerid() {
            return partnerid;
        }
        public void setPartnerid(String partnerid) {
            this.partnerid = partnerid;
        }
        public String getPrepayid() {
            return prepayid;
        }
        public void setPrepayid(String prepayid) {
            this.prepayid = prepayid;
        }
        public String getPackage_() {
            return package_;
        }
        public void setPackage_(String package_) {
            this.package_ = package_;
        }
        public String getNoncestr() {
            return noncestr;
        }
        public void setNoncestr(String noncestr) {
            this.noncestr = noncestr;
        }
        public String getTimestamp() {
            return timestamp;
        }
        public void setTimestamp(String timestamp) {
            this.timestamp = timestamp;
        }
        public String getSign() {
            return sign;
        }
        public void setSign(String sign) {
            this.sign = sign;
        }
    }

2.支付對象不一樣
定義一個泛型接口,讓支付對象去繼承這個接口

泛型接口

public interface BasePayWay {
void startPay(Activity activity, T t, PayCallBack payCallback);
}

支付寶支付對象

public class AliPayWay implements BasePayWay {
//商戶PID
public static final String PARTNER = "";
//商戶收款賬號
public static final String SELLER = "";
//商戶私鑰,pkcs8格式
public static String RSA_PRIVATE = "";
public static final int SDK_PAY_FLAG = 1;
public static final int SDK_CHECK_FLAG = 2;
public PayCallBack mPayCallBack;
private Activity mActivity;
//--------支付寶支付回調(diào)-----
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case AliPayWay.SDK_PAY_FLAG: {
                PayResult payResult = new PayResult((String) msg.obj);
                // 支付寶返回此次支付結(jié)果及加簽,建議對支付寶簽名信息拿簽約時支付寶提供的公鑰做驗簽
                String resultStatus = payResult.getResultStatus();
                // 判斷resultStatus 為“9000”則代表支付成功,具體狀態(tài)碼代表含義可參考接口文檔
                if (TextUtils.equals(resultStatus, "9000")) {
                    mPayCallBack.onResponse(0);
                } else {
                    // 判斷resultStatus 為非“9000”則代表可能支付失敗
                    // “8000”代表支付結(jié)果因為支付渠道原因或者系統(tǒng)原因還在等待支付結(jié)果確認,最終交易是否成功以服務(wù)端異步通知為準(小概率狀態(tài))
                    if (TextUtils.equals(resultStatus, "8000")) {
                        Toast.makeText(mActivity, "支付結(jié)果確認中", Toast.LENGTH_SHORT).show();
                    } else if (TextUtils.equals(resultStatus, "6001")) {
                        mPayCallBack.onResponse(-2);
                    } else {
                        // 其他值就可以判斷為支付失敗,包括用戶主動取消支付,或者系統(tǒng)返回的錯誤
                        mPayCallBack.onResponse(-1);
                    }
                }
                break;
            }
            case AliPayWay.SDK_CHECK_FLAG: {
                Toast.makeText(BaseApplication.getContext(), "檢查結(jié)果為:" + msg.obj, Toast.LENGTH_SHORT).show();
                break;
            }
            default:
                break;
        }
    }
};
/**
 * sign the order info. 對訂單信息進行簽名
 *
 * @param content 待簽名訂單信息
 */
public String sign(String content) {
    return SignUtils.sign(content, RSA_PRIVATE);
}
/**
 * get the sign type we use. 獲取簽名方式
 */
public String getSignType() {
    return "sign_type=\"RSA\"";
}
@Override
public void startPay(Activity activity, AliPayBean aliPayBean, PayCallBack payCallback) {
    this.mActivity = activity;
    this.mPayCallBack = payCallback;
    String orderInfo = aliPayBean.getData().getSign_string();
    String sign = aliPayBean.getData().getSign();
    try {
        // 僅需對sign 做URL編碼
        sign = URLEncoder.encode(sign, "UTF-8");
    } catch (UnsupportedEncodingException e) {
        e.printStackTrace();
    }
    // 完整的符合支付寶參數(shù)規(guī)范的訂單信息
    final String payInfo = orderInfo + "&sign=\"" + sign + "\"&" + getSignType();
    Log.d("payInfo------->", payInfo);
    Runnable payRunnable = new Runnable() {
        @Override
        public void run() {
            // 構(gòu)造PayTask 對象
            PayTask alipay = new PayTask(mActivity);
            // 調(diào)用支付接口,獲取支付結(jié)果
            String result = alipay.pay(payInfo, true);
            Message msg = new Message();
            msg.what = AliPayWay.SDK_PAY_FLAG;
            msg.obj = result;
            mHandler.sendMessage(msg);
        }
    };
    // 必須異步調(diào)用
    Thread payThread = new Thread(payRunnable);
    payThread.start();
}
}

微信支付對象

public class WeChatWay implements BasePayWay{
    private static WeChatWay mWeChatPay;
    private IWXAPI api;
    private Context mContext;
    private PayCallBack mPayCallBack;
    private static final int TIMELINE_SUPPORTED_VERSION = 0x21020001;
    private WeChatWay(String wxAppId) {
        api = WXAPIFactory.createWXAPI(mContext, null);
        api.registerApp(wxAppId);
    }
    public static WeChatWay getInstance(String wxAppId) {
        if (mWeChatPay == null) {
            synchronized (WeChatWay.class) {
                if (mWeChatPay == null) {
                    mWeChatPay = new WeChatWay(wxAppId);
                }
            }
        }
        return mWeChatPay;
    }
    public IWXAPI getApi() {
        return api;
    }
    private boolean checkWeChatPay() {
        int wxSdkVersion = api.getWXAppSupportAPI();
        boolean isWeChatAble = true;
        if (!api.isWXAppInstalled()) {
            CommonUtils.showToast(mContext, "使用微信支付必須先安裝微信客戶端");
            isWeChatAble = false;
        } else if (wxSdkVersion < TIMELINE_SUPPORTED_VERSION) {
            CommonUtils.showToast(mContext, "微信支付支持的最低版本高于當(dāng)前安裝版本,請先升級微信客戶端");
            isWeChatAble = false;
        }
        return isWeChatAble;
    }
    @Override
    public void startPay(Activity activity, WeChatBean weChatBean, PayCallBack payCallback) {
        mContext = activity.getApplicationContext();
        this.mPayCallBack = payCallback;
        if (checkWeChatPay()) {
            PayReq req = new PayReq();
            req.appId = weChatBean.getData().getAppid();
            req.partnerId = weChatBean.getData().getPartnerid();
            req.prepayId = weChatBean.getData().getPrepayid();
            req.packageValue = weChatBean.getData().getPackage_();
            req.nonceStr = weChatBean.getData().getNoncestr();
            req.timeStamp = weChatBean.getData().getTimestamp();
            req.sign = weChatBean.getData().getSign();
            Constant.PAY_FROM = 1;
            api.sendReq(req);
        } else {
            mPayCallBack.onResponse(-1);
        }
    }
    public void onResponse(int code) {
        mPayCallBack.onResponse(code);
    }
}

3.當(dāng)前頁面回調(diào)結(jié)果
使用接口統(tǒng)一回調(diào);
定義接口

public interface PayCallBack {
// code: 0為失敗,-1為失敗,-2為取消
//通過code統(tǒng)一刷新界面
void onResponse(int code);
}

支付寶回調(diào)

switch (msg.what) {
                case AliPayWay.SDK_PAY_FLAG: {
                    PayResult payResult = new PayResult((String) msg.obj);
                    // 支付寶返回此次支付結(jié)果及加簽,建議對支付寶簽名信息拿簽約時支付寶提供的公鑰做驗簽
                    String resultStatus = payResult.getResultStatus();
                    // 判斷resultStatus 為“9000”則代表支付成功,具體狀態(tài)碼代表含義可參考接口文檔
                    if (TextUtils.equals(resultStatus, "9000")) {
                        mPayCallBack.onResponse(0);
                    } else {
                        // 判斷resultStatus 為非“9000”則代表可能支付失敗
                        // “8000”代表支付結(jié)果因為支付渠道原因或者系統(tǒng)原因還在等待支付結(jié)果確認,最終交易是否成功以服務(wù)端異步通知為準(小概率狀態(tài))
                        if (TextUtils.equals(resultStatus, "8000")) {
                            Toast.makeText(mActivity, "支付結(jié)果確認中", Toast.LENGTH_SHORT).show();
                        } else if (TextUtils.equals(resultStatus, "6001")) {
                            mPayCallBack.onResponse(-2);
                        } else {
                            // 其他值就可以判斷為支付失敗,包括用戶主動取消支付,或者系統(tǒng)返回的錯誤
                            mPayCallBack.onResponse(-1);
                        }
                    }
                    break;
                }
                case AliPayWay.SDK_CHECK_FLAG: {                    Toast.makeText(BaseApplication.getContext(), "檢查結(jié)果為:" + msg.obj, Toast.LENGTH_SHORT).show();
                    break;
                }
                default:
                    break;
            }

微信回調(diào):
微信需要在WXPayEntryActivity單獨做一些處理。

public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);    
      WeChatWay.getInstance(Constant.APP_ID).getApi().handleIntent(getIntent(),this);
    }
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
       WeChatWay.getInstance(Constant.APP_ID).getApi().handleIntent(intent, this);
    }
    @Override
    public void onReq(BaseReq req) {
    }
    /**
     * 四、接收支付返回結(jié)果
     */
    @Override
    public void onResp(BaseResp resp) {
        WeChatWay.getInstance(Constant.APP_ID).onResponse(resp.errCode);
    }
}

使用方法

將服務(wù)端返回的數(shù)據(jù)解析成預(yù)先定義好的AlipayBean,然后調(diào)起支付。

▍支付寶

RxSubscriber rxSubscriber = new RxSubscriber(this, new Callback() {
                        @Override
                        public void onNext(WeChatBean weChatBean) {
                            if (weChatBean.getCode().equals("waitpay")) {
                                //調(diào)起微信支付
                                WeChatWay weChatWay = WeChatWay.getInstance(Constant.APP_ID);                                
                                                weChatWay.startPay(OrderDetailActivity.this, 
                                                weChatBean, new PayCallBack() {
                                    @Override
                                    public void onResponse(int code) {
                                        refreshDataWithCode(code);
                                    }
                                });
                            } else {
                                CommonUtils.showToast(mContext, weChatBean.getMessage());
                            }
                        }
                    });                 RequestManager.getInstance().sendRequest(weChatBeanObservable, rxSubscriber);

▍微信

ObservableweChatBeanObservable = RxRequest.getInstance().getProxy(false).callWeChatPay(map);
                    RxSubscriber rxSubscriber = new RxSubscriber(this, new Callback() {
                        @Override
                        public void onNext(WeChatBean weChatBean) {
                            if (weChatBean.getCode().equals("waitpay")) {
                                //調(diào)起微信支付
                                WeChatWay weChatWay = WeChatWay.getInstance(Constant.APP_ID);
                                weChatWay.startPay(OrderDetailActivity.this, weChatBean, new PayCallBack() {
                                    @Override
                                    public void onResponse(int code) {
                                        refreshDataWithCode(code);
                                    }
                                });
                            } else {
                                CommonUtils.showToast(mContext, weChatBean.getMessage());
                            }
                        }
                    });
                    RequestManager.getInstance().sendRequest(weChatBeanObservable, rxSubscriber);

其他

▍demo地址

https://github.com/wustor/PayDemo

▍參考文章

EasyPay(易支付),兩分鐘集成三種Android支付方式
http://www.lxweimin.com/p/bd4d44c33532#

▍作者:wustor,http://www.lxweimin.com/p/53359f844cfa

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,030評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,310評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,951評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,796評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,566評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,055評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,142評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,303評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,799評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,683評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,899評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,409評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,135評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,520評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,757評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,528評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,844評論 2 372

推薦閱讀更多精彩內(nèi)容