在開發的APP中專案整合了微信跟支付寶支付,分別是在訂單確認頁面,訂單列表以及訂單詳情裡面都需要進行支付,並且需要在當前介面處理支付結果,之前的處理是將程式碼拷貝了三份,後來隨著專案越來越大,感覺比較low,於是打算重構一下支付模組,下面分享一下重構的整個過程。
支付寶支付
支付寶的整合比較簡單,按照官方文件來整合,一般是比較順利的,不過我是2班的,當時做的時候簽名一直不成功,但是跟文件是一模一樣的,後來無意中發現(因為有意沒發現)是orderInfo的拼接順序跟簽名的順序不一樣導致的,所以要麼在本地簽名,要麼在服務端簽名,千萬不要一邊拼接另一邊簽名,這樣會導致orderInfo的拼接順序跟簽名的順序不一樣失敗,就呵呵了。
特點:
- 返回的結果可以實時通知當前頁面
因為實在一個類中,我們只需要在修改AlipayActivity的構造方法的時候傳入一個介面,在支付寶支付返回給當前呼叫的介面就OK了,不管是Activity,Fragment,或者是Adapter都是可以的
- 不需要簽名
在除錯的時候不管是debug模式還是release模式,都可以進行除錯
- 使用者不需要安裝微信客戶端
支付寶有自帶的H5頁面,不安裝客戶端也可以正常支付
微信支付
相對於支付寶支付,微信支付就稍微麻煩一下,而且文件資料比較少,一定要嚴格按照文件的規範,不然出問題了,比較麻煩。
特點:
- 返回的結果在固定的頁面
微信支付比較坑的一點就是,他的回撥必須是在固定的包名:專案包名+wxapi,而且名稱是WXPayEntryActivity。
- 必須簽名
所以開發的時候即使是debug模式也必須帶上簽名
- 需要檢測使用者是否安裝微信以及微信的版本
必須進行校驗,不然他只會返回給你一個code,然後並不會告訴你為什麼失敗,就是這麼任性
封裝之路
下面借用stay大神的What-How-Why的思路對這個支付方式進行了重構
what
- 我們希望在支付的時候傳入一個支付的物件,支付需要的引數就能夠進行支付,並且能夠返回給我們支付的結果
how
傳參不一樣
定義一個基類,讓支付引數物件繼承這個基類;
- 支付引數基類
public class BasePayBean implements Serializable {
}
複製程式碼
- 支付寶支付引數
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;
}
}
}
複製程式碼
- 微信支付引數
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;
}
}
複製程式碼
支付物件不一樣
定義一個泛型介面,讓支付物件去繼承這個介面
- 泛型介面
public interface BasePayWay<T extends BasePayBean> {
void startPay(Activity activity, T t, PayCallBack payCallback);
}
複製程式碼
- 支付寶支付物件
public class AliPayWay implements BasePayWay<AliPayBean> {
//商戶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;
//--------支付寶支付回撥-----
@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);
// 支付寶返回此次支付結果及加簽,建議對支付寶簽名資訊拿簽約時支付寶提供的公鑰做驗籤
String resultStatus = payResult.getResultStatus();
// 判斷resultStatus 為“9000”則代表支付成功,具體狀態碼代表含義可參考介面文件
if (TextUtils.equals(resultStatus, "9000")) {
mPayCallBack.onResponse(0);
} else {
// 判斷resultStatus 為非“9000”則代表可能支付失敗
// “8000”代表支付結果因為支付渠道原因或者系統原因還在等待支付結果確認,最終交易是否成功以服務端非同步通知為準(小概率狀態)
if (TextUtils.equals(resultStatus, "8000")) {
Toast.makeText(mActivity, "支付結果確認中", Toast.LENGTH_SHORT).show();
} else if (TextUtils.equals(resultStatus, "6001")) {
mPayCallBack.onResponse(-2);
} else {
// 其他值就可以判斷為支付失敗,包括使用者主動取消支付,或者系統返回的錯誤
mPayCallBack.onResponse(-1);
}
}
break;
}
case AliPayWay.SDK_CHECK_FLAG: {
Toast.makeText(BaseApplication.getContext(), "檢查結果為:" + 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();
}
// 完整的符合支付寶引數規範的訂單資訊
final String payInfo = orderInfo + "&sign=\"" + sign + "\"&" + getSignType();
Log.d("payInfo------->", payInfo);
Runnable payRunnable = new Runnable() {
@Override
public void run() {
// 構造PayTask 物件
PayTask alipay = new PayTask(mActivity);
// 呼叫支付介面,獲取支付結果
String result = alipay.pay(payInfo, true);
Message msg = new Message();
msg.what = AliPayWay.SDK_PAY_FLAG;
msg.obj = result;
mHandler.sendMessage(msg);
}
};
// 必須非同步呼叫
Thread payThread = new Thread(payRunnable);
payThread.start();
}
}
複製程式碼
- 微信支付物件
public class WeChatWay implements BasePayWay<WeChatBean> {
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, "微信支付支援的最低版本高於當前安裝版本,請先升級微信客戶端");
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);
}
}
複製程式碼
當前頁面回撥結果
使用介面統一回撥;
- 定義介面
public interface PayCallBack {
// code: 0為失敗,-1為失敗,-2為取消
//通過code統一重新整理介面
void onResponse(int code);
}
複製程式碼
- 支付寶回撥
switch (msg.what) {
case AliPayWay.SDK_PAY_FLAG: {
PayResult payResult = new PayResult((String) msg.obj);
// 支付寶返回此次支付結果及加簽,建議對支付寶簽名資訊拿簽約時支付寶提供的公鑰做驗籤
String resultStatus = payResult.getResultStatus();
// 判斷resultStatus 為“9000”則代表支付成功,具體狀態碼代表含義可參考介面文件
if (TextUtils.equals(resultStatus, "9000")) {
mPayCallBack.onResponse(0);
} else {
// 判斷resultStatus 為非“9000”則代表可能支付失敗
// “8000”代表支付結果因為支付渠道原因或者系統原因還在等待支付結果確認,最終交易是否成功以服務端非同步通知為準(小概率狀態)
if (TextUtils.equals(resultStatus, "8000")) {
Toast.makeText(mActivity, "支付結果確認中", Toast.LENGTH_SHORT).show();
} else if (TextUtils.equals(resultStatus, "6001")) {
mPayCallBack.onResponse(-2);
} else {
// 其他值就可以判斷為支付失敗,包括使用者主動取消支付,或者系統返回的錯誤
mPayCallBack.onResponse(-1);
}
}
break;
}
case AliPayWay.SDK_CHECK_FLAG: {
Toast.makeText(BaseApplication.getContext(), "檢查結果為:" + msg.obj, Toast.LENGTH_SHORT).show();
break;
}
default:
break;
}
複製程式碼
- 微信回撥:微信需要在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) {
}
/**
* 四、接收支付返回結果
*/
@Override
public void onResp(BaseResp resp) {
WeChatWay.getInstance(Constant.APP_ID).onResponse(resp.errCode);
}
}
複製程式碼
使用方法
將服務端返回的資料解析成預先定義好的AlipayBean,然後調起支付
- 支付寶
RxSubscriber rxSubscriber = new RxSubscriber(this, new Callback<WeChatBean>() {
@Override
public void onNext(WeChatBean weChatBean) {
if (weChatBean.getCode().equals("waitpay")) {
//調起微信支付
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);
複製程式碼
- 微信
Observable<WeChatBean> weChatBeanObservable = RxRequest.getInstance().getProxy(false).callWeChatPay(map);
RxSubscriber rxSubscriber = new RxSubscriber(this, new Callback<WeChatBean>() {
@Override
public void onNext(WeChatBean weChatBean) {
if (weChatBean.getCode().equals("waitpay")) {
//調起微信支付
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);
複製程式碼