微信小程序支付

Today now,微信小程序的支付顺利完成。
不要慌,先说几个我在做支付之前解决的问题:
1、我们公司有自己的App,App可以微信登录,所以做小程序的要求是和App的用户打通。简而言之,App的手机号绑定了一个微信号,如果小程序用该微信号授权,可以直接拿到他在App绑定的手机号。这就会用到UnionID来区分用户的唯一性;
在这里插入图片描述
具体的调用方法就不多说了,大家看微信的官方连接就可以
接下来

支付start

微信小程序的支付分为四步:
一、获取openId
二、统一下单(服务端先调用一下微信的后台,获取到一个最重要的值 “prepay_id”)
三、拿到服务端返回的六个参数掉起微信并支付(5个参数+sign)
四、支付回调

第一步:获取openId

微信调用openId的官方文档
注:因为openId是用来区分用户的唯一性的,不会轻易改变,所以我把它存到了数据库,不用每次都调用微信接口

第二步:统一下单

统一下单官方文档
把所有必填的参数都填上(非必填的根据自己的需要填写,这里有个注意的地方就是openId是必填的)
在这里插入图片描述
代码如下:

	public Map<String, Object> preOrder(Order order) {
		
		Map<String, String> params = new HashMap<String, String>();
		params.put("appid", "小程序的appID");
		params.put("mch_id", "商户号");
		params.put("nonce_str", WXPayUtil.generateNonceStr());	// 随机字符串
		params.put("body", "yly-"+StringUtils.subString(order.getGoodsName(), 0, 20));//商品描述
		params.put("out_trade_no", order.getPayRequestId()); //商户订单号
		//微信的金额要转成分
		params.put("total_fee", String.valueOf(AmountUtils.mul2Int(Double.valueOf(order.getTrxAmount()), 100,0))); 
		params.put("spbill_create_ip", order.getRequestIP());	//终端IP
		//回调地址
		params.put("notify_url", "回调地址");
		//交易类型,小程序取值如下:JSAPI
		params.put("trade_type", "JSAPI");
		//openID,必传
		params.put("openid", order.getOpenId());
		
		// 过滤参数值为空的参数
		Map<String, String> sPara = WXPayCore.paraFilter(params);
		//把数组所有元素,按照“参数=参数值”的模式用“&”字符拼接成字符串  并且拼接上key
		//String mySign = WXPayCore.sha256Sign(sPara,WXPayConstants.SECRET_KEY);
		String mySign = WXPayCore.md5Sign(sPara,WXPayConstants.MERCHANT_NO_SECRET);
		//签名
		params.put("sign", mySign);
		
		try {
			String paramsXml = WXPayUtil.mapToXml(params);
			LOG.info("paramsXml is{"+paramsXml+"}");
			String result = HttpUtils.post(WXPayConstants.UNIFIED_ORDER_URL, paramsXml,Constants.CHARSET_UTF8);
			LOG.info("result is{"+result+"}");
			Map<String, Object> resultMap = XmlUtils.Dom2Map(result);
			LOG.info("resultMap is{"+resultMap+"}");
			return resultMap;
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		return null;
	}

用到的工具类放到最后
按照微信要求的方式一步一步来,我遇到一个坑,那就是接口的调用结果报签名错误
首先检查自己的参数是否传错或者有误,可以用微信官方给的校验工具进行校验,
微信官方签名效验
如果检验不通过,那就检查自己的参数。但是呢,我调用了官方的效验工具,效验是通过的,一模一样,当时的我一脸懵逼,微信bug???
经过一系列排查,才发现是我的秘钥错了,我之前就是用的小程序的appSecret而不是商户的api秘钥,
这里的秘钥必须和上面商户号是匹配的
,否则会一直报错,换成商户秘钥,立马能够支付成功!!!

第三步:返给前端5个参数+sign,调起支付

小程序调起支付Api官方文档
代码如下:`public Map<String, String> pay(Order order) {
Map<String, String> params = new HashMap<String, String>();
//小程序ID
params.put(“appId”, “小程序的appID”);
//时间戳
params.put(“timeStamp”,String.valueOf(WXPayUtil.getCurrentTimestamp()));
//随机字符串
params.put(“nonceStr”, WXPayUtil.generateNonceStr());
//数据包
params.put(“package”, “prepay_id=”+“统一下单返回的prepay_id”);
//签名方式
params.put(“signType”, WXPayConstants.MD5);

	Map<String, String> sPara = WXPayCore.paraFilter(params);
	
	//String mySign = WXPayCore.sha256Sign(sPara,WXPayConstants.SECRET_KEY);
	String paySign = WXPayCore.md5Sign(sPara,"商户号的秘钥");
	
	params.put("paySign", paySign);
	
	LOG.info("返给前端调起微信支付的参数为:{"+params+"}");
	return params;
}`

调起支付需要的参数如下图:在这里插入图片描述
*这个有个问题需要注意一下:
调起支付是不需要appId这个参数的,但是签名的时候这个参数是必填的
而且统一下单的时候也需要这个参数,是小写的"appid";
这次是驼峰的大写"appId";
这个一定要注意,要不然会报"支付验证签名失败"

第四步:支付回调

支付回调官方文档
回调没什么可说的,就是修改订单状态以及各种逻辑处理,根据自己的实际业务来写回调,但是需要注意以下两点:
在这里插入图片描述
下面是用到的工具类:

	/**
	 * 获取随机字符串 Nonce Str
	 *
	 * @return String 随机字符串
	 */
	public static String generateNonceStr() {
		return UUID.randomUUID().toString().replaceAll("-", "").substring(0, 32);
	}
	
	/**
	 * 除去数组中的空值和签名参数
	 * 
	 * @param sArray
	 *            签名参数组
	 * @return 去掉空值与签名参数后的新签名参数组
	 */
	public static Map<String, String> paraFilter(Map<String, String> sArray) {

		Map<String, String> result = new HashMap<String, String>();

		if (sArray == null || sArray.size() <= 0) {
			return result;
		}

		for (String key : sArray.keySet()) {
			String value = sArray.get(key);
			if (value == null || value.equals("") || key.equalsIgnoreCase("sign")) {
				continue;
			}
			result.put(key, value);
		}

		return result;
	}
	
	/**
	 * 将Map转换为XML格式的字符串
	 *
	 * @param data
	 *            Map类型数据
	 * @return XML格式的字符串
	 * @throws Exception
	 */
	public static String mapToXml(Map<String, String> data) throws Exception {
		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
		org.w3c.dom.Document document = documentBuilder.newDocument();
		org.w3c.dom.Element root = document.createElement("xml");
		document.appendChild(root);
		for (String key : data.keySet()) {
			String value = data.get(key);
			if (value == null) {
				value = "";
			}
			value = value.trim();
			org.w3c.dom.Element filed = document.createElement(key);
			filed.appendChild(document.createTextNode(value));
			root.appendChild(filed);
		}
		TransformerFactory tf = TransformerFactory.newInstance();
		Transformer transformer = tf.newTransformer();
		DOMSource source = new DOMSource(document);
		transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
		transformer.setOutputProperty(OutputKeys.INDENT, "yes");
		StringWriter writer = new StringWriter();
		StreamResult result = new StreamResult(writer);
		transformer.transform(source, result);
		String output = writer.getBuffer().toString(); // .replaceAll("\n|\r",
														// "");
		try {
			writer.close();
		} catch (Exception ex) {
		}
		return output;
	}
	/**
     * xml数据转map
     * @param xml
     * @return
     * @throws DocumentException
     * @author Ban Wei
     */
    @SuppressWarnings("rawtypes")
	public static Map<String, Object> Dom2Map(String xml) throws DocumentException{  
        Map<String, Object> map = new HashMap<String, Object>(); 
        Document doc= DocumentHelper.parseText(xml);
        if(doc == null)  
            return map;  
        Element root = doc.getRootElement();  
        for (Iterator iterator = root.elementIterator(); iterator.hasNext();) {  
            Element e = (Element) iterator.next();  
            List list = e.elements();  
            if(list.size() > 0){  
                map.put(e.getName(), Dom2Map(e));  
            }else  
                map.put(e.getName(), e.getText());  
        }  
        return map;  
    }
	/**
	 * 获取当前时间戳,单位秒
	 * 
	 * @return
	 */
	public static long getCurrentTimestamp() {
		return System.currentTimeMillis() / 1000;
	}
	
	/**
	 * 把数组所有元素字典排序,并按照“参数=参数值”的模式用“&amp;”字符拼接成字符串
	 * 
	 * @param params
	 *            需要排序并参与字符拼接的参数组
	 * @return 拼接后字符串
	 */
	public static String createLinkString(Map<String, String> params) {

		List<String> keys = new ArrayList<String>(params.keySet());
		Collections.sort(keys);

		String prestr = "";

		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = params.get(key);
			prestr = prestr + key + "=" + value + "&";
		}

		return prestr;
	}
	
	/**
	 * 生成 MD5
	 *
	 * @param data
	 *            待处理数据
	 * @return MD5结果
	 * @throws Exception
	 */
	public static String MD5(String data) throws Exception {
		java.security.MessageDigest md = MessageDigest.getInstance("MD5");
		byte[] array = md.digest(data.getBytes("UTF-8"));
		StringBuilder sb = new StringBuilder();
		for (byte item : array) {
			sb.append(Integer.toHexString((item & 0xFF) | 0x100).substring(1, 3));
		}
		return sb.toString().toUpperCase();
	}
	
	/**
	 *发送post请求
	 * @param reqUrl
	 * @param content
	 * @param charset
	 * @return
	 * @throws IOException
	 */
	public static String post(String reqUrl, String content, String charset) throws IOException {

		URL url = new URL(reqUrl);
		HttpURLConnection con = (HttpURLConnection) url.openConnection();
		con.setDoInput(true);
		con.setDoOutput(true);
		con.setAllowUserInteraction(false);
		con.setUseCaches(false);

		con.setRequestMethod("POST");
		con.setConnectTimeout(30000);// 连接超时 单位毫秒
		con.setDoOutput(true);// 是否输入参数
		con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded;charset=" + charset);
		// con.setRequestProperty("Content-Type","text/plain;charset=" +
		// charset);

		DataOutputStream dataOutput = new DataOutputStream(con.getOutputStream());

		byte[] bytes = content.toString().getBytes(charset);
		dataOutput.write(bytes);// 输入参数

		dataOutput.flush();
		dataOutput.close();

		BufferedReader bin = new BufferedReader(new InputStreamReader(con.getInputStream(), charset), SIZE);

		StringBuilder result = new StringBuilder();
		while (true) {
			String line = bin.readLine();
			if (line == null) {
				break;
			} else {
				result.append(line);
			}
		}
		bin.close();
		return result.toString();
	}

到这里,小程序微信支付就大功告成了,是不是很简单呀
在这里插入图片描述
我也是第一次写博客,
有写的不对的地方欢迎指导,我也不吝赐教
大家如果有什么别的问题:可以加我的QQ:2454424223
我一定知无不言,言无不尽!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值