java springboot对接微信小程序和微信支付v3接口

news/2024/11/6 19:36:30/文章来源:https://www.cnblogs.com/liubaihui/p/18524824

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

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/826549.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

可操作度

如上图所示,好的可操作度可以帮助远离奇异点 1. 可操作度: \(m = \sqrt{det(J(q) J(q)^T)}\) 2. 可操作度雅可比:3. 可操作度控制器QP:

Chrome 130 版本新特性 Chrome 130 版本发行说明

Chrome 130 版本新特性& Chrome 130 版本发行说明 一、Chrome 130 版本浏览器更新 1. 新的桌面提示 Chrome 130 引入了一种新的 Toast 样式,用于在用户操作后提供视觉确认,或快速提供后续操作的途径。 当用户执行某个操作时,Toast 会在屏幕上短暂弹出,确认操作成功或提…

存储器的性能指标

存储器的性能指标 ‍存储容量:存储字数字长(如1M8位)。MDR位数反映存储字长。单位成本:每位价格=总成本/总容量。存储速度:数据传输率=数据的宽度/存储周期。数据的宽度即存储字长 ​​ 存取时间(Ta):存取时间是指从启动一次存储器操作到完成该操作所经历的时间,分为读…

置换,群论初探

写的不好别D啊,算是一些知识的归纳(虽然也是看的别人的学的吧)群论 仙姑 置换 置换与排列 对于一个集合 \(D\) ,其大小为 \(|D|\),而排列是指这 \(|D|\) 个元素按照某种规定按一定顺序进行重新组成。而置换是指对这 \(|D|\) 个元素重新排列,不同元素之间交换位置,从而形…

VMware虚拟机安装Ubuntu系统

安装虚拟机出现此主机支持 Intel VT-x,但 Intel VT-x 处于禁用状态 问题详情:解决办法: 直接修改BIOS设置即可 1、重启电脑,在启动时,按相关键进入 BIOS 设置页面; 2、找到选择Intel Virtualization Technology ,此时该选项应该是disabled(关闭)的; 3、将disabled(关…

Fo-DICOM通过storescu.exe确认CT连接类型

使用storescu.exe传输时,如下图中所示即为传输的CT类型:*****有道无术,术尚可求;有术无道,止于术。*****

【笔记-模板】二叉搜索树-平衡树

二叉搜索树 www.luogu.com.cn 定义 二叉搜索树(\(\text{Binary Search Tree}\))是一种形状如二叉树的数据结构,用于快速查找和增加删除操作,它有如下几个特殊性质:空树是二叉搜索树。 若二叉搜索树的左子树不为空,则其左子树上所有点的附加权值均小于其根节点的值。 若二…

10.20 每日总结(今天继续软考)

今天学习软考。学习时长两小时。 主要学习了下午题,下午的第二题基本上会出,E-R图和关系模式: 然后根据ER图生成关系

node-red使用echart

node-red使用echart 官网下载echart.js Apache ECharts 下载页面 查找Node-red的settings.js文件 方法一 登陆服务器,启动node-red。 $ node-red 3 Nov 20:22:38 - [info] Welcome to Node-RED ===================3 Nov 20:22:38 - [info] Node-RED version: v4.0.3 3 Nov 20…

什么是无线漫游?无线漫游的工作原理是什么?

什么是无线漫游无线漫游是指在相同SSID(Service Set Identifier,服务集标识)的无线网络中,确保无线终端STA(如手机、电脑等终端设备)在移动过程中能够保持不间断网络连接的技术。因为在无线局域网中,每个AP(Access Point,无线访问接入点)设备的信号覆盖范围都有限,无线…

【CoCollider】让系统和应用适配如此简单

在各平台应用开发过程中,随着业务的功能增加,不免会涉及到非公开的 API 依赖,针对某些应用或厂商系统的适配,每个版本都需要投入精力去排查,CoCollider 可以让我们的适配效率从几个星期提升到几小时即可完成。在各平台应用开发过程中,随着业务的功能增加,不免会涉及到非…

深入学习软件组件认证的三个关键

人工智能软件,特别是深学习组件,是目前实现自主汽车等自主系统的最先进和经济上可行的解决方案。然而,DL算法的性质及其当前的实现与汽车、卫星和火车等安全关键系统中严格的软件开发过程不一致。 传统的安全相关软件采用自上而下的方法,对组件进行分解,并相应地传播安全要求,直…