微信支付服务端的回调网上已经有很多例子了,如果你一直专注于服务端开发,那么应该是非常轻松的搞定,就是别忘了对业务数据处理加锁,因为微信会重复发来消息通知而造成数据重入的混乱。
我想说说手机端Android开发的回调如何处理,也许大家使用原生的开发比较多,所以依照微信的官方文档处理就应该能够解决,但是对于使用AngularJS+Ionic+Cordova的Hybrid混合开发的小伙伴来说就不是能够顺畅解决的状况了。
网上的大牛写的一个完整的Cordova Plugin形式的微信插件,大家可以借鉴一下:
https://github.com/xu-li/cordova-plugin-wechat/blob/develop/src/android/Wechat.java
大家也许已经有了这个链接,我看很多小伙伴也已经安装上了,但是对应到自己的项目中又会出现很多的Issue,我发现很多人也是出现了同样的问题,微信客户端回调自己的APP的时候回不来,很多人是出现自己的APP会全部退出然后重新登录到APP首页。
根本的原因就是,微信作为公网上的一个提供服务的接口,他要找到各个具体的APP只能通过你在微信中注册的信息来识别,就如同微信是一个送货小哥,你不提供具体的街道地址门牌号你让小哥怎么给你送货上门呢。
所以接下来我会说到具体的解决方案,在此之前还需要回到大牛的链接,因为插件形式开发微信支付都会走同一个套路,大牛提供的是完美的,其中最核心的,我认为,就是微信的Activity类(WXPayEntryActivity.java)和你自己开发的插件类(PayPlugin.java这是我自己项目中的类,这个类中就可以写调起微信支付或者支付宝支付等等)之间如何通信的问题。
因为往往你的插件入口,也就是前端AngularJS调用插件方法是通过PayPlugin.java调用的,微信的官网中有这么一句话:
所以必须要有这么一个类,但是打开官网Demo中的这个类,我们看看都写了什么:
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler
@Override
public void onCreate(Bundle savedInstanceState)
@Override
protected void onNewIntent(Intent intent)
@Override
public void onReq(BaseReq req)
@Override
public void onResp(BaseResp resp)
这是一个原生的Activity类,没有配套支持的话,这就是一个门,进去了就出不来了,所以作为插件的形式,我们应该能让Activity和PayPlugin能够通信,这样由我们自己的Controller.js调用PayPlugin的方法然后调起微信支付,支付后,微信支持的Activity类得到了回调信息才能再传出来给回PayPlugin,PayPlugin再给回Controller.js然后展示到前端。
(我自己写的Controller.js:
$scope.pay=function(){
if ($scope.selectPay === 'alipay') {
//aliPay handler
} else if ($scope.selectPay === 'weixin') {
URLUtils.weixinPayInfo($stateParams.orderNo).then(function(res){
if(res && res.entity){
window.plugins.PayPlugin.weixin(res.entity, function(res){
$state.go('app.assess',{orderNo:$stateParams.orderNo},{location: "replace", reload: true});
}, function(res){
//handle error message
});
}
});
} else {
alert("no pay way");
}
};
) 供大家参考。
所以这里大牛用了一个小技巧:
首先,WXPayEntryActivity类中需要注册APPID,这点很关键,而且微信回传的信息基本就是状态码,比如BaseResp resp对象的errCode。我们需要根据这个状态码来往回扔信息,或者直接赤裸裸的就扔状态码,但是谁等在彼岸接我们扔的信息呢,PayPlugin中是有回调对象等在那的,
public boolean execute(String action, JSONArray args,final CallbackContext callbackContext)
这就可以平滑地过渡到我们自己APP前端调用微信支付的入口了,也就是回到了Controller.js。
其次,大牛的设计是
public CallbackContext currentCallbackContext;
public IWXAPI wxAPI;
public static PayPlugin instance = null;
都升级为成员变量,建立自己的实例对象,利用方法
@Override
protected void pluginInitialize() {
super.pluginInitialize();
instance = this;
initWXAPI();
}
针对wxAPI进行初始化,在WXPayEntityActivity中通过
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
PayPlugin.instance.getWxAPI().handleIntent(getIntent(), this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
PayPlugin.instance.getWxAPI().handleIntent(intent, this);
}
替代原来官网Demo中同样的两个方法:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.pay_result);
api = WXAPIFactory.createWXAPI(this, Constants.APP_ID);
api.handleIntent(getIntent(), this);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
api.handleIntent(intent, this);
}
其中这个方法handleIntent就是微信自己写的将处理的消息广播出去,IWXAPIEventHandler提供的接口方法onResp就负责接收广播出去的消息。
准备工作结束,我们来看“最后”;
最后,PayPlugin在调起微信支付后:
if (api.sendReq(req)) {
sendNoResultPluginResult(callbackContext);
} else {
callbackContext.error(ERROR_SEND_REQUEST_FAILED);
}
通过sendNoResultPluginResult(callbackContext); 打开了一个相当于关于回调对象的通道的东西,
private void sendNoResultPluginResult(CallbackContext callbackContext) {
// save current callback context
currentCallbackContext = callbackContext;
// send no result and keep callback
PluginResult result = new PluginResult(PluginResult.Status.NO_RESULT);
result.setKeepCallback(true);
callbackContext.sendPluginResult(result);
}
通过注释 // send no result and keep callback 我们可以知道回调数据持久的发送无结果的数据以便保持回调的通道。等待什么呢?等待WXPayEntryActivity发送回来承载着微信回调信息的我们自己的回调对象。
看WXPayEntryActivity类中的onResp(BaseResp resp)方法:
@Override
public void onResp(BaseResp resp) {
switch (resp.errCode) {
case BaseResp.ErrCode.ERR_OK:
switch (resp.getType()) {
case ConstantsAPI.COMMAND_PAY_BY_WX:
default:
PayPlugin.instance.getCurrentCallbackContext().success();
break;
}
break;
case BaseResp.ErrCode.ERR_USER_CANCEL:
PayPlugin.instance.getCurrentCallbackContext().error(PayPlugin.ERROR_WECHAT_RESPONSE_USER_CANCEL);
break;
case BaseResp.ErrCode.ERR_AUTH_DENIED:
PayPlugin.instance.getCurrentCallbackContext().error(PayPlugin.ERROR_WECHAT_RESPONSE_AUTH_DENIED);
break;
case BaseResp.ErrCode.ERR_SENT_FAILED:
PayPlugin.instance.getCurrentCallbackContext().error(PayPlugin.ERROR_WECHAT_RESPONSE_SENT_FAILED);
break;
case BaseResp.ErrCode.ERR_UNSUPPORT:
PayPlugin.instance.getCurrentCallbackContext().error(PayPlugin.ERROR_WECHAT_RESPONSE_UNSUPPORT);
break;
case BaseResp.ErrCode.ERR_COMM:
PayPlugin.instance.getCurrentCallbackContext().error(PayPlugin.ERROR_WECHAT_RESPONSE_COMMON);
break;
default:
PayPlugin.instance.getCurrentCallbackContext().error(PayPlugin.ERROR_WECHAT_RESPONSE_UNKNOWN);
break;
}
finish();
}
至此,两个类之间的通信就架接起来了。
所以我也要说到最后的解决方案了(能够看到这里的小伙伴都是真爱,赞一个)
理论上讲,微信要求的类我们有了,并且因为是以插件的形式,插件作为中间一层阻断了微信类直接的返回信息给APP端,但是这一层我们也打通了,流程是没有问题的了。
但是,微信这时候就说了一句模棱两可的话了,也就是上文图中带红框的那句话:net.sourceforge.simcpux.wxapi包路径中实现WXPayEntryActivity类(包名或类名不一致会造成无法回调)
包名或类名要一致,一致的含义可能就会成为歧义了,有人认为是就要是这个包路径net.sourceforge.simcpux.wxapi,WXPayEntryActivity类名一样,所以照着这个包路径配置了一个WXPayEntryActivity类,如Plugin.xml中:
<source-file src="src/android/WXPayEntryActivity.java" target-dir="src/net/sourceforge/simcpux/wxapi"/>
所以生成的项目中就会有一个net文件夹然后……一路到wxapi文件夹其中有个WXPayEntryActivity类。
启动项目,调起微信支付,付款成功(这只是漫长测试付出的众多一分钱中的一个一分钱而已),点击完成,APP无情地在展示了一个白板后重新进入首页,可见应用已经退出重进了一次。
你看看夕阳西下,决心在天黑前务必解决此问题。此时logcat会报错:
什么NoClassDefFoundError啦,$ionicLoading没有定义啦,也就是这个Activity加载不了或者找不到。
Java中的NoClassDefFound极有可能是编译后的路径下找不到这个类,所以加载不了,那么我们在插件中的路径呢?
很怀疑src/net/sourceforge/simcpux/wxapi暴露在最终打好包的路径下对外还是不是这个路径了,况且无论大牛还是官网中都将这个Activity配置成这样
android:name=".wxapi.WXPayEntryActivity"
那么项目在启动时势必会有地方认明这个“.”默认了哪个路径,如果是net.sourceforge.simcpux的话,那么就会找到这个类了,但是现在没找到,那么我也不记得是在哪会配置这个net.sourceforge.simcpux.wxapi,因为这个就是新加进来的啊。此时就是开发过Android客户端的熟手会意识到的问题了,在AndroidMainfest.xml中有个地方配置了包名
**package="com.xxx.xxx" **
大牛也在xml配置了包名,但是因为是开源的项目插件,只是公开插件这个部分,所以这里配置包名不是在全局项目的角度突出出来,所以大家会忽略掉,这个点没有被强调。
把这个类输出到这个路径下,并且修改类中的包路径:
<source-file src="src/android/WXPayEntryActivity.java" target-dir="src/com/xxx/xxx/wxapi"/>
package com.xxx.xxx.wxapi;
微信从外面就会call到这个类了,自然之前的流程就会水到渠成了。
问题解决。
看到这里的小伙伴,希望能够对你们有所帮助。