一、最近又做了微信公众号支付,前一次做支付没有好好记录,这次又浪费了不少时间,故完整的记录下,下次就可以直接用了。

1、准备工作(微信公众号、微信商户号申请)
2、域名购买、域名备案(微信支付必须是备案的域名,测试环境支付测试不了)
测试环境能测试授权等功能,扫描关注可获得微信管方测试、app_id、app_secret 有了两个就可以了

二、准备工作续

第一步:开通微信公众号支付功能后,就可以获得app_id、app_secret,这个地方还需要设置一个ip白名单,代码所放置的服务器ip地址

第二步:设置回调域名,必须是备案的域名,域名去掉 http

第三步:设置调起微信支付h5页面的地址,在微信商户平台设置

以上工作准备好了,接下来开始上代码。

微信支付重点在签名的生成,这块错一点就很麻烦。

主代码:

复制代码
 package com.snp.app.controller;import com.alibaba.fastjson.JSONObject;
 import com.snp.app.domain.VO.PayVO;
 import com.snp.common.controller.BaseController;
 import com.snp.common.utils.ConfigUtil;
 import com.snp.common.utils.StringUtil;
 import com.snp.order.dao.FinancialClaimOrderDao;
 import com.snp.order.domain.FinancialClaimOrderDO;
 import com.snp.userManager.domain.UserWechatDO;
 import com.snp.userManager.service.UserWechatService;
 import com.snp.wechat.config.WechatConfig;
 import com.snp.wechat.model.bean.*;
 import com.snp.wechat.utils.AdvancedUtil;
 import com.snp.wechat.utils.PayUtils;
 import com.snp.wechat.utils.WeiXinOrderUtil;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMethod;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.bind.annotation.RestController;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.util.Map;/**
• 支付控制器
 */
 @RestController
 @RequestMapping("/interface/pay")
 public class PayController extends BaseController{
 private static Logger logger = LoggerFactory.getLogger(PayController.class);
 @Autowired
 private WeiXinOrderUtil weiXinOrderUtil;
 @Autowired
 private FinancialClaimOrderDao financialClaimOrderDao;
 @Autowired
 private WechatConfig wechatConfig;
 @Autowired
 private UserWechatService userWechatService;
@ResponseBody
 @RequestMapping(value = “/createWechatOrder”, method ={ RequestMethod.GET,RequestMethod.POST})
 public JSONObject createWechatOrder(PayVO payVO, HttpServletRequest request, HttpServletResponse response)
 throws Exception{
 payVO.setPayAmount(“1”);//1分钱
 payVO.setRobbingOrderNo(PayUtils.getTransferNo16());
 //ResultBody result = new ResultBody();
logger.info(“saveFinancialClaimOrder{}支付金额:”+payVO.getPayAmount());
logger.info(“saveFinancialClaimOrder{}订单号:”+payVO.getRobbingOrderNo());
 UserWechatDO userWechatDO = userWechatService.getUserWechatByUserId(ConfigUtil.getUserId(request));
 WechatOrder wechatOrder = new WechatOrder();
 wechatOrder.setOpenid(userWechatDO.getWechatOpenid());
 wechatOrder.setOrderName(“押金”);
//PayUtils.getTransferNo16()
 wechatOrder.setOut_trade_no(payVO.getRobbingOrderNo());
 wechatOrder.setNonce_str(PayUtils.getNonceStr());
 //wechatOrder.setTotal_fee(MoneyUtil.getOrderMoney(payVO.getPayAmount()));
 wechatOrder.setTotal_fee(Integer.parseInt(payVO.getPayAmount()));
 //请求微信下单 并返回参数
 String resultOrderStr = weiXinOrderUtil.buildWechatOrderParam(wechatOrder,request);
 JSONObject returnJson=JSONObject.parseObject(resultOrderStr);
 //更新 openID prepay_id 到抢单表中
 FinancialClaimOrderDO finaClaimDO = new FinancialClaimOrderDO();
 finaClaimDO.setOpenId(userWechatDO.getWechatOpenid());
 finaClaimDO.setPrepayId(returnJson.get(“pg”).toString());
//financialClaimOrderDao.update(finaClaimDO);
logger.info(“createWechatOrder{}支付结果:”+resultOrderStr);
//result.setData(resultOrderStr);
 return returnJson;
 }
//支付之前先去下单 获取用户openID
 @RequestMapping(value = “/getWechatOpenId”, method = RequestMethod.GET)
 public void getWechatOpenId(PayVO payVO,HttpServletRequest request,HttpServletResponse response) throws IOException{
 //静默授权 只能获得openid
 //获得微信公众号的唯一标识
 String appID = wechatConfig.getWechatAppId();
 String appSecret = wechatConfig.getWechatAppSecret();// 用户同意授权后,能获取到code
 String code = request.getParameter("code");
 WeixinOauth2Token weixinOauth2Token = AdvancedUtil.getOauth2AccessToken(appID, appSecret, code);
 String openId = null;
 String accessToken = null;
 if(null!=weixinOauth2Token){
     openId = weixinOauth2Token.getOpenId();
     accessToken = weixinOauth2Token.getAccessToken();
 }
 UserWechatDO userWechatDO = new UserWechatDO();
 userWechatDO.setUserId(ConfigUtil.getUserId(request));
 userWechatDO.setWechatOpenid(openId);
 userWechatDO.setAccessToken(accessToken);
 userWechatService.save(userWechatDO);
 //获取回调域名
 String url = wechatConfig.getRedirectUri();
 url=url+"/index.html";
 System.out.println(url);
 response.sendRedirect(url);}
/**
• 支付下单之前 先去判断是否需要静默授权获取openID
• @param request
• @param response
• @throws IOException
 */
 @RequestMapping(value = “/saveFinancialClaimOrder”, method = {RequestMethod.GET,RequestMethod.POST})
 public void saveFinancialClaimOrder(PayVO payVO,HttpServletRequest request,HttpServletResponse response)
 throws Exception{
 //查询数据库 如果没有记录就是第一次登录,如果有数据判断token有没有失效
 if(ConfigUtil.getUserId(request) == 0L){
 throw new Exception(“用户id为空”);
 }
 UserWechatDO userWechatDO = userWechatService.getUserWechatByUserId(ConfigUtil.getUserId(request));
 int isLoginFirst = 0;
 if(StringUtil.isEmpty(userWechatDO)){//首次支付 静默登录
 isLoginFirst = 1;
 }else{
 //第二次登录 支付这里不用检查token失效 openid唯一的不会失效
 isLoginFirst = 0;
 }
 //获取回调域名
 String url = wechatConfig.getRedirectUri();
 //获得微信公众号的唯一标识
 String appID = wechatConfig.getWechatAppId();
 //微信本地中转站 去获取token
 String reUrl = wechatConfig.getReUrl();
 reUrl = reUrl+"?userId="+ConfigUtil.getUserId(request);
 //微信静默授权地址
 String accreditUrlBase = wechatConfig.getAccreditUrlBase();
if(isLoginFirst == 1){
 accreditUrlBase=accreditUrlBase.replace(“Redirect_UI”, reUrl).replace(“appId”, appID);
 response.sendRedirect(accreditUrlBase);//未授权的用户继续授权
 }
 //token effective 支付页面
 url=url+"/index.html";
 System.out.println(url);
 response.sendRedirect(url);
 }
 /**• 提交支付后的微信异步返回接口
• @throws IOException
 */
 @RequestMapping(value="/weixinNotify")
 public void weixinNotify(HttpServletRequest request, HttpServletResponse response) throws IOException{
 String out_trade_no=null;
 String return_code =null;
 try {
 String resultNotify = weiXinOrderUtil.getOrderReturnStream(request);
 Map<String, String> resultMap = PayUtils.getH5PayMap(resultNotify,request);
 System.out.println(“支付回调参数:”+resultMap);
 out_trade_no = resultMap.get(“out_trade_no”);
 return_code = resultMap.get(“return_code”);//通知微信.异步确认成功.必写.不然微信会一直通知后台.八次之后就认为交易失败了.
 response.getWriter().write(PayUtils.setXML("SUCCESS", ""));} catch (Exception e) {
 logger.error(“微信回调接口出现错误:”,e);
 try {
 response.getWriter().write(PayUtils.setXML(“FAIL”, “error”));
 } catch (IOException e1) {
 e1.printStackTrace();
 }
 }
 FinancialClaimOrderDO fClaimDO = financialClaimOrderDao.getFinanClaimByOrderNo(out_trade_no);
 if(return_code.equals(“SUCCESS”)){
 //支付成功的业务逻辑
 fClaimDO.setStatus(1);
 }else{
 //支付失败的业务逻辑
 fClaimDO.setStatus(-1);
 }
 financialClaimOrderDao.update(fClaimDO);
 }
 }
 复制代码
 页面:复制代码
 
微信安全支付 复制代码 工具类: 
复制代码
 package com.snp.wechat.utils;import com.alibaba.fastjson.JSON;
 import com.alibaba.fastjson.JSONObject;
 import com.snp.wechat.config.WechatConfig;
 import com.snp.wechat.model.bean.WechatOrder;
 import com.snp.wechat.model.bean.WeixinOauth2Token;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Date;
 import java.util.Map;
 import java.util.SortedMap;
 import java.util.TreeMap;/**
• 微信公用工具类
 */
 @Component
 public class WeiXinOrderUtil {
@Autowired
 private WechatConfig wechatConfig;
/**• 组装统一下单参数
• @return
 */
 public String buildWechatOrderParam(WechatOrder wechatOrder,HttpServletRequest request) throws Exception{
 String payKey = wechatConfig.getPaykey();
 String signType = wechatConfig.getSignType();
 SortedMap<String, String> signMap = new TreeMap<String, String>();
 long timeStamp = PayUtils.getUnixTime(new Date());
 wechatOrder.setAppid(wechatConfig.getWechatAppId());
 wechatOrder.setTrade_type(wechatConfig.getTradeType());
 //构建参数返回前台 请求支付接口
 String prepayId = createWechatOrder(wechatOrder,request);
 signMap.put(“appId”,wechatOrder.getAppid());
 signMap.put(“timeStamp”, timeStamp+"");
 signMap.put(“nonceStr”, wechatOrder.getNonce_str());
 signMap.put(“package”, “prepay_id=”+prepayId);
 signMap.put(“signType”, signType);
 String paySign = PayUtils.getSign(signMap, payKey);
 signMap.put(“pg”, prepayId);
 signMap.put(“paySign”, paySign);
 signMap.put(“result”, “success”);
 String json = JSON.toJSONString(signMap);
 JSONObject returnJson=JSONObject.parseObject(json);
 return returnJson.toJSONString();
 }/**
• 创建下单签名 包含商品信息
• @return
 */
 public String createWechatSign(WechatOrder wechatOrder){
 String mch_id = wechatConfig.getMchId();
 String notify_url = wechatConfig.getNotifyUrl();
 String device_info = wechatConfig.getDeviceInfo();
 String payKey = wechatConfig.getPaykey();
//将商品信息打包
 SortedMap<String, String> parameters = new TreeMap<String, String>();
 parameters.put(“appid”, wechatOrder.getAppid());//公众号id 这地方一定要小写并跟下面xml文件对应都是小写
 parameters.put(“mch_id”,mch_id);//商户ID
 parameters.put(“device_info”, device_info);
 parameters.put(“body”, wechatOrder.getOrderName());//名称
 parameters.put(“trade_type”, wechatOrder.getTrade_type());
 parameters.put(“nonce_str”, wechatOrder.getNonce_str());//随机数
 parameters.put(“notify_url”, notify_url);
 parameters.put(“out_trade_no”, wechatOrder.getOut_trade_no());
 parameters.put(“total_fee”, wechatOrder.getTotal_fee()+"");
 // parameters.put(“spbill_create_ip”, spbill_create_ip );
 parameters.put(“openid”, wechatOrder.getOpenid());
 //根据上述的数据生成预支付订单号的前面sign
 return PayUtils.getSign(parameters, payKey);
 }/**
• 创建微信订单 请求微信接口下单
• @return
 */
 public String createWechatOrder(WechatOrder wechatOrder,HttpServletRequest request) throws Exception{
 String mch_id = wechatConfig.getMchId();
 String notify_url = wechatConfig.getNotifyUrl();
 String device_info = wechatConfig.getDeviceInfo();
 String createOrderURL = wechatConfig.getCreateOrderUrl();
//生成统一支付接口数据
 String xml = “”+
 “”+wechatOrder.getAppid()+""+
 “”+wechatOrder.getOrderName()+""+
 “<device_info>”+device_info+"</device_info>"+
 “<mch_id>”+mch_id+"</mch_id>"+
 “<nonce_str>”+wechatOrder.getNonce_str()+"</nonce_str>"+
 “<notify_url>”+notify_url+"</notify_url>"+
 “”+wechatOrder.getOpenid()+""+
 “<out_trade_no>”+wechatOrder.getOut_trade_no()+"</out_trade_no>"+
 “<total_fee>”+wechatOrder.getTotal_fee()+"</total_fee>"+
 “<trade_type>”+wechatOrder.getTrade_type()+"</trade_type>"+
 “”+createWechatSign(wechatOrder)+""+
 “”;
 //调用统一支付接口
 String result = PayUtils.httpsRequest(createOrderURL, “POST”, xml);
 System.out.println("-----------------------------统一下单结果---------------------------");
 System.out.println(result);
 Map<String, String> resultMap = null;
 resultMap=PayUtils.getH5PayMap(result,request);
 return resultMap.get(“prepay_id”); //预支付ID,保存到数据库中
 }/**
• 解析微信支付回调的结果
• @return
 */
 public static String getOrderReturnStream(HttpServletRequest request) throws IOException {
 InputStream inStream = request.getInputStream();
 ByteArrayOutputStream outSteam = new ByteArrayOutputStream();
 byte[] buffer = new byte[1024];
 int len = 0;
 while ((len = inStream.read(buffer)) != -1) {
 outSteam.write(buffer, 0, len);
 }
 outSteam.close();
 inStream.close();
 return new String(outSteam.toByteArray(),“utf-8”);
 }/**
• 获取 WeixinOauth2Token
• @return
 */
 public WeixinOauth2Token getWeixinOauth2Token(String code){
 //获得微信公众号的唯一标识
 String appId = wechatConfig.getWechatAppId();
 String appSecret = wechatConfig.getWechatAppSecret();
 return AdvancedUtil.getOauth2AccessToken(appId, appSecret, code);
 }
 }
 复制代码
 复制代码
 package com.snp.wechat.utils;import com.snp.common.utils.StringUtil;
 import com.snp.userManager.domain.UserWechatDO;
 import com.snp.wechat.model.bean.WeixinOauth2Token;
 import org.apache.commons.lang3.StringUtils;import com.alibaba.fastjson.JSONObject;
import java.util.Date;
/**
• @author yuwei
• 工具类
• 2016年12月21日 下午1:58:38
 */
 public class AdvancedUtil {
public AdvancedUtil() {
 super();
 // TODO Auto-generated constructor stub
 }
/**• 获取网页授权凭证
• 
• @param appId 公众账号的唯一标识
• @param appSecret 公众账号的密钥
• @param code
• @return
• @return WeixinAouth2Token
/
 public static WeixinOauth2Token getOauth2AccessToken(String appId, String appSecret, String code) {
 WeixinOauth2Token wat = null;
 // 拼接请求地址
 String requestUrl = “https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code”;
 requestUrl = requestUrl.replace(“APPID”, appId);
 requestUrl = requestUrl.replace(“SECRET”, appSecret);
 requestUrl = requestUrl.replace(“CODE”, code);
 // 获取网页授权凭证
 JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, “GET”, null);
 if (null != jsonObject) {
 try {
 wat = new WeixinOauth2Token();
 wat.setAccessToken(jsonObject.getString(“access_token”));
 wat.setExpiresIn(jsonObject.getInteger(“expires_in”));
 wat.setRefreshToken(jsonObject.getString(“refresh_token”));
 wat.setOpenId(jsonObject.getString(“openid”));
 wat.setScope(jsonObject.getString(“scope”));
 } catch (Exception e) {
 wat = null;
 int errorCode = jsonObject.getInteger(“errcode”);
 String errorMsg = jsonObject.getString(“errmsg”);
 System.out.println(errorCode);
 System.out.println(errorMsg);
 // log.error(“获取网页授权凭证失败 errcode:{} errmsg:{}”, errorCode, errorMsg);
 }
 }
 return wat;
 }
 /*• 通过网页授权获取用户信息
• 
• @param accessToken 网页授权接口调用凭证
• @param openId 用户标识
• @return SNSUserInfo
 */
 public static UserWechatDO getWechatInfo(String accessToken, String openId,String refreshToken) {
 UserWechatDO userWechatDO = null;
 // 拼接请求地址
 String requestUrl = “https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID”;
 requestUrl = requestUrl.replace(“ACCESS_TOKEN”, accessToken).replace(“OPENID”, openId);
 // 通过网页授权获取用户信息
 JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, “GET”, null);
if (!StringUtil.isEmpty(jsonObject)) {
 try {
 userWechatDO = new UserWechatDO();
 // 用户的标识
 userWechatDO.setWechatOpenid(jsonObject.getString(“openid”));
 // 昵称
 String nickname = jsonObject.getString(“nickname”);
 nickname = filterEmoji(nickname);
 userWechatDO.setNickName(nickname);
 // 性别(1是男性,2是女性,0是未知)
 userWechatDO.setSex(jsonObject.getInteger(“sex”));
 // 用户所在国家
 userWechatDO.setCountry(jsonObject.getString(“country”));
 // 用户所在省份
 userWechatDO.setProvince(jsonObject.getString(“province”));
 // 用户所在城市
 userWechatDO.setCity(jsonObject.getString(“city”));
 // 用户头像
 userWechatDO.setHeadImgUrl(jsonObject.getString(“headimgurl”));
 //UnicodeID
 userWechatDO.setWechatUnionid(jsonObject.getString(“unionid”));
//userWechatDO.setWin(0);
//userWechatDO.setLose(0);
 //查询时间
 // Date date = new Date();
 // player.setJoinTime(date);
 //首次授权时间
 userWechatDO.setCreateTime(new Date());
 //更新时间
 userWechatDO.setUpdateTime(new Date());
 userWechatDO.setLasttIme(new Date());
 //凭证保存
 userWechatDO.setAccessToken(accessToken);
 //刷新凭证
 userWechatDO.setRefreshToken(refreshToken);
 // 用户特权信息
 // snsUserInfo.setPrivilegeList(JSONArray.parseObject(jsonObject.getJSONArray(“privilege”), List.class));
 } catch (Exception e) {
 userWechatDO = null;
 int errorCode = jsonObject.getInteger(“errcode”);
 String errorMsg = jsonObject.getString(“errmsg”);
 System.out.println(errorCode);
 System.out.println(errorMsg);
 // log.error(“获取用户信息失败 errcode:{} errmsg:{}”, errorCode, errorMsg);
 }
 }
 return userWechatDO;
 }//检验凭证是否失效
 @SuppressWarnings(“unused”)
 public static boolean judgeToken(String accessToken, String openId){
 // 拼接请求地址
 String requestUrl = “https://api.weixin.qq.com/sns/auth?access_token=ACCESS_TOKEN&openid=OPENID”;
 requestUrl = requestUrl.replace(“ACCESS_TOKEN”, accessToken).replace(“OPENID”, openId);
 // 通过网页授权获取用户信息
 JSONObject jsonObject = CommonUtil.httpsRequest(requestUrl, “GET”, null);
 int errorCode = jsonObject.getInteger(“errcode”);
 String errorMsg = jsonObject.getString(“errmsg”);//正确返回OK
 errorMsg = errorMsg.toUpperCase();
 if(errorMsg.equals(“OK”)){
 return true;
 }
 return false;
 }//去掉ios特殊字符
 public static String filterEmoji(String source) {
 if (StringUtils.isBlank(source)) {
 return source;
 }
 StringBuilder buf = null;
 int len = source.length();
 for (int i = 0; i < len; i++) {
 char codePoint = source.charAt(i);
 if (isNotEmojiCharacter(codePoint)) {
 if (buf == null) {
 buf = new StringBuilder(source.length());
 }
 buf.append(codePoint);
 }
 }
 if (buf == null) {
 return source;
 } else {
 if (buf.length() == len) {
 buf = null;
 return source;
 } else {
 return buf.toString();
 }
 }
 }
 //判断特殊字符串
 private static boolean isNotEmojiCharacter(char codePoint) {
 return (codePoint == 0x0) ||
 (codePoint == 0x9) ||
 (codePoint == 0xA) ||
 (codePoint == 0xD) ||
 ((codePoint >= 0x20) && (codePoint <= 0xD7FF)) ||
 ((codePoint >= 0xE000) && (codePoint <= 0xFFFD)) ||
 ((codePoint >= 0x10000) && (codePoint <= 0x10FFFF));
 }
 }