见识2分钟集成微信和支付宝支付的威力

在开发的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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,761评论 5 460
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,953评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,998评论 0 320
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,248评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,130评论 4 356
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,145评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,550评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,236评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,510评论 1 291
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,601评论 2 310
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,376评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,247评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,613评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,911评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,191评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,532评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,739评论 2 335

推荐阅读更多精彩内容