微信公众号发起H5支付

寵の児 提交于 2019-12-06 20:14:19

1. 准备工作

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

        这些比较容易获取,过程此处省略.....

 

 ----->配置微信的OAuth2.0网页授权回调页面的域名。

        这个比较难找,我看文档看了1天没有找到,郁闷致死。 最后登录到微信公众号点左侧菜单,基本所有的菜单都点了一遍才被我发现,当时哭的心都有了。所以直接上图,明确位置。如下图:只需要点修改把自己的域名放进去就行。如m.baidu.com 或者 baidu.com

 

 

 -----> 配置微信公众号支付的授权目录

这个比较好找,直接上图:直接点修改,页面给的有提示。

以上工作都准备完毕 就剩下些代码了。

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,但并不保证它绝对可靠。 
    				       }
    				   ); 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!