1、controller代码
package com.saburo.server.controller.weixin;import cn.dev33.satoken.annotation.SaIgnore; import com.gcode.common.core.R; import com.saburo.server.common.dto.WeiXinUserInfoDto; import com.saburo.server.common.dto.money.PayDto; import com.saburo.server.common.vo.money.PayRollBackResultVo; import com.saburo.server.common.vo.user.UserInfoVo; import com.saburo.server.service.weixin.MiniProgramService; import com.saburo.server.common.vo.weixin.WeiXinVo; import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse; import com.wechat.pay.java.service.payments.model.Transaction; import com.wechat.pay.java.service.refund.model.Refund; import com.wechat.pay.java.service.refund.model.RefundNotification; import kotlin.Result; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*;import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.math.BigDecimal;/*** 微信小程序相关接口** @author liubh*/ @RestController @RequestMapping("/wx") @RequiredArgsConstructor public class MiniProgramController {private final MiniProgramService miniProgramService;/*** 获取微信小程序访问accessToken** @return accessToken*/@SaIgnore@GetMapping("/accessToken")public R<String> getAccessToken() {return R.ok(miniProgramService.getAccessToken());}/*** 获取微信小程序openId** @param weiXinUserInfoDto 微信小程序用户信息* @return 用户信息*/@SaIgnore@PostMapping("/loginMessageByCode")public R<WeiXinVo> getWeiXinLoginOpenId(@RequestBody WeiXinUserInfoDto weiXinUserInfoDto) {return R.ok(miniProgramService.getWeiXinLoginOpenId(weiXinUserInfoDto));}/*** 微信登录** @param weiXinUserInfo 用户信息* @return 用户信息*/@SaIgnore@PostMapping("/mobileLogin")public R<UserInfoVo> login(@RequestBody WeiXinUserInfoDto weiXinUserInfo) {return R.ok(miniProgramService.wxLogin(weiXinUserInfo));}/*** 获取微信小程序手机号** @param code code码* @return 登录信息*/@SaIgnore@GetMapping("/phone")public R<String> getPhoneNumber(@RequestParam("code") String code) {return R.ok(miniProgramService.getPhoneNumber(code));}/*** 微信支付** @param payDto 支付信息* @return 支付信息*/@SaIgnore@PostMapping("/pay")public R<PrepayWithRequestPaymentResponse> weiXinPay(@RequestBody PayDto payDto) {return R.ok(miniProgramService.weiXinPay(payDto));}/*** 微信支付回调 JSAPI 下单回调** @return 回调结果*/@SaIgnore@PostMapping("/paymentCallback")public R<Transaction> wxPaymentCallback(HttpServletRequest request) {return R.ok(miniProgramService.wxPaymentCallback(request));}/*** 退款** @param orderId 订单id* @param amount 金额* @return 退款信息*/@GetMapping("/refundPayment")public R<Refund> refundPayment(@RequestParam String orderId, @RequestParam BigDecimal amount) {Refund refund = miniProgramService.refundIndentPayment(orderId, amount);return R.ok(refund);}/*** 退款回调* @param request 请求* @return 退款信息*/@PostMapping("/refund/black")public R<Boolean> endRefundIndent(HttpServletRequest request) {return R.ok(miniProgramService.getRefundNotification(request));}}
2、接口
package com.saburo.server.service.weixin;import com.saburo.server.common.dto.WeiXinUserInfoDto; import com.saburo.server.common.dto.money.PayDto; import com.saburo.server.common.vo.money.PayRollBackResultVo; import com.saburo.server.common.vo.user.UserInfoVo; import com.saburo.server.common.vo.weixin.WeiXinVo; import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse; import com.wechat.pay.java.service.payments.model.Transaction; import com.wechat.pay.java.service.refund.model.Refund; import com.wechat.pay.java.service.refund.model.RefundNotification;import javax.servlet.http.HttpServletRequest; import java.math.BigDecimal;/*** @Author: liubh* @Date: 2024/10/28 13:37*/ public interface MiniProgramService {/*** 获取微信小程序访问accessToken* @return accessToken*/String getAccessToken();/*** 微信小程序登录* @return 微信登录信息*/WeiXinVo getWeiXinLoginOpenId(WeiXinUserInfoDto weiXinUserInfoDto);/*** 获取微信小程序手机号** @param code code码* @return 登录信息*/String getPhoneNumber(String code);/*** 微信小程序登录* @return 微信登录信息*/UserInfoVo wxLogin(WeiXinUserInfoDto weiXinUserInfo);/*** 微信支付* @param payDto 支付信息* @return 支付信息*/PrepayWithRequestPaymentResponse weiXinPay(PayDto payDto);/*** 微信支付回调 JSAPI 下单回调* @return 回调结果*/Transaction wxPaymentCallback(HttpServletRequest request);/*** 退款* @param orderId 订单id* @param amount 金额* @return 退款信息*/Refund refundIndentPayment(String orderId, BigDecimal amount);/*** 退款回调* @param request 请求* @return 退款是否成功*/boolean getRefundNotification(HttpServletRequest request);}
3、实现类
package com.saburo.server.service.weixin.impl;import cn.dev33.satoken.secure.SaSecureUtil; import cn.dev33.satoken.stp.SaLoginModel; import cn.dev33.satoken.stp.SaTokenInfo; import cn.dev33.satoken.stp.StpUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.http.HttpUtil; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.gcode.common.exception.ApiException; import com.gcode.common.utils.DateUtil; import com.gcode.webmvc.entity.SysUserEntity; import com.gcode.webmvc.mapper.SysUserMapper; import com.gcode.webmvc.utils.IpUtils; import com.saburo.server.common.dto.WeiXinUserInfoDto; import com.saburo.server.common.dto.money.PayDto; import com.saburo.server.common.enums.OrderMachineStateEnum; import com.saburo.server.common.enums.OrderPowerBankStateEnum; import com.saburo.server.common.properties.WeiXinConfigureProperties; import com.saburo.server.common.utils.AesCbcUtil; import com.saburo.server.common.utils.NumberComputeUtil; import com.saburo.server.common.vo.user.UserInfoVo; import com.saburo.server.entity.money.MoneyPointEntity; import com.saburo.server.entity.money.MoneySetPointEntity; import com.saburo.server.entity.money.MoneyWaterEntity; import com.saburo.server.entity.product.PowerBankOrderEntity; import com.saburo.server.entity.product.ProductInfoEntity; import com.saburo.server.entity.product.ProductOrderDetailEntity; import com.saburo.server.entity.product.ProductOrderEntity; import com.saburo.server.entity.user.UserInfoEntity; import com.saburo.server.mapper.money.MoneyPointMapper; import com.saburo.server.mapper.money.MoneySetPointMapper; import com.saburo.server.mapper.money.MoneyWaterMapper; import com.saburo.server.mapper.product.PowerBankOrderMapper; import com.saburo.server.mapper.product.ProductInfoMapper; import com.saburo.server.mapper.product.ProductOrderDetailMapper; import com.saburo.server.mapper.product.ProductOrderMapper; import com.saburo.server.mapper.user.UserInfoMapper; import com.saburo.server.service.money.MoneyWaterService; import com.saburo.server.service.weixin.MiniProgramService; import com.saburo.server.common.vo.weixin.WeiXinVo; import com.wechat.pay.java.core.notification.NotificationParser; import com.wechat.pay.java.core.notification.RequestParam; import com.wechat.pay.java.service.payments.jsapi.model.Amount; import com.wechat.pay.java.service.payments.jsapi.model.Payer; import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest; import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse; import com.wechat.pay.java.service.payments.model.Transaction; import com.wechat.pay.java.service.refund.model.*; import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.BeanUtils; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.math.BigDecimal; import java.math.RoundingMode; import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; import java.util.Date; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock;/*** 参考:https://blog.csdn.net/weixin_66709611/article/details/142059592** @Author: liubh* @Date: 2024/10/28 13:38*/ @Service @Slf4j @RequiredArgsConstructor public class MiniProgramServiceImpl implements MiniProgramService {private final WeiXinConfigureProperties weiXinConfigureProperties;private final StringRedisTemplate stringRedisTemplate;private final UserInfoMapper userInfoMapper;private final SysUserMapper sysUserMapper;private final MoneyWaterService moneyWaterService;private final ProductOrderMapper productOrderMapper;private final PowerBankOrderMapper powerBankOrderMapper;private final MoneyPointMapper moneyPointMapper;private final MoneySetPointMapper moneySetPointMapper;private final MoneyWaterMapper moneyWaterMapper;private final String WX_TOKEN = "wx_token";private final ReentrantLock lock = new ReentrantLock();/*** 请求参数*/public static RequestParam requestParam = null;/*** 获取微信小程序访问accessToken** @return accessToken*/@Overridepublic String getAccessToken() {String accessToken = stringRedisTemplate.opsForValue().get(WX_TOKEN + ":" + weiXinConfigureProperties.getAppId());if (StringUtils.isEmpty(accessToken)) {WeiXinVo miniProgramAccessToken = getMiniProgramAccessToken();if (miniProgramAccessToken != null) {return miniProgramAccessToken.getAccessToken();}}return accessToken;}/*** 微信小程序登录** @return 微信登录信息*/@Overridepublic WeiXinVo getWeiXinLoginOpenId(WeiXinUserInfoDto weiXinUserInfoDto) {String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + weiXinConfigureProperties.getAppId() + "&secret=" + weiXinConfigureProperties.getAppSecret() +"&js_code=" + weiXinUserInfoDto.getCode() + "&grant_type=authorization_code";String response = null;try {response = HttpUtil.get(url);} catch (Exception e) {log.info("获取登录信息失败 " + e.getMessage());throw new ApiException("获取登录信息失败");}log.info("获取登录信息 {}", response);WeiXinVo weiXinVo = JSONObject.parseObject(response, WeiXinVo.class);if (weiXinVo != null) {if (StringUtils.isNotEmpty(weiXinVo.getOpenId())) {// 解密用户信息String userInfo = null;try {userInfo = AesCbcUtil.decrypt(weiXinUserInfoDto.getEncryptedData(), weiXinVo.getSessionKey(), weiXinUserInfoDto.getIv(), "UTF-8");} catch (Exception e) {throw new ApiException("解析用户信息失败");}JSONObject userInfoJSON = JSON.parseObject(userInfo);String nickName = userInfoJSON.getString("nickName");String avatarUrl = userInfoJSON.getString("avatarUrl");weiXinVo.setNickName(nickName);weiXinVo.setAvatarUrl(avatarUrl);weiXinVo.setGender((String) userInfoJSON.get("gender"));weiXinVo.setCity((String) userInfoJSON.get("city"));weiXinVo.setProvince((String) userInfoJSON.get("province"));weiXinVo.setCountry((String) userInfoJSON.get("country"));//获取手机号String phoneNumber = getPhoneNumber(weiXinUserInfoDto.getCode());weiXinVo.setPhoneNumber(phoneNumber);weiXinVo.setPhoneInfo(new WeiXinVo.PhoneInfo().setPhoneNumber(phoneNumber));log.info("获取用户信息 {}", weiXinVo);return weiXinVo;}}return weiXinVo;}/*** 获取微信小程序手机号** @param code code码* @return 登录信息*/@Overridepublic String getPhoneNumber(String code) {String url = "weiXinApi/wxa/business/getuserphonenumber?access_token=ACCESS_TOKEN";url = url.replace("weiXinApi", weiXinConfigureProperties.getUrl()).replace("ACCESS_TOKEN", getAccessToken());JSONObject jsonObject = new JSONObject();jsonObject.put("code", code);log.info("获取手机号参数 {}", jsonObject.toJSONString());String response = null;try {response = HttpUtil.post(url, jsonObject.toJSONString());} catch (Exception e) {throw new ApiException("获取手机号失败");}log.info("获取手机号返回结果 {}", response);WeiXinVo weiXinVo = JSONObject.parseObject(response, WeiXinVo.class);if (weiXinVo != null) {WeiXinVo.PhoneInfo phoneInfo = weiXinVo.getPhoneInfo();if (phoneInfo != null) {return phoneInfo.getPhoneNumber();}} else {throw new ApiException("获取手机号失败");}return null;}/*** 微信小程序登录** @return 微信登录信息*/@Transactional(rollbackFor = Exception.class)@Overridepublic UserInfoVo wxLogin(WeiXinUserInfoDto weiXinUserInfo) {UserInfoVo userInfoVo = new UserInfoVo();//判断用户是否注册,如果没有注册,注册用户,注册的用户,直接登录LambdaQueryWrapper<UserInfoEntity> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(UserInfoEntity::getOpenId, weiXinUserInfo.getOpenId());List<UserInfoEntity> userInfos = userInfoMapper.selectList(queryWrapper);SysUserEntity entity = new SysUserEntity();if (CollectionUtils.isNotEmpty(userInfos)) {UserInfoEntity userInfo = userInfos.get(0);entity.setNickName(userInfo.getNickName());//手机号作为用户名 entity.setUserName(userInfo.getPhoneNumber());entity.setAvatar(userInfo.getAvatarUrl());entity.setPhoneNumber(userInfo.getPhoneNumber());entity.setId(userInfo.getId());StpUtil.login(entity.getId(), SaLoginModel.create().build());StpUtil.getSession().set("LoginUser", entity);BeanUtils.copyProperties(userInfos.get(0), userInfoVo);} else {//保存系统用户表SysUserEntity sysUserEntity = getSysUserEntity(weiXinUserInfo);//保存用户信息表UserInfoEntity userInfo = new UserInfoEntity();//系统用户表id和用户表id设置成一样的 userInfo.setId(sysUserEntity.getId());userInfo.setOpenId(weiXinUserInfo.getOpenId());userInfo.setNickName(weiXinUserInfo.getNickName());userInfo.setAvatarUrl(weiXinUserInfo.getAvatarUrl());userInfo.setPhoneNumber(weiXinUserInfo.getPhoneNumber());userInfoMapper.insert(userInfo);entity.setNickName(userInfo.getNickName());//手机号作为用户名 entity.setUserName(userInfo.getPhoneNumber());entity.setAvatar(userInfo.getAvatarUrl());entity.setPhoneNumber(userInfo.getPhoneNumber());entity.setId(sysUserEntity.getId());StpUtil.login(entity.getId(), SaLoginModel.create().build());StpUtil.getSession().set("LoginUser", entity);BeanUtils.copyProperties(userInfo, userInfoVo);}// 获取当前会话的 token 信息参数SaTokenInfo tokenInfo = StpUtil.getTokenInfo();BeanUtils.copyProperties(tokenInfo, userInfoVo);return userInfoVo;}/*** 设置系统用户信息** @param weiXinUserInfo 系统用户信息* @return 系统用户信息*/private SysUserEntity getSysUserEntity(WeiXinUserInfoDto weiXinUserInfo) {SysUserEntity sysUserEntity = new SysUserEntity();sysUserEntity.setUserName(weiXinUserInfo.getPhoneNumber());sysUserEntity.setNickName(weiXinUserInfo.getNickName());//默认密码,手机号后六位if (StringUtils.isNotEmpty(weiXinUserInfo.getPhoneNumber())) {String passWord = weiXinUserInfo.getPhoneNumber().substring(weiXinUserInfo.getPhoneNumber().length() - 6);sysUserEntity.setPassword(SaSecureUtil.md5(passWord));}sysUserEntity.setAvatar(weiXinUserInfo.getAvatarUrl());sysUserEntity.setStatus("0");sysUserEntity.setDelFlag(false);sysUserEntity.setPhoneNumber(weiXinUserInfo.getPhoneNumber());sysUserEntity.setUserType("01");sysUserEntity.setLoginIp(IpUtils.getIpAddr());sysUserEntity.setLoginDate(DateUtil.now());sysUserMapper.insert(sysUserEntity);return sysUserEntity;}/*** 微信支付** @param payDto 支付信息* @return 支付信息*/@Overridepublic PrepayWithRequestPaymentResponse weiXinPay(PayDto payDto) { // PayVo payVo = new PayVo();PrepayRequest request = new PrepayRequest();//构建支付参数 request.setAppid(weiXinConfigureProperties.getAppId());request.setMchid(weiXinConfigureProperties.getMchId());request.setDescription(payDto.getOrderCode() + "order");request.setNotifyUrl(weiXinConfigureProperties.getNotifyUrl());request.setOutTradeNo(payDto.getOrderCode());Amount amount = new Amount();amount.setCurrency("CNY");BigDecimal multiply = payDto.getAmount().multiply(new BigDecimal(100));amount.setTotal(multiply.setScale(0, RoundingMode.HALF_UP).intValue());Payer payer = new Payer();payer.setOpenid(payDto.getOpenId());request.setPayer(payer);log.info("微信支付参数 {}", request);PrepayWithRequestPaymentResponse response;try {response = weiXinConfigureProperties.getJsapiServiceExtension().prepayWithRequestPayment(request);return response;} catch (Exception e) {log.info("微信小程序支付异常 {}", e.getMessage());throw new ApiException("支付异常");}}/*** 微信支付回调 JSAPI 下单回调** @return 回调结果*/@Transactional(rollbackFor = Exception.class)@Overridepublic Transaction wxPaymentCallback(HttpServletRequest request) {NotificationParser notificationParser = getNotificationParser(request);Transaction parse = notificationParser.parse(requestParam, Transaction.class);//订单号String outTradeNo = parse.getOutTradeNo();//微信支付流水号String transactionId = parse.getTransactionId();log.info("微信支付回调信息:{}", parse);//校验金额是否正确LambdaQueryWrapper<ProductOrderEntity> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(ProductOrderEntity::getOrderCode, outTradeNo);List<ProductOrderEntity> productOrderEntities = productOrderMapper.selectList(queryWrapper);if (CollectionUtils.isNotEmpty(productOrderEntities)) {ProductOrderEntity productOrderEntity = productOrderEntities.get(0);BigDecimal orderAmount = productOrderEntity.getOrderAmount();log.info("订单金额:{}", orderAmount);BigDecimal amount = new BigDecimal(parse.getAmount().getTotal()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);log.info("支付金额:{}", amount);if (orderAmount.compareTo(amount) != 0) {log.info("订单金额与支付金额不相等");return null;}//修改订单状态 productOrderEntity.setOrderState(payState(parse.getTradeState()));productOrderMapper.updateById(productOrderEntity);//增加或者更新流水 moneyWaterService.addBreakFastMachineMoneyWater(productOrderEntity, transactionId, payState(parse.getTradeState()));} else {LambdaQueryWrapper<PowerBankOrderEntity> powerOrder = new LambdaQueryWrapper<>();powerOrder.eq(PowerBankOrderEntity::getOrderCode, outTradeNo);List<PowerBankOrderEntity> powerBankOrders = powerBankOrderMapper.selectList(powerOrder);if (CollectionUtils.isNotEmpty(powerBankOrders)) {PowerBankOrderEntity powerBankOrderEntity = powerBankOrders.get(0);//校验金额是否正确BigDecimal orderAmount = powerBankOrderEntity.getOrderAmount();log.info("订单金额:{}", orderAmount);BigDecimal amount = new BigDecimal(parse.getAmount().getTotal()).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);log.info("支付金额:{}", amount);if (orderAmount.compareTo(amount) != 0) {log.info("订单金额与支付金额不相等");return null;}powerBankOrderEntity.setOrderState(payState(parse.getTradeState()));//更新订单状态 powerBankOrderMapper.updateById(powerBankOrderEntity);//增加或者是更新流水 moneyWaterService.addPowerBankMoneyWater(powerBankOrderEntity, transactionId, payState(parse.getTradeState()));//只有支付成功的时候,才设置积分if (parse.getTradeState().equals(Transaction.TradeStateEnum.SUCCESS)) {//设置积分List<MoneySetPointEntity> moneySetPointEntities = moneySetPointMapper.selectList(null);if (CollectionUtils.isNotEmpty(moneySetPointEntities)) {MoneySetPointEntity moneySetPointEntity = moneySetPointEntities.get(0);BigDecimal pointAmount = moneySetPointEntity.getPointAmount();BigDecimal moneyAmount = moneySetPointEntity.getMoneyAmount();MoneyPointEntity moneyPointEntity = new MoneyPointEntity();moneyPointEntity.setOrderId(powerBankOrderEntity.getId()).setOrderType(2).setPointType(1).setUserId(powerBankOrderEntity.getUserId());moneyPointEntity.setPointAmount(NumberComputeUtil.numberMul(NumberComputeUtil.numberDiv(pointAmount, moneyAmount), orderAmount));moneyPointMapper.insert(moneyPointEntity);}}}}return notificationParser.parse(requestParam, Transaction.class);}/*** 退款** @param orderId 订单id* @param amount 金额* @return 退款信息*/@Overridepublic Refund refundIndentPayment(String orderId, BigDecimal amount) {LambdaQueryWrapper<MoneyWaterEntity> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(MoneyWaterEntity::getOrderId, orderId);List<MoneyWaterEntity> waterEntities = moneyWaterMapper.selectList(queryWrapper);Refund refund = null;if (CollectionUtils.isNotEmpty(waterEntities)) {MoneyWaterEntity moneyWaterEntity = waterEntities.get(0);String orderCode = "";if (moneyWaterEntity.getOrderType() == 1) {ProductOrderEntity productOrderEntity = productOrderMapper.selectById(moneyWaterEntity.getOrderId());if (productOrderEntity != null) {orderCode = productOrderEntity.getOrderCode();}} else {PowerBankOrderEntity powerBankOrderEntity = powerBankOrderMapper.selectById(moneyWaterEntity.getOrderId());if (powerBankOrderEntity != null) {orderCode = powerBankOrderEntity.getOrderCode();}}// 退款请求CreateRequest createRequest = new CreateRequest();// 商户订单号 createRequest.setOutTradeNo(orderCode);// 商户退款单号 lock.lock();try {createRequest.setOutRefundNo(IdUtil.getSnowflakeNextIdStr());} finally {lock.unlock();}// 退款结果回调 createRequest.setNotifyUrl(weiXinConfigureProperties.getRefundPayUrl());// 退款金额AmountReq amountReq = new AmountReq();long refundAmount = new BigDecimal(String.valueOf(amount)).movePointRight(2).intValue();amountReq.setRefund(refundAmount);long totalAmount = new BigDecimal(String.valueOf(moneyWaterEntity.getAmount())).movePointRight(2).intValue();amountReq.setTotal(totalAmount);amountReq.setCurrency("CNY");createRequest.setAmount(amountReq);// 申请退款try {refund = weiXinConfigureProperties.getRefundService().create(createRequest);} catch (Exception e) {log.error("退款申请失败", e);throw new ApiException("退款失败");}log.info("退款申请结果:{}", JSON.toJSONString(refund));}return refund;}/*** 退款回调** @param request 请求* @return 退款是否成功*/@Transactional(rollbackFor = Exception.class)@Overridepublic boolean getRefundNotification(HttpServletRequest request) {NotificationParser notificationParser = getNotificationParser(request);RefundNotification refundNotification = notificationParser.parse(requestParam, RefundNotification.class);log.info("退款通知,{}", refundNotification.getRefundStatus());//支付单号String transactionId = refundNotification.getTransactionId();LambdaQueryWrapper<MoneyWaterEntity> queryWrapper = new LambdaQueryWrapper<>();queryWrapper.eq(MoneyWaterEntity::getTransactionId, transactionId);List<MoneyWaterEntity> moneyWaterEntities = moneyWaterMapper.selectList(queryWrapper);if (CollectionUtils.isNotEmpty(moneyWaterEntities)) {MoneyWaterEntity moneyWaterEntity = moneyWaterEntities.get(0);//修改流水订单状态 moneyWaterEntity.setTradeState(refundState(refundNotification.getRefundStatus()));SimpleDateFormat originalFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX");Date date = null;try {date = originalFormat.parse(refundNotification.getSuccessTime());moneyWaterEntity.setRefundTime(date);} catch (ParseException e) {log.error("退款时间转换失败", e);}moneyWaterMapper.updateById(moneyWaterEntity);if (moneyWaterEntity.getOrderType() == 1) {//修改订单状态ProductOrderEntity productOrderEntity = productOrderMapper.selectById(moneyWaterEntity.getOrderId());productOrderEntity.setOrderState(refundState(refundNotification.getRefundStatus()));productOrderMapper.updateById(productOrderEntity);} else {//修改订单状态PowerBankOrderEntity powerBankOrderEntity = powerBankOrderMapper.selectById(moneyWaterEntity.getOrderId());powerBankOrderEntity.setOrderState(refundState(refundNotification.getRefundStatus()));powerBankOrderMapper.updateById(powerBankOrderEntity);}}return true;}/*** 支付状态* @param tradeStateEnum 微信返回支付状态* @return 自定义支付状态*/public Integer payState(Transaction.TradeStateEnum tradeStateEnum) {switch (tradeStateEnum) {case SUCCESS://支付成功return OrderMachineStateEnum.PAY.getType();case REFUND://退款中return OrderMachineStateEnum.REFUND.getType();case NOTPAY://未支付return OrderMachineStateEnum.NOT_PAY.getType();case CLOSED://已关闭return OrderMachineStateEnum.CLOSE.getType();case REVOKED://已撤销return OrderMachineStateEnum.REVOKED.getType();case USERPAYING://用户支付中return OrderMachineStateEnum.ACCEPT.getType();case PAYERROR://支付失败return OrderMachineStateEnum.FAIL.getType();}return 0;}/*** 退款状态* @param status 微信返回退款状态* @return 自定义退款状态*/public Integer refundState(Status status) {switch (status) {//退款成功状态case SUCCESS:return OrderMachineStateEnum.REFUND_SUCCESS.getType();//退款关闭case CLOSED:return OrderMachineStateEnum.REFUND_CLOSE.getType();//退款处理中case PROCESSING:return OrderMachineStateEnum.REFUND_PROCESSING.getType();//退款异常case ABNORMAL:return OrderMachineStateEnum.REFUND_ABNORMAL.getType();}return 0;}/*** 根据微信官方发送的请求获取信息*/@SneakyThrowspublic NotificationParser getNotificationParser(HttpServletRequest request) {// 获取RSA配置NotificationParser notificationParser = new NotificationParser(weiXinConfigureProperties.getRSAConfig());// 构建请求StringBuilder bodyBuilder = new StringBuilder();BufferedReader reader = request.getReader();String line;while ((line = reader.readLine()) != null) {bodyBuilder.append(line);}String body = bodyBuilder.toString();String timestamp = request.getHeader("Wechatpay-Timestamp");String nonce = request.getHeader("Wechatpay-Nonce");String signature = request.getHeader("Wechatpay-Signature");String singType = request.getHeader("Wechatpay-Signature-Type");String wechatPayCertificateSerialNumber = request.getHeader("Wechatpay-Serial");requestParam = new RequestParam.Builder().serialNumber(wechatPayCertificateSerialNumber).nonce(nonce).signature(signature).timestamp(timestamp).signType(singType).body(body).build();return notificationParser;}/*** 获取小程序接口调用凭证** @return accessToken*/private WeiXinVo getMiniProgramAccessToken() {String url = "weiXinApi/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";url = url.replace("weiXinApi", weiXinConfigureProperties.getUrl()).replace("APPID", weiXinConfigureProperties.getAppId()).replace("APPSECRET", weiXinConfigureProperties.getAppSecret());WeiXinVo result = null;String data = null;try {data = HttpUtil.get(url);log.info("获取微信小程序accessToken 返回结果 " + data);result = JSONObject.parseObject(data, WeiXinVo.class);stringRedisTemplate.opsForValue().set(WX_TOKEN + ":" + weiXinConfigureProperties.getAppId(), result.getAccessToken(), result.getExpiresIn() - 10, TimeUnit.SECONDS);} catch (Exception e) {log.error("获取微信小程序accessToken 异常 " + weiXinConfigureProperties.getAppId() + data, e);throw new ApiException("获取accessToken失败");}return result;} }
4、配置类
package com.saburo.server.common.properties; import com.wechat.pay.java.core.RSAAutoCertificateConfig; import com.wechat.pay.java.core.util.IOUtil; import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension; import com.wechat.pay.java.service.refund.RefundService; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.core.io.ClassPathResource; import org.springframework.stereotype.Component;import java.io.IOException;/*** 微信小程序属性配置信息* 参考:https://blog.csdn.net/weixin_66709611/article/details/142059592* @author liubh*/ @Data @Component @ConfigurationProperties(prefix = "wx") public class WeiXinConfigureProperties {/*** 微信访问地址*/private String url;/*** 微信小程序ID*/private String appId;/*** 微信支付商户号*/private String mchId;/*** 微信小程序秘钥*/private String appSecret;/*** 微信支付回调地址*/private String notifyUrl;/*** 微信支付退款回调地址*/private String refundPayUrl;/*** 微信支付私钥*/private String privateKeyPath;/*** 微信支付公钥*/private String privateCertPath;/*** 微信支付V3密钥*/private String apiV3Key;// RSA配置private RSAAutoCertificateConfig RSAConfig;// JSAPI支付private JsapiServiceExtension jsapiServiceExtension;// 退款private RefundService refundService;// 商户API证书序列号private String merchantSerialNumber;/*** 初始化配置*/@Beanpublic void initWxPayConfig() throws IOException {this.RSAConfig = buildRSAAutoCertificateConfig();this.jsapiServiceExtension = buildJsapiServiceExtension(RSAConfig);this.refundService = buildRefundService(RSAConfig);}/*** 构建并使用自动更新平台证书的RSA配置,一个商户号只能初始化一个配置,否则会因为重复的下载任务报错* @return RSA配置*/private RSAAutoCertificateConfig buildRSAAutoCertificateConfig() throws IOException {// 将 resource 目录下的文件转为 InputStream,然后利用 IOUtil.toString(inputStream) 转化为密钥String privateKey = IOUtil.toString(new ClassPathResource(privateKeyPath).getInputStream());return new RSAAutoCertificateConfig.Builder().merchantId(mchId).privateKey(privateKey).merchantSerialNumber(merchantSerialNumber).apiV3Key(apiV3Key).build();}// 构建JSAPIprivate JsapiServiceExtension buildJsapiServiceExtension(RSAAutoCertificateConfig config) {return new JsapiServiceExtension.Builder().config(config).build();}// 构建退款private RefundService buildRefundService(RSAAutoCertificateConfig config) {return new RefundService.Builder().config(config).build();}