Android微信支付爬坑

 

我的视频课程:《FFmpeg打造Android万能音频播放器》

我的视频课程(编码直播推流):《Android视频编码和直播推流》

 

        最近在做支付模块,最常用的就是微信支付和支付宝支付,其中最坑的就是微信支付了!!各种问题,官方文档也写得不详细。。。哎 不过最后还是成功的爬坑完成集成了微信支付。先附上一张支付成功的页面高兴高兴 哈哈哈:

 

下面就是爬坑过程

微信支付分为以下几个步骤:

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

 

5、这样就成功的调到了支付界面 

 

这是刚开始解决中文乱码是,单独对中文转码后的结果,微信端没有转码,就成这样了。

这是body是英文的时候,能正常支付。

6、最后在微信回调页面处理支付结果:

 

public class WXPayEntryActivity extends Activity implements IWXAPIEventHandler{

	private IWXAPI api;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
//		setContentView(R.layout.pay_result);

		api = WXAPIFactory.createWXAPI(this, AppConfig.WX_APP_ID);

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

		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() {

					@Override
					public void onClick(DialogInterface dialog, int which) {
						// TODO Auto-generated method stub
						finish();
					}
				});
				builder.show();
			}
			else if(resp.errCode == -1)
			{
				CommonUtil.showToast("支付出错:" + resp.errStr);
				finish();
			}
			else if(resp.errCode == -2)
			{
				CommonUtil.showToast("取消支付");
				finish();
			}
		}
	}
}


这样微信支付爬坑结束,不容易啊。。。 哈哈哈

 

 

 

 

没有更多推荐了,返回首页