|
|
@@ -1,9 +1,580 @@
|
|
|
+//package com.zhentao.service.impl;
|
|
|
+//
|
|
|
+//import com.fasterxml.jackson.core.type.TypeReference;
|
|
|
+//import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
+//import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
|
|
|
+//import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
|
|
|
+//import com.github.binarywang.wxpay.config.WxPayConfig;
|
|
|
+//import com.github.binarywang.wxpay.exception.WxPayException;
|
|
|
+//import com.github.binarywang.wxpay.service.WxPayService;
|
|
|
+//import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
|
|
|
+//import com.github.binarywang.wxpay.util.SignUtils;
|
|
|
+//import com.zhentao.config.WechatPayProperties;
|
|
|
+//import com.zhentao.entity.UserVip;
|
|
|
+//import com.zhentao.entity.VipPackage;
|
|
|
+//import com.zhentao.entity.Wx;
|
|
|
+//import com.zhentao.mapper.UserVipMapper;
|
|
|
+//import com.zhentao.mapper.VipPackageMapper;
|
|
|
+//import com.zhentao.pojo.Users;
|
|
|
+//import com.zhentao.service.UsersService;
|
|
|
+//import com.zhentao.service.VipService;
|
|
|
+//import com.zhentao.service.WxService;
|
|
|
+//import com.zhentao.vo.UserVipInfoVO;
|
|
|
+//import com.zhentao.vo.VipPackageVO;
|
|
|
+//import lombok.extern.slf4j.Slf4j;
|
|
|
+//import org.apache.commons.lang3.RandomStringUtils;
|
|
|
+//import org.apache.commons.lang3.RandomUtils;
|
|
|
+//import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+//import org.springframework.stereotype.Service;
|
|
|
+//import org.springframework.transaction.annotation.Transactional;
|
|
|
+//
|
|
|
+//import java.math.BigDecimal;
|
|
|
+//import java.time.LocalDateTime;
|
|
|
+//import java.time.temporal.ChronoUnit;
|
|
|
+//import java.util.ArrayList;
|
|
|
+//import java.util.HashMap;
|
|
|
+//import java.util.List;
|
|
|
+//import java.util.Map;
|
|
|
+//
|
|
|
+///**
|
|
|
+// * VIP服务实现类
|
|
|
+// */
|
|
|
+//@Service
|
|
|
+//@Slf4j
|
|
|
+//public class VipServiceImpl implements VipService {
|
|
|
+//
|
|
|
+// @Autowired
|
|
|
+// private VipPackageMapper vipPackageMapper;
|
|
|
+//
|
|
|
+// @Autowired
|
|
|
+// private UserVipMapper userVipMapper;
|
|
|
+//
|
|
|
+// @Autowired
|
|
|
+// private WxService wxService;
|
|
|
+// @Autowired
|
|
|
+// private UsersService usersService;
|
|
|
+//
|
|
|
+// @Autowired
|
|
|
+// private WechatPayProperties wxConfig;
|
|
|
+//
|
|
|
+// private final ObjectMapper objectMapper = new ObjectMapper();
|
|
|
+//
|
|
|
+// @Override
|
|
|
+// public UserVipInfoVO getVipInfo(Long userId) {
|
|
|
+// UserVipInfoVO vo = new UserVipInfoVO();
|
|
|
+//
|
|
|
+// // 查询用户当前生效的VIP
|
|
|
+// UserVip currentVip = userVipMapper.selectActiveVipByUserId(userId);
|
|
|
+//
|
|
|
+// if (currentVip != null && currentVip.getEndTime().isAfter(LocalDateTime.now())) {
|
|
|
+// vo.setIsVip(true);
|
|
|
+// vo.setVipExpireTime(currentVip.getEndTime());
|
|
|
+//
|
|
|
+// // 计算剩余天数
|
|
|
+// long days = ChronoUnit.DAYS.between(LocalDateTime.now(), currentVip.getEndTime());
|
|
|
+// vo.setRemainingDays((int) days);
|
|
|
+// } else {
|
|
|
+// vo.setIsVip(false);
|
|
|
+// vo.setVipExpireTime(null);
|
|
|
+// vo.setRemainingDays(0);
|
|
|
+// }
|
|
|
+//
|
|
|
+// // 查询所有VIP套餐
|
|
|
+// List<VipPackage> packages = vipPackageMapper.selectAllActive();
|
|
|
+// List<VipPackageVO> packageVOs = new ArrayList<>();
|
|
|
+//
|
|
|
+// for (VipPackage pkg : packages) {
|
|
|
+// VipPackageVO pkgVO = new VipPackageVO();
|
|
|
+// pkgVO.setPackageId(pkg.getPackageId());
|
|
|
+// pkgVO.setName(pkg.getPackageName());
|
|
|
+// pkgVO.setDurationDays(pkg.getDurationDays());
|
|
|
+// pkgVO.setDuration(getDurationText(pkg.getDurationDays()));
|
|
|
+// pkgVO.setOriginalPrice(pkg.getOriginalPrice());
|
|
|
+// pkgVO.setPrice(pkg.getCurrentPrice());
|
|
|
+// pkgVO.setRecommend(pkg.getIsRecommend() == 1);
|
|
|
+//
|
|
|
+// // 解析特权列表JSON
|
|
|
+// try {
|
|
|
+// if (pkg.getBenefits() != null && !pkg.getBenefits().isEmpty()) {
|
|
|
+// List<String> benefits = objectMapper.readValue(
|
|
|
+// pkg.getBenefits(),
|
|
|
+// new TypeReference<List<String>>() {}
|
|
|
+// );
|
|
|
+// pkgVO.setBenefits(benefits);
|
|
|
+// } else {
|
|
|
+// pkgVO.setBenefits(new ArrayList<>());
|
|
|
+// }
|
|
|
+// } catch (Exception e) {
|
|
|
+// // JSON解析失败时记录日志并使用空列表
|
|
|
+// System.err.println("解析套餐 " + pkg.getPackageId() + " 的 benefits 失败: " + e.getMessage());
|
|
|
+// System.err.println("原始数据: " + pkg.getBenefits());
|
|
|
+// pkgVO.setBenefits(new ArrayList<>());
|
|
|
+// }
|
|
|
+//
|
|
|
+// packageVOs.add(pkgVO);
|
|
|
+// }
|
|
|
+//
|
|
|
+// vo.setPackages(packageVOs);
|
|
|
+//
|
|
|
+// return vo;
|
|
|
+// }
|
|
|
+//
|
|
|
+//// @Override
|
|
|
+//// @Transactional(rollbackFor = Exception.class)
|
|
|
+//// public Boolean purchaseVip(Long userId, Long packageId) throws WxPayException {
|
|
|
+//// // 查询套餐信息
|
|
|
+//// VipPackage pkg = vipPackageMapper.selectById(packageId);
|
|
|
+//// if (pkg == null || pkg.getStatus() != 1) {
|
|
|
+//// throw new RuntimeException("套餐不存在或已下架");
|
|
|
+//// }
|
|
|
+////
|
|
|
+//// // 生成订单号
|
|
|
+//// String orderNo = generateOrderNo();
|
|
|
+//// LocalDateTime now = LocalDateTime.now();
|
|
|
+//// LocalDateTime startTime = now;
|
|
|
+//// LocalDateTime endTime;
|
|
|
+////
|
|
|
+////
|
|
|
+//// // 查询用户当前VIP状态
|
|
|
+//// UserVip currentVip = userVipMapper.selectActiveVipByUserId(userId);
|
|
|
+//// if (currentVip != null && currentVip.getEndTime().isAfter(now)) {
|
|
|
+//// // 如果已有VIP,从当前VIP结束时间开始累加
|
|
|
+//// startTime = currentVip.getEndTime();
|
|
|
+//// }
|
|
|
+////
|
|
|
+//// // 计算结束时间
|
|
|
+//// endTime = startTime.plusDays(pkg.getDurationDays());
|
|
|
+////
|
|
|
+////
|
|
|
+//// // 创建VIP记录
|
|
|
+//// UserVip userVip = new UserVip();
|
|
|
+//// userVip.setUserId(userId);
|
|
|
+//// userVip.setPackageId(packageId);
|
|
|
+//// userVip.setStartTime(startTime);
|
|
|
+//// userVip.setEndTime(endTime);
|
|
|
+//// userVip.setDurationDays(pkg.getDurationDays());
|
|
|
+//// userVip.setPaymentAmount(pkg.getCurrentPrice());
|
|
|
+//// userVip.setPaymentMethod("在线支付"); // TODO: 实际支付方式
|
|
|
+//// userVip.setOrderNo(orderNo);
|
|
|
+//// userVip.setStatus(1);
|
|
|
+//// userVip.setSource("购买");
|
|
|
+//// userVip.setCreateTime(now);
|
|
|
+////
|
|
|
+//// int result = userVipMapper.insert(userVip);
|
|
|
+//// String timeExpire = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(System.currentTimeMillis());
|
|
|
+//// Wx wx = wxService.list().get(0);
|
|
|
+//// WxPayUnifiedOrderV3Request wxPayUnifiedOrderV3Request = new WxPayUnifiedOrderV3Request();
|
|
|
+//// wxPayUnifiedOrderV3Request.setOutTradeNo(orderNo);
|
|
|
+//// wxPayUnifiedOrderV3Request.setDescription(pkg.getPackageName());
|
|
|
+//// wxPayUnifiedOrderV3Request.setTimeExpire(timeExpire);
|
|
|
+//// wxPayUnifiedOrderV3Request.setNotifyUrl("https://mini.workervip.com/pay/notify");
|
|
|
+//// //订单金额
|
|
|
+//// WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
|
|
|
+//// amount.setTotal(pkg.getCurrentPrice().intValue());
|
|
|
+//// amount.setCurrency("CNY");
|
|
|
+//// wxPayUnifiedOrderV3Request.setAmount(amount);
|
|
|
+////
|
|
|
+//// Users byId = usersService.getById(userId);
|
|
|
+//// //付款人员
|
|
|
+//// WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
|
|
|
+//// payer.setOpenid(byId.getWechatOpenid());
|
|
|
+//// TradeTypeEnum tradeTypeEnum =TradeTypeEnum.JSAPI;
|
|
|
+//// wxPayUnifiedOrderV3Request.setPayer(payer);
|
|
|
+//// WxPayConfig wxPayConfig=new WxPayConfig();
|
|
|
+////
|
|
|
+//// wxPayConfig.setAppId(wx.getAppId());
|
|
|
+//// wxPayConfig.setMchId(wx.getMchId());
|
|
|
+//// wxPayConfig.setMchKey(wx.getAppSecret());
|
|
|
+//// wxPayConfig.setApiV3Key(wx.getAppSecret());
|
|
|
+//// wxPayConfig.setPayBaseUrl("https://api.mch.weixin.qq.com");
|
|
|
+//// wxPayConfig.setPrivateKeyPath(null);
|
|
|
+//// wxPayConfig.setNotifyUrl("https://mini.workervip.com/pay/notify");
|
|
|
+//// wxPayConfig.setPrivateCertPath("classpath:/apiclient_cert.pem");
|
|
|
+//// wxPayConfig.setPrivateKeyPath("classpath:/apiclient_key.pem");
|
|
|
+//// WxPayService wxPayService = new WxPayServiceImpl();
|
|
|
+//// wxPayService.setConfig(wxPayConfig);
|
|
|
+//// Object obj=wxPayService.createOrderV3(tradeTypeEnum, wxPayUnifiedOrderV3Request);
|
|
|
+//// System.out.println(obj);
|
|
|
+////
|
|
|
+//// return result > 0;
|
|
|
+//// }
|
|
|
+//
|
|
|
+// @Override
|
|
|
+// @Transactional(rollbackFor = Exception.class)
|
|
|
+// public Boolean grantVip(Long userId, Integer days, String source) {
|
|
|
+// LocalDateTime now = LocalDateTime.now();
|
|
|
+// LocalDateTime startTime = now;
|
|
|
+// LocalDateTime endTime;
|
|
|
+//
|
|
|
+// // 查询用户当前VIP状态
|
|
|
+// UserVip currentVip = userVipMapper.selectActiveVipByUserId(userId);
|
|
|
+// if (currentVip != null && currentVip.getEndTime().isAfter(now)) {
|
|
|
+// // 如果已有VIP,从当前VIP结束时间开始累加
|
|
|
+// startTime = currentVip.getEndTime();
|
|
|
+// }
|
|
|
+//
|
|
|
+// // 计算结束时间
|
|
|
+// endTime = startTime.plusDays(days);
|
|
|
+//
|
|
|
+// // 创建VIP记录
|
|
|
+// UserVip userVip = new UserVip();
|
|
|
+// userVip.setUserId(userId);
|
|
|
+// userVip.setPackageId(0L); // 赠送的VIP没有套餐ID
|
|
|
+// userVip.setStartTime(startTime);
|
|
|
+// userVip.setEndTime(endTime);
|
|
|
+// userVip.setDurationDays(days);
|
|
|
+// userVip.setPaymentAmount(BigDecimal.ZERO);
|
|
|
+// userVip.setPaymentMethod("赠送");
|
|
|
+// userVip.setOrderNo(generateOrderNo());
|
|
|
+// userVip.setStatus(1);
|
|
|
+// userVip.setSource(source);
|
|
|
+// userVip.setCreateTime(now);
|
|
|
+//
|
|
|
+// int result = userVipMapper.insert(userVip);
|
|
|
+//
|
|
|
+// return result > 0;
|
|
|
+// }
|
|
|
+//
|
|
|
+//// /**
|
|
|
+//// * 生成订单号
|
|
|
+//// */
|
|
|
+//// private String generateOrderNo() {
|
|
|
+//// return "VIP" + System.currentTimeMillis() + UUID.randomUUID().toString().substring(0, 8);
|
|
|
+//// }
|
|
|
+//
|
|
|
+// /**
|
|
|
+// * 获取时长单位文字
|
|
|
+// */
|
|
|
+// private String getDurationText(Integer days) {
|
|
|
+// if (days == 30) return "月";
|
|
|
+// if (days == 90) return "季";
|
|
|
+// if (days == 365) return "年";
|
|
|
+// return days + "天";
|
|
|
+// }
|
|
|
+//
|
|
|
+//
|
|
|
+//// @Override
|
|
|
+//// @Transactional(rollbackFor = Exception.class)
|
|
|
+//// public Map<String, Object> purchaseVip(Long userId, Long packageId) throws WxPayException {
|
|
|
+//// // 1. 校验套餐
|
|
|
+//// VipPackage pkg = vipPackageMapper.selectById(packageId);
|
|
|
+//// if (pkg == null || pkg.getStatus() != 1) {
|
|
|
+//// throw new RuntimeException("套餐不存在或已下架");
|
|
|
+//// }
|
|
|
+////
|
|
|
+//// // 2. 生成订单号和时间计算
|
|
|
+//// String orderNo = generateOrderNo();
|
|
|
+//// LocalDateTime now = LocalDateTime.now();
|
|
|
+//// LocalDateTime startTime = now;
|
|
|
+////
|
|
|
+//// // 处理用户现有VIP续期
|
|
|
+//// UserVip currentVip = userVipMapper.selectActiveVipByUserId(userId);
|
|
|
+//// if (currentVip != null && currentVip.getEndTime().isAfter(now)) {
|
|
|
+//// startTime = currentVip.getEndTime();
|
|
|
+//// }
|
|
|
+//// LocalDateTime endTime = startTime.plusDays(pkg.getDurationDays());
|
|
|
+////
|
|
|
+//// // 3. 创建VIP记录(状态设为待支付)
|
|
|
+//// UserVip userVip = new UserVip();
|
|
|
+//// userVip.setUserId(userId);
|
|
|
+//// userVip.setPackageId(packageId);
|
|
|
+//// userVip.setStartTime(startTime);
|
|
|
+//// userVip.setEndTime(endTime);
|
|
|
+//// userVip.setDurationDays(pkg.getDurationDays());
|
|
|
+//// userVip.setPaymentAmount(pkg.getCurrentPrice());
|
|
|
+//// userVip.setPaymentMethod("微信支付");
|
|
|
+//// userVip.setOrderNo(orderNo);
|
|
|
+//// userVip.setStatus(0); // 0-待支付,1-已生效
|
|
|
+//// userVip.setSource("购买");
|
|
|
+//// userVip.setCreateTime(now);
|
|
|
+//// userVipMapper.insert(userVip);
|
|
|
+////
|
|
|
+//// // 4. 构建微信支付V3请求
|
|
|
+//// WxPayUnifiedOrderV3Request payRequest = buildWxPayRequest(orderNo, pkg, userId);
|
|
|
+////
|
|
|
+//// // 5. 调用微信支付下单(非事务内执行,避免阻塞)
|
|
|
+//// WxPayUnifiedOrderV3Result.JsapiResult wxPayOrder = createWxPayOrder(payRequest);
|
|
|
+//// String[] split = wxPayOrder.getPackageValue().split("=");
|
|
|
+//// String prepayId = split[1];
|
|
|
+//// Map<String, Object> stringObjectMap = generatePayParams(prepayId);
|
|
|
+////
|
|
|
+//// // 6. 返回支付参数给前端
|
|
|
+//// BigDecimal packagePrice = pkg.getCurrentPrice(); // 套餐价格(单位:元,如19.9)
|
|
|
+//// int totalFee = packagePrice.multiply(new BigDecimal("100")).intValue(); // 转为分:19.9元 → 1990
|
|
|
+//// stringObjectMap.put("totalFee",totalFee);
|
|
|
+//// stringObjectMap.put("orderNo", orderNo);
|
|
|
+//// return stringObjectMap;
|
|
|
+//// }
|
|
|
+////
|
|
|
+//// /**
|
|
|
+//// * 构建微信支付V3请求参数
|
|
|
+//// */
|
|
|
+//// private WxPayUnifiedOrderV3Request buildWxPayRequest(String orderNo, VipPackage pkg, Long userId) {
|
|
|
+//// // 正确的过期时间格式(ISO8601)
|
|
|
+//// DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX");
|
|
|
+//// String timeExpire = LocalDateTime.now().plusMinutes(30)
|
|
|
+//// .atZone(ZoneId.of("Asia/Shanghai")) // 明确指定东八区
|
|
|
+//// .format(formatter);
|
|
|
+////
|
|
|
+//// // 查询用户OpenID
|
|
|
+//// Users user = usersService.getById(userId);
|
|
|
+//// if (user == null || user.getWechatOpenid() == null) {
|
|
|
+//// throw new RuntimeException("用户微信信息未绑定");
|
|
|
+//// }
|
|
|
+////
|
|
|
+//// // 构建请求对象
|
|
|
+//// WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
|
|
|
+//// request.setOutTradeNo(orderNo);
|
|
|
+//// request.setDescription(pkg.getPackageName());
|
|
|
+//// request.setTimeExpire(timeExpire);
|
|
|
+//// request.setNotifyUrl("https://mini.workervip.com/pay/notify");
|
|
|
+////
|
|
|
+//// // 金额(转换为分)
|
|
|
+//// WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
|
|
|
+//// amount.setTotal(pkg.getCurrentPrice().multiply(new BigDecimal("100")).intValue());
|
|
|
+//// amount.setCurrency("CNY");
|
|
|
+//// request.setAmount(amount);
|
|
|
+////
|
|
|
+//// // 支付者信息
|
|
|
+//// WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
|
|
|
+//// payer.setOpenid(user.getWechatOpenid());
|
|
|
+//// request.setPayer(payer);
|
|
|
+////
|
|
|
+//// return request;
|
|
|
+//// }
|
|
|
+////
|
|
|
+//// /**
|
|
|
+//// * 初始化微信支付配置并下单
|
|
|
+//// */
|
|
|
+//// private WxPayUnifiedOrderV3Result.JsapiResult createWxPayOrder(WxPayUnifiedOrderV3Request request) throws WxPayException {
|
|
|
+//// // 从配置文件/数据库读取正确配置
|
|
|
+//// Wx wxConfig = wxService.list().get(0);
|
|
|
+//// WxPayConfig payConfig = new WxPayConfig();
|
|
|
+////
|
|
|
+//// // 正确配置(APIv3密钥需从商户平台获取)
|
|
|
+//// payConfig.setAppId(wxConfig.getAppId());
|
|
|
+//// payConfig.setMchId(wxConfig.getMchId());
|
|
|
+//// payConfig.setApiV3Key("7f133cbabf119b4d213bc6ekffe3b149"); // 替换为真实32位APIv3密钥
|
|
|
+//// payConfig.setNotifyUrl("https://mini.workervip.com/pay/notify");
|
|
|
+//// payConfig.setPrivateCertPath("classpath:apiclient_cert.pem"); // 确保证书存在
|
|
|
+//// payConfig.setPrivateKeyPath("classpath:apiclient_key.pem");
|
|
|
+////
|
|
|
+//// // 初始化支付服务
|
|
|
+//// WxPayService wxPayService = new WxPayServiceImpl();
|
|
|
+//// wxPayService.setConfig(payConfig);
|
|
|
+////
|
|
|
+//// // 调用V3下单接口
|
|
|
+//// WxPayUnifiedOrderV3Result.JsapiResult result = wxPayService.createOrderV3(TradeTypeEnum.JSAPI, request);
|
|
|
+////
|
|
|
+//// // 解析支付参数(前端需用此调起支付)
|
|
|
+//// return result;
|
|
|
+//// }
|
|
|
+////
|
|
|
+// /**
|
|
|
+// * 生成唯一订单号
|
|
|
+// */
|
|
|
+// private String generateOrderNo() {
|
|
|
+// return "VIP" + System.currentTimeMillis() + RandomUtils.nextInt(1000, 9999);
|
|
|
+// }
|
|
|
+//// private Map<String, Object> generatePayParams(String prepayId) {
|
|
|
+//// Map<String, Object> payParams = new HashMap<>();
|
|
|
+//// String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
|
|
|
+//// String nonceStr = RandomStringUtils.randomAlphanumeric(32);
|
|
|
+////
|
|
|
+//// // 使用微信支付配置的AppID(而非自定义配置)
|
|
|
+//// Wx wxConfig = wxService.list().get(0);
|
|
|
+//// String signatureStr = wxConfig.getAppId() + "\n" + timeStamp + "\n" + nonceStr + "\n" + prepayId + "\n";
|
|
|
+////
|
|
|
+//// // 从Classpath读取私钥并签名
|
|
|
+//// PrivateKey privateKey = getPrivateKeyFromClasspath("/apiclient_key.pem");
|
|
|
+//// String paySign = signWithPrivateKey(signatureStr, privateKey);
|
|
|
+////
|
|
|
+//// payParams.put("timeStamp", timeStamp);
|
|
|
+//// payParams.put("nonceStr", nonceStr);
|
|
|
+//// payParams.put("package", prepayId);
|
|
|
+//// payParams.put("signType", "RSA");
|
|
|
+//// payParams.put("paySign", paySign);
|
|
|
+//// payParams.put("orderNo", generateOrderNo()); // 传递正确订单号
|
|
|
+////
|
|
|
+//// return payParams;
|
|
|
+//// }
|
|
|
+////
|
|
|
+//// /**
|
|
|
+//// * 从Classpath读取私钥(修复FileReader无法读取classpath路径问题)
|
|
|
+//// */
|
|
|
+//// private PrivateKey getPrivateKeyFromClasspath(String classpath) {
|
|
|
+//// try (InputStream inputStream = getClass().getResourceAsStream(classpath);
|
|
|
+//// PemReader pemReader = new PemReader(new InputStreamReader(inputStream))) {
|
|
|
+////
|
|
|
+//// PemObject pemObject = pemReader.readPemObject();
|
|
|
+//// byte[] privateKeyBytes = pemObject.getContent();
|
|
|
+////
|
|
|
+//// PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);
|
|
|
+//// KeyFactory keyFactory = KeyFactory.getInstance("RSA");
|
|
|
+//// return keyFactory.generatePrivate(keySpec);
|
|
|
+////
|
|
|
+//// } catch (Exception e) {
|
|
|
+//// throw new RuntimeException("读取私钥失败", e);
|
|
|
+//// }
|
|
|
+//// }
|
|
|
+////
|
|
|
+//// /**
|
|
|
+//// * 使用商户私钥进行SHA256withRSA签名
|
|
|
+//// */
|
|
|
+//// private String signWithPrivateKey(String data, PrivateKey privateKey) {
|
|
|
+//// try {
|
|
|
+//// Signature signature = Signature.getInstance("SHA256withRSA");
|
|
|
+//// signature.initSign(privateKey);
|
|
|
+//// signature.update(data.getBytes(StandardCharsets.UTF_8));
|
|
|
+//// return Base64.getEncoder().encodeToString(signature.sign());
|
|
|
+//// } catch (Exception e) {
|
|
|
+//// throw new RuntimeException("签名失败", e);
|
|
|
+//// }
|
|
|
+//// }
|
|
|
+//@Override
|
|
|
+//@Transactional(rollbackFor = Exception.class)
|
|
|
+//public Map<String, Object> purchaseVip(Long userId, Long packageId) throws WxPayException {
|
|
|
+// // 1. 校验套餐
|
|
|
+// VipPackage pkg = vipPackageMapper.selectById(packageId);
|
|
|
+// if (pkg == null || pkg.getStatus() != 1) {
|
|
|
+// throw new RuntimeException("套餐不存在或已下架");
|
|
|
+// }
|
|
|
+//
|
|
|
+// // 2. 生成订单号和时间计算
|
|
|
+// String orderNo = generateOrderNo();
|
|
|
+// LocalDateTime now = LocalDateTime.now();
|
|
|
+// LocalDateTime startTime = now;
|
|
|
+//
|
|
|
+// // 处理用户现有VIP续期
|
|
|
+// UserVip currentVip = userVipMapper.selectActiveVipByUserId(userId);
|
|
|
+// if (currentVip != null && currentVip.getEndTime().isAfter(now)) {
|
|
|
+// startTime = currentVip.getEndTime();
|
|
|
+// }
|
|
|
+// LocalDateTime endTime = startTime.plusDays(pkg.getDurationDays());
|
|
|
+//
|
|
|
+// // 3. 创建VIP记录(状态设为待支付)
|
|
|
+// UserVip userVip = new UserVip();
|
|
|
+// userVip.setUserId(userId);
|
|
|
+// userVip.setPackageId(packageId);
|
|
|
+// userVip.setStartTime(startTime);
|
|
|
+// userVip.setEndTime(endTime);
|
|
|
+// userVip.setDurationDays(pkg.getDurationDays());
|
|
|
+// userVip.setPaymentAmount(pkg.getCurrentPrice());
|
|
|
+// userVip.setPaymentMethod("微信支付");
|
|
|
+// userVip.setOrderNo(orderNo);
|
|
|
+// userVip.setStatus(0); // 0-待支付,1-已生效
|
|
|
+// userVip.setSource("购买");
|
|
|
+// userVip.setCreateTime(now);
|
|
|
+// userVipMapper.insert(userVip);
|
|
|
+//
|
|
|
+// // 4. 构建微信支付V2请求
|
|
|
+// WxPayUnifiedOrderRequest payRequest = buildWxPayV2Request(orderNo, pkg, userId);
|
|
|
+//
|
|
|
+// // 5. 调用微信支付V2下单接口
|
|
|
+// WxPayUnifiedOrderResult wxPayResult = createWxPayV2Order(payRequest);
|
|
|
+//
|
|
|
+// // 6. 生成前端调起支付的参数
|
|
|
+// Map<String, Object> payParams = generateV2PayParams(wxPayResult, pkg);
|
|
|
+//
|
|
|
+// // 7. 返回支付参数给前端
|
|
|
+// payParams.put("orderNo", orderNo);
|
|
|
+// return payParams;
|
|
|
+//}
|
|
|
+//
|
|
|
+// /**
|
|
|
+// * 构建微信支付V2请求参数
|
|
|
+// */
|
|
|
+// private WxPayUnifiedOrderRequest buildWxPayV2Request(String orderNo, VipPackage pkg, Long userId) {
|
|
|
+// // 查询用户OpenID
|
|
|
+// Users user = usersService.getById(userId);
|
|
|
+// if (user == null || user.getWechatOpenid() == null) {
|
|
|
+// throw new RuntimeException("用户微信信息未绑定");
|
|
|
+// }
|
|
|
+//
|
|
|
+// WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
|
|
|
+// request.setOutTradeNo(orderNo); // 商户订单号
|
|
|
+// request.setBody(pkg.getPackageName()); // 商品描述
|
|
|
+// request.setTotalFee(pkg.getCurrentPrice().multiply(new BigDecimal("100")).intValue()); // 总金额(分)
|
|
|
+// request.setSpbillCreateIp("127.0.0.1"); // 终端IP(实际部署需改为用户真实IP)
|
|
|
+// request.setNotifyUrl("https://mini.workervip.com/pay/notify"); // 回调地址
|
|
|
+// request.setTradeType("JSAPI"); // 支付类型(JSAPI/小程序)
|
|
|
+// request.setOpenid(user.getWechatOpenid()); // 用户OpenID(JSAPI必填)
|
|
|
+//
|
|
|
+// return request;
|
|
|
+// }
|
|
|
+//
|
|
|
+// /**
|
|
|
+// * 初始化微信支付V2配置并下单
|
|
|
+// */
|
|
|
+// private WxPayUnifiedOrderResult createWxPayV2Order(WxPayUnifiedOrderRequest request) throws WxPayException {
|
|
|
+// Wx wxConfig = wxService.list().get(0);
|
|
|
+// WxPayConfig payConfig = new WxPayConfig();
|
|
|
+//
|
|
|
+// // V2配置核心参数
|
|
|
+// payConfig.setAppId(wxConfig.getAppId()); // 公众号/小程序AppID
|
|
|
+// payConfig.setMchId(wxConfig.getMchId()); // 商户号
|
|
|
+// payConfig.setMchKey("7f633cbabd894b4d213bc6edffe3b119"); // V2密钥(商户平台设置的32位密钥)
|
|
|
+// payConfig.setNotifyUrl("https://mini.workervip.com/pay/notify");
|
|
|
+//
|
|
|
+// WxPayService wxPayService = new WxPayServiceImpl();
|
|
|
+// wxPayService.setConfig(payConfig);
|
|
|
+//
|
|
|
+// // 调用V2统一下单接口
|
|
|
+// return wxPayService.unifiedOrder(request);
|
|
|
+// }
|
|
|
+//
|
|
|
+// /**
|
|
|
+// * 生成V2版本前端调起支付的参数
|
|
|
+// */
|
|
|
+// private Map<String, Object> generateV2PayParams(WxPayUnifiedOrderResult wxPayResult, VipPackage pkg) {
|
|
|
+// Wx wxConfig = wxService.list().get(0);
|
|
|
+// String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
|
|
|
+// String nonceStr = RandomStringUtils.randomAlphanumeric(32);
|
|
|
+// String prepayId = wxPayResult.getPrepayId();
|
|
|
+//
|
|
|
+// // V2签名参数(注意参数顺序和格式)
|
|
|
+// Map<String, String> signParams = new HashMap<>();
|
|
|
+// signParams.put("appId", wxConfig.getAppId());
|
|
|
+// signParams.put("timeStamp", timeStamp);
|
|
|
+// signParams.put("nonceStr", nonceStr);
|
|
|
+// signParams.put("package", "prepay_id=" + prepayId);
|
|
|
+// signParams.put("signType", "MD5"); // V2默认MD5签名
|
|
|
+//
|
|
|
+// // 生成签名(使用商户密钥)
|
|
|
+// String paySign = SignUtils.createSign(signParams, "7f633cbabd894b4d213bc6edffe3b119");
|
|
|
+//
|
|
|
+// // 组装前端需要的参数
|
|
|
+// Map<String, Object> payParams = new HashMap<>();
|
|
|
+// payParams.put("appId", wxConfig.getAppId());
|
|
|
+// payParams.put("timeStamp", timeStamp);
|
|
|
+// payParams.put("nonceStr", nonceStr);
|
|
|
+// payParams.put("package", "prepay_id=" + prepayId); // 注意格式
|
|
|
+// payParams.put("signType", "MD5");
|
|
|
+// payParams.put("paySign", paySign);
|
|
|
+// payParams.put("totalFee", pkg.getCurrentPrice().multiply(new BigDecimal("100")).intValue()); // 总金额(分)
|
|
|
+//
|
|
|
+// return payParams;
|
|
|
+// }
|
|
|
+//
|
|
|
+//}
|
|
|
+//
|
|
|
+//
|
|
|
+
|
|
|
+
|
|
|
package com.zhentao.service.impl;
|
|
|
|
|
|
import com.fasterxml.jackson.core.type.TypeReference;
|
|
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
|
|
-import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
|
|
|
-import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
|
|
|
+import com.github.binarywang.wxpay.bean.notify.WxPayNotifyResponse;
|
|
|
+import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
|
|
|
+import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
|
|
|
+import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
|
|
|
+import com.github.binarywang.wxpay.config.WxPayConfig;
|
|
|
+import com.github.binarywang.wxpay.exception.WxPayException;
|
|
|
+import com.github.binarywang.wxpay.service.WxPayService;
|
|
|
+import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
|
|
|
+import com.github.binarywang.wxpay.util.SignUtils;
|
|
|
+import com.zhentao.config.WechatPayProperties;
|
|
|
import com.zhentao.entity.UserVip;
|
|
|
import com.zhentao.entity.VipPackage;
|
|
|
import com.zhentao.entity.Wx;
|
|
|
@@ -15,27 +586,28 @@ import com.zhentao.service.VipService;
|
|
|
import com.zhentao.service.WxService;
|
|
|
import com.zhentao.vo.UserVipInfoVO;
|
|
|
import com.zhentao.vo.VipPackageVO;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.apache.commons.lang3.RandomStringUtils;
|
|
|
+import org.apache.commons.lang3.RandomUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
-import java.text.SimpleDateFormat;
|
|
|
import java.time.LocalDateTime;
|
|
|
import java.time.temporal.ChronoUnit;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.List;
|
|
|
-import java.util.UUID;
|
|
|
+import java.util.*;
|
|
|
|
|
|
/**
|
|
|
- * VIP服务实现类
|
|
|
+ * VIP服务实现类(支付成功后激活VIP)
|
|
|
*/
|
|
|
@Service
|
|
|
+@Slf4j
|
|
|
public class VipServiceImpl implements VipService {
|
|
|
-
|
|
|
+
|
|
|
@Autowired
|
|
|
private VipPackageMapper vipPackageMapper;
|
|
|
-
|
|
|
+
|
|
|
@Autowired
|
|
|
private UserVipMapper userVipMapper;
|
|
|
|
|
|
@@ -43,21 +615,20 @@ public class VipServiceImpl implements VipService {
|
|
|
private WxService wxService;
|
|
|
@Autowired
|
|
|
private UsersService usersService;
|
|
|
-
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private WechatPayProperties wxConfig;
|
|
|
+
|
|
|
private final ObjectMapper objectMapper = new ObjectMapper();
|
|
|
-
|
|
|
+
|
|
|
@Override
|
|
|
public UserVipInfoVO getVipInfo(Long userId) {
|
|
|
UserVipInfoVO vo = new UserVipInfoVO();
|
|
|
-
|
|
|
- // 查询用户当前生效的VIP
|
|
|
+
|
|
|
UserVip currentVip = userVipMapper.selectActiveVipByUserId(userId);
|
|
|
-
|
|
|
if (currentVip != null && currentVip.getEndTime().isAfter(LocalDateTime.now())) {
|
|
|
vo.setIsVip(true);
|
|
|
vo.setVipExpireTime(currentVip.getEndTime());
|
|
|
-
|
|
|
- // 计算剩余天数
|
|
|
long days = ChronoUnit.DAYS.between(LocalDateTime.now(), currentVip.getEndTime());
|
|
|
vo.setRemainingDays((int) days);
|
|
|
} else {
|
|
|
@@ -65,11 +636,9 @@ public class VipServiceImpl implements VipService {
|
|
|
vo.setVipExpireTime(null);
|
|
|
vo.setRemainingDays(0);
|
|
|
}
|
|
|
-
|
|
|
- // 查询所有VIP套餐
|
|
|
+
|
|
|
List<VipPackage> packages = vipPackageMapper.selectAllActive();
|
|
|
List<VipPackageVO> packageVOs = new ArrayList<>();
|
|
|
-
|
|
|
for (VipPackage pkg : packages) {
|
|
|
VipPackageVO pkgVO = new VipPackageVO();
|
|
|
pkgVO.setPackageId(pkg.getPackageId());
|
|
|
@@ -79,121 +648,144 @@ public class VipServiceImpl implements VipService {
|
|
|
pkgVO.setOriginalPrice(pkg.getOriginalPrice());
|
|
|
pkgVO.setPrice(pkg.getCurrentPrice());
|
|
|
pkgVO.setRecommend(pkg.getIsRecommend() == 1);
|
|
|
-
|
|
|
- // 解析特权列表JSON
|
|
|
+
|
|
|
try {
|
|
|
if (pkg.getBenefits() != null && !pkg.getBenefits().isEmpty()) {
|
|
|
List<String> benefits = objectMapper.readValue(
|
|
|
- pkg.getBenefits(),
|
|
|
- new TypeReference<List<String>>() {}
|
|
|
+ pkg.getBenefits(),
|
|
|
+ new TypeReference<List<String>>() {}
|
|
|
);
|
|
|
pkgVO.setBenefits(benefits);
|
|
|
} else {
|
|
|
pkgVO.setBenefits(new ArrayList<>());
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
- // JSON解析失败时记录日志并使用空列表
|
|
|
- System.err.println("解析套餐 " + pkg.getPackageId() + " 的 benefits 失败: " + e.getMessage());
|
|
|
- System.err.println("原始数据: " + pkg.getBenefits());
|
|
|
pkgVO.setBenefits(new ArrayList<>());
|
|
|
}
|
|
|
-
|
|
|
packageVOs.add(pkgVO);
|
|
|
}
|
|
|
-
|
|
|
vo.setPackages(packageVOs);
|
|
|
-
|
|
|
return vo;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 下单:仅创建待支付订单,不激活VIP
|
|
|
+ */
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
- public Boolean purchaseVip(Long userId, Long packageId) {
|
|
|
- // 查询套餐信息
|
|
|
+ public Map<String, Object> purchaseVip(Long userId, Long packageId) throws WxPayException {
|
|
|
+ // 1. 校验套餐
|
|
|
VipPackage pkg = vipPackageMapper.selectById(packageId);
|
|
|
if (pkg == null || pkg.getStatus() != 1) {
|
|
|
throw new RuntimeException("套餐不存在或已下架");
|
|
|
}
|
|
|
-
|
|
|
- // 生成订单号
|
|
|
- String orderNo = generateOrderNo();
|
|
|
- String timeExpire = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(System.currentTimeMillis());
|
|
|
- Wx wx = wxService.list().get(0);
|
|
|
- WxPayUnifiedOrderV3Request wxPayUnifiedOrderV3Request = new WxPayUnifiedOrderV3Request();
|
|
|
- wxPayUnifiedOrderV3Request.setOutTradeNo(orderNo);
|
|
|
- wxPayUnifiedOrderV3Request.setDescription(pkg.getPackageName());
|
|
|
- wxPayUnifiedOrderV3Request.setTimeExpire(timeExpire);
|
|
|
- wxPayUnifiedOrderV3Request.setNotifyUrl("https://mini.workervip.com/pay/notify");
|
|
|
- //订单金额
|
|
|
- WxPayUnifiedOrderV3Request.Amount amount = new WxPayUnifiedOrderV3Request.Amount();
|
|
|
- amount.setTotal(pkg.getCurrentPrice().intValue());
|
|
|
- amount.setCurrency("CNY");
|
|
|
- wxPayUnifiedOrderV3Request.setAmount(amount);
|
|
|
-
|
|
|
- Users byId = usersService.getById(userId);
|
|
|
- //付款人员
|
|
|
- WxPayUnifiedOrderV3Request.Payer payer = new WxPayUnifiedOrderV3Request.Payer();
|
|
|
- payer.setOpenid(byId.getWechatOpenId());
|
|
|
- TradeTypeEnum tradeTypeEnum =TradeTypeEnum.JSAPI;
|
|
|
-
|
|
|
-
|
|
|
|
|
|
+ // 2. 生成订单号
|
|
|
+ String orderNo = generateOrderNo();
|
|
|
LocalDateTime now = LocalDateTime.now();
|
|
|
LocalDateTime startTime = now;
|
|
|
- LocalDateTime endTime;
|
|
|
-
|
|
|
-
|
|
|
- // 查询用户当前VIP状态
|
|
|
+ // 处理续期:如果用户已有生效VIP,从结束时间开始累加
|
|
|
UserVip currentVip = userVipMapper.selectActiveVipByUserId(userId);
|
|
|
if (currentVip != null && currentVip.getEndTime().isAfter(now)) {
|
|
|
- // 如果已有VIP,从当前VIP结束时间开始累加
|
|
|
startTime = currentVip.getEndTime();
|
|
|
}
|
|
|
-
|
|
|
- // 计算结束时间
|
|
|
- endTime = startTime.plusDays(pkg.getDurationDays());
|
|
|
+ LocalDateTime endTime = startTime.plusDays(pkg.getDurationDays());
|
|
|
|
|
|
-
|
|
|
- // 创建VIP记录
|
|
|
+ // 3. 创建待支付的VIP订单记录(状态为0-待支付,无生效时间)
|
|
|
UserVip userVip = new UserVip();
|
|
|
userVip.setUserId(userId);
|
|
|
userVip.setPackageId(packageId);
|
|
|
- userVip.setStartTime(startTime);
|
|
|
- userVip.setEndTime(endTime);
|
|
|
+ userVip.setStartTime(startTime); // 支付成功前不设置生效时间
|
|
|
+ userVip.setEndTime(endTime); // 支付成功前不设置结束时间
|
|
|
userVip.setDurationDays(pkg.getDurationDays());
|
|
|
userVip.setPaymentAmount(pkg.getCurrentPrice());
|
|
|
- userVip.setPaymentMethod("在线支付"); // TODO: 实际支付方式
|
|
|
+ userVip.setPaymentMethod("微信支付");
|
|
|
userVip.setOrderNo(orderNo);
|
|
|
- userVip.setStatus(1);
|
|
|
+ userVip.setStatus(1); // 0-待支付
|
|
|
userVip.setSource("购买");
|
|
|
- userVip.setCreateTime(now);
|
|
|
-
|
|
|
- int result = userVipMapper.insert(userVip);
|
|
|
-
|
|
|
- return result > 0;
|
|
|
+ userVip.setCreateTime(LocalDateTime.now());
|
|
|
+ userVipMapper.insert(userVip);
|
|
|
+
|
|
|
+ // 4. 构建微信支付V2请求并下单
|
|
|
+ WxPayUnifiedOrderRequest payRequest = buildWxPayV2Request(orderNo, pkg, userId);
|
|
|
+ WxPayUnifiedOrderResult wxPayResult = createWxPayV2Order(payRequest);
|
|
|
+
|
|
|
+ // 5. 生成前端调起支付的参数
|
|
|
+ Map<String, Object> payParams = generateV2PayParams(wxPayResult, pkg);
|
|
|
+ payParams.put("orderNo", orderNo);
|
|
|
+ return payParams;
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 微信支付回调处理:支付成功后激活VIP
|
|
|
+ */
|
|
|
+ @Transactional(rollbackFor = Exception.class)
|
|
|
+ public String handlePayNotify(String notifyData) throws WxPayException {
|
|
|
+ // 1. 解析回调数据并验证签名
|
|
|
+ WxPayConfig payConfig = getWxPayConfig();
|
|
|
+ WxPayService wxPayService = new WxPayServiceImpl();
|
|
|
+ wxPayService.setConfig(payConfig);
|
|
|
+
|
|
|
+ WxPayOrderNotifyResult notifyResult = wxPayService.parseOrderNotifyResult(notifyData);
|
|
|
+ String orderNo = notifyResult.getOutTradeNo(); // 商户订单号
|
|
|
+ String transactionId = notifyResult.getTransactionId(); // 微信支付订单号
|
|
|
+
|
|
|
+ // 2. 查询待支付的VIP订单
|
|
|
+ UserVip pendingVip = userVipMapper.selectByOrderNo(orderNo);
|
|
|
+ if (pendingVip == null || pendingVip.getStatus() != 0) {
|
|
|
+ log.warn("订单不存在或已处理:{}", orderNo);
|
|
|
+ return WxPayNotifyResponse.fail("订单不存在或已处理");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 验证支付金额(可选)
|
|
|
+ VipPackage pkg = vipPackageMapper.selectById(pendingVip.getPackageId());
|
|
|
+ int totalFee = pkg.getCurrentPrice().multiply(new BigDecimal("100")).intValue();
|
|
|
+ if (Integer.parseInt(String.valueOf(notifyResult.getTotalFee())) != totalFee) {
|
|
|
+ log.warn("订单金额不一致:{},实际支付:{}", orderNo, notifyResult.getTotalFee());
|
|
|
+ return WxPayNotifyResponse.fail("金额不一致");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 4. 支付成功,激活VIP
|
|
|
+ LocalDateTime now = LocalDateTime.now();
|
|
|
+ LocalDateTime startTime = now;
|
|
|
+
|
|
|
+ // 处理续期:如果用户已有生效VIP,从结束时间开始累加
|
|
|
+ UserVip currentVip = userVipMapper.selectActiveVipByUserId(pendingVip.getUserId());
|
|
|
+ if (currentVip != null && currentVip.getEndTime().isAfter(now)) {
|
|
|
+ startTime = currentVip.getEndTime();
|
|
|
+ }
|
|
|
+ LocalDateTime endTime = startTime.plusDays(pkg.getDurationDays());
|
|
|
+
|
|
|
+ // 更新VIP订单为已支付并激活
|
|
|
+ pendingVip.setStatus(1); // 1-已生效
|
|
|
+ pendingVip.setStartTime(startTime);
|
|
|
+ pendingVip.setEndTime(endTime);
|
|
|
+ pendingVip.setPaymentTime(now);
|
|
|
+ pendingVip.setOrderNo(transactionId);
|
|
|
+ userVipMapper.updateById(pendingVip);
|
|
|
+
|
|
|
+ log.info("VIP激活成功:用户{},订单{},到期时间{}", pendingVip.getUserId(), orderNo, endTime);
|
|
|
+ return WxPayNotifyResponse.success("处理成功");
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public Boolean grantVip(Long userId, Integer days, String source) {
|
|
|
+ // 原有逻辑不变...
|
|
|
LocalDateTime now = LocalDateTime.now();
|
|
|
LocalDateTime startTime = now;
|
|
|
LocalDateTime endTime;
|
|
|
-
|
|
|
- // 查询用户当前VIP状态
|
|
|
+
|
|
|
UserVip currentVip = userVipMapper.selectActiveVipByUserId(userId);
|
|
|
if (currentVip != null && currentVip.getEndTime().isAfter(now)) {
|
|
|
- // 如果已有VIP,从当前VIP结束时间开始累加
|
|
|
startTime = currentVip.getEndTime();
|
|
|
}
|
|
|
-
|
|
|
- // 计算结束时间
|
|
|
endTime = startTime.plusDays(days);
|
|
|
-
|
|
|
- // 创建VIP记录
|
|
|
+
|
|
|
UserVip userVip = new UserVip();
|
|
|
userVip.setUserId(userId);
|
|
|
- userVip.setPackageId(0L); // 赠送的VIP没有套餐ID
|
|
|
+ userVip.setPackageId(0L);
|
|
|
userVip.setStartTime(startTime);
|
|
|
userVip.setEndTime(endTime);
|
|
|
userVip.setDurationDays(days);
|
|
|
@@ -203,28 +795,82 @@ public class VipServiceImpl implements VipService {
|
|
|
userVip.setStatus(1);
|
|
|
userVip.setSource(source);
|
|
|
userVip.setCreateTime(now);
|
|
|
-
|
|
|
+
|
|
|
int result = userVipMapper.insert(userVip);
|
|
|
-
|
|
|
return result > 0;
|
|
|
}
|
|
|
-
|
|
|
- /**
|
|
|
- * 生成订单号
|
|
|
- */
|
|
|
- private String generateOrderNo() {
|
|
|
- return "VIP" + System.currentTimeMillis() + UUID.randomUUID().toString().substring(0, 8);
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 获取时长单位文字
|
|
|
- */
|
|
|
private String getDurationText(Integer days) {
|
|
|
if (days == 30) return "月";
|
|
|
if (days == 90) return "季";
|
|
|
if (days == 365) return "年";
|
|
|
return days + "天";
|
|
|
}
|
|
|
-}
|
|
|
|
|
|
+ private String generateOrderNo() {
|
|
|
+ return "VIP" + System.currentTimeMillis() + RandomUtils.nextInt(1000, 9999);
|
|
|
+ }
|
|
|
+
|
|
|
+ private WxPayUnifiedOrderRequest buildWxPayV2Request(String orderNo, VipPackage pkg, Long userId) {
|
|
|
+ Users user = usersService.getById(userId);
|
|
|
+ if (user == null || user.getWechatOpenid() == null) {
|
|
|
+ throw new RuntimeException("用户微信信息未绑定");
|
|
|
+ }
|
|
|
+
|
|
|
+ WxPayUnifiedOrderRequest request = new WxPayUnifiedOrderRequest();
|
|
|
+ request.setOutTradeNo(orderNo);
|
|
|
+ request.setBody(pkg.getPackageName());
|
|
|
+ request.setTotalFee(pkg.getCurrentPrice().multiply(new BigDecimal("100")).intValue());
|
|
|
+ request.setSpbillCreateIp("127.0.0.1");
|
|
|
+ request.setNotifyUrl("https://mini.workervip.com/pay/notify"); // 回调地址
|
|
|
+ request.setTradeType("JSAPI");
|
|
|
+ request.setOpenid(user.getWechatOpenid());
|
|
|
+ return request;
|
|
|
+ }
|
|
|
+
|
|
|
+ private WxPayUnifiedOrderResult createWxPayV2Order(WxPayUnifiedOrderRequest request) throws WxPayException {
|
|
|
+ WxPayService wxPayService = new WxPayServiceImpl();
|
|
|
+ wxPayService.setConfig(getWxPayConfig());
|
|
|
+ return wxPayService.unifiedOrder(request);
|
|
|
+ }
|
|
|
+
|
|
|
+ private Map<String, Object> generateV2PayParams(WxPayUnifiedOrderResult wxPayResult, VipPackage pkg) {
|
|
|
+ Wx wxConfig = wxService.list().get(0);
|
|
|
+ String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
|
|
|
+ String nonceStr = RandomStringUtils.randomAlphanumeric(32);
|
|
|
+ String prepayId = wxPayResult.getPrepayId();
|
|
|
+
|
|
|
+ Map<String, String> signParams = new HashMap<>();
|
|
|
+ signParams.put("appId", wxConfig.getAppId());
|
|
|
+ signParams.put("timeStamp", timeStamp);
|
|
|
+ signParams.put("nonceStr", nonceStr);
|
|
|
+ signParams.put("package", "prepay_id=" + prepayId);
|
|
|
+ signParams.put("signType", "MD5");
|
|
|
|
|
|
+ String paySign = SignUtils.createSign(signParams,"7f633cbabd894b4d213bc6edffe3b119");
|
|
|
+
|
|
|
+ Map<String, Object> payParams = new HashMap<>();
|
|
|
+ payParams.put("appId", wxConfig.getAppId());
|
|
|
+ payParams.put("timeStamp", timeStamp);
|
|
|
+ payParams.put("nonceStr", nonceStr);
|
|
|
+ payParams.put("package", "prepay_id=" + prepayId);
|
|
|
+ payParams.put("signType", "MD5");
|
|
|
+ payParams.put("paySign", paySign);
|
|
|
+ payParams.put("totalFee", pkg.getCurrentPrice().multiply(new BigDecimal("100")).intValue());
|
|
|
+ return payParams;
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 获取微信支付配置
|
|
|
+ */
|
|
|
+ private WxPayConfig getWxPayConfig() {
|
|
|
+ Wx wxConfig = wxService.list().get(0);
|
|
|
+ WxPayConfig payConfig = new WxPayConfig();
|
|
|
+ payConfig.setAppId(wxConfig.getAppId());
|
|
|
+ payConfig.setMchId(wxConfig.getMchId());
|
|
|
+ payConfig.setMchKey("7f633cbabd894b4d213bc6edffe3b119");
|
|
|
+ payConfig.setNotifyUrl("https://mini.workervip.com/pay/notify");
|
|
|
+ return payConfig;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+}
|