微信公衆號發起H5支付

1. 準備工做javascript

  ----> 微信提供的appid、 appSecret、payKey、 MchId、tokenphp

        這些比較容易獲取,過程此處省略.....html

 

 ----->配置微信的OAuth2.0網頁受權回調頁面的域名。前端

        這個比較難找,我看文檔看了1天沒有找到,鬱悶致死。 最後登陸到微信公衆號點左側菜單,基本全部的菜單都點了一遍才被我發現,當時哭的心都有了。因此直接上圖,明確位置。以下圖:只須要點修改把本身的域名放進去就行。如m.baidu.com 或者 baidu.comjava

 

 

 -----> 配置微信公衆號支付的受權目錄git

這個比較好找,直接上圖:直接點修改,頁面給的有提示。json

以上工做都準備完畢 就剩下些代碼了。api

2. 功能實現數組

實現流程微信

--> 統一下單API查看微信提供的文檔 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_1

--> 頁面受權獲取openid

    受權文檔 http://mp.weixin.qq.com/wiki/17/c0f37d5704f0b64713d5d2c37b468d75.html

  甭看他們囉嗦 直接拉到頁面的中部看目錄部分。走到目錄中的第二步就能獲取用戶的openid

第一步:重定向用戶受權的URL。

    控制器重定向一下地址:

https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect

參數:

這裏要注意的是 redirect_uri。 這個地址中的域名必定要是上面準備工做【配置微信的OAuth2.0網頁受權回調頁面的域名】提到的域名。 

 注意:只有參數scope=snsapi_userinfo 的時候纔會出現須要用戶點擊受權的頁面,其它的不出現。

第二步:經過code換取網頁受權access_token

在這個回調地址的控制器中獲取返回的參數code。實現代碼以下:

public static AccessTokenOAuth getWeiXinOAuthAccessToken(String code){
		AccessTokenOAuth token = null;
		StringBuffer sb = new StringBuffer(WeiXinConfig.getOAuthAccessTokenURL);
		sb.append("?appid=").append(WeiXinConfig.AppId).append("&secret=").append(WeiXinConfig.AppSecret);
		sb.append("&code=").append(code).append("&grant_type=authorization_code");
		try {
			URL url = new URL(sb.toString());
			HttpURLConnection connection = (HttpURLConnection) url.openConnection();
			connection.setRequestMethod("GET");
			InputStream is =url.openStream();
			//轉換返回值
			String returnStr = SendMsgUtil.convertStreamToString(is);
			// 返回結果爲{"access_token":"ACCESS_TOKEN","expires_in":7200}
			Gson gson = new Gson();
			token = gson.fromJson(returnStr, AccessTokenOAuth.class);
			
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return token;
	}

AccessTokenOAuth 的實體代碼以下:

public class AccessTokenOAuth implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -9011346947427899815L;

	private String access_token; //網頁受權接口調用憑證,注意:此access_token與基礎支持的access_token不一樣
	
	private Long expires_in;
	
	private String refresh_token;
	
	private String openid;
	
	private String scope;
	
	private String errcode;
	
	private String errmsg;

	public String getAccess_token() {
		return access_token;
	}

	public void setAccess_token(String access_token) {
		this.access_token = access_token;
	}

	public Long getExpires_in() {
		return expires_in;
	}

	public void setExpires_in(Long expires_in) {
		this.expires_in = expires_in;
	}

	public String getRefresh_token() {
		return refresh_token;
	}

	public void setRefresh_token(String refresh_token) {
		this.refresh_token = refresh_token;
	}

	public String getOpenid() {
		return openid;
	}

	public void setOpenid(String openid) {
		this.openid = openid;
	}

	public String getScope() {
		return scope;
	}

	public void setScope(String scope) {
		this.scope = scope;
	}

	public String getErrcode() {
		return errcode;
	}

	public void setErrcode(String errcode) {
		this.errcode = errcode;
	}

	public String getErrmsg() {
		return errmsg;
	}

	public void setErrmsg(String errmsg) {
		this.errmsg = errmsg;
	}
	
	
}

此時就能夠從token中獲取到用戶的openid

第三步:調用微信提供的統一下單API

  請求的參數trade_type = JSAPI   Openid = 獲取用戶的openid

  微信的統一下單API返回結果中 若是return_code="SUCCESS" 獲取下單成功,

 而後根據返回的參數生成簽名 此簽名不一樣與統一下單 的簽名,此簽名主要用於頁面經過JS調用微信的H5支付請求。

組裝簽名的代碼:

Map<String,String> paramMap = new HashMap<String,String>();
				paramMap.put("appId", WeiXinConfig.AppId);
				paramMap.put("timeStamp", String.valueOf(new Date().getTime()));
				paramMap.put("nonceStr", WeiXinConfig.getRandomStr());
				paramMap.put("package", "prepay_id="+returnXML.get("prepay_id"));
				paramMap.put("signType", "MD5");
				String _signData = SignUtil.genSignData(JSON.parseObject(JSON.toJSONString(paramMap)));
				_signData +="&key="+WeiXinConfig.PayKey;
				String _sign = SignUtil.addSignMD5(_signData);

簽名須要進行MD5加密。

微信簽名工具類

public class SignUtil {

	private static String token = "XXXXXXX"; //在微信公衆平臺配置
	/** 
     * 驗證簽名 
     *  
     * @param signature 
     * @param timestamp 
     * @param nonce 
     * @return 
     */  
    public static boolean checkSignature(String signature, String timestamp, String nonce) {  
        String[] arr = new String[] { token, timestamp, nonce };  
        // 將token、timestamp、nonce三個參數進行字典序排序  
        Arrays.sort(arr);  
        StringBuilder content = new StringBuilder();  
        for (int i = 0; i < arr.length; i++) {  
            content.append(arr[i]);  
        }  
        MessageDigest md = null;  
        String tmpStr = null;  
  
        try {  
            md = MessageDigest.getInstance("SHA-1");  
            // 將三個參數字符串拼接成一個字符串進行sha1加密  
            byte[] digest = md.digest(content.toString().getBytes());  
            tmpStr = byteToStr(digest);  
        } catch (NoSuchAlgorithmException e) {  
            e.printStackTrace();  
        }  
  
        content = null;  
        // 將sha1加密後的字符串可與signature對比,標識該請求來源於微信  
        return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;  
    }  
  
    /**
     * 獲取jsapi的簽名
     * @param jsapiTicket
     * @return
     */
    public static String getSignature(String jsapiTicket){
    	String[] arr = new String[] {jsapiTicket}; 
    	Arrays.sort(arr);
    	StringBuilder content = new StringBuilder();  
        for (int i = 0; i < arr.length; i++) {  
            content.append(arr[i]);  
        }  
        MessageDigest md = null;  
        String tmpStr = null;  
        try {  
            md = MessageDigest.getInstance("SHA-1");  
            // 將三個參數字符串拼接成一個字符串進行sha1加密  
            byte[] digest = md.digest(content.toString().getBytes());  
            tmpStr = byteToStr(digest);  
        } catch (NoSuchAlgorithmException e) {  
            e.printStackTrace();  
        }  
    	return tmpStr;
    }
    /** 
     * 將字節數組轉換爲十六進制字符串 
     *  
     * @param byteArray 
     * @return 
     */  
    private static String byteToStr(byte[] byteArray) {  
        String strDigest = "";  
        for (int i = 0; i < byteArray.length; i++) {  
            strDigest += byteToHexStr(byteArray[i]);  
        }  
        return strDigest;  
    }  
  
    /** 
     * 將字節轉換爲十六進制字符串 
     *  
     * @param mByte 
     * @return 
     */  
    private static String byteToHexStr(byte mByte) {  
        char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };  
        char[] tempArr = new char[2];  
        tempArr[0] = Digit[(mByte >>> 4) & 0X0F];  
        tempArr[1] = Digit[mByte & 0X0F];  
  
        String s = new String(tempArr);  
        return s;  
    }
    /**
     * 簽名字符串
     * @param sign_src 須要簽名的字符串
     * @return
     */
    public static String addSignMD5(String sign_src){
    	if (sign_src == null) return "";
    	try
        {
    		String md5 = DigestUtils.md5Hex(getContentBytes(sign_src, "UTF-8")).toUpperCase();
    		return md5;
        }catch (Exception e){
            return "";
        }
    }
    /**
     * 校驗MD5簽名
     * @param text 須要簽名的字符串
     * @param sign 簽名結果
     * @return
     */
    public static boolean verifySignMD5(String text, String sign) {
    	String mysign = DigestUtils.md5Hex(getContentBytes(text, "UTF-8")).toUpperCase();
    	if(mysign.equals(sign)) {
    		return true;
    	}
    	else {
    		return false;
    	}
    }
    
    private static byte[] getContentBytes(String content, String charset) {
        if (charset == null || "".equals(charset)) {
            return content.getBytes();
        }
        try {
            return content.getBytes(charset);
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("MD5簽名過程當中出現錯誤,指定的編碼集不對,您目前指定的編碼集是:" + charset);
        }
    }
    
    public static String genSignData(JSONObject jsonObject)
    {
        StringBuffer content = new StringBuffer();
        // 按照key作首字母升序排列
        List<String> keys = new ArrayList<String>(jsonObject.keySet());
        Collections.sort(keys, String.CASE_INSENSITIVE_ORDER);
        for (int i = 0; i < keys.size(); i++)
        {
            String key = (String) keys.get(i);
            if ("sign".equals(key))
            {
                continue;
            }
            String value = jsonObject.getString(key);
            // 空串不參與簽名
            if (isnull(value))
            {
                continue;
            }
            content.append((i == 0 ? "" : "&") + key + "=" + value);
        }
        String signSrc = content.toString();
        if (signSrc.startsWith("&"))
        {
            signSrc = signSrc.replaceFirst("&", "");
        }
        return signSrc;
    }
    public static boolean isnull(String str)
    {
        if (null == str || str.equalsIgnoreCase("null") || str.equals(""))
        {
            return true;
        } else
            return false;
    }
}

 

最後一步在頁面經過js發起微信的H5支付請求

WeixinJSBridge.invoke(
    				       'getBrandWCPayRequest', {
    				           "appId":appId,     //公衆號名稱,由商戶傳入     
    				           "timeStamp":timeStamp,         //時間戳,自1970年以來的秒數     
    				           "nonceStr" :nonceStr, //隨機串     
    				           "package":paypackage,     
    				           "signType":"MD5",         //微信簽名方式:     
    				           "paySign":sign //微信簽名 
    				       },
    				       function(res){     
    				           if(res.err_msg == "get_brand_wcpay_request:ok" ) {
    				        	   Util.alert("支付成功!!",5000);
    				           }else if(res.err_msg=="get_brand_wcpay_request:cancel"){//支付過程當中用戶取消
    								Util.alert("您取消了支付!!",5000);
    							}else if(res.err_msg=="get_brand_wcpay_request:fail"){//支付失敗
    								Util.alert("支付失敗!!",-1);
    							}    // 使用以上方式判斷前端返回,微信團隊鄭重提示:res.err_msg將在用戶支付成功後返回    ok,但並不保證它絕對可靠。 
    				       }
    				   );
相關文章
相關標籤/搜索