1. 程式人生 > >java開發-微信支付

java開發-微信支付

一、最近又做了微信公眾號支付,前一次做支付沒有好好記錄,這次又浪費了不少時間,故完整的記錄下,下次就可以直接用了。

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));
    }
    }