文档章节

微信公众号发起H5支付

limsky
 limsky
发布于 2016/07/14 17:26
字数 1679
阅读 1576
收藏 16

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

 

© 著作权归作者所有

limsky

limsky

粉丝 2
博文 6
码字总数 2211
作品 0
杭州
技术主管
私信 提问
微信支付之扫码支付、公众号支付、H5支付、小程序支付相关业务流程分析总结

前言 很久以来,一直想写一篇微信支付有关的总结文档;一方面是总结自己的一些心得,另一方面也可以帮助别人,但是因种种原因未能完全理解透彻微信支付的几大支付方式,今天有幸做一些总结上...

龙行天涯
2018/08/21
0
0
微信H5支付

本篇文件来聊聊微信服务商模式以及商户模式下微信H5支付 先说一个事情。8月1号开始微信公众平台支付的开发配置页面迁移至商户平台 详细说明参考这个或者看下面的截图 平台公告 微信支付商户平...

Javen205
2017/08/23
0
0
web开发中的支付宝支付和微信支付

支付宝支付分为支付宝app内的网页支付和app外(即普通浏览器)网页支付, 同样微信支付也分为微信app内的支付(在这里叫公众号支付)和app外的支付(微信H5支付) 还有一种微信公众号的支付宝支付 ...

唯有时光_f2e9
2018/07/20
0
0
微信公众号支付问题?很严重。。。。。

大家好呀!有个问题来请教一下!首先!先说一下奇葩的需求!就是一个公众号下面的商城买东西调用另一个公众号发起支付。所以逼不得已要这样把参数传到pugu.ilenor.cn下面的H5_weixin_pay.htm...

X-Lin
2017/09/25
192
3
闵克东/wxpay_sdk

wxpay_sdk 微信支付(暂时实现了扫码支付、app支付、h5支付、回调辅助函数) 由于工作中暂时只用到了这些,按照微信支付官方SDK的PHP版本,实现了python版本,后面如果有时间会继续实现其他类...

闵克东
2016/07/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

C# 视频多人脸识别的实现过程

整个项目是用虹软技术完成开发 上一篇内容的调整,提交到git了,https://github.com/catzhou2002/ArcFaceDemo 基本思路如下: 一、识别线程 1.获取当前图片 2.识别当前图片的人脸位置,并将结...

是哇兴哥棒棒哒
19分钟前
1
0
Spring Cloud Eureka 你还在让它裸奔吗??

前些天栈长在微信公众号Java技术栈分享了 Spring Cloud Eureka 最新版 实现注册中心的实战教程:Spring Cloud Eureka 注册中心集群搭建,Greenwich 最新版!,成功进入 Eureka 控制台页面。 ...

Java技术栈
36分钟前
1
0
linux gyp ERR! stack Error: EACCES: permission denied, mkdir ‘xxx’

在使用linux npm install的出现这个错误了,百度了下,没有权限加个参数即可 npm install --unsafe-perm

朝如青丝暮成雪
37分钟前
1
0
使用kubeadm 搭建K8s集群

1. 参考官网 https://kubernetes.io/docs/setup/independent/install-kubeadm/

whhbb
今天
2
0
Dubbo 3.0 !提升不止一点点!

Dubbo 自 2011 年 10 月 27 日开源后,已被许多非阿里系的公司使用,其中既有当当网、网易考拉等互联网公司,也不乏中国人寿、青岛海尔等大型传统企业。 自去年 12 月开始,Dubbo 3.0 便已正...

编程SHA
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部