Anroid微信支付从统一下单到唤起支付

项目需要集成微信支付功能,老是返回-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<String, String> params = new TreeMap<String, String>();
        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<String,String> 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<String, String> 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<String, String> params = new TreeMap<String, String>();//一定要注意键名,去掉下划线,字母全是小写
                        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<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){
                            //实例化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<String, String> map, String sign) {
        StringBuffer sb = new StringBuffer();
        map.put("sign",sign);
        sb.append("<xml>");
        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("</xml>");
        return sb.toString();
    }

    /**
     * 签名获得sign字段
     * @param params
     * @return
     */
    private String getSign(SortedMap<String, String> 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<NetworkInterface> en=NetworkInterface.getNetworkInterfaces();
                    for (Enumeration<NetworkInterface> en = NetworkInterface.getNetworkInterfaces(); en.hasMoreElements(); ) {
                        NetworkInterface intf = en.nextElement();
                        for (Enumeration<InetAddress> 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);
    }

MD5加密类

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);

如果唤起微信返回baseResp.errCode == -1,

检查看看在获取到微信返回的xml后,是否再次加密sign了?

加密的时候键名是否去掉了下划线,因为返回的XML解析后的键名是带下划线的,键名是全小写字母。



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值