在開發(fā)的APP中項(xiàng)目集成了微信跟支付寶支付捧存,分別是在訂單確認(rèn)頁面,訂單列表以及訂單詳情里面都需要進(jìn)行支付,并且需要在當(dāng)前界面處理支付結(jié)果。
之前的處理是將代碼拷貝了三份,后來隨著項(xiàng)目越來越大春缕,感覺比較low。
于是打算重構(gòu)一下支付模塊艘蹋,下面分享一下重構(gòu)的整個(gè)過程锄贼。
支付寶支付
支付寶的集成比較簡單,按照官方文檔來集成女阀,一般是比較順利的宅荤。
不過當(dāng)時(shí)做的時(shí)候簽名一直不成功屑迂,但是跟文檔是一模一樣的,后來無意中發(fā)現(xiàn)是orderInfo的拼接順序跟簽名的順序不一樣導(dǎo)致的冯键。
所以要么在本地簽名惹盼,要么在服務(wù)端簽名,千萬不要一邊拼接另一邊簽名琼了,這樣會(huì)導(dǎo)致orderInfo的拼接順序跟簽名的順序不一樣失敗逻锐。
▍特點(diǎn):
1.返回的結(jié)果可以實(shí)時(shí)通知當(dāng)前頁面
因?yàn)槭窃谝粋€(gè)類中夫晌,我們只需要在修改AlipayActivity的構(gòu)造方法的時(shí)候傳入一個(gè)接口雕薪,在支付寶支付返回給當(dāng)前調(diào)用的界面就OK了,不管是Activity晓淀,F(xiàn)ragment所袁,或者是Adapter都是可以的。
2.不需要簽名
在調(diào)試的時(shí)候不管是debug模式還是release模式凶掰,都可以進(jìn)行調(diào)試燥爷。
3.用戶不需要安裝微信客戶端
支付寶有自帶的H5頁面,不安裝客戶端也可以正常支付
微信支付
相對(duì)于支付寶支付懦窘,微信支付就稍微麻煩一下前翎,而且文檔資料比較少,一定要嚴(yán)格按照文檔的規(guī)范畅涂,不然出問題了港华,比較麻煩。
▍特點(diǎn):
1.返回的結(jié)果在固定的頁面
微信支付比較坑的一點(diǎn)就是午衰,他的回調(diào)必須是在固定的包名:項(xiàng)目包名+wxapi,而且名稱是WXPayEntryActivity立宜。
2.必須簽名
所以開發(fā)的時(shí)候即使是debug模式也必須帶上簽名
3.需要檢測用戶是否安裝微信以及微信的版本
必須進(jìn)行校驗(yàn),不然他只會(huì)返回給你一個(gè)code臊岸,然后并不會(huì)告訴你為什么失敗橙数,就是這么任性。
封裝之路
下面借用stay大神的What-How-Why的思路對(duì)這個(gè)支付方式進(jìn)行了重構(gòu)
▍what
我們希望在支付的時(shí)候傳入一個(gè)支付的對(duì)象帅戒,支付需要的參數(shù)就能夠進(jìn)行支付灯帮,并且能夠返回給我們支付的結(jié)果。
▍how
1.傳參不一樣
定義一個(gè)基類逻住,讓支付參數(shù)對(duì)象繼承這個(gè)基類钟哥。
支付參數(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.支付對(duì)象不一樣
定義一個(gè)泛型接口,讓支付對(duì)象去繼承這個(gè)接口
泛型接口
public interface BasePayWay {
void startPay(Activity activity, T t, PayCallBack payCallback);
}
支付寶支付對(duì)象
public class AliPayWay implements BasePayWay {
//商戶PID
public static final String PARTNER = "";
//商戶收款賬號(hào)
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é)果及加簽鄙信,建議對(duì)支付寶簽名信息拿簽約時(shí)支付寶提供的公鑰做驗(yàn)簽
String resultStatus = payResult.getResultStatus();
// 判斷resultStatus 為“9000”則代表支付成功瞪醋,具體狀態(tài)碼代表含義可參考接口文檔
if (TextUtils.equals(resultStatus, "9000")) {
mPayCallBack.onResponse(0);
} else {
// 判斷resultStatus 為非“9000”則代表可能支付失敗
// “8000”代表支付結(jié)果因?yàn)橹Ц肚涝蚧蛘呦到y(tǒng)原因還在等待支付結(jié)果確認(rèn),最終交易是否成功以服務(wù)端異步通知為準(zhǔn)(小概率狀態(tài))
if (TextUtils.equals(resultStatus, "8000")) {
Toast.makeText(mActivity, "支付結(jié)果確認(rèn)中", Toast.LENGTH_SHORT).show();
} else if (TextUtils.equals(resultStatus, "6001")) {
mPayCallBack.onResponse(-2);
} else {
// 其他值就可以判斷為支付失敗装诡,包括用戶主動(dòng)取消支付银受,或者系統(tǒng)返回的錯(cuò)誤
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. 對(duì)訂單信息進(jìn)行簽名
*
* @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 {
// 僅需對(duì)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 對(duì)象
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();
}
}
微信支付對(duì)象
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)前安裝版本,請先升級(jí)微信客戶端");
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é)果及加簽,建議對(duì)支付寶簽名信息拿簽約時(shí)支付寶提供的公鑰做驗(yàn)簽
String resultStatus = payResult.getResultStatus();
// 判斷resultStatus 為“9000”則代表支付成功顶霞,具體狀態(tài)碼代表含義可參考接口文檔
if (TextUtils.equals(resultStatus, "9000")) {
mPayCallBack.onResponse(0);
} else {
// 判斷resultStatus 為非“9000”則代表可能支付失敗
// “8000”代表支付結(jié)果因?yàn)橹Ц肚涝蚧蛘呦到y(tǒng)原因還在等待支付結(jié)果確認(rèn)肄程,最終交易是否成功以服務(wù)端異步通知為準(zhǔn)(小概率狀態(tài))
if (TextUtils.equals(resultStatus, "8000")) {
Toast.makeText(mActivity, "支付結(jié)果確認(rèn)中", Toast.LENGTH_SHORT).show();
} else if (TextUtils.equals(resultStatus, "6001")) {
mPayCallBack.onResponse(-2);
} else {
// 其他值就可以判斷為支付失敗,包括用戶主動(dòng)取消支付选浑,或者系統(tǒng)返回的錯(cuò)誤
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單獨(dú)做一些處理蓝厌。
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.reibang.com/p/bd4d44c33532#)
▍作者:wustor,http://www.reibang.com/p/53359f844cfa