Java-实现微信H5支付

        作的是微信H5支付,微信APP支付已经有了,H5支付尚未,须要注意的是:H5支付须要在微信支付商家后台单独开通。php

微信H5支付官方文档前端

微信H5支付开发流程图:java

        红色的就是咱们服务端须要作的,简单来讲的业务流程就是:node

  1. 在咱们本身的系统里下订单
  2. 把咱们系统的订单号,和一系列参数编辑好,以XML的格式传给微信统一下单接口
  3. 获得微信下单回执,在成功的状况下,把mweb_url参数传给前端访问它
  4. 在前端用户已付款的状况下,微信访问你以前传的回调参数,在你本身的系统里运行一些业务逻辑(如更改订单状态)

        到这里微信H5支付的业务流程基本上算完成了。web

具体的代码是:算法

// 须要的jar等,这里为了方便就把一些工具类的方法写在一块儿了,须要的jar在这里,没有的在下面具体的类上的注释也有标出来
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

import org.apache.hc.client5.http.fluent.Request;
import org.apache.hc.core5.http.entity.ContentType;
import org.dom4j.DocumentException;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;

	/*建立微信的H5订单信息*/
	@Override
	public OrderCreateVO getWechatH5PayOrderInfo(int fee, String callback,
			Long uid, String out_trade_no) {
		// 请求和响应参数参考:https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1
		HashMap<String, String> params = new HashMap<String, String>();
		
		// 公众帐号ID:微信分配的公众帐号ID(企业号corpid即为此appId)
		params.put("appid", WX_APP_ID);
		// 商户号:微信支付分配的商户号
        params.put("mch_id",WX_MCH_ID);
        // 设备号:终端设备号(门店号或收银设备ID),注意:PC网页或公众号内支付请传"WEB"
        params.put("device_info", "WEB");
        // 随机字符串:随机字符串,不长于32位。推荐随机数生成算法(https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=4_3)
        params.put("nonce_str", PayUtil.getRandomHex(32));
        // 签名类型:签名类型,目前支持HMAC-SHA256和MD5,默认为MD5
        params.put("sign_type", "MD5");
        // 商品描述:商品简单描述,该字段须严格按照规范传递,具体请见参数规定(https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=4_2)
        params.put("body", "XXX-支付");
        // 商户订单号:商户系统内部的订单号,32个字符内、可包含字母, 其余说明见商户订单号(https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=4_2)
        params.put("out_trade_no", out_trade_no);
        // 总金额:订单总金额,单位为分,详见支付金额(https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=4_2)
        params.put("total_fee", fee + "");
        // 终端IP:必须传正确的用户端IP,详见获取用户ip指引(https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_5)
        params.put("spbill_create_ip", PayUtil.getHostIp());
        // 通知地址:接收微信支付异步通知回调地址,通知url必须为直接可访问的url,不能携带参数。
        // 这里的callback就是微信检测到付款成功之后访问你的系统后台方法,就是你要改票的状态或者推送通知什么的业务,就写到这个连接的方法里面去,方法是本身系统的
        params.put("notify_url", callback);
        // 交易类型:H5支付的交易类型为MWEB
        params.put("trade_type", "MWEB");
        // 签名:签名,详见签名生成算法(https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=4_3)
        params.put("sign", PayUtil.getWeChatSign(params, WX_API_KEY));
        
        // 参数XML
        String body = PayUtil.mapToXmlString(params);
        // 返回响应XML
        String response;
		try {
			response = Request.Post(WX_UNIFIED_ORDER_URL).bodyString(body, APPLICATION_XML).execute().returnContent()
					.asString(Charset.forName("UTF-8"));
		} catch (IOException e) {
			e.printStackTrace();
			return null;
		}
		
		//解析相应信息
		Map<String, String> xmlResponse;
		try {
			xmlResponse = PayUtil.xmlStringToMap(response);
		} catch (DocumentException e) {
			e.printStackTrace();
			return null;
		}
		
		// 取得解析过的微信响应回执,这个就至关于微信用你的订单,在他系统里下了一个单,返回给你(预支付订单),你再把订单给前端H5页面,页面把这些参数发给微信。其中公众号内支付是访问接口发参数,H5支付是访问mweb_url连接
		if ("SUCCESS".equals(xmlResponse.get("return_code")) && "SUCCESS".equals(xmlResponse.get("result_code"))) {
	        // 封装H5页面调用参数,参考文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
			Map<String, String> resultMap = new HashMap<String, String>();
			// 回调地址:当微信支付成功后,微信会访问回调地址,访问咱们系统的方法,修改票的状态等
			String redirectUrl = "&redirect_url=" + callback;
			// 支付跳转连接:mweb_url为拉起微信支付收银台的中间页面,可经过访问该url来拉起微信客户端,完成支付,mweb_url的有效期为5分钟。
			resultMap.put("mwebUrl", xmlResponse.get("mweb_url") + redirectUrl);
	        // 公众帐号ID:调用接口提交的公众帐号ID
	        resultMap.put("appId", xmlResponse.get("appid"));
	        // 时间戳:自1970年以来的秒数 
	        resultMap.put("timeStamp", System.currentTimeMillis() / 1000 + "");
	        // 随机字符串:微信返回的随机字符串
	        resultMap.put("nonceStr", xmlResponse.get("nonce_str"));
	        // 订单详情扩展字符串:统一下单接口返回的prepay_id参数值,提交格式如:prepay_id=***
	        resultMap.put("prepayIdPackage", "prepay_id=" + xmlResponse.get("prepay_id"));
	        // 签名方式:签名类型,默认为MD5,支持HMAC-SHA256和MD5。注意此处需与统一下单的签名类型一致
	        resultMap.put("signType", "MD5");
	        // 签名:微信返回的签名,详见签名算法(https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=4_3)
	        // 这里是给前端H5封装的请求参数,不能用刚才的请求参数了,须要用此次的请求Map再次签名
	        resultMap.put("paySign", PayUtil.getWeChatSign(resultMap, WX_API_KEY));
	        
	        OrderCreateVO createVO = new OrderCreateVO();
	        createVO.setOrderId(out_trade_no);
	        createVO.setOrderInfo(resultMap);
	        return createVO;
        } else {
        	System.out.println("微信下单失败..." + xmlResponse.get("return_code") + "; 返回信息: "+xmlResponse.get("return_msg"));
        	return null;
        }
		
	}

     /**
     * nonce_str生成算法
     *
     * @param len
     * @return
     */
    public static String getRandomHex(int len) {
        if (len == 0) return "";
        Random random = new Random();
        char[] str = "0123456789abcdefABCDEF".toCharArray();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < len; i++) {
            sb.append(str[random.nextInt(str.length)]);
        }
        return sb.toString();
    }

   /**
    * spbill_create_ip:获得本地机器的IP
    * @return
    */
   public static String getHostIp(){
      String ip = "";
      try{
         ip = InetAddress.getLocalHost().getHostAddress();
      }catch(UnknownHostException e){
         e.printStackTrace();
      }
      return ip;
   }

    /**
    * 获取微信支付的参数签名
    * 参考文档:https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter=4_3
    *
    * @param map
    * @param apiKey
    * @return
    */
   public static String getWeChatSign(Map<String, String> map, String apiKey) {
       // 先对参数Map进行ASCII字典排序
       String authInfo = mapToSortString(map);
       // 在排序后的参数字符串后拼接API KEY
       String stringSignTemp = authInfo + "&key=" + apiKey;
       // 使用org.apache.commons.codec.digest.DigestUtils.md5Hex()进行MD5加密并大写
       String sign = DigestUtils.md5Hex(stringSignTemp).toUpperCase();
       return sign;
   }

    /**
    * 对Map进行ASCII字典排序
    *
    * @param map
    * @return
    */
   public static String mapToSortString(Map<String, String> map) {
	   Collection<String> keyset= map.keySet();   
	   List<String> list=new ArrayList<String>(keyset);  
	   Collections.sort(list); //排序
	   
	   StringBuffer sb = new StringBuffer();
       for (String key : list) {
           sb.append(buildKeyValue(key, map.get(key), false));
           sb.append("&");
       }
       removeLastChar(sb);
	   return sb.toString();
   }

   /**
    * Map转XML
    *
    * @param map
    * @return
    */
    public static String mapToXmlString(Map<String, String> map) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("<xml>");
        for (Map.Entry<String, String> entry : map.entrySet()) {
            stringBuilder.append(String.format("<%s>%s</%s>", entry.getKey(), entry.getValue(), entry.getKey()));
        }
        stringBuilder.append("</xml>");
        return stringBuilder.toString();
    }

   /**
    * XML转Map
    *
    * @param map
    * @return
    */
    public static Map<String, String> xmlStringToMap(String xml) throws DocumentException {
        Map<String, String> stringMap = new HashMap<String, String>();
        // org.dom4j.DocumentHelper
        Element element = DocumentHelper.parseText(xml).getRootElement();
        @SuppressWarnings("unchecked")
        Iterator<Element> iterator = element.elementIterator();
        while (iterator.hasNext()) {
            Element node = iterator.next();
            stringMap.put(node.getName(), node.getTextTrim());
        }
        return stringMap;
    }

        而后前端H5页面支付的问题,由于是测试环境,而微信H5支付须要域名和“商家后台-产品中心-开发配置”里的域名一致,而测试环境域名不一致,再因为短期不能上线,之后上线了再加上。spring

相关文章
相关标签/搜索