前几天做了一个项目,有关于微信支付的一项,在网上找到的很多还是老的,走了很多弯路,经过三天的奋斗才做出来,下面就讲讲我在做微信支付时遇到的问题
在做微信支付前,必须准备的几个有关微信支付的参数:
1. APP_ID 就是你在微信公众平台申请后得到的
2. MCH_ID 在商户平台注册得到的商户号
3.API_KEY api key这个微信官方API没有说明,就是一个坑人的货,这么重要的官方API也不说一下,就为这个让我搞了一个上午,具体就在 在商户平台 账户设置》API安全 哪里 设置,记住,新注册的用户必须重新设置一遍,不然在获取prepay_id时会报签名错误;
当这三个参数得到后,就可以执行微信支付了,下面我的的项目的一些代码
/*
* 生成随机字符串
*/
Map<String,String> resultunifiedorder;
private String genNonceStr() {
Random random = new Random();
return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
//生成时间戳
private long genTimeStamp() {
return System.currentTimeMillis() / 1000;
}
/*
* 生成预定单参数
*/
private String genProductArgs() {
String nonceStr = genNonceStr().toUpperCase();
SortedMap<Object,Object> parameters = new TreeMap<Object,Object>();
parameters.put("appid", Constants.APP_ID);
parameters.put("mch_id", Constants.MCH_ID);
parameters.put("device_info", "APP-001");
parameters.put("body", getShopname());
parameters.put("nonce_str", nonceStr);
parameters.put("notify_url", "http://www.ruweiwang.com.cn/Shop/weixinNotify.action");
parameters.put("out_trade_no",mQueRenDingDanJieMian.getOrderList().get(0).getOrderId());
parameters.put("spbill_create_ip","120.24.250.188");
parameters.put("total_fee", "1");
parameters.put("trade_type", "APP");
parameters.put("attach", getattach());
String characterEncoding = "UTF-8";
String mySign = createSign(characterEncoding,parameters);
String xml="<xml>"
+"<appid>"+Constants.APP_ID+"</appid>"
+"<mch_id>"+Constants.MCH_ID+"</mch_id>"
+"<device_info>"+"APP-001"+"</device_info>"
+"<body>"+getShopname()+"</body>"
+"<nonce_str>"+nonceStr+"</nonce_str>"
+"<notify_url>"+"http://www.ruweiwang.com.cn/Shop/weixinNotify.action"+"</notify_url>"
+"<out_trade_no>"+mQueRenDingDanJieMian.getOrderList().get(0).getOrderId()+"</out_trade_no>"
+"<spbill_create_ip>"+"120.24.250.188"+"</spbill_create_ip>"
+"<total_fee>"+1+"</total_fee>"
+"<trade_type>"+"APP"+"</trade_type>"
+"<attach>"+getattach()+"</attach>"
+"<sign>"+mySign+"</sign>"
+"</xml>";
return xml;
}
//上面生产第一次签名时先进行字体编码,因为我在网上看到有一些人提到订单名是中文会出现签名错误
//生产XML文件在官方的demo有一个方法,可以不按照我的方法写,各个参数代表什么在官方上有
//<attach>"+getattach()+"</attach>"这句话可以不加,因为是我做项目时需要的
/*
* 微信支付签名算法sign
* @param characterEncoding
* @param parameters
* @return
*/
//主要是对sign的第一次签名,从网上直接复制过来的
@SuppressWarnings("rawtypes")
public static String createSign(String characterEncoding,SortedMap<Object,Object> parameters){
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=" +Constants.API_KEY);
//MD5Util这个文件也是从官方demo里面直接复制过来
String sign = MD5Util.MD5Encode(sb.toString(), characterEncoding).toUpperCase();
return sign;
}
//当上面的参数准备好后可以先得到微信支付的prepay_id 预支付ID,在发起支付时要用到
/*
* 得到预支付ID
* post形式
*/
private class GetPrepayIdTask extends AsyncTask<Void, Void, Map<String,String>> {
private ProgressDialog dialog;
@Override
protected void onPreExecute() {
dialog = ProgressDialog.show(QueRenDingDanActivity.this, getString(R.string.app_tip),getString(R.string.getting_prepayid));
}
@Override
protected void onPostExecute(Map<String,String> result) {
if (dialog != null) {
dialog.dismiss();
}
sb.append("prepay_id\n"+result.get("prepay_id")+"\n\n");
resultunifiedorder=result;
}
@Override
protected void onCancelled() {
super.onCancelled();
}
@Override
protected Map<String,String> doInBackground(Void... params) {
String url = String.format("https://api.mch.weixin.qq.com/pay/unifiedorder");
String entity = genProductArgs();
byte[] buf = null;
try {
buf = Util.httpPost(url, new String(entity.getBytes(),"ISO8859-1"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
String content = new String(buf);
Map<String,String> xml=decodeXml(content);
return xml;
}
}
//上面是联网请求的方法,具体调用可以在自己要调用的位置加上下面的代码
GetPrepayIdTask getPrepayId = new GetPrepayIdTask();
getPrepayId.execute();
//上面的发送中加了一句buf = Util.httpPost(url, new String(entity.getBytes(),"ISO8859-1")); 就是发送的时候要再进行一次编码,就为这句话我整了两天
如果有跟我一样获取预支付ID老是出现签名错误,而各个参数又检查无误,不妨加上这句试试,具体为什么,网上的解释是给自己当前的开发环境的
编码有关
//得到预支付ID后,可以准备支付的参数了
/*
* 生成支付参数
*/
private void genPayReq() {
String nonceStr = genNonceStr().toUpperCase();
String timeStamp=String.valueOf(genTimeStamp());
if(resultunifiedorder.get("prepay_id")!=null){
req.appId = Constants.APP_ID;
req.partnerId = Constants.MCH_ID;
req.prepayId = resultunifiedorder.get("prepay_id");
req.packageValue ="Sign=WXPay";
req.nonceStr =nonceStr;
req.timeStamp =timeStamp;
List<NameValuePair> signParams = new LinkedList<NameValuePair>();
signParams.add(new BasicNameValuePair("appid", req.appId));
signParams.add(new BasicNameValuePair("noncestr", req.nonceStr));
signParams.add(new BasicNameValuePair("package", req.packageValue));
signParams.add(new BasicNameValuePair("partnerid", req.partnerId));
signParams.add(new BasicNameValuePair("prepayid", req.prepayId));
signParams.add(new BasicNameValuePair("timestamp", req.timeStamp));
req.sign = genAppSign(signParams);
sb.append("sign\n"+req.sign+"\n\n");
}
}
//参数生产后就可以发起支付,至于后续的内容看官方的API
//调起支付
private void sendPayReq() {
genPayReq();
final IWXAPI msgApi = WXAPIFactory.createWXAPI(context,Constants.APP_ID, false);
msgApi.registerApp(Constants.APP_ID);
msgApi.sendReq(req);
}
//如果想一步完成可以将sendPayReq()放到得到预支付ID的方法中
//加在这一步既可以了
@Override
protected void onPostExecute(Map<String,String> result) {
if (dialog != null) {
dialog.dismiss();
}
sb.append("prepay_id\n"+result.get("prepay_id")+"\n\n");
resultunifiedorder=result;
sendPayReq();
}
/*
* 第二步签名生成
*/
private String genAppSign(List<NameValuePair> params) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < params.size(); i++) {
sb.append(params.get(i).getName());
sb.append('=');
sb.append(params.get(i).getValue());
sb.append('&');
}
sb.append("key=");
sb.append(Constants.API_KEY);
this.sb.append("sign str\n"+sb.toString()+"\n\n");
String appSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
return appSign;
}
//上面的MD5文件在下载的微信官方demo里面直接复制过来
/*
* 解析XML格式,主要是用于解析从微信官方返回来的数据
*/
public Map<String,String> decodeXml(String content) {
try {
Map<String, String> xml = new HashMap<String, String>();
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new StringReader(content));
int event = parser.getEventType();
while (event != XmlPullParser.END_DOCUMENT) {
String nodeName=parser.getName();
switch (event) {
case XmlPullParser.START_DOCUMENT:
break;
case XmlPullParser.START_TAG:
if("xml".equals(nodeName)==false){
xml.put(nodeName,parser.nextText());
}
break;
case XmlPullParser.END_TAG:
break;
}
event = parser.next();
}
return xml;
} catch (Exception e) {
}
return null;
}
//当以上完成后调不起支付界面可以检查以下几个方面
1.看一下你的WXPayEntryActivity类包名和类名是否一样
2.把你的项目打包成apk再安装到手机上,微信支付必须要到真机上测试,而且还要安装微信
3.以上两步还没有出现,就先把你的APK卸载掉,把微信也卸载掉,关机在重启,然后安装微信,安装项目APK,在调试
4.如果这样还没有出现,只能自己去找答案了,因为我到这一步就行了