支付
微信支付,官方给出的时序图如下:
支付流程总体还是遵循SDK的基本集成流程的。因此,也分为:
包装请求(req)--> 发送请求 --> 处理请求
三个步骤。但从时序图上,我们不难看出整个流程其实应该分为两大部分:
1. 订单支付流程
2. 订单查询流程
先后顺序上,支付流程在前,查询流程在后。即:
订单支付流程 --> 订单查询流程
这两大块分别完成了相互可独立划分,但数据上存在关联,存在因果关系的表目的不同的过程。一起组成了整个支付流程。所以,我们分别从这两部分入手。
订单支付流程
订单支付流程是包装订单请求和发送请求建立支付的这样的一个过程的封装。支付请求的本质目的是为了将本次交易的具体信息,封装成微信可识别的订单启动信息(即预支付信息)。然后交予服务器,服务器收到App的数据后会和微信支付服务器做“沟通”,开启支付流,并将本次订单的预支付信息返回给客户端。客户端通过服务端返回的数据生成预支付信息,就可以通知本地的微信客户端来连接并支付了。
需要注意的是:微信支付的返回值处理是在WXPayEntryActivity当中进行的,同样实现了 IWXAPIEventHandler,并在onResp当中处理了交易状态 返回值。因此,我们demo中的支付页面(Vip中心),使用的就是WXPayEntryActivity。
所以我们可以将这个过程放在同一个异步线程当中,避免主线程负载(存在数据搜集、加载过程),同时在时序上有个限定:
/** 1.发起交易 */
public void doPayDebtAction(final VipPackInfo data) {
new AsyncTask<Integer, Void, Boolean>() {
@Override
protected Boolean doInBackground(Integer... params) {
MessageManager.showProgressDialog((Activity) context, "搜集数据中...");
VipDebtPayInfo myPackDebt = sendReqToServer();
if (myPackDebt == null) return false;
return doWechatPayAction(myPackDebt);
}
@Override
protected void onPostExecute(Boolean isSuccess) {
super.onPostExecute(isSuccess);
MessageManager.closeProgressDialog();
if (!isSuccess) {
MessageManager.showMessage((Activity) context, "交易失败");
}
}
/***************************************具体流程*****************************************/
/** (1).请求服务端,发起交易会话 */
private VipDebtPayInfo sendReqToServer() {
return // TODO 这里放置你的数据搜集结果,这是和服务器端约定的,每个App间有所不同。
}
/** (2).根据会话返回结果,发起本地交易 */
public boolean doWechatPayAction(final VipDebtPayInfo debt) {
orderNumber = debt.getOrderNumber();
// 构造微信请求
PayReq req = new PayReq();
req.appId = debt.getAppId();
req.partnerId = debt.getPartnerId();
req.prepayId = debt.getPrepayId();
req.nonceStr = debt.getNonceStr();
req.timeStamp = debt.getTimeStamp();
req.packageValue = debt.getPackage();
req.sign = debt.getSign();
// 调用API接口发送到微信
IWXAPI wxApi = WXAPIFactory.createWXAPI(context, FirstPageConstants.WX.APP_ID);
wxApi.registerApp(FirstPageConstants.WX.APP_ID);
return wxApi.sendReq(req);
}
}.execute();
}
代码demo模拟的情况是一个Vip的购买流程,其中VipDebtPayInfo即是服务端在接受了客户端数据后返回的数据了。如要使用查询功能,则VipDebtPayInfo的数据在包含预支付所需信息外,还需要携带一个唯一的C-S约定的“订单号”字段,用于客户端后续连接服务器查询订单信息。
订单查询流程
因为涉及到钱,且微信本身只管支付过程,并不会(并且也没办法)帮你处理支付后的本地数据更新。所以,我们需要必须得订单查询流程,以确保:
1. 订单支付成功,服务端更新了数据。
2. 客户端能够确认服务端数据更新。
因此,查询流程的目的有两个:
1. 通知并确认服务端数据得到了更新。
2. 客户端数据同步更新。
所以我们有:
/** 2.交易结束(提交检查支付是否成功) */
public void checkWhenPayFinish() {
new AsyncTask<Integer, Void, Boolean>() {
@Override
protected Boolean doInBackground(Integer... params) {
MessageManager.showProgressDialog((Activity) context, "订单检测中...");
return checkFinalResult();
}
@Override
protected void onPostExecute(Boolean isSuccess) {
super.onPostExecute(isSuccess);
if (!isSuccess && !timerInfo.isDone()) { // 重新检测(未成功且小于3次)
debtTripleCheck();
} else { // 结果处理(成功或3次失败后)
resultDeal(isSuccess);
}
}
/***************************************具体流程*****************************************/
/** (1).最终结果校验(当前采用的是简单的本地线上比对,最好由服务端提交微信安全查询) */
private Boolean checkFinalResult() {
VipPayCheckInfo checkResult = mVipsRepository.checkPackDebt(orderNumber);
if (checkResult != null && checkResult.isSuccess()) {
VipProfilesInfo mProfilesOld = mVipsRepository.doLoadUserProfileByCache();
VipProfilesInfo mProfilesNew = mVipsRepository.doLoadVipsProfile();
// 更新个人数据
UserInfo usersInfo = mUserRepository.getUserInfo();
if (usersInfo != null && usersInfo.getChuyeUser() != null) {
UserSocial basicInfos = usersInfo.getChuyeUser();
basicInfos.setVipData(mProfilesNew.getVipData());
mUserRepository.saveBasicUserInfo(basicInfos);
mUserRepository.saveUserInfo(usersInfo);
}
return (mProfilesNew != null && !mProfilesNew.equals(mProfilesOld));
} else {
return false;
}
}
/** (2).网路订单三重校验(服务器特性,保证可靠性) */
@NonNull
private void debtTripleCheck() {
final Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
int count = timerInfo.getCount();
if (count >= 2) {
timerInfo.setIsDone(true);
} else {
timerInfo.setCount(count + 1);
checkWhenPayFinish();
}
}
}, 1000);
}
/** (3).结果处理 */
private void resultDeal(Boolean isSuccess) {
timerInfo.reset();
MessageManager.closeProgressDialog();
if (isSuccess) {
doUpdateProfile();
MessageManager.showMessage((Activity) context, "支付成功");
} else {
MessageManager.showMessage((Activity) context, "支付处理中,请等待微信通知");
}
}
/** (4).交易成功,更新Vip中心 */
private void doUpdateProfile() {
if (mOnDoNetFinishListener != null) {
VipCenterReunionInfo result = new VipCenterReunionInfo(
mVipsRepository.doLoadPackListByCache(),
mVipsRepository.doLoadUserProfileByCache()
);
mOnDoNetFinishListener.onLoadFinish(result);
}
}
}.execute();
}
因为模拟的是Vip的支付流,所以更新数据后,需要更新用户的Vip信息,同时更新Vip支付中心的信息(即交易发起界面的信息)。同时,为了保证避免检测过程中的数据延迟导致获取不正确的数据,我们需要进行三次重复校验,以保证交易准确性。
总结
前面我们讨论了SDK通用集成方法,并通过微信的登录、分享、支付展示了一下具体的使用过程,相信大家对SDK的使用已经有了一定的了解。其实,不论是聊天开发、天气信息、还是bug反馈,当使用第三方类库.jar文件时,基本是依照这个流程来的,只不过聊天需要你本地自定义的细节比较多,具体注册过程也会在随后的初始化流程中存在大量监听和细节(看具体SDK),消息推送根据不同的SDK在初始化上也存在大量的自定义及细节。可以看出,基本流程存在较大变数的位置就在于初始化流程当中了,这时会根据具体情况多处少至几行代码,多至几个模块的代码量了。具体情况还需要在使用中体会了。