项目需要集成微信支付功能,老是返回-1,反反复复看文档,还有一条条看官方demo代码,看了三天看到吐血。
我觉得可以把微信写官方文档的人拉出去杀了祭天,官方文档都那么坑。
所以分享一下,给各位免得踩一样的坑。
下面是下单和唤起的方法,一定要看仔细了。
//微信支付
private void wxPay() {
String appid = PublicStaticUtil.WX_APPID;//app在微信申请的appid
String mch_id = "1400000000";//申请支付功能的商户号
String nonce_str = getRandomString();//随机码
String body = "test";//商品描述,随便写
String out_trade_no = orderInfo.getOrderNo();//订单编号
int total_fee = orderInfo.getPrice();//总金额
String time_start = getCurrentTime();//时间戳,格式yyyyMMddHHmmss
String trade_type = "APP";//交易类型
String notify_url = "http://xx.xx.xx/";//通知回调地址,然后后台做个回调,无参的,必须放到商户平台上配置添加
String spbill_create_ip = getIPAddress(PayOrderActivity.this);//设备ip
SortedMap params = new TreeMap();
params.put("appid",appid);
params.put("mch_id",mch_id);
params.put("nonce_str",nonce_str);
params.put("body",body);
params.put("out_trade_no",out_trade_no);
params.put("total_fee",total_fee+"");
params.put("time_start",time_start);
params.put("trade_type",trade_type);
params.put("notify_url",notify_url);
params.put("spbill_create_ip",spbill_create_ip);
//获取签名sign,文档有些参数是必须传的,但是少了还是能请求成功xml
String sign = getSign(params);
//参数xml化
String xmlParams = parseString2Xml(params,sign);
LogUtil.e("------下单xml化---->"+"\n"+xmlParams);
String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";//下单统一接口
//下单请求
Map map = new HashMap<>();
map.put("XML",xmlParams);
OkHttpManager.getInstance().postAsync(url, map, new OkHttpManager.DataCallBack() {//我用okhttp3请求的post,这个不重要,其他的也行
@Override
public void requestFailure(Request request, Exception e) {
Toast.makeText(PayOrderActivity.this, "网络连接失败!", Toast.LENGTH_SHORT).show();
}
@Override
public void requestSuccess(Object result) throws Exception {
String xmlStr = ((String) result);
LogUtil.e("----下单返回--->"+"\n"+xmlStr);
Map map = decodeXml(xmlStr);
Set set = map.entrySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
Map.Entry entry = (Map.Entry) iterator.next();
LogUtil.e("-----entry.getKey()/entry.getValue()---->"+entry.getKey()+"/"+entry.getValue());
}
if (map.get("return_code").equalsIgnoreCase("SUCCESS")) {
if (map.get("return_msg").equalsIgnoreCase("OK")) {
PayReq req = new PayReq();
req.appId = PublicStaticUtil.WX_APPID;//APPID
req.partnerId = "1400000000";//商户号
req.prepayId = map.get("prepay_id");
req.nonceStr = map.get("nonce_str");
String time = System.currentTimeMillis() / 1000 + "";
req.timeStamp = time;//时间戳,这次是截取long类型时间的前10位
req.packageValue = "Sign=WXPay";//参数是固定的,写死的
SortedMap params = new TreeMap();//一定要注意键名,去掉下划线,字母全是小写
params.put("appid", PublicStaticUtil.WX_APPID);
params.put("partnerid","1493732082");
params.put("prepayid",map.get("prepay_id"));
params.put("noncestr",map.get("nonce_str"));
params.put("timestamp",time);
params.put("package","Sign=WXPay");
req.sign = getSign(params);//重新存除了sign字段之外,再次签名,要不然唤起微信支付会返回-1,特别坑爹的是,键名一定要去掉下划线,不然返回-1
LogUtil.e("====================================================");
LogUtil.e("-----appid---"+req.appId);
LogUtil.e("-----partnerId---"+req.partnerId);
LogUtil.e("-----prepayId---"+req.prepayId);
LogUtil.e("-----nonceStr---"+req.nonceStr);
LogUtil.e("-----timeStamp---"+req.timeStamp);
LogUtil.e("-----packageValue---"+req.packageValue);
LogUtil.e("-----sign---"+req.sign);
LogUtil.e("====================================================");
YDZApplication.getWXAPI().sendReq(req);//因为我已经在application注册了微信,全局调用
} else {
Toast.makeText(PayOrderActivity.this, "签名失败", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(PayOrderActivity.this, "交易失败", Toast.LENGTH_SHORT).show();
}
}
});
}
/**
* 解析返回的XML为键值对
* @param content
* @return
*/
public Map decodeXml(String content) {
try {
Map xml = new HashMap();
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){
//实例化student对象
xml.put(nodeName,parser.nextText());
}
break;
case XmlPullParser.END_TAG:
break;
}
event = parser.next();
}
return xml;
} catch (Exception e) {
LogUtil.e(e.toString());
}
return null;
}
/***
* 将键值对xml化
* @param map
* @param sign
* @return
*/
private String parseString2Xml(SortedMap map, String sign) {
StringBuffer sb = new StringBuffer();
map.put("sign",sign);
sb.append("");
Set es = map.entrySet();
Iterator iterator = es.iterator();
while(iterator.hasNext()){
Map.Entry entry = (Map.Entry)iterator.next();
String k = (String)entry.getKey();
String v = (String)entry.getValue();
sb.append("<"+k+">"+v+""+k+">");
}
sb.append(" ");
return sb.toString();
}
/**
* 签名获得sign字段
* @param params
* @return
*/
private String getSign(SortedMap params) {
StringBuffer sb = new StringBuffer();
Set es = params.entrySet();
Iterator iterator = es.iterator();
while (iterator.hasNext()){
Map.Entry entry = (Map.Entry) iterator.next();
String key = (String) entry.getKey();
String value = (String) entry.getValue();
if (null != value && !TextUtils.isEmpty(value) && !key.equals("key")){
sb.append(key + "=" + value + "&");
}
}
sb.append("key="+"aaAAbbBBccCCddDDeeEEffFFggGGhhHH");//商品平台API密钥,32位的字母数字,找申请支付功能的人要,就在商户平台那里
LogUtil.e("-------------------------------sign-------------->"+"\n"+sb.toString());
String packageSign = MD5.getMessageDigest(sb.toString().getBytes()).toUpperCase();
return packageSign;
}
/**
* MD5加密
* @return
*/
private String getRandomString() {
Random random = new Random();
return MD5.getMessageDigest(String.valueOf(random.nextInt(10000)).getBytes());
}
/**获取设备ip*/
public String getIPAddress(Context context) {
NetworkInfo info = ((ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
if (info != null && info.isConnected()) {
if (info.getType() == ConnectivityManager.TYPE_MOBILE) {//当前使用2G/3G/4G网络
try {
//Enumeration en=NetworkInterface.getNetworkInterfaces();
for (Enumeration en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
NetworkInterface intf = en.nextElement();
for (Enumeration enumIpAddr = intf.getInetAddresses(); enumIpAddr.hasMoreElements(); ) {
InetAddress inetAddress = enumIpAddr.nextElement();
if (!inetAddress.isLoopbackAddress() && inetAddress instanceof Inet4Address) {
return inetAddress.getHostAddress();
}
}
}
} catch (SocketException e) {
e.printStackTrace();
}
} else if (info.getType() == ConnectivityManager.TYPE_WIFI) {//当前使用无线网络
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
String ipAddress = intIP2StringIP(wifiInfo.getIpAddress());//得到IPV4地址
return ipAddress;
}
} else {
//当前无网络连接,请在设置中打开网络
}
return null;
}
/**
* 将得到的int类型的IP转换为String类型
*
* @param ip
* @return
*/
public String intIP2StringIP(int ip) {
return (ip & 0xFF) + "." +
((ip >> 8) & 0xFF) + "." +
((ip >> 16) & 0xFF) + "." +
(ip >> 24 & 0xFF);
}
import java.security.MessageDigest;
public class MD5 {
private MD5() {}
public final static String getMessageDigest(byte[] buffer) {
char hexDigits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
try {
MessageDigest mdTemp = MessageDigest.getInstance("MD5");
mdTemp.update(buffer);
byte[] md = mdTemp.digest();
int j = md.length;
char str[] = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
str[k++] = hexDigits[byte0 >>> 4 & 0xf];
str[k++] = hexDigits[byte0 & 0xf];
}
return new String(str);
} catch (Exception e) {
return null;
}
}
}
import java.security.MessageDigest;
public 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"};
}
建议统一下单接口,解析微信返回的xml,还有再次签名sign都要放到后台完成,这一段,后台也能参照我这篇对比一下错在哪,
最好app终端只负责唤起微信支付就好了,只做下面的事情
PayReq req = new PayReq();
req.appId = xx;//公司后台传
req.partnerId = xx;//公司后台传
req.prepayId = xx;//公司后台传
req.nonceStr = xx;//公司后台传
req.timeStamp = xx;//公司后台传
req.packageValue = xx;//公司后台传
req.sign = xx;//公司后台传
YDApplication.getWXAPI().sendReq(req);
检查看看在获取到微信返回的xml后,是否再次加密sign了?
加密的时候键名是否去掉了下划线,因为返回的XML解析后的键名是带下划线的,键名是全小写字母。