package com.rf.psychological.wxpay.controller; import cn.hutool.core.date.DateUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.google.gson.Gson; import com.google.gson.internal.LinkedTreeMap; import com.rf.psychological.base.rest.BaseController; import com.rf.psychological.enums.UserRole; import com.rf.psychological.group.service.GroupInfoService; import com.rf.psychological.security.AesEncryptUtils; import com.rf.psychological.security.SafetyProcess; import com.rf.psychological.user.dao.model.OpenIdEntity; import com.rf.psychological.user.dao.model.UserEntity; import com.rf.psychological.user.service.OpenidService; import com.rf.psychological.user.service.UserService; import com.rf.psychological.utils.Constant; import com.rf.psychological.utils.IPUtiles; import com.rf.psychological.utils.JWTUtil; import com.rf.psychological.utils.Result; import com.rf.psychological.wxpay.config.WxPayConfig; import com.rf.psychological.wxpay.service.WxPayService; import com.rf.psychological.wxpay.utils.HttpUtils; import com.rf.psychological.wxpay.utils.WechatPay2ValidatorForRequest; import com.wechat.pay.contrib.apache.httpclient.auth.Verifier; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang.StringUtils; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.util.EntityUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.HashMap; import java.util.Map; /** * @Description:微信支付接口 * @Author: zsf * @Date: 2022/6/21 */ @CrossOrigin //跨域 @RestController @RequestMapping("/api/wx-pay") @Api(tags = "网站微信支付APIv3") @Slf4j @ConditionalOnProperty(prefix="wx.pay",name = "is_lan", havingValue = "false") public class WxPayController extends BaseController { @Autowired private WxPayService wxPayService; @Autowired private WxPayConfig wxPayConfig; @Autowired private Verifier verifier; @Autowired private OpenidService openidService; @Resource private CloseableHttpClient wxPayClient; @Autowired private UserService userService; @Autowired private GroupInfoService groupInfoService; /** * 调用统一下单API,生成支付二维码 * @return */ @SafetyProcess @ApiOperation(value = "调用统一下单API,生成支付二维码",notes = "json字符串形式传参(加密)," + "data参数中包括(必传):productId:量表id,userId:用户id,resultId:测试结果id,description:订单描述信息," + "total:订单总金额,单位为分;") @PostMapping("/native") public Result> nativePay(@RequestBody String paramJson){ try { log.info(paramJson); String paramData = AesEncryptUtils.decrypt(JSONObject.parseObject(paramJson).getString("data")); log.info(paramData); if (StringUtils.isEmpty(paramData)){ return fail(null,"参数不能为空"); } Map map = wxPayService.nativePay(JSON.parseObject(paramData)); return success(map); }catch (Exception e){ e.printStackTrace(); return fail("二维码生成失败"); } } /** * h5下单API,生成支付二维码 * @return */ @SafetyProcess @ApiOperation(value = "h5下单API,生成支付二维码",notes = "json字符串形式传参(加密)," + "data参数中包括(必传):productId:量表id,userId:用户id,resultId:测试结果id,description:订单描述信息," + "payerClientIp:用户的客户端IP,支持IPv4和IPv6两种格式的IP地址,sceneType:场景类型ios、Android," + "total:订单总金额,单位为分") @PostMapping("/h5Pay") public Result h5Pay(@RequestBody String paramJson, HttpServletRequest request) throws Exception { try { String paramData = AesEncryptUtils.decrypt(JSONObject.parseObject(paramJson).getString("data")); log.info("paramData:"+paramData); if (StringUtils.isEmpty(paramData)){ return fail(null,"参数不能为空"); } //获取必要参数 JSONObject paramObject = JSONObject.parseObject(paramData); //获取ip String payerClientIp = IPUtiles.getRealIp(request); paramObject.put("payerClientIp",payerClientIp); Map result = wxPayService.h5Pay(paramObject); return success(result); }catch (Exception e){ e.printStackTrace(); return fail("h5_url生成失败"); } } @SafetyProcess @ApiOperation(value = "小程序预下单",notes = "参数同native下单") @PostMapping("/aspiPay") public Result aspiPay(@RequestBody String json){ log.info("json请求参数:{}",json); try { String paramData = AesEncryptUtils.decrypt(JSONObject.parseObject(json).getString("data")); if (StringUtils.isEmpty(paramData)){ return fail(null,"参数不能为空"); } //获取必要参数 JSONObject paramObject = JSONObject.parseObject(paramData); if(!paramObject.containsKey("openId")||StringUtils.isBlank(paramObject.getString("openId"))){ UserEntity userEntity = userService.findUserById(paramObject.getString("userId")); // OpenIdEntity openIdEntity = this.openidService.findByUserId(paramObject.getString("userId")); if(userEntity == null){ return fail("用户未登录"); } paramObject.put("openId",userEntity.getPhone()); } Map result = wxPayService.aspiPay(paramObject); return success(result); }catch (Exception e){ e.printStackTrace(); return fail("生成失败"); } } @SafetyProcess @ApiOperation(value = "JSAPI支付",notes = "参数同native下单") @PostMapping("/jsapiPay") public Result jsapiPay(@RequestBody String json){ log.info("json请求参数:{}",json); try { String paramData = AesEncryptUtils.decrypt(JSONObject.parseObject(json).getString("data")); if (StringUtils.isEmpty(paramData)){ return fail(null,"参数不能为空"); } //获取必要参数 JSONObject paramObject = JSONObject.parseObject(paramData); Map result = wxPayService.jsapiPay(paramObject); return success(result); }catch (Exception e){ e.printStackTrace(); return fail("生成失败"); } } /** * 支付通知 微信支付通过支付通知接口将用户支付成功消息通知给商户 * @param request * @param response * @return */ @ApiOperation("支付通知") @PostMapping("/native/notify") public String nativeNotify(HttpServletRequest request, HttpServletResponse response){ log.info("支付通知===================================================="); Gson gson = new Gson(); Map map = new HashMap<>(); //处理通知参数 try { String body = HttpUtils.readData( request); Map bodyMap = gson.fromJson(body, HashMap.class); String requestId = (String)bodyMap.get("id"); log.info("支付通知的id ===> {}", requestId); log.info("支付通知的完整数据 ===> {}", body); //签名的验证 WechatPay2ValidatorForRequest wechatPay2ValidatorForRequest = new WechatPay2ValidatorForRequest(verifier, requestId, body); if(!wechatPay2ValidatorForRequest.validate(request)){ log.error("通知验签失败"); //失败应答 response.setStatus(500); map.put("code", "ERROR"); map.put("message", "通知验签失败"); return gson.toJson(map); } log.info("通知验签成功"); //处理订单 wxPayService.processOrder(bodyMap); //成功应答 response.setStatus(200); map.put("code", "SUCCESS"); map.put("message", "成功"); return gson.toJson(map); }catch (Exception e){ e.printStackTrace(); //失败应答 response.setStatus(500); map.put("code", "ERROR"); map.put("message", "失败"); return gson.toJson(map); } } /** * 用户取消订单 * @param orderNo * @return * @throws Exception */ @ApiOperation("用户取消订单-PC") @GetMapping("/cancel/{orderNo}") public Result cancel(@PathVariable String orderNo) throws Exception { log.info("取消订单"); wxPayService.cancelOrder(orderNo); return success(null,"订单已取消"); } @SafetyProcess @ApiOperation("查询订单") @GetMapping("/queryOrder/{orderNo}") public Result queryOrder(@PathVariable String orderNo) throws Exception { log.info("查询订单订单"); return success( wxPayService.queryOrder(orderNo)); } @ApiOperation("交易账单--暂未开发") @GetMapping("/queryTradeBill/{billDate}/{type}") public Result queryTradeBill(@PathVariable String billDate, @PathVariable String type){ return success(); } @GetMapping("/refund/{orderNo}") public Result refund(@PathVariable String orderNo) throws IOException { wxPayService.refund(orderNo); return success(); } // @PostMapping("/code2openid") // @ApiOperation(value = "code 换 opernid",notes = "modelPhone:手机号;code:临时code,获取openid;petName:用户名") // @SafetyProcess // public Result code2id(@RequestBody String json) throws Exception { // JSONObject resultJson = new JSONObject(); // JSONObject data = JSONObject.parseObject(AesEncryptUtils.decrypt(JSONObject.parseObject(json).getString("data"))); // OpenIdEntity openIdEntity = this.openidService.findByPhone(data.getString("modelPhone")); // if(openIdEntity == null){ // CloseableHttpResponse response = getToken(data.getString("code")); // int statusCode = response.getStatusLine().getStatusCode();//响应状态码 // String body = EntityUtils.toString(response.getEntity()); // Gson gson = new Gson(); // HashMap resultBody = gson.fromJson(body, HashMap.class); // if(statusCode == 200 || statusCode == 204){ // openIdEntity = new OpenIdEntity(); // openIdEntity.setOpenId((String) resultBody.get("openid")); // openIdEntity.setAuthType("vx"); // }else{ // log.error("获取openid失败:错误码 "+resultBody.get("errcode")+" "+resultBody.get("errmsg")); // log.error("错误码:{}",resultBody.get("errcode")); // log.error("错误消息:{}",resultBody.get("errmsg")); // return fail(); // } // } // //静默注册登录 // UserEntity userEntity = this.userService.findPhoneAndInstitutionNoAndRoleType(data.getString("modelPhone"), Constant.WEB_INSTITUTION_CODE, UserRole.COMMON.getType()); // if(userEntity != null){ // openIdEntity.setUserId(userEntity.getId()); // openIdEntity.setPhone(data.getString("modelPhone")); // openIdEntity = this.openidService.save(openIdEntity); // // if(StringUtils.isNotBlank(data.getString("petName"))&&(!userEntity.getPetName().equals(data.getString("petName")))){ // userEntity.setPetName(data.getString("petName")); // this.userService.save(userEntity); // } // userEntity.setPassword("-"); // resultJson.put("user", userEntity); // resultJson.put("type", userEntity.getRoleType()); // resultJson.put("improve",false); // }else{ // //注册 // userEntity = new UserEntity(); // userEntity.setPassword("-"); // userEntity.setModelPhone(data.getString("modelPhone")); // userEntity.setGId(this.groupInfoService.findGroupByInstitutionNoAndName(Constant.WEB_INSTITUTION_CODE,Constant.DEFAULT_GROUP_NAME).getId()); // userEntity.setInstitutionName(Constant.WEB_INSTITUTION_NAME); // userEntity.setInstitutionNo(Constant.WEB_INSTITUTION_CODE); // userEntity.setUserStatus(Constant.USER_STATUS_NORMAL); // userEntity.setBirthday("-"); // userEntity.setGender("-"); // userEntity.setPetName(data.getString("petName")); // userEntity.setProfession("-"); // userEntity.setAdditionInfo("微信小程序注册用户"); // userEntity.setPhone(data.getString("modelPhone")); // userEntity.setRoleType(UserRole.COMMON.getType()); // userEntity = this.userService.save(userEntity); // resultJson.put("improve",true); // //保存三方授权信息 openid // openIdEntity.setUserId(userEntity.getId()); // openIdEntity.setPhone(data.getString("modelPhone")); // openIdEntity = this.openidService.save(openIdEntity); // //登录 //// TODO 将登录用户放入redis已登录用户信息,统计在线人数 // } // // resultJson.put("token", JWTUtil.getTokenByUserInfo(userEntity)); // resultJson.put("user",userEntity); // resultJson.put("type",userEntity.getRoleType()); // resultJson.put("openId",openIdEntity); // log.info("响应消息:"+resultJson.toJSONString()); // return success(resultJson); // } @PostMapping("/code2openid2") @ApiOperation(value = "code 换 opernid",notes = "code:临时code,获取openid;petName:用户名") @SafetyProcess public Result code2openid2(@RequestBody String json) throws Exception { JSONObject resultJson = new JSONObject(); JSONObject data = JSONObject.parseObject(AesEncryptUtils.decrypt(JSONObject.parseObject(json).getString("data"))); String openId; CloseableHttpResponse response = getToken(data.getString("code")); int statusCode = response.getStatusLine().getStatusCode();//响应状态码 String body = EntityUtils.toString(response.getEntity()); Gson gson = new Gson(); HashMap resultBody = gson.fromJson(body, HashMap.class); if(statusCode == 200 || statusCode == 204){ openId = (String) resultBody.get("openid"); }else{ log.error("获取openid失败:错误码 "+resultBody.get("errcode")+" "+resultBody.get("errmsg")); log.error("错误码:{}",resultBody.get("errcode")); log.error("错误消息:{}",resultBody.get("errmsg")); return fail(); } //静默注册登录 UserEntity userEntity = this.userService.findPhoneAndInstitutionNoAndRoleType(openId, Constant.WEB_INSTITUTION_CODE, UserRole.COMMON.getType()); if(userEntity != null){ // if(StringUtils.isNotBlank(data.getString("petName"))&&(!userEntity.getPetName().equals(data.getString("petName")))){ // userEntity.setPetName(data.getString("petName")); // this.userService.save(userEntity); // } userEntity.setPassword("-"); resultJson.put("user", userEntity); resultJson.put("type", userEntity.getRoleType()); resultJson.put("improve",false); }else{ //注册 userEntity = new UserEntity(); userEntity.setPassword("-"); userEntity.setGId(this.groupInfoService.findGroupByInstitutionNoAndName(Constant.WEB_INSTITUTION_CODE,Constant.DEFAULT_GROUP_NAME).getId()); userEntity.setInstitutionName(Constant.WEB_INSTITUTION_NAME); userEntity.setInstitutionNo(Constant.WEB_INSTITUTION_CODE); userEntity.setUserStatus(Constant.USER_STATUS_NORMAL); userEntity.setBirthday("-"); userEntity.setGender("-"); userEntity.setPetName("微信用户"); userEntity.setProfession(DateUtil.now()); userEntity.setAdditionInfo("微信小程序注册用户"); userEntity.setPhone(openId); userEntity.setRoleType(UserRole.COMMON.getType()); userEntity = this.userService.save(userEntity); resultJson.put("improve",true); } resultJson.put("token", JWTUtil.getTokenByUserInfo(userEntity)); resultJson.put("user",userEntity); resultJson.put("type",userEntity.getRoleType()); resultJson.put("openId",openId); log.info("响应消息:"+resultJson.toJSONString()); return success(resultJson); } @GetMapping("/getPhone/{code}") @SafetyProcess @ApiOperation(value = "获取用户手机号",notes = "code :临时code") public Result getPhone(@PathVariable String code) throws IOException { // 1、获取access_token String access_token = wxPayService.getAccessToken(); // 2、获取用户手机号 HashMap map = getPhoneNumber(code,access_token); int errcode = ((Number) map.get("errcode")).intValue(); if(errcode == 0){ //{"errcode":0,"errmsg":"ok","phone_info":{"phoneNumber":"19138984495","purePhoneNumber":"19138984495","countryCode":"86","watermark":{"timestamp":1661308946,"appid":"wx2f422a2a1cb24c3c"}}} LinkedTreeMap map2 = (LinkedTreeMap) map.get("phone_info"); String phoneNumber = String.valueOf(map2.get("phoneNumber")); if(StringUtils.isBlank(phoneNumber)){ phoneNumber = String.valueOf(map2.get("purePhoneNumber")); } JSONObject resultJson = new JSONObject(); resultJson.put("phone",phoneNumber); UserEntity userEntity = this.userService.findPhoneAndInstitutionNoAndRoleType(phoneNumber,Constant.WEB_INSTITUTION_CODE,Constant.DEFAULT_VALUE_ONE); if(userEntity != null){ resultJson.put("newer","0"); }else { resultJson.put("newer","1"); } return success(resultJson); }else if (errcode == -1){ return fail("系统繁忙"); }else if (errcode == 40029){ log.info("code 不合法"); return fail("获取手机号失败"); }else { log.info("获取手机号失败错误码:{},错误消息{}",errcode,map.get("errmsg")); return fail("获取手机号失败"); } } /** * 获取 * @param code * @param access_token * @return */ private HashMap getPhoneNumber(String code, String access_token) throws IOException { String url = "https://api.weixin.qq.com/wxa/business/getuserphonenumber?access_token="+access_token; HttpPost httpPost = new HttpPost(url); httpPost.setHeader("Accept", "application/json"); Map paramsMap = new HashMap<>(); paramsMap.put("code",code); Gson gson = new Gson(); String jsonParams = gson.toJson(paramsMap); StringEntity entity = new StringEntity(jsonParams,"utf-8"); entity.setContentType("application/json"); httpPost.setEntity(entity); httpPost.setHeader("Accept","application/json"); CloseableHttpResponse response = wxPayClient.execute(httpPost); String body = EntityUtils.toString(response.getEntity());//获取响应体 log.info("获取手机号:响应体{}",body); //响应结果 return gson.fromJson(body,HashMap.class); } /** * 获取access_token * @return * @throws IOException */ // public String getAccessToken() throws IOException { // String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid="+wxPayConfig.getAspi_appid()+"&secret="+wxPayConfig.getAspi_secret() ; // HttpGet httpGet = new HttpGet(url); // CloseableHttpResponse response = wxPayClient.execute(httpGet); // String body = EntityUtils.toString(response.getEntity()); // Gson gson = new Gson(); // HashMap resultBody = gson.fromJson(body, HashMap.class); // if(resultBody.containsKey("errcode")){ // log.error("获取token异常:"); // log.error("errcode:"+resultBody.get("errcode")); // log.error("errmsg:"+resultBody.get("errmsg")); // }else{ // return (String) resultBody.get("access_token"); // } // return null; // } public CloseableHttpResponse getToken(String code) throws IOException { String url = wxPayConfig.getAspi_url() + "?appid=" + wxPayConfig.getAspi_appid() + "&secret=" + wxPayConfig.getAspi_secret() + "&js_code=" + code + "&grant_type=authorization_code"; log.info("code2openid URL :"+url); HttpGet httpGet = new HttpGet(url); httpGet.setHeader("Accept", "application/json"); return wxPayClient.execute(httpGet); } @GetMapping("/generateShortLink") @ApiOperation(value = "获取到分享链接",notes = "path:跳转路径和参数") public Result generateShortLink(@RequestParam String path,@RequestParam String query){ try { if (StringUtils.isEmpty(path)){ return fail(null,"参数不能为空"); } String link = wxPayService.generateLink(path,query); return success(link); }catch (Exception e){ e.printStackTrace(); return fail("操作失败"); } } @GetMapping("/generateWxCode") @SafetyProcess @ApiOperation(value = "获取到小程序",notes = "page:跳转路径scene参数envVersion环境") public Result generateWxCode(@RequestParam String page,@RequestParam String scene,@RequestParam(required = false) String envVersion){ try { if (StringUtils.isEmpty(page)){ return fail(null,"参数不能为空"); } String link = wxPayService.generateWxCode(page,scene,envVersion); return success(link); }catch (Exception e){ e.printStackTrace(); return fail("操作失败"); } } }