Android微信支付爬坑
来源:互联网 发布:淘宝 蒋凡 编辑:程序博客网 时间:2024/05/16 09:19
最近在做支付模块,最常用的就是微信支付和支付宝支付,其中最坑的就是微信支付了!!各种问题,官方文档也写得不详细。。。哎 不过最后还是成功的爬坑完成集成了微信支付。先附上一张支付成功的页面高兴高兴 哈哈哈:
下面就是爬坑过程
微信支付分为以下几个步骤:
1、首先要在微信开放平台注册,添加自己的APP并成功申请支付功能
2、下载微信支付的SDK并添加到自己的项目里(以上都是最基本的,问题不大)
3、现在就可以着手集成微信支付了(从下单到支付):
(1):准备好需要的资料数据,并向微信注册当前APP
首先要在微信后台配置当前APP打包key所生成的签名(微信官网有签名工具),然后还必须设置商户的key(32位,商户自己设置的),然后就是APP_ID这个是微信为每一个APP生成的,最后就是开通了支付功能的商户的ID用户我们把钱支付给商家。
然后在适当的地方注册APP(oncreate中)
<span style="white-space:pre"></span>msgApi = WXAPIFactory.createWXAPI(context, null); msgApi.registerApp(AppConfig.WX_APP_ID);//wxappkey
(2):调用统一下单接口(https://api.mch.weixin.qq.com/pay/unifiedorder)生成订单,这一步是最容易出错的(此乃大坑)。
1、首先我们设计好所要传给微信的必要参数(OrderPayBean):
private String appid; //appid private String body; //商品描述 private String mch_id; //商户ID private String nonce_str; //随机字符串 private String notify_url; //微信通知后台支付结果url private String out_trade_no; //我们自己的订单号 private String spbill_create_ip; //客户端IP private int total_fee; //总的支付金额 private String trade_type; //因为是移动应用 所以是APP private String sign; //以上所有参数的MD5签名例如以下商品数据:
<span style="white-space:pre"></span>//微信支付 orderPaybean.setAppid(AppConfig.WX_APP_ID); orderPaybean.setBody("操蛋的微信支付"); orderPaybean.setMch_id(AppConfig.WX_MCH_ID); orderPaybean.setNonce_str(nonceStr); orderPaybean.setNotify_url("http://********/payNotify/wx.do"); orderPaybean.setTotal_fee(1);<span style="white-space:pre"></span>//wxPayBean.setTotal_fee(totlefee + ""); orderPaybean.setTrade_type("APP"); orderPaybean.setSpbill_create_ip("196.168.1.1");2、商品参数准备好了,接下来我们为之生成签名:
签名算法如下:
/** * 微信支付签名算法sign * @param characterEncoding 签名编码(UTF-8) * @param parameters 要签名的参数的集合 * @param key 商户自己设置的key */ @SuppressWarnings("unchecked") public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters, String key){ StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序) Iterator it = es.iterator(); while(it.hasNext()) { 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); System.out.println(sb.toString()); String sign = WxMd5.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); System.out.println(sign); return sign; }
构造商品参数集合:
<span style="white-space:pre"></span>SortedMap<Object, Object> parameters = new TreeMap<Object, Object>(); parameters.put("appid", orderPaybean.getAppid()); parameters.put("body", orderPaybean.getBody()); parameters.put("mch_id", orderPaybean.getMch_id()); parameters.put("nonce_str", orderPaybean.getNonce_str()); parameters.put("notify_url", orderPaybean.getNotify_url()); parameters.put("out_trade_no", orderPaybean.getOut_trade_no()); parameters.put("total_fee", orderPaybean.getTotal_fee()); parameters.put("trade_type", orderPaybean.getTrade_type()); parameters.put("spbill_create_ip", orderPaybean.getSpbill_create_ip()); parameters.put("sign", CommonUtil.createSign("UTF-8", parameters, AppConfig.WX_KEY));//传入签名好的参数值3、因为统一下单接口需要以xml格式post发送给微信,所以我们先拼接xml格式的参数:
<span style="white-space:pre"></span>StringBuilder xmlBuilder = new StringBuilder(); xmlBuilder.append("<xml>"); Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序) Iterator it = es.iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry) it.next(); String k = (String) entry.getKey(); Object v = entry.getValue(); xmlBuilder.append("<").append(k).append(">"); xmlBuilder.append(v); xmlBuilder.append("</").append(k).append(">"); } xmlBuilder.append("</xml>"); System.out.println(xmlBuilder.toString()); try { new GetPrepayId(new String(xmlBuilder.toString().getBytes(), "ISO8859-1")).execute();//这一步非常重要,不这样转换编码的话,传递中文就会报“签名错误”,这是很多人都会遇到的错误。 } catch (Exception e) { e.printStackTrace(); }然后是我们的异步线程请求统一下单接口:
<span style="white-space:pre"></span>public class GetPrepayId extends AsyncTask { String str; public GetPrepayId(String str) { this.str = str; } @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected Object doInBackground(Object[] params) { String url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; byte[] buf = Util.httpPost(url, str); String content = new String(buf); return content; } }这里从微信返回来的正确的结果为:xml格式的字符串,里面的“prepay_id”就是我们需要用在调取支付界面所要的重要参数。其中的Util.httpPost(url, str)方法可以在微信提供的demo中拷贝过来就行。(在这一步很多时候都返回的是“签名错误”,就要检查商户key是否正确,最常见的错误就是“body”字段是中文,然后post发送的时候没有转换为“iso8859-1”编码,导致签名错误。
4、通过统一下单接口成功获取到了“prepay_id”后,就可以调取支付接口了(如果是服务器生成订单,可以直接从这一步开始):
<span style="white-space:pre"></span>@Override protected Object doInBackground(Object[] params) { String url = "https://api.mch.weixin.qq.com/pay/unifiedorder"; byte[] buf = Util.httpPost(url, str); String content = new String(buf); Map<String, String> map = xmlToMap(content); String nonceStr = CommonUtil.genNonceStr(); String timeStamp = String.valueOf(CommonUtil.genTimeStamp()); SortedMap<Object, Object> parameters = new TreeMap<Object, Object>(); parameters.put("appid", orderPaybean.getAppid()); parameters.put("partnerid", orderPaybean.getMch_id()); parameters.put("prepayid", map.get("prepay_id")); parameters.put("package", "Sign=WXPay"); parameters.put("noncestr", nonceStr); parameters.put("timestamp", timeStamp); PayReq request = new PayReq(); request.appId = orderPaybean.getAppid(); request.partnerId = orderPaybean.getMch_id(); request.prepayId = map.get("prepay_id"); request.packageValue = "Sign=WXPay"; request.nonceStr = nonceStr; request.timeStamp = timeStamp; request.sign = CommonUtil.createSign("UTF-8", parameters, AppConfig.WX_KEY); msgApi.sendReq(request); return content; }
使用到的工具类方法:
<span style="font-size:14px;">/** * 微信支付签名算法sign * @param characterEncoding * @param parameters * @return */@SuppressWarnings("unchecked")public static String createSign(String characterEncoding, SortedMap<Object,Object> parameters, String key){ StringBuffer sb = new StringBuffer(); Set es = parameters.entrySet();//所有参与传参的参数按照accsii排序(升序) Iterator it = es.iterator(); while(it.hasNext()) { 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); System.out.println(sb.toString()); String sign = WxMd5.MD5Encode(sb.toString(), characterEncoding).toUpperCase(); System.out.println(sign); return sign;}/** * 获取时间戳 * @return */public static long genTimeStamp() { return System.currentTimeMillis() / 1000;}/** * 获得随机字符串 * @return */public static String genNonceStr() { Random random = new Random(); return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());}/** * 微信商户key */public static final String WX_KEY = "******************************";</span>
WxMd5.java
import java.security.MessageDigest;/** * Created by window10 on 2016/3/10. */public class WxMd5 { 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("utf-8"))); } 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" };}
其中:
这里用到了把xml转换为list的方法(用的是dom4j.jar):
<span style="white-space:pre"></span>public Map<String, String> xmlToMap(String xmlstr) { Map<String, String> map = new HashMap<>(); try { SAXReader reader = new SAXReader(); InputStream ins = new ByteArrayInputStream(xmlstr.getBytes("UTF-8")); Document doc = reader.read(ins); Element root = doc.getRootElement(); List<Element> list = root.elements(); for (Element e : list) { map.put(e.getName(), e.getText()); } ins.close(); } catch (Exception e) { e.printStackTrace(); } return map; }
这是刚开始解决中文乱码是,单独对中文转码后的结果,微信端没有转码,就成这样了。
6、最后在微信回调页面处理支付结果:
public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler{private IWXAPI api;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//setContentView(R.layout.pay_result);api = WXAPIFactory.createWXAPI(this, AppConfig.WX_APP_ID);api.handleIntent(getIntent(), this);}@Overrideprotected void onNewIntent(Intent intent) {super.onNewIntent(intent);setIntent(intent);api.handleIntent(intent, this);}@Overridepublic void onReq(BaseReq req) {}@Overridepublic void onResp(BaseResp resp) {System.out.println(resp.errCode);if (resp.getType() == ConstantsAPI.COMMAND_PAY_BY_WX) {if(resp.errCode == 0){AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setTitle("微信支付结果");builder.setMessage("支付订单成功!");builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {// TODO Auto-generated method stubfinish();}});builder.show();}else if(resp.errCode == -1){CommonUtil.showToast("支付出错:" + resp.errStr);finish();}else if(resp.errCode == -2){CommonUtil.showToast("取消支付");finish();}}}}
这样微信支付爬坑结束,不容易啊。。。 哈哈哈
- Android微信支付爬坑
- Android微信支付爬坑【转】
- Android微信支付爬坑
- android 微信支付爬坑
- android微信支付
- android微信支付
- Android 微信支付
- android微信支付
- android微信支付
- android微信支付
- Android 微信支付
- Android 微信支付
- android微信支付
- android 微信支付
- Android 微信支付
- android微信支付
- Android微信支付
- Android微信支付
- APNS推送服务证书制作 图文详解教程
- Powerdesigner反向工程从Mysql数据库导出数据结构的ODBC配置
- Kubernets
- TexturePacker打包后的文件在cocos2d-x加载和使用
- ACM内部函数--数学问题--大数相加
- Android微信支付爬坑
- 8.1.1 Android中的13种Drawable小结 Part 1
- cocos2d-x手游性能优化总结
- [pat]1104. Sum of Number Segments (20)
- ImageLoader硬盘缓存解析
- a simple email test in python
- 网速和带宽的区别
- 如何写出程序员无法维护的代码?
- 11个强大的Visual Studio调试小技巧