微信支付V3.3.7(jsapi支付)

时间:2022-10-25 11:43:14

     最近在做微信的过程中遇到不少问题,历经了不少坑,才发现网上的资料真的很少,大多都混乱零碎,而且做的时候遇到一些问题在两个微信群里问了,做完才发现他们的有些答案给了我陷阱。

     加上最近身边很多童鞋也在做微信支付,而我上周刚好做完,现在整理一下,记录下来,希望也能帮到大家,注意这篇文章只是对API文档的一个解读。

 

     首先还是先理清一下思路,做微信支付需要怎么入手,v3.3.7里面的demo只有php版本,里面是这样写的:

    /**
 * JS_API支付demo
 * ====================================================
 * 在微信浏览器里面打开H5网页中执行JS调起支付。接口输入输出数据格式为JSON。
 * 成功调起支付需要三个步骤:
 * 步骤1:网页授权获取用户openid
 * 步骤2:使用统一支付接口,获取prepay_id
 * 步骤3:使用jsapi调起支付
*/

  

        看到这里心里就有个大概的思路了:

        第一步应该不难,openid我们先跳过,关键在于获取prepay_id,有了prepay_id才能构建package,才能调用JSAPI进行下一步操作。

       看文档发现获取需要调用API里面的统一支付接口,它返回的是预支付订单号。

      微信支付V3.3.7(jsapi支付)

   

 

 

 

       首先列举一些必填的参数(其他非必填的我这里都忽略了),接口需要的请求参数为:

          预支付请求参数: appid,body,notify_url,out_trade_no,mch_id,total_fee,spbill_create_ip,trade_type,nonce_str,openid,sign

        具体的要求和说明参考文档这里面的关键部分就是构建签名

      微信支付V3.3.7(jsapi支付)

 

       这里很清晰了,之前我的签名一直不对,而且感觉微信支付API怎么这么模糊,到底传哪些参数都不知道。后来仔细看文档才发现,需要对所有传入参数进行加工生成签名。

       思路是:对预支付参数进行排序生成String1,在string1后面拼接上商户的支付密钥得到String2,然后对String2 进行MD5加密得到String3,将String3转换为大写得到sign。

  具体的方法和参数为:

 

 // 设置预支付参数
		SortedMap<String, String> signParams = new TreeMap<String, String>();
		signParams.put("appid", APP_ID);
		signParams.put("body", order.getInfo()); // 商品描述
		signParams.put("notify_url", NOTIFY_URL); // 通知地址
		signParams.put("out_trade_no", order.getOrderNo()); // 商户订单号
		signParams.put("mch_id", PARTNER); // 设置商户号
		signParams.put("total_fee", WxPayUtil.changeY2F(String.valueOf(order.getPaySum()))); // 商品总金额,以分为单位
		signParams.put("spbill_create_ip", this.getClientIp()); // 订单生成机器IP,指用户浏览器端IP
		signParams.put("trade_type", "JSAPI");
		signParams.put("nonce_str", noncestr);
		signParams.put("openid", openId);
                String sign = "";
		try {
			sign = WxPayHelper.createSign(signParams, PAY_KEY);
		} catch (Exception e) {
			e.printStackTrace();
		}
		// 增加非参与签名的额外参数
		signParams.put("sign", sign);
		String prePayId = WxPayHelper.getPrePayId(reqPrePayUrl, signParams);// 预支付订单号


 

 

               
这里需要注意的是:生成签名的sign并不需要参与签名,但是请求参数里面是必填的参数。           
/**
	 * 创建sign签名
	 * 
	 * @param packageParams
	 * @param paykey
	 * @return
	 */
	public static String createSign(SortedMap<String, String> packageParams, String paykey) {
		StringBuffer sb = new StringBuffer();
		Set es = packageParams.entrySet();
		Iterator it = es.iterator();
		while (it.hasNext()) {
			Map.Entry entry = (Map.Entry) it.next();
			String k = (String) entry.getKey();
			String v = (String) entry.getValue();
			if (null != v && !"".equals(v) && !"sign".equals(k) && !"key".equals(k)) {
				sb.append(k + "=" + v + "&");
			}
		}
		sb.append("key=" + paykey);
		String sign = MD5Util.MD5Encode(sb.toString(), "utf-8").toUpperCase();
		return sign;
	}


此时就生成了sign签名,这里需要注意的是:openid,mch_id,total_fee,商户支付密钥。会走弯路的地方是:

1.total_fee的单位是分,不能带有小数点;

2.商户支付密钥会迷惑我们,我一开始以为是PARTNER_KEY,然后发起预支付的时候报错,又仔细检查了参数和文档发现:这里要的商户支付密钥是要安装证书后自己设置的,不是PARTNER_KEY。

微信支付V3.3.7(jsapi支付)

剩下的就看请求的部分了,请求的时候我使用的是httpclient。

        拼接xml字符串感觉很麻烦,我这里是先构建一个对象,然后将对象转换为XML,调预支付接口返回应答的XML。如果成功后就能得到返回结果里的prepay_id了。具体问题看返回的结果,注意请求和接收的数据格式都要是XML格式的。

 

       然后就是调JSAPI 支付接口,这里我们看到了又需要一次签名得到paySign,方法和上面的一样,参数需要换掉,分别是:

appId,timeStamp,nonceStr,package,signType,paySign。同样的paySign不参与签名,具体的要求和说明参考文档注意,注意!

 

                // 设置package参数
		SortedMap<String, String> packageParams = new TreeMap<String, String>();
		packageParams.put("appId", APP_ID);
		packageParams.put("timeStamp", timestamp);
		packageParams.put("nonceStr", noncestr);
		packageParams.put("package", "prepay_id=" + prePayId);
		packageParams.put("signType", "MD5");

		String paySign = "";
		try {
			paySign = WxPayUtil.createSign(packageParams, PAY_KEY);
		} catch (Exception e) {
			e.printStackTrace();
		}
		packageParams.put("paySign", paySign);            
                Gson gs = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();// 特殊字符不被转义
		String jsonStr = gs.toJson(packageParams);

 

需要注意的是:package的格式要求是:prepay_id=***,我这里对应的是

packageParams.put("package", "prepay_id=" + prePayId);(文档里都有详细的说明)

       到这里就完成了JSAPI所需要的所有请求参数了,然后就得到了需要的JSON字符串,对应我这里的jsonStr。

最后就是调用WeixinJSBridge了。