本文的写作目的是为了让开发者在集成微信支付时尽可能少的耗费时间,因为微信支付的官方文档的确粗糙的很让人头疼
文章后半部分有微信支付的详细集成方法,但还是要先提一下我在第一次集成时遇到的奇葩坑:
!!!第一处:
官方demo中AppRegister是下面这样的:
public class AppRegister extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { final IWXAPI api = WXAPIFactory.createWXAPI(context, null); api.registerApp(Constants.APP_ID); } }
为了不出错,请修改成下面这样:
public class AppRegister extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { final IWXAPI api = WXAPIFactory.createWXAPI(context, Constant.APP_ID,false); api.registerApp(Constants.APP_ID); } }app_ID传空时,此方法实现了去AndroidManifest.xml中找到已配置好的APP_ID,所以官方Demo直接传了空,
但是,从AndroidManifest.xml获取APP_ID并不是所有情况下都能成功,至少我这里遇到的情况是获取不到的,所以导致请求时APP_ID一直为空
因此,为了确保不被坑,请使用上面第二段代码,主动传入APP_ID;
!!!第二处:
在AndroidManifest.xml中配置所有跟微信支付回调相关的路径时:
我们平时自己创建的Activity之类的路径一般都是缺省了包名的,如下:
<receiver android:name=".wxapi.AppRegister"> <intent-filter> <action android:name="com.tencent.mm.plugin.openapi.Intent.ACTION_REFRESH_WXAPP"/> </intent-filter> </receiver>
还是这句话,为了不被坑,针对跟微信支付回调相关的请务必把包名补全,如下这样,不然微信回调时可能会找不到页面:
<receiver android:name="com.xykmrxx.xmr.wxapi.AppRegister"> <intent-filter> <action android:name="com.tencent.mm.plugin.openapi.Intent.ACTION_REFRESH_WXAPP"/> </intent-filter> </receiver>
下面是集成微信支付的方法:
第一步当然还是引入微信JAR包,该包可以从官方微信支付demo中获得,当然现在可以。
然后新建包名wxapi,在wxapi下新建回调页面WXPayEntryActivity,命名和存放路径要求不能错:
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler { private static final String TAG = "wx"; private IWXAPI api; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); api = WXAPIFactory.createWXAPI(this, Constant.APP_ID); api.handleIntent(getIntent(), this); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); api.handleIntent(intent, this); } @Override public void onReq(BaseReq req) {} @Override public void onResp(BaseResp resp) { //resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX时表示是微信支付返回 switch (resp.errCode){ case 0: Intent in=new Intent(); in.setClass(WXPayEntryActivity.this, PayFinishedActivity.class); startActivity(in); finish(); break; case -1: Log.d(TAG, "onResp: -1"); break; case -2: Log.d(TAG, "onResp: -2"); break; } }然后再新增AppRegister,这个存放路径没要求:
public class AppRegister extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { final IWXAPI msgApi = WXAPIFactory.createWXAPI(context, Constant.APP_ID,false); msgApi.registerApp(Constant.APP_ID); } }
然后在AndroidManifest.xml中配置:
<!-- 微信支付 --> <activity android:name=".wxapi.WXPayEntryActivity" android:exported="true" android:label="@string/app_name" android:launchMode="singleTop" android:screenOrientation="portrait"> <intent-filter> <action android:name="android.intent.action.VIEW"/> <category android:name="android.intent.category.DEFAULT"/> <data android:scheme="替换成你的APP_ID"/> <!-- appId --> </intent-filter> </activity> <receiver android:name="com.xykmrxx.xmr.wxapi.AppRegister"> <intent-filter> <action android:name="com.tencent.mm.plugin.openapi.Intent.ACTION_REFRESH_WXAPP"/> </intent-filter> </receiver>
最后在需要发起微信支付的页面调用:
IWXAPI msgApi;
msgApi = WXAPIFactory.createWXAPI(this, Constant.APP_ID,false);//paymentApp是我从服务器请求返回的json字符串,其中parameter字段中存放的是微信支付需要的相关参数
try { JSONObject jso = new JSONObject(paymentApp.getParameter()); PayReq request = new PayReq(); request.appId = jso.getString("appid"); request.nonceStr = jso.getString("noncestr"); request.packageValue = jso.getString("package"); request.partnerId = jso.getString("partnerid"); request.prepayId = jso.getString("prepayid"); request.timeStamp = jso.getString("timestamp"); request.sign = jso.getString("sign"); msgApi.registerApp(Constant.APP_ID); msgApi.sendReq(request);
集成代码就这些!
照着上面的步骤,只要集成过程不出错是肯定能成功的。
当然,如果你还是集成失败了,请继续往下看:
这里主要针对调不起微信支付页面的问题,调不起微信支付页面时,请先在WXPayEntryActivity中查看onResp(BaseRespmBaseResp)方法中的回调结果
switch (mBaseResp.errCode) { case 0: LogUtil.d(TAG, "onResp: 0"); ToastUtil.show(this,"成功"); // Intent in = new Intent(); // in.setClass(WXPayEntryActivity.this, PayFinishedActivity.class); // startActivity(in); finish(); break; case -1: LogUtil.d(TAG, "onResp: -1"); break; case -2://取消 LogUtil.d(TAG, "onResp: -2"); break; }如果errCode=-1,请先检查你的appId是否配置正确,同时检查你的应用签名是否与配置在微信支付平台的一致,应用签名只由包名和签名密钥决定,所以只要包名正确并且使用的是同一个签名密钥则应用签名一定是对的。如果appId与应用签名都是正确的,那问题就应该是出在发起支付时的签名参数,微信建议PayReq的参数全部由服务端配置生成,所以请先联系你的后端同学检查参数是否正确,尤其是这个签名参数 request.sign,建议自己在app自签测试一下,详细签名规则如下, 微信支付平台生成sign的签名规则:
1、从服务端获取签名要用到的参数,然后按字典序排列
SortedMap<String, String> map_param = new TreeMap<>(); map_param.put("appid", APP_ID); map_param.put("partnerid", partnerId); map_param.put("prepayid", prepayId); map_param.put("noncestr", nonceStr); map_param.put("timestamp", timeStamp); map_param.put("package", packageValue); String sign=createSign("utf-8", map_param);2、按微信支付平台的规则进行参数拼接并进行MD5加密,此处secretkey的获取请参阅 微信支付平台生成sign的签名规则
//定义签名,微信根据参数字段的ASCII码值进行排序 加密签名,故使用SortMap进行参数排序 public static String createSign(String characterEncoding, SortedMap<String, String> parameters) { StringBuffer sb = new StringBuffer(); for (Map.Entry entry : parameters.entrySet() ) { sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&"); } //最后加密时添加商户密钥,由于key值放在最后,所以不用添加到SortMap里面去,单独处理,编码方式采用UTF-8 sb.append("key="+ 配置在微信支付平台的secretKey); return MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); } //微信Md5加密工具 public static class MD5Util { private static String byteArrayToHexString(byte b[]) { StringBuffer resultSb = new StringBuffer(); for (int i = 0; i < b.length; i++) resultSb.append(byteToHexString(b[i])); return resultSb.toString(); } private static String byteToHexString(byte b) { int n = b; if (n < 0) n += 256; int d1 = n / 16; int d2 = n % 16; return hexDigits[d1] + hexDigits[d2]; } public static String MD5Encode(String origin, String charsetname) { String resultString = null; try { resultString = new String(origin); MessageDigest md = MessageDigest.getInstance("MD5"); if (charsetname == null || "".equals(charsetname)) resultString = byteArrayToHexString(md.digest(resultString .getBytes())); else resultString = byteArrayToHexString(md.digest(resultString .getBytes(charsetname))); } catch (Exception exception) { } return resultString; } private static final String hexDigits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"}; }然后用以上两步获取的sign去发起请求即可。
如果还不懂,可以参考:http://blog.csdn.net/androidstarjack/article/details/50951941
2017.8.15附加:
关于支付之后的回调方法
@Override public void onResp(BaseResp mBaseResp) { }此方法提供一个BaseResp对象
public abstract class BaseResp { public int errCode; public String errStr; public String transaction; public String openId;
再看一下PyaResp这个类
public class PayResp extends BaseResp { public String prepayId; public String returnKey; public String extData;
调试一下就会发现,返回的BaseResp实际就是一个PayResp对象,所以我们可以在支付时通过extData字段携带自定义数据然后在回调时取用,比如支付成功后获取订单号通知
后台app端已经成功支付