package com.stylefeng.guns.modular.system.service.impl; import com.baomidou.mybatisplus.mapper.EntityWrapper; import com.stylefeng.guns.config.properties.WxPayProperties; import com.stylefeng.guns.modular.system.model.Driver; import com.stylefeng.guns.modular.system.model.TransactionDetails; import com.stylefeng.guns.modular.system.service.IDriverService; import com.stylefeng.guns.modular.system.service.IPayService; import com.stylefeng.guns.modular.system.service.ITransactionDetailsService; import com.stylefeng.guns.modular.system.util.RedisUtil; import com.stylefeng.guns.modular.system.util.UUIDUtil; import com.stylefeng.guns.modular.vo.WxPayAppVO; import com.wechat.pay.java.core.Config; import com.wechat.pay.java.core.cipher.Signer; import com.wechat.pay.java.core.notification.NotificationConfig; import com.wechat.pay.java.core.notification.NotificationParser; import com.wechat.pay.java.core.notification.RequestParam; import com.wechat.pay.java.service.partnerpayments.app.model.Transaction; import com.wechat.pay.java.service.payments.app.AppService; import com.wechat.pay.java.service.payments.app.model.Amount; import com.wechat.pay.java.service.payments.app.model.PrepayRequest; import com.wechat.pay.java.service.payments.app.model.PrepayResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.io.IOException; import java.math.BigDecimal; import java.text.SimpleDateFormat; import java.util.*; import java.util.stream.Collectors; /** * 支付服务 * * @author Lux * @version 1.0.0 * @since 2026/6/3 15:25 */ @Slf4j @Service @RequiredArgsConstructor public class PayServiceImpl implements IPayService { private final WxPayProperties properties; private final Config wxPayConfig; private final AppService appService; private final ITransactionDetailsService transactionDetailsService; private final IDriverService driverService; private final RedisUtil redisUtil; /** * 微信充值 * * @param userId 充值用户 * @param money 充值金额 */ @Transactional public WxPayAppVO wechatRecharge(Integer userId, BigDecimal money) throws Exception { PrepayRequest prepayRequest = buildPrepayRequest(money); // 1. 调用微信API下单 PrepayResponse response = appService.prepay(prepayRequest); String prepayId = response.getPrepayId(); if(StringUtils.isEmpty(prepayId)) { return null; } // 2. 保存数据 新增扣除使用费记录 TransactionDetails transactionDetails = new TransactionDetails(); transactionDetails.setCode(prepayRequest.getOutTradeNo()); transactionDetails.setUserId(userId); transactionDetails.setInsertTime(new Date()); transactionDetails.setRemark("充值"); transactionDetails.setMoney(money.doubleValue()); // 将money转化为double类型 transactionDetails.setState(1); transactionDetails.setType(1); transactionDetails.setUserType(2); transactionDetails.setOrderType(5); // 余额充值 transactionDetailsService.insert(transactionDetails); // 3. 对 App 拉起支付所需的参数进行二次签名 return buildAppPayParams(prepayId); } /** * 微信支付成功,回调服务 */ @Transactional public Map wechatNotify(HttpServletRequest request) throws IOException { String body = new BufferedReader(request.getReader()).lines().collect(Collectors.joining("\n")); RequestParam requestParam = new RequestParam.Builder() .serialNumber(request.getHeader("Wechatpay-Serial")) .nonce(request.getHeader("Wechatpay-Nonce")) .signature(request.getHeader("Wechatpay-Signature")) .timestamp(request.getHeader("Wechatpay-Timestamp")) .body(body) .build(); log.info("司机充值-回调业务处理开始-回调参数为:{}, body为:{}", requestParam, body); NotificationConfig notificationConfig = (NotificationConfig) wxPayConfig; NotificationParser notificationParser = new NotificationParser(notificationConfig); Transaction transaction = notificationParser.parse(requestParam, Transaction.class); String outTradeNo = transaction.getOutTradeNo(); // 商户订单号 String transactionId = transaction.getTransactionId(); // 微信订单唯一标识 // 幂等处理 String existTransactionId = redisUtil.getValue(outTradeNo); if(StringUtils.isNotEmpty(existTransactionId) && existTransactionId.equals(transactionId)) { // 已经存在改条数据的处理则直接返回 return notifyResult(); } else { redisUtil.setStrValue(outTradeNo, transactionId, 60 * 60); //设置60分钟过期 } // 支付成功 if (Transaction.TradeStateEnum.SUCCESS.equals(transaction.getTradeState())) { TransactionDetails transactionDetails = transactionDetailsService.selectOne(new EntityWrapper().eq("code", outTradeNo)); if (Objects.nonNull(transactionDetails)) { if (transactionDetails.getPayState()==2) { // 已支付 return notifyResult(); } Driver driver = driverService.selectById(transactionDetails.getUserId()); if (Objects.nonNull(driver)) { driver.setBalance(driver.getBalance() + transactionDetails.getMoney()); driverService.updateById(driver); } transactionDetails.setPayState(2); transactionDetailsService.updateById(transactionDetails); } } redisUtil.remove(outTradeNo); // 处理完逻辑删除 log.info("司机充值-回调业务处理结束"); return notifyResult(); } private Map notifyResult() { Map result = new HashMap<>(); result.put("code","SUCCESS"); result.put("message","成功"); return result; } /** * 为 App 端生成拉起微信支付所需的全部参数 * @param prepayId 从下单接口获得的预支付ID * @return Map 包含 appId、partnerId、prepayId、nonceStr、timeStamp、package、sign 等 */ public WxPayAppVO buildAppPayParams(String prepayId) throws Exception { // 生成随机字符串(32位以内) String nonceStr = generateOrderNo(); // 当前秒级时间戳 long timestamp = System.currentTimeMillis() / 1000; // 构建待签名的字符串(严格按照顺序拼接,每行以\n结尾); 格式:Appid\n时间戳\n随机字符串\n预支付交易会话标识\n String messageStr = String.format("%s\n%s\n%s\n%s\n", properties.getAppId(), // 移动应用的AppID timestamp, // 时间戳,秒级 nonceStr, // 随机字符串 prepayId // 预支付ID ); // 使用商户API证书私钥进行签名(RSASSA-PKCS1-v1_5 with SHA256) Signer signer = wxPayConfig.createSigner(); String sign = signer.sign(messageStr).getSign(); // 最终返回给App的参数 return WxPayAppVO.builder() .appId(properties.getAppId()) .partnerId(properties.getMchId()) // 注意这里是 partnerid .prepayId(prepayId) .packageValue("Sign=WXPay") // 固定值 .nonceStr(nonceStr) .timeStamp(String.valueOf(timestamp)) .sign(sign) .build(); } /** * 创建商户系统内部订单,生成唯一订单号 */ private String generateOrderNo() throws Exception { SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); return sdf.format(new Date()) + UUIDUtil.getRandomCode(3); } private PrepayRequest buildPrepayRequest(BigDecimal money) throws Exception { // 1、创建商户系统内部订单,生成唯一订单号,需要写入数据库待支付 String outTradeNo = generateOrderNo(); // 2、构建微信支付下单请求 int payAmount = money.multiply(new BigDecimal("100")).intValue(); // 充值金额元转位分 PrepayRequest prepayRequest = new PrepayRequest(); prepayRequest.setAppid(properties.getAppId()); prepayRequest.setMchid(properties.getMchId()); prepayRequest.setDescription("用户充值"); // 商品描述,不超过127个字符 prepayRequest.setOutTradeNo(outTradeNo); // 商户订单号 prepayRequest.setNotifyUrl(properties.getNotifyUrl()); // 回调地址 Amount amount = new Amount(); amount.setTotal(payAmount); // 金额:单位分(整数) amount.setCurrency("CNY"); // 币种 prepayRequest.setAmount(amount); return prepayRequest; } }