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

相關文章
相關標籤/搜索