package com.stylefeng.guns.modular.system.service.impl;
|
|
import com.alibaba.fastjson.JSON;
|
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.beans.factory.annotation.Value;
|
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;
|
|
@Value("${callbackPath}")
|
private String callbackPath; //支付回调网关地址
|
|
/**
|
* 微信充值
|
*
|
* @param userId 充值用户
|
* @param money 充值金额,单位为元
|
*/
|
@Transactional
|
public WxPayAppVO wechatRecharge(Integer userId, BigDecimal money) throws Exception {
|
|
PrepayRequest prepayRequest = buildPrepayRequest(money);
|
log.info("司机充值-微信支付下单-请求数据为:{}", JSON.toJSONString(prepayRequest));
|
// 1. 调用微信API下单
|
PrepayResponse response = appService.prepay(prepayRequest);
|
log.info("司机充值-微信支付下单-返回数据为:{}", JSON.toJSONString(response));
|
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<String,Object> 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<TransactionDetails>().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<String,Object> notifyResult() {
|
Map<String,Object> result = new HashMap<>();
|
result.put("code","SUCCESS");
|
result.put("message","成功");
|
return result;
|
}
|
|
/**
|
* 为 App 端生成拉起微信支付所需的全部参数
|
* @param prepayId 从下单接口获得的预支付ID
|
* @return Map<String, String> 包含 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(callbackPath+properties.getNotifyUrl()); // 回调地址
|
Amount amount = new Amount();
|
amount.setTotal(payAmount); // 金额:单位分(整数)
|
amount.setCurrency("CNY"); // 币种
|
prepayRequest.setAmount(amount);
|
return prepayRequest;
|
}
|
|
}
|