微信支付-JSAPI方式

前言

  因为老是用到还需要回顾以前的代码,所以写篇文章记录一下,同时也希望可以帮助到正好有需要的朋友。

准备

  使用微信JSAPI支付需要的几个重要参数:

  1. 登录公众号获得appId,appsecret,配置验证文件
    1. 公众号设置-功能设置-网页授权域名-添加域名,下载验证文件MP_verify_Q0U1Chj03asdacac.txt
    2. 配置域名      http://域名/MP_verify_Q0U1Chj03asdacac.txt
  2. 登录商户号获得商户号mchId,配置商户号
    1. 商户号-账号中心-安全中心-设置apikey
    2. 商户号-产品中心-开通jsapi支付
    3. 商户号-产品中心-开发配置,设置JSAPI支付授权目录

  准备工作结束,到这里我们就拿到了appId,appsecret,mchId,apikey。

开发

  1. 获取用户openId,openId为微信用户对应一个公众号的唯一标识。我采用的是网页授权的方式获取用户信息(OAuth2.0)
    1. 用户同意授权获取code
      1. https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx2b9faf36c563c77c&redirect_uri=接口&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect
      2. 接口为你自己写的接口,通过上面的方式在微信浏览器请求,微信服务器会回调你的接口并带上code参数,注意接口需要进行urlecode编码。例如我的接口为http://域名/pay
        
        @RequestMapping("pay")
        public Stringvote(HttpServletRequest request) {	
        	//微信回调给你的code	
        	String code = request.getParameter("code");
        	//因为我的业务需求,把code传回支付页面,通过code获取支付参数
        	request.setAttribute("code", code);
        	return "支付页面";
        }
      3. scope=snsapi_base只能获取openId,scope=snsapi_userinfo可以获得用户的头像昵称等,但是这个时候会弹框提示用户,将获取你的昵称什么的需要用户确认。如下图
    2. 通过code换取网页授权access_token,获得openid,直接贴代码
      /**
       * 获取微信用户的openid,Access_token
       * 
       * @param appid
       * @param secret
       * @param code
       * @return
       */
      public static OAuthInfo getAccess_token(String appid, String secret, String code) {
      	OAuthInfo oAuthInfo = null;
      	String o_auth_openid_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid + "&secret=" + secret + "&code=" + code + "&grant_type=authorization_code";
      	//请求o_auth_openid_url,返回结果是json格式
      	String result = HttpUtils.oAuth(o_auth_openid_url);
      	if (CommonUtil.isEmpty(result)) {
      		return oAuthInfo;
      	}
      	JSONObject jsonObject = JSONObject.parseObject(result);
      	// 如果请求成功
      	if (!CommonUtil.isEmpty(jsonObject)) {
      		try {
      			oAuthInfo = new OAuthInfo();
      			oAuthInfo.setAccessToken(jsonObject.getString("access_token"));
      			oAuthInfo.setExpiresIn(jsonObject.getIntValue("expires_in"));
      			oAuthInfo.setRefreshToken(jsonObject.getString("refresh_token"));
      			oAuthInfo.setOpenId(jsonObject.getString("openid"));
      			oAuthInfo.setScope(jsonObject.getString("scope"));
      		} catch (JSONException e) {
      			oAuthInfo = null;
      		}
      	}
      	return oAuthInfo;
      }
      请求上图代码中的o_auth_openid_url 返回结果格式,OAuthInfo 为自己写的实体类,封装了这几个属性方便自己用。
      { 
      "access_token":"ACCESS_TOKEN",
      "expires_in":7200,
      "refresh_token":"REFRESH_TOKEN",
      "openid":"OPENID",
      "scope":"SCOPE" 
      }
    3. 如果不需要获得用户昵称头像信息,在这里我们已经拿到了openid,可以跳过第三步,如果需要继续看,注意这里的code必须是第一步scope=snsapi_userinfo获取的code,因为我不需要用户信息,所以我用的是scope=snsapi_base
      /**
       * 
       * 获取微信用户基本信息,公众平台(微信网页授权版,access_token和通用access_token不一样,通用的有调用限制,网页版无限制)
       * 
       * @param appid
       * @param secret
       * @param code
       * @return
       */
      public static WxUserInfo getWxUserInfo(String appid, String secret, String code) {
      	WxUserInfo wxUserInfo = null;
      	String o_auth_openid_url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=" + appid + "&secret=" + secret + "&code=" + code + "&grant_type=authorization_code";
      	String result = HttpUtils.oAuth(o_auth_openid_url);
      	if (CommonUtil.isEmpty(result)) {
      		return null;
      	}
      	JSONObject jsonObject = JSONObject.parseObject(result);
      	String access_token = jsonObject.getString("access_token");
      	String openid = jsonObject.getString("openid");
      	if (CommonUtil.isEmpty(access_token) || CommonUtil.isEmpty(openid)) {
      		return wxUserInfo;
      	}
      	String getWxUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo?access_token=" + access_token + "&openid=" + openid + "&lang=zh_CN";
      	try {
      		result = HttpUtils.doGet(getWxUserInfoUrl);
      		JSONObject userJson = JSONObject.parseObject(result);
      		String errcode = userJson.getString("errcode");
      		if (CommonUtil.isEmpty(errcode)) {
      			wxUserInfo = new WxUserInfo();
      			wxUserInfo.setOpenId(userJson.getString("openid"));
      			wxUserInfo.setNickName(userJson.getString("nickname"));
      			wxUserInfo.setSex(userJson.getInteger("sex"));
      			wxUserInfo.setProvince(userJson.getString("province"));
      			wxUserInfo.setCity(userJson.getString("city"));
      			wxUserInfo.setCountry(userJson.getString("country"));
      			wxUserInfo.setHeadimgUrl(userJson.getString("headimgurl"));
      			wxUserInfo.setUnionId(userJson.getString("unionid"));
      		}
      	} catch (Exception e) {
      		wxUserInfo = null;
      	}
      	return wxUserInfo;
      }

      请求上图代码中的getWxUserInfoUrl 返回格式如下,wxUserInfo 为自己定义的实体类,封装了下面的参数方便自己调用。

      { 
      "openid":" OPENID",
      " nickname": NICKNAME,
      "sex":"1",
      "province":"PROVINCE"
      "city":"CITY",
      "country":"COUNTRY",
      "headimgurl":    "http://thirdwx.qlogo.cn/mmopen/g3MonUZtNHkdmzicIlibx6iaFqAc56vxLSUfpb6n5WKSYVY0ChQKkiaJSgQ1dZuTOgvLLrhJbERQQ4eMsv84eavHiaiceqxibJxCfHe/46",
      "privilege":[ "PRIVILEGE1" "PRIVILEGE2"     ],
      "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
      }
  2. 获取openId之后,我们就可以调用微信支付,获取JSAPI支付所需要的参数,服务端代码,直接上代码
    // 微信基础支付url,app,web,js
    public final static String WX_BASE_PAYURL = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    //用户支付之后,微信服务器回调你的接口通知你用户已经支付
    public final static String WEIXIN_APPPAY_NOTIFY_URL = "https://域名/wxWebPayNotify";
    
    public static JSONObject unifiedAppOrderJsApi(Long price,  String code) throws Exception {
    	HashMap<String, Object> payParams = new HashMap<String, Object>();
        //getAccess_token方法在第一步获取code中
    	OAuthInfo authInfo =getAccess_token("appId","Appsecret", code);
    	if (CommonUtil.isEmpty(authInfo) || CommonUtil.isEmpty(authInfo.getOpenId())) {
    		return null;
    	}
    	System.out.println(authInfo.toString());
    	//公众号appid
    	payParams.put("appid", "appId");
    	//商户号id
    	payParams.put("mch_id","MchId");
    	//随机字符串
    	payParams.put("nonce_str","C48F31602C2D8A525DE00FA01CB91554");
    	//标题
    	payParams.put("body", "梦幻西游点卡充值50");
    	//你自己的订单号
    	payParams.put("out_trade_no","WX20190123110332917098E88");
    	//商品价格,单位分
    	payParams.put("total_fee", 5000);
    	//用户openId
    	payParams.put("openid", authInfo.getOpenId());
    	//调用微信支付的服务端ip
    	payParams.put("spbill_create_ip","服务端ip");
    	//支付完成之后,微信回调地址
    	payParams.put("notify_url", WEIXIN_APPPAY_NOTIFY_URL);
    	//支付方式,固定值
    	payParams.put("trade_type", "JSAPI");
    	//梦幻西游点卡充值  对应的商品编号
    	payParams.put("product_id",10001);
    	String sign = getSign(payParams,"apikey");
    	String postXml = createPostXml(payParams, sign);
    	String result = HttpUtils.doPost(WX_BASE_PAYURL, postXml);
    	Map<String, String> resultMap =parseXmlToMap(result);
    	if ("SUCCESS".equals(resultMap.get("return_code")) && "SUCCESS".equals(resultMap.get("result_code"))) {
    		long timestamp = System.currentTimeMillis() / 1000;
    		payParams = new HashMap<String, Object>();
    		payParams.put("appId","appId");
    		payParams.put("package", "prepay_id=" + resultMap.get("prepay_id"));
    		payParams.put("nonceStr", "C48F31602C2D8A525DE00FA01CB91554");
    		payParams.put("signType", "MD5");
    		payParams.put("timeStamp", timestamp);
    		sign = getSign(payParams,"apikey");
    
    		JSONObject payJson = new JSONObject();
    		payJson.put("appid","appId");
    		payJson.put("prepayid", resultMap.get("prepay_id"));
    		payJson.put("package", "prepay_id=" + resultMap.get("prepay_id"));
    		payJson.put("noncestr", "C48F31602C2D8A525DE00FA01CB91554");
    		payJson.put("timestamp", timestamp);
    		payJson.put("sign", sign);
    		return payJson;
    	} else {
    		return null;
    	}
    }
    
    
    /**
     * 组装签名串
     * 
     * @param params
     * @return
     */
    public static String getSign(HashMap<String, Object> params, String key) {
    	Map<String, Object> sortedParams = new TreeMap<String, Object>(params);
    	Set<Entry<String, Object>> entrys = sortedParams.entrySet();
    	// 遍历排序后的字典,将所有参数按"key=value"格式拼接在一起
    	StringBuilder basestring = new StringBuilder();
    	for (Entry<String, Object> param : entrys) {
    		basestring.append(param.getKey()).append("=").append(param.getValue()).append("&");
    	}
    	String result = basestring.toString();
    	if (result.contains("&")) {
    		result = result.substring(0, result.length() - 1);
    	}
    	result = result + "&key=" + key;
    	// System.out.println("result---" + result);
    	String sign = MD5.getMD5String(result).toUpperCase();
    	// System.out.println("sign---" + sign);
    	return sign;
    }
    
    /**
     * 创建需要发送的post数据
     * 
     * @param params
     * @param sign
     * @return
     */
    public static String createPostXml(HashMap<String, Object> params, String sign) {
    	StringBuilder basestring = new StringBuilder();
    	basestring.append("<xml>");
    	for (String key : params.keySet()) {
    		basestring.append("<").append(key).append(">").append(params.get(key)).append("</").append(key).append(">");
    	}
    	basestring.append("<sign>").append(sign).append("</sign>");
    	basestring.append("</xml>");
    	return basestring.toString();
    }
    
    /**
     * 将xml转换为map对象
     * 
     * @param xml
     * @return
     */
    public static Map<String, String> parseXmlToMap(String xml) {
    	Map<String, String> result = new HashMap<String, String>();
    	try {
    		Document dom = DocumentHelper.parseText(xml);
    		Element root = dom.getRootElement();
    		Iterator<Element> iterator = root.elementIterator();
    		while (iterator.hasNext()) {
    			Element element = iterator.next();
    			result.put(element.getName(), element.getText());
    		}
    	} catch (DocumentException e) {
    		e.printStackTrace();
    	}
    	return result;
    }

    其中payJson就是页面调用微信支付所需要的参数。

结尾

    至此,微信支付JSAPI方式完毕,后续有空了会把H5支付,扫码支付,app支付,企业给用户直接打钱到零钱这几种方式都搞一套这样的流程。如果有什么写的不对的地方欢迎大家留言,一起进步。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值