首页 > 编程语言 >java springboot对接微信小程序和微信支付v3接口

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

时间:2024-11-04 11:19:57浏览次数:3  
标签:return springboot 微信 saburo import java com String

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
     */
    @Override
    public 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 微信登录信息
     */
    @Override
    public 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 登录信息
     */
    @Override
    public 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)
    @Override
    public 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 支付信息
     */
    @Override
    public 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)
    @Override
    public 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 退款信息
     */
    @Override
    public 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)
    @Override
    public 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;
    }


    /**
     * 根据微信官方发送的请求获取信息
     */
    @SneakyThrows
    public 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;

    /**
     * 初始化配置
     */
    @Bean
    public 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();
    }

    // 构建JSAPI
    private JsapiServiceExtension buildJsapiServiceExtension(RSAAutoCertificateConfig config) {
        return new JsapiServiceExtension.Builder().config(config).build();
    }

    // 构建退款
    private RefundService buildRefundService(RSAAutoCertificateConfig config) {
        return new RefundService.Builder().config(config).build();
    }



 

 

标签:return,springboot,微信,saburo,import,java,com,String
From: https://www.cnblogs.com/liubaihui/p/18524824

相关文章

  • Java的泛型
    Java的泛型(Generics)是一种编程技术,它允许类、接口和方法在定义时使用参数化类型。通过泛型,可以编写更加通用和类型安全的代码。以下是Java泛型的一些关键知识点:1. 泛型类(GenericClass)定义泛型类时,使用尖括号 <> 来声明类型参数。例如:publicclassBox<T>{priva......
  • springboot 食念外卖软件app-计算机毕业设计源码65729
    摘 要随着移动互联网的快速发展和智能手机的普及,外卖行业已经成为了人们日常生活中不可或缺的一部分。传统的外卖方式通常需要用户打电话或者使用第三方外卖平台进行订餐,这种方式存在着操作繁琐、信息不准确等问题。为了解决这些问题,本项目设计并实现了一款食念外卖软件app......
  • Java面试系列-Java并发面试题20道,结合手撕Java系列学习效果更佳,知识点更深入
    文章目录1.什么是线程安全?2.解释下Java中的Thread类和Runnable接口的区别。3.Java中的synchronized关键字有哪些特性?4.volatile关键字的作用及限制是什么?5.解释Java内存模型(JMM)。6.Java中如何实现线程间通信?7.AQS(AbstractQueuedSynchronizer)的工作原理是什么?8.......
  • 第一章 JavaScript基础
    1.什么是JavaScriptJavaScript是一种基于对象和事件驱动的、并具有安全性能的脚本语言JavaScript特点向HTML页面中添加交互行为脚本语言,语法和Java类似解释性语言,边执行边解释JavaScript组成ES基础语法DOM操作HTML元素BOM操作浏览器前进,后退,刷新2.JavaScrip......
  • springboot reids缓存
    pom<?xmlversion="1.0"encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/......
  • SpringBoot3+Vue3+ElementPlus通用权限后台系统 | 小蚂蚁云
     项目介绍基于SpringBoot3、SpringSecurity、MybatisPlus、Vue3、TypeScript、Vite、ElementPlus、MySQL等技术栈实现的单体前后端分离后台管理系统;后端基于Java语言采用SpringBoot3、SpringSecurity、MybatisPlus、MySQL等主流技术栈,前端基于Vue3、TypeScript、Vite等技术栈......
  • SpringBoot3+Vue3+ElementPlus搭建后台系统脚手架 | 小蚂蚁云
     项目介绍基于SpringBoot3、SpringSecurity、MybatisPlus、Vue3、TypeScript、Vite、ElementPlus、MySQL等技术栈实现的单体前后端分离后台管理系统;后端基于Java语言采用SpringBoot3、SpringSecurity、MybatisPlus、MySQL等主流技术栈,前端基于Vue3、TypeScript、Vite等技术栈......