之前写过一篇微信支付的官方demo篇,点击查看详情。现在支付模块已经完成了,所以应该把正式项目中的问题和源码和记录下,以备以后参考。
安卓端的app支付流程官方讲的比较详细了,所以开发的时候一定要仔细阅读,按照步骤开发,碰到问题基本百度都能搞定。点击这里查看官方app开发文档
Android开发要点说明
1、后台设置
商户在微信开放平台申请开发应用后,微信开放平台会生成APP的唯一标识APPID。由于需要保证支付安全,需要在开放平台绑定商户应用包名和应用签名,设置好后才能正常发起支付。设置界面在【开放平台】中的栏目【管理中心 / 修改应用 / 修改开发信息】里面,如图8.8红框内所示。
应用包名:是在APP项目配置文件AndroidManifest.xml中声明的package值,例如DEMO中的package="net.sourceforge.simcpux"。(这里强调一下,应用包名是用来支付结果回调的,所以要配置清单文件中的包名,否则支付后不会调起回调!)
应用签名:根据项目的应用包名和编译使用的keystore,可由签名工具生成一个32位的md5串,在调试的手机上安装签名工具后,运行可生成应用签名串,如图8.9所示,绿色串即应用签名。签名工具下载地址https://open.weixin.qq.com/zh_CN/htmledition/res/dev/download/sdk/Gen_Signature_Android.apk(应用签名是为了保证传输的安全性)
图8.9
2、注册APPID
商户APP工程中引入微信JAR包,调用API前,需要先向微信注册您的APPID,代码如下:
private IWXAPI api= WXAPIFactory.createWXAPI(this, null);
将该app注册到本地微信
api.registerApp("这里添你申请的appid");
3、调起支付
商户服务器生成支付订单,先调用统一下单API(点这里,这个接口由后台人员调用)生成预付单,获取到prepay_id后封装数据(点击查看需要的参数列表)调其本地微信APP发起支付。app端请求服务器获取预支付订单号,其他信息可以在app端生成。(比如签名key可以保存在app端,但是不太安全)
以下是调起微信支付的关键代码:
<span style="white-space:pre"> </span>/** * 本地根据key生成签名sign */ String randomString = getRandomStringByLength(12);//生成唯一字随机字符串 String time = System.currentTimeMillis()+"";//生成时间戳 String pre_id = json.getString("prepay_id");//从返回数据获取订单号 SortedMap<Object,Object> parameters = new TreeMap<Object,Object>(); parameters.put("appid", "你的appid"); parameters.put("partnerid", "你的商户id"); parameters.put("prepayid", pre_id); parameters.put("package", "Sign=WXPay"); parameters.put("noncestr", randomString); parameters.put("timestamp",time ); String signed = createSign("UTF_8", parameters); /** * 封装好请求参数 */ PayReq req = new PayReq(); req.appId = "你的appid"; req.partnerId = "你的商户id"; req.prepayId = pre_id; req.nonceStr = randomString; req.timeStamp = time; req.packageValue = "Sign=WXPay"; req.sign =signed; LogUtil.d("pre_id",pre_id.toString()); // 在支付之前,如果应用没有注册到微信,应该先调用IWXMsg.registerApp将应用注册到微信 api.sendReq(req);请求后台获取预支付订单号以后,封装数据加签名,然后调起微信支付。
生成随机字符串的方法:(直接用)
/** * 生成随机字符串 * @param length * @return */ public static String getRandomStringByLength(int length) { String base = "abcdefghijklmnopqrstuvwxyz0123456789"; Random random = new Random(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < length; i++) { int number = random.nextInt(base.length()); sb.append(base.charAt(number)); } return sb.toString(); }签名方法:(直接用)
private static String Key = "输入你设置的key";//<span style="color: rgb(34, 34, 34); font-family: 'Helvetica Neue', 'Hiragino Sans GB', 'Microsoft YaHei', 黑体, Arial, sans-serif; font-size: 14px; line-height: 22.4px;">key设置路径:微信商户平台(pay.weixin.qq.com)-->账户设置-->API安全-->密钥设置</span> /** * 微信支付签名算法sign * @param characterEncoding * @param parameters * @return */ @SuppressWarnings("unchecked") public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){ StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet();//1.所有参与传参的参数按照accsii排序(升序) Iterator it = es.iterator(); while(it.hasNext()) {//2.生成字符串 Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); Object v = entry.getValue(); if(null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) { sb.append(k + "=" + v + "&"); } } sb.append("key=" + Key);//3.把key拼接进去 String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();//md5加密 return sign; }附上官方的签名算法解释: 点这里
4、支付结果回调
参照微信SDK Sample,在xxx.xxx.xxx.wxapi包路径中实现WXPayEntryActivity类(注意这里的xxx路径要和清单文件中的报名路径一致。)
比如我的路径是这样的
在清单文件中注册的包名必须是
如果这2个路径不一致,无法调起回调方法。
在WXPayEntryActivity类中实现onResp函数,支付完成后,微信APP会返回到商户APP并回调onResp函数,开发者需要在该函数中接收通知,判断返回错误码,如果支付成功则去后台查询支付结果再展示用户实际支付结果。注意一定不能以客户端返回作为用户支付的结果,应以服务器端的接收的支付通知或查询API返回的结果为准。代码示例如下:(代码可以直接用)
package com.dudu.helper3.wxapi; import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.TextView; import com.dudu.helper3.BaseActivity; import com.dudu.helper3.R; import com.dudu.helper3.Utils.LogUtil; import com.tencent.mm.sdk.modelbase.BaseReq; import com.tencent.mm.sdk.modelbase.BaseResp; import com.tencent.mm.sdk.openapi.IWXAPI; import com.tencent.mm.sdk.openapi.IWXAPIEventHandler; import com.tencent.mm.sdk.openapi.WXAPIFactory; public class WXPayEntryActivity extends BaseActivity implements IWXAPIEventHandler { private IWXAPI api; private TextView tv_res; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); LogUtil.d("jieguo","ol"); setContentView(R.layout.activity_pay_result); tv_res = (TextView) findViewById(R.id.tv_res); Button btn_back = (Button) findViewById(R.id.btn_back); btn_back.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { finish(); } }); initHeadView("支付结果",true,false,0); api = WXAPIFactory.createWXAPI(this, "wxa43dd59c979e6ab7"); 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) { int errCode = resp.errCode; LogUtil.d("resp",resp.errCode+""); if (errCode == 0) { // 0成功 展示成功页面 tv_res.setText("支付成功"); } else if (errCode == -1) { //-1 错误 可能的原因:签名错误、未注册APPID、项目设置APPID不正确、注册的APPID与设置的不匹配、其他异常等。 tv_res.setText("支付失败:"+resp.errStr); finish(); } else if (errCode == -2) { //-2 用户取消 无需处理。发生场景:用户不支付了,点击取消,返回APP。 finish(); } } }
PS:补充一个问题,上线以后反应客户不能正常调起支付,原因是应用签名当时提交的debug版本的,而正式版的keystore是不一样的,所以不能正常调起