package com.zsElectric.boot.business.service.impl; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ObjectUtil; import com.aliyun.oss.ServiceException; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.google.gson.Gson; import com.google.gson.JsonObject; import com.zsElectric.boot.business.model.entity.UserRefundsOrderInfo; import com.zsElectric.boot.business.mapper.*; import com.zsElectric.boot.business.model.entity.*; import com.zsElectric.boot.business.model.form.applet.AppLevelOrderForm; import com.zsElectric.boot.business.model.form.applet.AppUserPayForm; import com.zsElectric.boot.business.model.query.applet.AppUserOrderInfoQuery; import com.zsElectric.boot.business.model.vo.applet.AppUserInfoVO; import com.zsElectric.boot.business.model.vo.applet.WechatPayParamsVO; import com.zsElectric.boot.business.service.*; import com.zsElectric.boot.common.constant.SystemConstants; import com.zsElectric.boot.core.exception.BusinessException; import com.zsElectric.boot.core.pay.*; import com.zsElectric.boot.security.util.SecurityUtils; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.RandomStringUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.zsElectric.boot.business.model.form.UserOrderInfoForm; import com.zsElectric.boot.business.model.query.UserOrderInfoQuery; import com.zsElectric.boot.business.model.vo.UserOrderInfoVO; import com.zsElectric.boot.business.converter.UserOrderInfoConverter; import java.math.BigDecimal; import java.math.RoundingMode; import java.text.DecimalFormat; import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.util.*; import java.util.concurrent.locks.ReentrantLock; import cn.hutool.core.lang.Assert; import cn.hutool.core.util.StrUtil; import org.springframework.transaction.annotation.Transactional; /** * 用户支付订单信息服务实现类 * * @author zsElectric * @since 2025-12-16 16:25 */ @Slf4j @Service @RequiredArgsConstructor public class UserOrderInfoServiceImpl extends ServiceImpl implements UserOrderInfoService { private final UserOrderInfoConverter userOrderInfoConverter; private final UserInfoMapper userInfoMapper; private final RechargeLevelMapper rechargeLevelMapper; private final UserAccountMapper userAccountMapper; private final UserAccountService userAccountService; private final WechatPayV3Utils wechatPayV3Utils; private final ChargeOrderInfoMapper chargeOrderInfoMapper; private final UserRefundsOrderInfoMapper userRefundsOrderInfoMapper; // 声明一个可重入锁 private final ReentrantLock lock = new ReentrantLock(); /** * 获取用户支付订单信息分页列表 * * @param queryParams 查询参数 * @return {@link IPage} 用户支付订单信息分页列表 */ @Override public IPage getUserOrderInfoPage(UserOrderInfoQuery queryParams) { Page pageVO = this.baseMapper.getUserOrderInfoPage( new Page<>(queryParams.getPageNum(), queryParams.getPageSize()), queryParams ); return pageVO; } /** * 获取用户支付订单信息表单数据 * * @param id 用户支付订单信息ID * @return 用户支付订单信息表单数据 */ @Override public UserOrderInfoForm getUserOrderInfoFormData(Long id) { UserOrderInfo entity = this.getById(id); return userOrderInfoConverter.toForm(entity); } /** * 新增用户支付订单信息 * * @param formData 用户支付订单信息表单对象 * @return 是否新增成功 */ @Override public boolean saveUserOrderInfo(UserOrderInfoForm formData) { UserOrderInfo entity = userOrderInfoConverter.toEntity(formData); return this.save(entity); } /** * 更新用户支付订单信息 * * @param id 用户支付订单信息ID * @param formData 用户支付订单信息表单对象 * @return 是否修改成功 */ @Override public boolean updateUserOrderInfo(Long id, UserOrderInfoForm formData) { UserOrderInfo entity = userOrderInfoConverter.toEntity(formData); return this.updateById(entity); } /** * 删除用户支付订单信息 * * @param ids 用户支付订单信息ID,多个以英文逗号(,)分割 * @return 是否删除成功 */ @Override public boolean deleteUserOrderInfos(String ids) { Assert.isTrue(StrUtil.isNotBlank(ids), "删除的用户支付订单信息数据为空"); // 逻辑删除 List idList = Arrays.stream(ids.split(",")) .map(Long::parseLong) .toList(); return this.removeByIds(idList); } @Override public IPage getTicketRecords(AppUserOrderInfoQuery queryParams) { UserOrderInfoQuery userOrderInfoQuery = new UserOrderInfoQuery(); userOrderInfoQuery.setUserId(SecurityUtils.getUserId()); Page pageVO = this.baseMapper.getUserOrderInfoPage( new Page<>(queryParams.getPageNum(), queryParams.getPageSize()), userOrderInfoQuery ); return pageVO; } @Override public AppUserInfoVO getAppletUserInfo(Long userId){ return userInfoMapper.getAppletUserInfo(userId); } /** * 创建订单 * * @param appLevelOrderForm * @return */ @Override public AppUserPayForm createOrder(AppLevelOrderForm appLevelOrderForm) { Long userId = SecurityUtils.getUserId(); String userOpenId = this.getAppletUserInfo(userId).getOpenid(); String orderNo = createOrderNo("SP", userId); //创建订单 UserOrderInfo orderInfo = new UserOrderInfo(); orderInfo.setUserId(userId); orderInfo.setOrderNo(orderNo); orderInfo.setLevelId(appLevelOrderForm.getLevelId()); orderInfo.setOpenid(userOpenId); this.save(orderInfo); //构建支付表单返回给前端支撑JsApi支付调用 AppUserPayForm payForm = new AppUserPayForm(); payForm.setOrderId(orderInfo.getId()).setOrderNo(orderNo); //查询档位 RechargeLevel level = rechargeLevelMapper.selectById(appLevelOrderForm.getLevelId()); Map result = payment(userOpenId, orderNo, level.getMoney()); payForm.setParams(result); return payForm; } /** * 支付订单 * * @param orderId * @return */ @Override public AppUserPayForm payOrder(String orderId) { UserOrderInfo orderInfo = this.getById(orderId); //构建支付表单 AppUserPayForm payForm = new AppUserPayForm(); payForm.setOrderId(orderInfo.getId()).setOrderNo(orderInfo.getOrderNo()); //查询档位 RechargeLevel level = rechargeLevelMapper.selectById(orderInfo.getLevelId()); Map result = payment(orderInfo.getOpenid(), orderInfo.getOrderNo(), level.getMoney()); payForm.setParams(result); return payForm; } /** * 订单查询 * * @param orderNo * @return */ @Override public String orderQuery(String orderNo) throws Exception { //查询订单 UserOrderInfo orderInfo = this.getById(Wrappers.lambdaQuery().eq(UserOrderInfo::getOrderNo, orderNo).last("limit 1")); if (ObjectUtil.isEmpty(orderInfo)) { throw new RuntimeException("当前订单不存在"); } //null代表查询失败 SUCCESS-成功 USERPAYING和ACCEPT为中间态 其他为支付失败 JsonObject res = orderQueryByOutTradeNo(orderNo, WechatConstants.WECHAT_MCH_ID); String s = res == null ? null : res.get("trade_state").getAsString(); // String s = "SUCCESS"; if ("SUCCESS".equals(s)) { if (ObjectUtil.isNotEmpty(orderInfo) && Objects.equals(orderInfo.getOrderStatus(), SystemConstants.STATUS_ONE)) { String transactionId = res.get("transaction_id").getAsString(); LocalDateTime payTime = LocalDateTime.now(); // 微信返回的金额单位是分,需要转换为元 BigDecimal payMoney = new BigDecimal(res.get("amount").getAsJsonObject().get("total").getAsString()) .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); //执行业务操作 this.successPayOrder(orderInfo, transactionId, payTime, payMoney); } return "100001";//支付成功 } if (s == null) { //查询订单 return "100002";//查询失败 } if ("USERPAYING".equals(s) || "ACCEPT".equals(s)) { //查询订单 return "100003";//查询中 } return "100004";//支付失败 } /** * 微信支付回调 * * @param request * @param response * @return */ @Override public Map wechatPayNotify(HttpServletRequest request, HttpServletResponse response) { Map result = new HashMap<>(2); //验签及解析返回数据 JsonObject res = wechatPayV3Utils.getCallbackData(request); if (res == null) { result.put("code", "FAIL"); result.put("message", "失败"); return result; } log.info("最终拿到的微信支付通知数据:" + res); String orderNo = res.get("out_trade_no").getAsString(); if (lock.tryLock()) { // 处理支付成功后的业务 例如 将订单状态修改为已支付 具体参数键值可参考文档 注意!!! 微信可能会多次发送重复的通知 因此要判断业务是否已经处理过了 避免重复处理 try { //查询订单,判断是否已修改为已支付状态 UserOrderInfo orderInfo = this.getOne(Wrappers.lambdaQuery().eq(UserOrderInfo::getOrderNo, orderNo).last("limit 1")); if (ObjectUtil.isNotEmpty(orderInfo)) { if (Objects.equals(orderInfo.getOrderStatus(), SystemConstants.STATUS_TWO)) { result.put("code", "SUCCESS"); result.put("message", "OK"); return result; } if (Objects.equals(orderInfo.getOrderStatus(), SystemConstants.STATUS_ONE)) { String transactionId = res.get("transaction_id").getAsString(); LocalDateTime payTime = LocalDateTime.now(); // 微信返回的金额单位是分,需要转换为元 BigDecimal payMoney = new BigDecimal(res.get("amount").getAsJsonObject().get("total").getAsString()) .divide(new BigDecimal("100"), 2, RoundingMode.HALF_UP); //执行业务操作 this.successPayOrder(orderInfo, transactionId, payTime, payMoney); } } result.put("code", "SUCCESS"); result.put("message", "OK"); return result; } catch (Exception e) { log.error("微信支付回调异常:" + e.getMessage()); result.put("code", "FAIL"); result.put("message", "失败"); return result; } finally { lock.unlock(); } } else { result.put("code", "FAIL"); result.put("message", "失败"); return result; } } /** * 支付成功业务处理 * * @param orderInfo * @param transactionId * @param payTime * @param orderMoney * @throws Exception */ @Transactional(rollbackFor = Exception.class) protected void successPayOrder(UserOrderInfo orderInfo, String transactionId, LocalDateTime payTime, BigDecimal orderMoney) throws Exception { //修改订单状态为已支付 orderInfo.setOrderStatus(SystemConstants.STATUS_TWO); orderInfo.setPayTime(payTime); orderInfo.setTransactionId(transactionId); orderInfo.setPayMoney(orderMoney); this.updateById(orderInfo); //账户变动及日志记录 Long userId = orderInfo.getUserId(); UserAccount userAccount = userAccountService.updateAccountBalanceAndLog( userId, orderMoney, SystemConstants.CHANGE_TYPE_ADD, SystemConstants.ACCOUNT_LOG_PAY_NOTE, orderInfo.getId() ); //平订单超充金额(使用更新后的账户信息) orderBackTax(userId, userAccount); } private void orderBackTax(Long userId, UserAccount userAccount) { //查询超充订单 List chargeOrderInfoList = chargeOrderInfoMapper.selectList(Wrappers.lambdaQuery() .eq(ChargeOrderInfo::getUserId, userId) .eq(ChargeOrderInfo::getMaspStatus, SystemConstants.STATUS_ONE) .orderByAsc(ChargeOrderInfo::getCreateTime) ); if (CollUtil.isNotEmpty(chargeOrderInfoList)) { //余额减去超充金额(使用更新后的余额) BigDecimal balance = userAccount.getBalance(); for (ChargeOrderInfo chargeOrderInfo : chargeOrderInfoList) { if (balance.compareTo(BigDecimal.ZERO) > 0) { BigDecimal deductAmount; // 本次扣除金额 if (balance.compareTo(chargeOrderInfo.getTotalMaspMoney()) < 0) { //余额不足,部分补缴 deductAmount = balance; chargeOrderInfo.setAlreadyMaspMoney(deductAmount); chargeOrderInfo.setMaspDesc("部分补缴"); chargeOrderInfo.setMaspStatus(SystemConstants.STATUS_TWO); chargeOrderInfoMapper.updateById(chargeOrderInfo); //账户变动及日志记录 userAccountService.updateAccountBalanceAndLog( userId, deductAmount.negate(), SystemConstants.CHANGE_TYPE_REDUCE, SystemConstants.ACCOUNT_LOG_BACK_TAX_NOTE, chargeOrderInfo.getId() ); balance = BigDecimal.ZERO; continue; } //余额足够,完成补缴 deductAmount = chargeOrderInfo.getTotalMaspMoney(); balance = balance.subtract(deductAmount); chargeOrderInfo.setAlreadyMaspMoney(deductAmount); chargeOrderInfo.setMaspDesc("完成补缴"); chargeOrderInfo.setMaspStatus(SystemConstants.STATUS_TWO); chargeOrderInfoMapper.updateById(chargeOrderInfo); //账户变动及日志记录 userAccountService.updateAccountBalanceAndLog( userId, deductAmount.negate(), SystemConstants.CHANGE_TYPE_REDUCE, SystemConstants.ACCOUNT_LOG_BACK_TAX_NOTE, chargeOrderInfo.getId() ); } } } } /** * 取消订单 * * @param orderId * @return */ @Override public String cancelOrder(String orderId) { UserOrderInfo orderInfo = this.getById(orderId); if (ObjectUtil.isNotEmpty(orderInfo)) { if (orderInfo.getOrderStatus() == 1) { log.info("修改订单:{},支付状态为已取消", orderId); orderInfo.setOrderStatus(SystemConstants.STATUS_THREE); this.updateById(orderInfo); } return "取消成功!"; } return "取消失败,请刷新后重试!"; } /** * 账户退款 * * @return */ @Override @Transactional(rollbackFor = Exception.class) public String refundOrder() throws Exception { //查询账户余额 UserAccount userAccount = userAccountMapper.selectOne(Wrappers.lambdaQuery().eq(UserAccount::getUserId, SecurityUtils.getUserId()).last("limit 1")); if (userAccount.getBalance().compareTo(BigDecimal.ZERO) == 0) { return "账户余额为 0"; } BigDecimal refundMoney = userAccount.getBalance(); //查询一年内已支付的所有券订单 List userOrderInfoList = baseMapper.selectList(Wrappers.lambdaQuery() .eq(UserOrderInfo::getUserId, userAccount.getId()) .eq(UserOrderInfo::getOrderStatus, SystemConstants.STATUS_TWO) .between(UserOrderInfo::getCreateTime, LocalDateTime.now().minusYears(1), LocalDateTime.now()) ); if (CollUtil.isEmpty(userOrderInfoList)) { log.info("当前用户一年内未支付任何券订单,无法进行退款操作!"); throw new BusinessException("无法进行退款操作,请联系客服处理!"); } for (UserOrderInfo userOrderInfo : userOrderInfoList) { if(refundMoney.compareTo(BigDecimal.ZERO) == 0){ break; } if ((userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney())).compareTo(refundMoney) < 0) { //退款金额大于订单金额,则直接退退款金额 refundOrder(userOrderInfo,refundMoney, "账户退款", SystemConstants.STATUS_ONE); //账户变动及日志记录 userAccountService.updateAccountBalanceAndLog( SecurityUtils.getUserId(), refundMoney, SystemConstants.CHANGE_TYPE_REDUCE, SystemConstants.ACCOUNT_LOG_REFUND_NOTE, userOrderInfo.getId() ); //修改订单状态 userOrderInfo.setOrderStatus(SystemConstants.STATUS_FOUR); this.updateById(userOrderInfo); refundMoney = BigDecimal.ZERO; break; } if ((userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney())).compareTo(refundMoney) > 0) { //退款金额小于订单金额,则先退订单金额 refundOrder(userOrderInfo,userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney()), "账户退款", SystemConstants.STATUS_ONE); //账户变动及日志记录 userAccountService.updateAccountBalanceAndLog( SecurityUtils.getUserId(), userOrderInfo.getOrderMoney().subtract(userOrderInfo.getRefundMoney()), SystemConstants.CHANGE_TYPE_REDUCE, SystemConstants.ACCOUNT_LOG_REFUND_NOTE, userOrderInfo.getId() ); //修改订单状态 userOrderInfo.setOrderStatus(SystemConstants.STATUS_FOUR); this.updateById(userOrderInfo); refundMoney = refundMoney.subtract(userOrderInfo.getOrderMoney()); } } return "账户退款,预计3个工作日内分一笔或多笔退还!如未收到,请联系客服!"; } public void refundOrder(UserOrderInfo userOrderInfo, BigDecimal refundAmount, String reason, Integer type) { log.info("进入退款接口------>"); log.info("执行操作的 原支付交易对应的商户订单号:{}", userOrderInfo.getOrderNo()); //退款单号 String out_refund_no = createOrderNo("TK",userOrderInfo.getId()); // 创建退款订单 UserRefundsOrderInfo userRefundsOrderInfo = new UserRefundsOrderInfo(); userRefundsOrderInfo.setOrderId(userOrderInfo.getId()); userRefundsOrderInfo.setOrderNo(userOrderInfo.getOrderNo()); userRefundsOrderInfo.setOutRefundNo(out_refund_no); userRefundsOrderInfo.setReason(reason); userRefundsOrderInfo.setAmount(refundAmount); userRefundsOrderInfo.setType(type); userRefundsOrderInfo.setCreateTime(LocalDateTime.now()); Map params = new HashMap(); params.put("transaction_id", userOrderInfo.getTransactionId()); params.put("out_trade_no", userOrderInfo.getOrderNo());//商户订单号 params.put("out_refund_no", out_refund_no);//商户退款单号 params.put("reason", reason);//退款原因 params.put("notify_url", WechatUrlConstants.PAY_V3_REFUND_NOTIFY);//退款通知 Map amount = new HashMap(); amount.put("refund", amount_fee(refundAmount));//退款金额 amount.put("currency", "CNY"); amount.put("total", amount_fee(userOrderInfo.getOrderMoney()));//原订单金额 params.put("amount", amount); // 执行请求POST 请求发送到微信退款接口 Gson gson = new Gson(); JsonObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_REFUND, gson.toJsonTree(params).getAsJsonObject()); log.info("最终拿到的微信支付通知数据:" + res); final String status = res.get("status").getAsString(); switch (status) { case "SUCCESS": log.info("订单:{},退款成功!原因:{}", userOrderInfo.getOrderNo(), reason); //修改订单状态 userOrderInfo.setOrderStatus(SystemConstants.STATUS_FIVE); userOrderInfo.setRefundMoney(userOrderInfo.getRefundMoney().add(refundAmount)); userOrderInfo.setRefundTime(LocalDateTime.now()); this.updateById(userOrderInfo); break; case "CLOSED": log.info("退款关闭"); break; case "PROCESSING": log.info("退款处理中"); //修改订单状态 userOrderInfo.setRefundMoney(userOrderInfo.getRefundMoney().add(refundAmount)); this.updateById(userOrderInfo); break; case "ABNORMAL": log.info("订单:{},退款异常", userOrderInfo.getOrderNo()); break; } userRefundsOrderInfo.setRefundId(res.get("refund_id").getAsString()); userRefundsOrderInfo.setNotifyRequest(res.toString()); userRefundsOrderInfo.setTransactionId(res.get("transaction_id").getAsString()); userRefundsOrderInfo.setAcceptedTime(LocalDateTime.now()); userRefundsOrderInfoMapper.insert(userRefundsOrderInfo); } @Override public Map refundCallback(HttpServletRequest request, HttpServletResponse response) { WechatRefundCallback refundCallback = new WechatRefundCallback() { @Override public void success(WechatCallbackRefundData refundData) { log.info("微信支付退款成功!"); UserOrderInfo userOrderInfo = baseMapper.selectOne(Wrappers.lambdaQuery(UserOrderInfo.class).eq(UserOrderInfo::getOrderNo, refundData.getOrderNo())); userOrderInfo.setOrderStatus(SystemConstants.STATUS_FIVE); userOrderInfo.setRefundTime(refundData.getSuccessTime()); baseMapper.updateById(userOrderInfo); UserRefundsOrderInfo userRefundsOrderInfo = userRefundsOrderInfoMapper.selectOne(Wrappers.lambdaQuery() .eq(UserRefundsOrderInfo::getOrderId, userOrderInfo.getId()) .eq(UserRefundsOrderInfo::getRefundId, refundData.getTransactionRefundId()) .last("limit 1") ); userRefundsOrderInfo.setStatus(refundData.getStatus()); userRefundsOrderInfo.setSuccessTime(refundData.getSuccessTime()); userRefundsOrderInfoMapper.updateById(userRefundsOrderInfo); } @Override public void fail(WechatCallbackRefundData refundData) { log.info("微信支付退款失败!"); UserOrderInfo userOrderInfo = baseMapper.selectOne(Wrappers.lambdaQuery(UserOrderInfo.class).eq(UserOrderInfo::getOrderNo, refundData.getOrderNo())); UserRefundsOrderInfo userRefundsOrderInfo = userRefundsOrderInfoMapper.selectOne(Wrappers.lambdaQuery() .eq(UserRefundsOrderInfo::getOrderId, userOrderInfo.getId()) .eq(UserRefundsOrderInfo::getRefundId, refundData.getTransactionRefundId()) .last("limit 1") ); userRefundsOrderInfo.setStatus(refundData.getStatus()); userRefundsOrderInfo.setSuccessTime(refundData.getSuccessTime()); userRefundsOrderInfoMapper.updateById(userRefundsOrderInfo); } }; Map result = new HashMap<>(); if (lock.tryLock()) { // 2.签名验证 //验签及解析返回数据 JsonObject res = wechatPayV3Utils.getCallbackData(request); if (res == null) { result.put("code", "FAIL"); result.put("message", "失败"); return result; } log.info("最终拿到的微信支付通知数据:" + res); // 4.封装微信返回的数据 WechatCallbackRefundData refundData = getRefundCallbackData(res); if ("SUCCESS".equals(refundData.getStatus())) { refundCallback.success(refundData); } else { // 特殊情况退款失败业务处理,退款到银行发现用户的卡作废或者冻结了,导致原路退款银行卡失败,可前往商户平台-交易中心,手动处理此笔退款 refundCallback.fail(refundData); } // 5.成功应答 response.setStatus(200); result.put("code", "SUCCESS"); result.put("message", "成功"); } else { result.put("code", "FAIL"); result.put("message", "失败"); } return result; } private static WechatCallbackRefundData getRefundCallbackData(JsonObject res) { WechatCallbackRefundData refundData = new WechatCallbackRefundData(); String successTime = res.get("success_time").getAsString(); if (StringUtils.isNoneBlank(successTime)) { refundData.setSuccessTime(successTime); } refundData.setOrderNo(res.get("out_trade_no").getAsString()); refundData.setRefundId(res.get("out_refund_no").getAsString()); refundData.setTransactionId(res.get("transaction_id").getAsString()); refundData.setTransactionRefundId(res.get("refund_id").getAsString()); refundData.setChannel(res.get("channel").getAsString()); final String status = res.get("refund_status").getAsString(); refundData.setStatus(status); String refundMoney = res.getAsJsonObject("amount").get("refund").getAsString(); refundData.setRefundMoney(new BigDecimal(refundMoney).movePointLeft(2)); log.info("refundData:{}", refundData); return refundData; } /** * 通过商户订单号查询订单在微信侧支付状态 * * @param out_trade_no 发起支付时创建的商户订单号 * @return null代表查询失败 SUCCESS-成功 USERPAYING和ACCEPT为中间态 其他为支付失败 */ public JsonObject orderQueryByOutTradeNo(String out_trade_no, String subMchId) { String url = WechatUrlConstants.PAY_V3_QUERY_OUT; url = url.replace("{out_trade_no}", WXPayUtility.urlEncode(out_trade_no)); Map args = new HashMap<>(); args.put("sub_mchid", subMchId); url = url + "?" + WXPayUtility.urlEncode(args); return wechatPayV3Utils.sendGet(url); } /** * 构建支付表单返回给前端支撑JsApi支付调用 * * @param openId * @param orderNo * @param amount * @return */ private Map payment(String openId, String orderNo, BigDecimal amount) { //15分钟超时限制 Calendar calendar = Calendar.getInstance(); calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) + 15); SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX"); //构建微信支付参数 Map params = new HashMap<>(); params.put("appid", WechatConstants.WECHAT_APPID); //小程序appid params.put("mchid", WechatConstants.WECHAT_MCH_ID); //商户号 params.put("description", "订单业务"); //商品描述 params.put("out_trade_no", orderNo); //商户订单号 params.put("time_expire", sdf.format(calendar.getTime())); //交易结束时间 选填 时间到了之后将不能再支付 遵循rfc3339标准格式 params.put("attach", orderNo); //附加数据 选填 // 在查询API和支付通知中原样返回 可作为自定义参数使用 params.put("notify_url", WechatUrlConstants.PAY_V3_NOTIFY); //支付结果异步通知接口 //订单金额信息 Map amount_json = new HashMap<>(); //支付金额 单位:分 // amount_json.put("total", Integer.parseInt(amount_fee(Double.valueOf("0.1"))));测试用例 amount_json.put("total", Integer.parseInt(amount_fee(amount))); params.put("amount", amount_json); //支付者信息 Map payer = new HashMap<>(); //用户在小程序侧的openid payer.put("openid", openId); params.put("payer", payer); //小程序支付拉起参数 Gson gson = new Gson(); return wechatPay(gson.toJsonTree(params).getAsJsonObject()); } /** * 小程序支付拉起 * * @param params * @return * @throws ServiceException */ public Map wechatPay(JsonObject params) throws ServiceException { //发起请求 JsonObject res = wechatPayV3Utils.sendPost(WechatUrlConstants.PAY_V3_JSAPI, params); log.info("wechatPay res:{}", res.toString()); if (StrUtil.isEmpty(res.get("prepay_id").getAsString())) { throw new ServiceException("支付发起失败"); } StringBuilder sb = new StringBuilder(); //返回给小程序拉起微信支付的参数 Map result = new HashMap(); // result.setAppId(WechatConstants.WECHAT_APPID); //小程序appid // sb.append(result.getAppId()).append("\n"); // result.setTimeStamp((new Date().getTime() / 1000) + ""); //时间戳 // sb.append(result.getTimeStamp()).append("\n"); // result.setNonceStr(RandomStringUtils.randomAlphanumeric(32)); //32位随机字符串 // sb.append(result.getNonceStr()).append("\n"); // result.setPackageValue("prepay_id=" + res.get("prepay_id").getAsString()); //预支付id 格式为 prepay_id=xxx // sb.append(result.getPackageValue()).append("\n"); // //签名 // result.setPaySign(wechatPayV3Utils.signRSA(sb.toString())); // result.setSignType("RSA"); //加密方式 固定RSA // result.setOutTradeNo(params.get("out_trade_no").getAsString()); //商户订单号 此参数不是小程序拉起支付所需的参数 因此不参与签名 return result; } /** * 创建商户订单号 * 要求 32个字符内,只能是数字、大小写字母_-|*且在同一个商户号下唯一 * 组成 两位前缀 + 17位时间戳 + 9位id补零 + 4位随机数 合计32位 * * @param head 例如 商品-SP 退款-TK 等等 * @param id 用户id * @return */ public String createOrderNo(String head, Long id) { StringBuilder uid = new StringBuilder(id.toString()); Date date = new Date(); SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS"); int length = uid.length(); for (int i = 0; i < 9 - length; i++) { uid.insert(0, "0"); } return head + sdf.format(date) + uid + (int) ((Math.random() * 9 + 1) * 1000); } /** * 金额元转分字符串 * * @param cny 元 * @return */ public String amount_fee(BigDecimal cny) { BigDecimal b2 = new BigDecimal("100"); return cny.multiply(b2).setScale(0, RoundingMode.DOWN).toString(); } public static String amount_fee(double price) { DecimalFormat df = new DecimalFormat("#.00"); price = Double.valueOf(df.format(price)); int money = (int) (price * 100); return money + ""; } }