在开发的APP中项目集成了微信跟支付宝支付,分别是在订单确认页面,订单列表以及订单详情里面都需要进行支付,并且需要在当前界面处理支付结果。
之前的处理是将代码拷贝了三份,后来随着项目越来越大,感觉比较low。
于是打算重构一下支付模块,下面分享一下重构的整个过程。
支付宝支付
支付宝的集成比较简单,按照官方文档来集成,一般是比较顺利的。
不过当时做的时候签名一直不成功,但是跟文档是一模一样的,后来无意中发现是orderInfo的拼接顺序跟签名的顺序不一样导致的。
所以要么在本地签名,要么在服务端签名,千万不要一边拼接另一边签名,这样会导致orderInfo的拼接顺序跟签名的顺序不一样失败。
▍特点:
1.返回的结果可以实时通知当前页面
因为是在一个类中,我们只需要在修改AlipayActivity的构造方法的时候传入一个接口,在支付宝支付返回给当前调用的界面就OK了,不管是Activity,Fragment,或者是Adapter都是可以的。
2.不需要签名
在调试的时候不管是debug模式还是release模式,都可以进行调试。
3.用户不需要安装微信客户端
支付宝有自带的H5页面,不安装客户端也可以正常支付
微信支付
相对于支付宝支付,微信支付就稍微麻烦一下,而且文档资料比较少,一定要严格按照文档的规范,不然出问题了,比较麻烦。
▍特点:
1.返回的结果在固定的页面
微信支付比较坑的一点就是,他的回调必须是在固定的包名:项目包名+wxapi,而且名称是WXPayEntryActivity。
2.必须签名
所以开发的时候即使是debug模式也必须带上签名
3.需要检测用户是否安装微信以及微信的版本
必须进行校验,不然他只会返回给你一个code,然后并不会告诉你为什么失败,就是这么任性。
封装之路
下面借用stay大神的What-How-Why的思路对这个支付方式进行了重构
▍what
我们希望在支付的时候传入一个支付的对象,支付需要的参数就能够进行支付,并且能够返回给我们支付的结果。
▍how
1.传参不一样
定义一个基类,让支付参数对象继承这个基类。
支付参数基类
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;
}
}
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;
//--------支付宝支付回调-----
@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{
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);
}
}
3.当前页面回调结果
使用接口统一回调;
定义接口
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() {
@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);
▍微信
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")) {
//调起微信支付
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.jianshu.com/p/bd4d44c33532#)
▍作者:wustor,http://www.jianshu.com/p/53359f844cfa