
C:\zhuyrWork\外卖项目代码\meal\src\main\java\com\mayocase\meal\service\utls\MessageUtil.java
package com.mayocase.meal.service.utls;import com.mayocase.meal.service.dto.TextMessageDTO;import com.thoughtworks.xstream.XStream;import com.thoughtworks.xstream.core.util.QuickWriter;import com.thoughtworks.xstream.io.HierarchicalStreamWriter;import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;import com.thoughtworks.xstream.io.xml.XppDriver;import org.dom4j.Document;import org.dom4j.Element;import org.dom4j.io.SAXReader;import javax.servlet.http.HttpServletRequest;import java.io.InputStream;import java.io.Writer;import java.util.Date;import java.util.HashMap;import java.util.List;import java.util.Map;/*** Created by wrlai on 2020/2/19*/public class MessageUtil {/*** 返回消息类型:文本*/public static final String RESP_MESSAGE_TYPE_TEXT = "text";/*** 返回消息类型:音乐*/public static final String RESP_MESSAGE_TYPE_MUSIC = "music";/*** 返回消息类型:图文*/public static final String RESP_MESSAGE_TYPE_NEWS = "news";/*** 返回消息类型:图片*/public static final String RESP_MESSAGE_TYPE_Image = "image";/*** 返回消息类型:语音*/public static final String RESP_MESSAGE_TYPE_Voice = "voice";/*** 返回消息类型:视频*/public static final String RESP_MESSAGE_TYPE_Video = "video";/*** 请求消息类型:文本*/public static final String REQ_MESSAGE_TYPE_TEXT = "text";/*** 请求消息类型:图片*/public static final String REQ_MESSAGE_TYPE_IMAGE = "image";/*** 请求消息类型:链接*/public static final String REQ_MESSAGE_TYPE_LINK = "link";/*** 请求消息类型:地理位置*/public static final String REQ_MESSAGE_TYPE_LOCATION = "location";/*** 请求消息类型:音频*/public static final String REQ_MESSAGE_TYPE_VOICE = "voice";/*** 请求消息类型:视频*/public static final String REQ_MESSAGE_TYPE_VIDEO = "video";/*** 请求消息类型:推送*/public static final String REQ_MESSAGE_TYPE_EVENT = "event";/*** 事件类型:subscribe(订阅)*/public static final String EVENT_TYPE_SUBSCRIBE = "subscribe";/*** 事件类型:unsubscribe(取消订阅)*/public static final String EVENT_TYPE_UNSUBSCRIBE = "unsubscribe";/*** 事件类型:CLICK(自定义菜单点击事件)*/public static final String EVENT_TYPE_CLICK = "CLICK";/*** 事件类型:VIEW(自定义菜单 URl 视图)*/public static final String EVENT_TYPE_VIEW = "VIEW";/*** 事件类型:LOCATION(上报地理位置事件)*/public static final String EVENT_TYPE_LOCATION = "LOCATION";/*** 事件类型:LOCATION(上报地理位置事件)*/@SuppressWarnings("unchecked")public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {// 将解析结果存储在HashMap中Map<String, String> map = new HashMap<String, String>();// 从request中取得输入流InputStream inputStream = request.getInputStream();// 读取输入流SAXReader reader = new SAXReader();Document document = reader.read(inputStream);// 得到xml根元素Element root = document.getRootElement();// 得到根元素的所有子节点List<Element> elementList = root.elements();// 遍历所有子节点for (Element e : elementList)map.put(e.getName(), e.getText());// 释放资源inputStream.close();inputStream = null;return map;}@SuppressWarnings("unused")private static XStream xstream = new XStream(new XppDriver() {public HierarchicalStreamWriter createWriter(Writer out) {return new PrettyPrintWriter(out) {// 对所有xml节点的转换都增加CDATA标记boolean cdata = true;@SuppressWarnings("rawtypes")public void startNode(String name, Class clazz) {super.startNode(name, clazz);}protected void writeText(QuickWriter writer, String text) {if (cdata) {writer.write("<![CDATA[");writer.write(text);writer.write("]]>");} else {writer.write(text);}}};}});/*** 将文本消息对象转成XML* @param textMessage* @return*/public static String textMessageToXml(TextMessageDTO textMessage){XStream xstream = new XStream();//将xml的根节点替换成<xml> 默认为TextMessage的包名xstream.alias("xml", textMessage.getClass());return xstream.toXML(textMessage);}/*** 拼接关注主菜单*/public static String subscribeText(String mphost, String merchantName){StringBuffer sb = new StringBuffer();sb.append("亲爱的,你终于来啦!感谢关注"+merchantName+"服务号。\n\n");sb.append("点击菜单里的<a href=\"" + mphost + "\">「开始点餐」</a> 即可微信下单订餐!");return sb.toString();}public static String menuAllText(){StringBuffer sb = new StringBuffer();sb.append("如有意见或建议请留言,或者拨打门店电话!谢谢!");return sb.toString();}/*** 初始化回复消息*/public static String initText(String toUSerName,String fromUserName,String content){TextMessageDTO text = new TextMessageDTO();text.setFromUserName(toUSerName);text.setToUserName(fromUserName);text.setMsgType(REQ_MESSAGE_TYPE_TEXT);text.setCreateTime(new Date().getTime()+"");text.setContent(content);return MessageUtil.textMessageToXml(text);}}

C:\zhuyrWork\外卖项目代码\meal\src\main\java\com\mayocase\meal\web\rest\WxWebViewAuthResource.java
package com.mayocase.meal.web.rest;import com.mayocase.meal.client.WxException;import com.mayocase.meal.client.WxMPClient;import com.mayocase.meal.client.WxQrcodeClient;import com.mayocase.meal.config.ApplicationProperties;import com.mayocase.meal.service.MemberService;import com.mayocase.meal.service.RewardPointsService;import com.mayocase.meal.service.dto.MemberDTO;import com.mayocase.meal.service.dto.RewardPointsDTO;import com.mayocase.meal.service.dto.ShopDTO;import com.mayocase.meal.service.utls.MessageUtil;import com.mayocase.meal.service.utls.WeChatCheckUtil;import liquibase.util.StringUtils;import org.apache.http.client.ClientProtocolException;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.BeanUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestParam;import org.springframework.web.bind.annotation.ResponseBody;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.net.URLEncoder;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.text.SimpleDateFormat;import java.time.Instant;import java.util.*;@Controller@RequestMapping("/weixin")public class WxWebViewAuthResource {// 微信网页授权路径private final String WX_WEBVIEW_AUTH_PATH = "/connect/oauth2/authorize?appid=%s&redirect_uri=%s&response_type=code&scope=%s&state=%s#wechat_redirect";@Autowiredprivate ApplicationProperties applicationProperties;@Autowiredprivate WxMPClient wxMPClient;@Autowiredprivate MemberService memberService;@Autowiredprivate RewardPointsService rewardPointsService;@Value("${weixin.open.host}")private String wxOpenHost;@Value("${weixin.mp.appID}")private String wxMPAppID;@Value("${application.mp-url}")private String wxUrl;@Value("${application.merchant-name}")private String merchantName;@Autowiredprivate WxQrcodeClient wxQrcodeClient;private static final Logger logger = LoggerFactory.getLogger(WxWebViewAuthResource.class);@GetMapping("/authorize")public String authorize(@RequestParam("return_url") String returnURL) throws UnsupportedEncodingException {logger.info("微信开始过授权-------------------------------");logger.info("回调地址-------------------------------" + returnURL);String redirectURI = applicationProperties.getHost() + "/weixin/userinfo";String encodedRedirectURI = URLEncoder.encode(redirectURI, "utf-8");String state = URLEncoder.encode(returnURL, "utf-8");String authURL = String.format(wxOpenHost + WX_WEBVIEW_AUTH_PATH, wxMPAppID, encodedRedirectURI, "snsapi_userinfo",state);logger.info("跳转地址:" + authURL);return "redirect:" + authURL;}@GetMapping("/userinfo")public String userinfo(@RequestParam("code") String code, @RequestParam("state") String returnURL)throws ClientProtocolException, IOException, WxException {MemberDTO member = wxMPClient.getUserInfoWithWebViewAuth(code);logger.info("回调地址-------------------------------userinfo");// 注册或更新会员信息MemberDTO persistedMember = memberService.findByOpenId(member.getOpenId());if (persistedMember != null) {member.setId(persistedMember.getId());member.setSn(persistedMember.getSn());member.setCreatedDate(persistedMember.getCreatedDate());member.setLastLoginDate(persistedMember.getLastLoginDate());}else{member.setLastLoginDate(Instant.now());}try {member = memberService.save(member);}catch (Exception e){member.setSn(getDate());memberService.save(member);}logger.info("登入的会员信息:{}", member.toString());// 检查积分帐户是否存在,不存在则创建boolean existed = rewardPointsService.isExistByMember(persistedMember);if (!existed) {RewardPointsDTO rewardPointsDTO = new RewardPointsDTO();rewardPointsDTO.setBalance(0);rewardPointsDTO.setMemberId(member.getId());rewardPointsDTO.setMemberSn(member.getSn());rewardPointsDTO.setLastStatisticalDate(Instant.now());rewardPointsService.save(rewardPointsDTO);}return "redirect:" + returnURL + "?openId=" + member.getOpenId();}@RequestMapping("/getJsApi")@ResponseBodypublic Map<String,Object> getJsApi(String wxMphost) throws Exception {Map map = new HashMap();String token = wxMPClient.getAccessToken();String ticket = wxMPClient.getJsapiTicket(token);//3、时间戳和随机字符串String noncestr = UUID.randomUUID().toString().replace("-", "").substring(0, 16);//随机字符串String timestamp = String.valueOf(System.currentTimeMillis() / 1000);//时间戳StringBuilder string1Builder = new StringBuilder();string1Builder.append("jsapi_ticket=").append(ticket).append("&").append("noncestr=").append(noncestr).append("&").append("timestamp=").append(timestamp).append("&").append("url=").append(wxMphost);logger.info("jsApi签名参数: " + string1Builder.toString());String sign = SHA1(string1Builder.toString());map.put("sign", sign);map.put("timestamp", timestamp);map.put("noncestr", noncestr);map.put("appid", wxMPAppID);return map;}/* @RequestMapping("/getTicket")@ResponseBodypublic String getTicket() throws Exception {String token = wxMPClient.getAccessToken();String ticket = wxMPClient.getJsapiTicket(token);return ticket;}@RequestMapping("/getToken")@ResponseBodypublic String getToken() throws Exception {Map map = new HashMap();String token = wxMPClient.getAccessToken();return token;}*/@RequestMapping("/getQrcode")@ResponseBodypublic String getQrcode() throws Exception{String result = wxQrcodeClient.getQrcode1(new Random(99999).nextInt());return result;}/*** 微信核心接入** @param signature 签名* @param timestamp 时间戳* @param nonce 随机数* @param echostr 随机字符串* @return 接入成功:enchostr,接入失败:""*/@GetMapping@ResponseBodypublic String wxCoreAccess(@RequestParam(name = "signature", required = false) String signature,@RequestParam(name = "timestamp", required = false) String timestamp,@RequestParam(name = "nonce", required = false) String nonce,@RequestParam(name = "echostr", required = false) String echostr) {logger.info("【微信核心接入】,请求接入参数,signature={},timestamp={},nonce={},echostr={}", signature, timestamp, nonce, echostr);if (WeChatCheckUtil.checkSignature(signature, timestamp, nonce)) {logger.info("【微信核心接入】,参数校验正确,接入成功!");return echostr;} else {logger.info("【微信核心接入,参数校验错误,接入失败!】");return "";}}// post方法用于接收微信服务端消息@RequestMappingpublic void DoPost(HttpServletRequest request, HttpServletResponse response) throws Exception {request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");logger.info("接收微信推送消息---------------------------------------------------");String message = "";try {Map<String, String> map = MessageUtil.parseXml(request);//添加门店logger.info("微信推送事件类型:" + map.get("Event"));logger.info("微信推送事件类型:" + map.get("MsgType"));logger.info("参数 " + map.toString());String mesType = map.get("MsgType");String event = map.get("Event");if(!StringUtils.isEmpty(mesType)){if("text".equals(mesType) || "voice".equals(mesType) || "image".equals(mesType) || "video".equals(mesType)|| "link".equals(mesType) || "location".equals(mesType) || "shortvideo".equals(mesType)){logger.info("文本");String ToUserName = map.get("ToUserName");String FromUserName = map.get("FromUserName");message = MessageUtil.initText(ToUserName, FromUserName, MessageUtil.menuAllText());logger.info("返回message: " + message.toString());response.getWriter().write(message);}else if(event.equals("subscribe")){String ToUserName = map.get("ToUserName");String FromUserName = map.get("FromUserName");message = MessageUtil.initText(ToUserName, FromUserName, MessageUtil.subscribeText(wxUrl,merchantName));logger.info("返回message: " + message.toString());response.getWriter().write(message);}}} catch (Exception e) {logger.error(e.getMessage());}}public static String SHA1(String decript) {try {MessageDigest digest = MessageDigest.getInstance("SHA-1");digest.update(decript.getBytes());byte messageDigest[] = digest.digest();// Create Hex StringStringBuffer hexString = new StringBuffer();// 字节数组转换为 十六进制 数for (int i = 0; i < messageDigest.length; i++) {String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);if (shaHex.length() < 2) {hexString.append(0);}hexString.append(shaHex);}return hexString.toString();} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}public String getDate(){SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS");String date = sdf.format(new Date());date= date.replace("-","");date= date.replace(" ","");date=date.replace(":","");date=date.replace(".","");return date;}}

C:\zhuyrWork\外卖项目代码\meal\src\main\java\com\mayocase\meal\config\ApplicationProperties.java
package com.mayocase.meal.config;import org.springframework.boot.context.properties.ConfigurationProperties;/*** Properties specific to Meal.* <p>* Properties are configured in the {@code application.yml} file.* See {@link io.github.jhipster.config.JHipsterProperties} for a good example.*/@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false)public class ApplicationProperties {private String host;private String mpUrl;private String merchantName;/*** @return the host*/public String getHost() {return host;}/*** @param host the host to set*/public void setHost(String host) {this.host = host;}/*** @return String return the mpUrl*/public String getMpUrl() {return mpUrl;}/*** @param mpUrl the mpUrl to set*/public void setMpUrl(String mpUrl) {this.mpUrl = mpUrl;}public String getMerchantName() {return merchantName;}public void setMerchantName(String merchantName) {this.merchantName = merchantName;}}

C:\zhuyrWork\外卖项目代码\meal\src\main\resources\config\application.yml
# ===================================================================# Application specific properties# Add your own application properties here, see the ApplicationProperties class# to have type-safe configuration, like in the JHipsterProperties above## More documentation is available at:# https://www.jhipster.tech/common-application-properties/# ===================================================================application:host: 'http://localhost'mp-url: 'http://localhost/#/'merchant-name: '商户名称'

C:\zhuyrWork\外卖项目代码\meal\src\main\java\com\mayocase\meal\service\PayService.java
package com.mayocase.meal.service;import com.lly835.bestpay.constants.WxPayConstants;import com.lly835.bestpay.model.PayResponse;import com.lly835.bestpay.model.RefundResponse;import com.lly835.bestpay.model.wxpay.WxPayApi;import com.lly835.bestpay.model.wxpay.response.WxPayAsyncResponse;import com.lly835.bestpay.model.wxpay.response.WxPayRefundResponse;import com.lly835.bestpay.model.wxpay.response.WxPaySyncResponse;import com.lly835.bestpay.service.impl.WxPaySignature;import com.lly835.bestpay.utils.JsonUtil;import com.lly835.bestpay.utils.MoneyUtil;import com.lly835.bestpay.utils.RandomUtil;import com.lly835.bestpay.utils.XmlUtil;import com.mayocase.meal.client.PrintClient;import com.mayocase.meal.config.MyX509TrustManager;import com.mayocase.meal.domain.SalesOrder;import com.mayocase.meal.domain.SalesOrderMealItem;import com.mayocase.meal.domain.SetMealDetails;import com.mayocase.meal.domain.enumeration.OrderStatus;import com.mayocase.meal.domain.enumeration.OrderType;import com.mayocase.meal.service.dto.MealDTO;import com.mayocase.meal.service.dto.MealInventoryDTO;import com.mayocase.meal.service.dto.MerchantDTO;import com.mayocase.meal.service.dto.SalesOrderDTO;import com.mayocase.meal.service.utls.WXPayUtil;import com.mayocase.meal.web.rest.errors.ResultEnum;import com.mayocase.meal.web.rest.errors.SellerException;import okhttp3.MediaType;import okhttp3.OkHttpClient;import okhttp3.RequestBody;import org.apache.commons.compress.utils.IOUtils;import org.apache.http.ssl.SSLContexts;import org.redisson.RedissonMultiLock;import org.redisson.api.RBucket;import org.redisson.api.RLock;import org.redisson.api.RedissonClient;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.BeanUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Value;import org.springframework.cloud.cloudfoundry.com.fasterxml.jackson.databind.ObjectMapper;import org.springframework.orm.ObjectOptimisticLockingFailureException;import org.springframework.stereotype.Service;import org.springframework.transaction.annotation.Transactional;import org.springframework.util.StringUtils;import retrofit2.Call;import retrofit2.Response;import retrofit2.Retrofit;import retrofit2.converter.simplexml.SimpleXmlConverterFactory;import javax.net.ssl.SSLContext;import java.io.File;import java.io.FileInputStream;import java.io.IOException;import java.math.BigDecimal;import java.security.KeyStore;import java.time.*;import java.util.*;import java.util.concurrent.TimeUnit;@Service@Transactionalpublic class PayService {private final Logger log = LoggerFactory.getLogger(PayService.class);private static final String ORDER_NAME = "营养餐";@Value("${weixin.mp.appID}")private String wxMPAppID;@Value("${weixin.mp.mchId}")private String wxMchId;@Value("${weixin.mp.notifyUrl}")private String notifyUrl;@Value("${weixin.mp.mchKey}")private String mchKey;@Value("${weixin.mp.keyPath}")private String keyPath;@Autowiredprivate MealInventoryService mealInventoryService;@Autowiredprivate SalesOrderService salesOrderService;@Autowiredprivate PrintClient printClient;@Autowiredprivate MerchantService merchantService;@Autowiredprivate RedissonClient redissonClient;public RefundResponse refund(SalesOrder order, Integer refundFee) {Map<String, String> paraMap = new HashMap<String, String>();paraMap.put("out_trade_no",order.getSn());paraMap.put("out_refund_no",order.getSn());paraMap.put("total_fee", order.getPaymentInfo().getTotalPaymentAmount().toString());paraMap.put("refund_fee", refundFee.toString());paraMap.put("appid",wxMPAppID);paraMap.put("mch_id",wxMchId);paraMap.put("nonce_str", RandomUtil.getRandomStr());try {String sign = WXPayUtil.generateSignature(paraMap, mchKey);paraMap.put("sign", sign);//初始化证书this.initSSLContext();MyX509TrustManager myX509TrustManager = new MyX509TrustManager();OkHttpClient clinet = new OkHttpClient.Builder().sslSocketFactory(sslContext.getSocketFactory(), myX509TrustManager).build();Retrofit retrofit = new Retrofit.Builder().baseUrl(WxPayConstants.WXPAY_GATEWAY).addConverterFactory(SimpleXmlConverterFactory.create()).client(clinet).build();String xml = WXPayUtil.mapToXml(paraMap);//将所有参数(map)转xml格式RequestBody body = RequestBody.create(MediaType.parse("application/xml; charset=utf-8"), xml);Call<WxPayRefundResponse> call = retrofit.create(WxPayApi.class).refund(body);Response<WxPayRefundResponse> retrofitResponse = null;try {retrofitResponse = call.execute();} catch (IOException e) {e.printStackTrace();}if (!retrofitResponse.isSuccessful()) {log.error("【微信退款】发起退款, 网络异常,订单编号:" + order.getSn());}WxPayRefundResponse response = retrofitResponse.body();log.warn("退款结果:" + response.toString());log.warn("退款结果:" + response);if (!response.getReturnCode().equals(WxPayConstants.SUCCESS)) {log.error("【微信退款】发起退款, returnCode != SUCCESS, returnMsg = " + response.getReturnMsg() + ", 订单编号:" + order.getSn());throw new SellerException(ResultEnum.REFUND_ERROR);}if (!response.getResultCode().equals(WxPayConstants.SUCCESS)) {log.error("【微信退款】发起退款, resultCode != SUCCESS, err_code = " + response.getErrCode() + " err_code_des=" + response.getErrCodeDes() +", 订单编号:" + order.getSn());log.error("-----------" + response.getErrCodeDes().equals("订单已全额退款"));//微信公众号人工退款if(response.getErrCodeDes().equals("订单已全额退款")){return null;}else{throw new SellerException(ResultEnum.REFUND_ERROR);}}return this.buildRefundResponse(response);}catch (Exception e){e.printStackTrace();throw new SellerException(ResultEnum.REFUND_ERROR);}}private PayResponse buildPayResponse(WxPaySyncResponse response) {String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);String nonceStr = RandomUtil.getRandomStr();String packAge = "prepay_id=" + response.getPrepayId();String signType = "MD5";//先构造要签名的mapMap<String, String> map = new HashMap<>();map.put("appId", response.getAppid());map.put("timeStamp", timeStamp);map.put("nonceStr", nonceStr);map.put("package", packAge);map.put("signType", signType);PayResponse payResponse = new PayResponse();payResponse.setAppId(response.getAppid());payResponse.setTimeStamp(timeStamp);payResponse.setNonceStr(nonceStr);payResponse.setPackAge(packAge);payResponse.setSignType(signType);payResponse.setPaySign(WxPaySignature.sign(map, mchKey));return payResponse;}public PayResponse asyncNotify(String notifyData) {//xml解析为对象WxPayAsyncResponse asyncResponse = (WxPayAsyncResponse) XmlUtil.fromXML(notifyData, WxPayAsyncResponse.class);//签名校验if (!WxPaySignature.verify(buildMap(asyncResponse), mchKey)) {log.error("【微信支付异步通知】签名验证失败, response={}", asyncResponse);throw new RuntimeException("【微信支付异步通知】签名验证失败");}if(!asyncResponse.getReturnCode().equals(WxPayConstants.SUCCESS)) {throw new RuntimeException("【微信支付异步通知】发起支付, returnCode != SUCCESS, returnMsg = " + asyncResponse.getReturnMsg());}//该订单已支付直接返回if (!asyncResponse.getResultCode().equals(WxPayConstants.SUCCESS)&& asyncResponse.getErrCode().equals("ORDERPAID")) {return buildPayResponse(asyncResponse);}if (!asyncResponse.getResultCode().equals(WxPayConstants.SUCCESS)) {throw new RuntimeException("【微信支付异步通知】发起支付, resultCode != SUCCESS, err_code = " + asyncResponse.getErrCode() + " err_code_des=" + asyncResponse.getErrCodeDes());}return buildPayResponse(asyncResponse);}public PayResponse create(SalesOrderDTO orderDTO, String openId) {//拼接统一下单地址参数Map<String, String> paraMap = new HashMap<String, String>();//获取请求ip地址paraMap.put("appid", wxMPAppID);paraMap.put("body", ORDER_NAME);paraMap.put("mch_id", wxMchId);paraMap.put("nonce_str", WXPayUtil.generateNonceStr());paraMap.put("openid", openId);paraMap.put("out_trade_no", orderDTO.getSn());//订单号paraMap.put("spbill_create_ip", "8.8.8.8");paraMap.put("total_fee", orderDTO.getPaymentInfo().getTotalPaymentAmount().toString());paraMap.put("trade_type", "JSAPI");paraMap.put("notify_url",notifyUrl);// 此路径是微信服务器调用支付结果通知路径随意写try{String sign = WXPayUtil.generateSignature(paraMap, mchKey);paraMap.put("sign", sign);String xml = WXPayUtil.mapToXml(paraMap);//将所有参数(map)转xml格式log.info("请求参数{}" + xml);RequestBody body = RequestBody.create(MediaType.parse("application/xml; charset=utf-8"),xml);Retrofit retrofit = new Retrofit.Builder().baseUrl(WxPayConstants.WXPAY_GATEWAY).addConverterFactory(SimpleXmlConverterFactory.create()).build();Call<WxPaySyncResponse> call = retrofit.create(WxPayApi.class).unifiedorder(body);Response<WxPaySyncResponse> retrofitResponse = null;try{retrofitResponse = call.execute();}catch (IOException e) {e.printStackTrace();}if (!retrofitResponse.isSuccessful()) {throw new RuntimeException("【微信统一支付】发起支付, 网络异常");}WxPaySyncResponse response = retrofitResponse.body();log.info("【微信统一支付】response={}", JsonUtil.toJson(response));if(!response.getReturnCode().equals(WxPayConstants.SUCCESS)) {throw new RuntimeException("【微信统一支付】发起支付, returnCode != SUCCESS, returnMsg = " + response.getReturnMsg());}if (!response.getResultCode().equals(WxPayConstants.SUCCESS)) {throw new RuntimeException("【微信统一支付】发起支付, resultCode != SUCCESS, err_code = " + response.getErrCode() + " err_code_des=" + response.getErrCodeDes());}return this.buildPayResponse(response);}catch (Exception e){e.printStackTrace();throw new SellerException(ResultEnum.PARAM_ERROR.getCode(), "sign失败");}}public void zeroNotify(String sn) throws Exception{log.info("支付为0开始");Optional<SalesOrderDTO> op = salesOrderService.getBySn(sn);//判断订单是否存在if (!op.isPresent()) {log.info("【微信支付】异步通知, 订单不存在, orderId={}"+ sn);throw new SellerException(ResultEnum.ORDER_NOT_EXIST);}SalesOrderDTO orderDTO = op.get();if(orderDTO.getStatus().equals(OrderStatus.PAID)){return;}//修改订单的支付状态和修改实际数量try {salesOrderService.paid(orderDTO);}catch (ObjectOptimisticLockingFailureException s){op = salesOrderService.getBySn(sn);salesOrderService.paid(op.get());}LocalDateTime create = LocalDateTime.ofInstant(orderDTO.getDiningDate(), ZoneOffset.UTC);LocalDate localDate = create.toLocalDate();LocalDateTime localDateTimeNow = LocalDateTime.of(localDate.getYear(), localDate.getMonth(), localDate.getDayOfMonth(),0,0,0);localDateTimeNow = localDateTimeNow.minusHours(8);Instant from = localDateTimeNow.toInstant(ZoneOffset.UTC);log.info("查询reids时间段:" + from);//当天的去除库存占用,添加到销量boolean istoday = false;LocalDate localDate1 = LocalDate.now();LocalDateTime localDateTime = LocalDateTime.of(localDate1.getYear(), localDate1.getMonth(), localDate1.getDayOfMonth(),0,0,0);localDateTime = localDateTime.minusHours(8);Instant todayFrom = localDateTime.toInstant(ZoneOffset.UTC);Instant todayTo = localDateTime.minusDays(-1).toInstant(ZoneOffset.UTC);if(orderDTO.getDiningDate().compareTo(todayFrom)>0 && orderDTO.getDiningDate().compareTo(todayTo) <0){istoday = true;}log.info("是否是今天:" + istoday);RLock rLockStock = redissonClient.getLock("create");boolean lock = rLockStock.tryLock(10,10,TimeUnit.SECONDS);if(lock){Iterator<SalesOrderMealItem> it = orderDTO.getMealItems().iterator();while (it.hasNext()){SalesOrderMealItem item = it.next();if(item.isSetMeal()){Iterator<SetMealDetails> detailsIterator = item.getSetMealDetails().iterator();while (detailsIterator.hasNext()){SetMealDetails details = detailsIterator.next();RBucket<Map<String,Object>> stock = redissonClient.getBucket(from+orderDTO.getShopSn()+details.getMealPartSn());Map<String,Object> m = stock.get();m.put("soldQuantity", (Integer.valueOf(m.get("soldQuantity").toString()) + details.getQuantity()));if(istoday) {log.info("当天的去除占用--------------------------------");m.put("heldQuantity", (Integer.valueOf(m.get("heldQuantity").toString()) - details.getQuantity()));}stock.set(m);}}else{RBucket<Map<String,Object>> stock = redissonClient.getBucket(from+orderDTO.getShopSn()+item.getMealSn());Map<String,Object> m = stock.get();m.put("soldQuantity", (Integer.valueOf(m.get("soldQuantity").toString()) + item.getQuantity()));if(istoday) {log.info("当天的去除占用--------------------------------");m.put("heldQuantity", (Integer.valueOf(m.get("heldQuantity").toString()) - item.getQuantity()));}stock.set(m);}}}List<MerchantDTO> list = merchantService.findAll();try {printClient.print(op.get(), (list == null || list.size() == 0) ? new MerchantDTO() : list.get(0),3);}catch (Exception e){e.printStackTrace();}log.info("微信回调流程走完");}@Transactionalpublic PayResponse notify(String notifyData) throws Exception{log.info("微信支付回调{}" + notifyData);PayResponse payResponse = this.asyncNotify(notifyData);log.info("【微信支付】异步通知, payResponse={}"+ JsonUtil.toJson(payResponse));RLock rLock = redissonClient.getLock(payResponse.getOrderId());try {rLock.lock();//查询订单Optional<SalesOrderDTO> op = salesOrderService.getBySn(payResponse.getOrderId());//判断订单是否存在if (!op.isPresent()) {log.info("【微信支付】异步通知, 订单不存在, orderId={}"+ payResponse.getOrderId());throw new SellerException(ResultEnum.ORDER_NOT_EXIST);}SalesOrderDTO orderDTO = op.get();if(orderDTO.getStatus().equals(OrderStatus.PAID)){return payResponse;}BigDecimal num1 = new BigDecimal("100");BigDecimal bigDecimal = new BigDecimal(payResponse.getOrderAmount().toString());bigDecimal = bigDecimal.multiply(num1);//判断金额是否一致(0.10 0.1)BigDecimal b = new BigDecimal(orderDTO.getPaymentInfo().getTotalPaymentAmount());if (bigDecimal.compareTo(b) != 0) {log.info("【微信支付】异步通知, 订单金额不一致, orderId={}, 微信通知金额={}, 系统金额={}"+payResponse.getOrderId()+","+payResponse.getOrderAmount()+","+orderDTO.getPaymentInfo().getTotalPaymentAmount());throw new SellerException(ResultEnum.WXPAY_NOTIFY_MONEY_VERIFY_ERROR);}LocalDateTime create = LocalDateTime.ofInstant(orderDTO.getDiningDate(), ZoneOffset.UTC);LocalDate localDate = create.toLocalDate();LocalDateTime localDateTimeNow = LocalDateTime.of(localDate.getYear(), localDate.getMonth(), localDate.getDayOfMonth(),0,0,0);localDateTimeNow = localDateTimeNow.minusHours(8);Instant from = localDateTimeNow.toInstant(ZoneOffset.UTC);log.info("查询reids时间段:" + from);//当天的去除库存占用,添加到销量boolean istoday = false;LocalDate localDate1 = LocalDate.now();LocalDateTime localDateTime = LocalDateTime.of(localDate1.getYear(), localDate1.getMonth(), localDate1.getDayOfMonth(),0,0,0);localDateTime = localDateTime.minusHours(8);Instant todayFrom = localDateTime.toInstant(ZoneOffset.UTC);Instant todayTo = localDateTime.minusDays(-1).toInstant(ZoneOffset.UTC);if(orderDTO.getDiningDate().compareTo(todayFrom)>0 && orderDTO.getDiningDate().compareTo(todayTo) <0){istoday = true;}log.info("是否是今天:" + istoday);RLock rLockStock = redissonClient.getLock("create");boolean lock = rLockStock.tryLock(10,10,TimeUnit.SECONDS);if(lock){Iterator<SalesOrderMealItem> it = orderDTO.getMealItems().iterator();while (it.hasNext()){SalesOrderMealItem item = it.next();if(item.isSetMeal()){Iterator<SetMealDetails> detailsIterator = item.getSetMealDetails().iterator();while (detailsIterator.hasNext()){SetMealDetails details = detailsIterator.next();RBucket<Map<String,Object>> stock = redissonClient.getBucket(from+orderDTO.getShopSn()+details.getMealPartSn());Map<String,Object> m = stock.get();m.put("soldQuantity", (Integer.valueOf(m.get("soldQuantity").toString()) + details.getQuantity()*item.getQuantity()));if(istoday) {log.info("当天的去除占用--------------------------------");m.put("heldQuantity", (Integer.valueOf(m.get("heldQuantity").toString()) - details.getQuantity()*item.getQuantity()));}else{log.info("预售的添加预售数量--------------------------------");m.put("reservedQuantity", (Integer.valueOf(m.get("reservedQuantity").toString()) + details.getQuantity()*item.getQuantity()));}stock.set(m);}}else{RBucket<Map<String,Object>> stock = redissonClient.getBucket(from+orderDTO.getShopSn()+item.getMealSn());Map<String,Object> m = stock.get();m.put("soldQuantity", (Integer.valueOf(m.get("soldQuantity").toString()) + item.getQuantity()));if(istoday) {log.info("当天的去除占用--------------------------------");m.put("heldQuantity", (Integer.valueOf(m.get("heldQuantity").toString()) - item.getQuantity()));}else{log.info("预售的添加预售数量--------------------------------");m.put("reservedQuantity", (Integer.valueOf(m.get("reservedQuantity").toString()) + item.getQuantity()));}stock.set(m);}}}//打印小票SalesOrder salesOrder = salesOrderService.paid(orderDTO);List<MerchantDTO> list = merchantService.findAll();try {printClient.print(salesOrder, (list == null || list.size() == 0) ? new MerchantDTO() : list.get(0),3);}catch (Exception e){e.printStackTrace();}log.info("微信回调流程走完");return payResponse;}catch (Exception e){e.printStackTrace();throw new Exception();}finally {rLock.unlock();}}private Map<String, String> buildMap(WxPayAsyncResponse response) {Map<String, String> map = new HashMap<>();map.put("return_code", response.getReturnCode());map.put("return_msg", response.getReturnMsg());map.put("appid", response.getAppid());map.put("mch_id", response.getMchId());map.put("device_info", response.getDeviceInfo());map.put("nonce_str", response.getNonceStr());map.put("sign", response.getSign());map.put("result_code", response.getResultCode());map.put("err_code", response.getErrCode());map.put("err_code_des", response.getErrCodeDes());map.put("openid", response.getOpenid());map.put("is_subscribe", response.getIsSubscribe());map.put("trade_type", response.getTradeType());map.put("bank_type", response.getBankType());map.put("total_fee", String.valueOf(response.getTotalFee()));map.put("fee_type", response.getFeeType());map.put("cash_fee", response.getCashFee());map.put("cash_fee_type", response.getCashFeeType());map.put("coupon_fee", response.getCouponFee());map.put("coupon_count", response.getCouponCount());map.put("transaction_id", response.getTransactionId());map.put("out_trade_no", response.getOutTradeNo());map.put("attach", response.getAttach());map.put("time_end", response.getTimeEnd());return map;}private PayResponse buildPayResponse(WxPayAsyncResponse response) {PayResponse payResponse = new PayResponse();payResponse.setOrderAmount(MoneyUtil.Fen2Yuan(response.getTotalFee()));payResponse.setOrderId(response.getOutTradeNo());payResponse.setOutTradeNo(response.getTransactionId());return payResponse;}/*** 初始化证书* @return*/private SSLContext initSSLContext() {FileInputStream inputStream = null;try {String jarPath = this.getClass().getClassLoader().getResource(keyPath).getPath();log.info("文件路径------------------------" + jarPath);inputStream = new FileInputStream(new File(jarPath));} catch (IOException e) {throw new RuntimeException("读取微信商户证书文件出错", e);}try {KeyStore keystore = KeyStore.getInstance("PKCS12");char[] partnerId2charArray = wxMchId.toCharArray();keystore.load(inputStream, partnerId2charArray);this.sslContext = SSLContexts.custom().loadKeyMaterial(keystore, partnerId2charArray).build();return this.sslContext;} catch (Exception e) {e.printStackTrace();throw new RuntimeException("证书文件有问题,请核实!", e);} finally {IOUtils.closeQuietly(inputStream);}}/*** 证书内容*/private SSLContext sslContext;private RefundResponse buildRefundResponse(WxPayRefundResponse response) {RefundResponse refundResponse = new RefundResponse();refundResponse.setOrderId(response.getOutTradeNo());refundResponse.setOrderAmount(MoneyUtil.Fen2Yuan(response.getTotalFee()));refundResponse.setOutTradeNo(response.getTransactionId());refundResponse.setRefundId(response.getOutRefundNo());refundResponse.setOutRefundNo(response.getRefundId());return refundResponse;}}
