From ba214b7290b7b8302ba351262c145d8833f20e63 Mon Sep 17 00:00:00 2001 From: gxwebsoft Date: Sun, 12 May 2024 22:52:37 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=EF=BC=9A=E5=BE=AE=E4=BF=A1Na?= =?UTF-8?q?tive=E6=94=AF=E4=BB=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../common/core/security/SecurityConfig.java | 1 + .../common/core/utils/WxNativeUtil.java | 19 ++ .../system/controller/OrderController.java | 23 +- .../controller/WxNativePayController.java | 105 +++---- .../controller/WxPayNotifyController.java | 83 +++--- .../gxwebsoft/common/system/entity/Order.java | 270 ++++++++++++------ .../common/system/entity/OrderInfo.java | 8 + .../common/system/entity/Payment.java | 3 + .../common/system/mapper/xml/OrderMapper.xml | 3 +- .../system/service/impl/OrderServiceImpl.java | 107 +++---- ...3231584E93B6AE77820074D07EADEACCB7E223.pem | 24 ++ 11 files changed, 392 insertions(+), 254 deletions(-) create mode 100644 src/main/java/com/gxwebsoft/common/core/utils/WxNativeUtil.java create mode 100644 src/main/resources/cert/wechatpay_4A3231584E93B6AE77820074D07EADEACCB7E223.pem diff --git a/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java b/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java index b74261b..8cc2d3a 100644 --- a/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java +++ b/src/main/java/com/gxwebsoft/common/core/security/SecurityConfig.java @@ -52,6 +52,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter { "/api/login-alipay/*", "/api/wx-login/loginByMpWxPhone", "/api/system/wx-native-pay/**", + "/api/system/wx-pay/**", "/api/wxWorkQrConnect", "/api/sys/user-plan-log/wx-pay/**", "/api/wx-official/**", diff --git a/src/main/java/com/gxwebsoft/common/core/utils/WxNativeUtil.java b/src/main/java/com/gxwebsoft/common/core/utils/WxNativeUtil.java new file mode 100644 index 0000000..67d6eff --- /dev/null +++ b/src/main/java/com/gxwebsoft/common/core/utils/WxNativeUtil.java @@ -0,0 +1,19 @@ +package com.gxwebsoft.common.core.utils; + +import java.util.HashMap; +import java.util.Map; +import com.wechat.pay.java.core.Config; + + +public class WxNativeUtil { + + private static final Map tenantConfigs = new HashMap<>(); + + public static void addConfig(Integer tenantId, Config config) { + tenantConfigs.put(tenantId, config); + } + + public static Config getConfig(Integer tenantId) { + return tenantConfigs.get(tenantId); + } +} diff --git a/src/main/java/com/gxwebsoft/common/system/controller/OrderController.java b/src/main/java/com/gxwebsoft/common/system/controller/OrderController.java index 2cb02c8..fbffaab 100644 --- a/src/main/java/com/gxwebsoft/common/system/controller/OrderController.java +++ b/src/main/java/com/gxwebsoft/common/system/controller/OrderController.java @@ -1,22 +1,20 @@ package com.gxwebsoft.common.system.controller; +import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.IdUtil; import cn.hutool.core.util.StrUtil; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.gxwebsoft.common.core.annotation.OperationLog; +import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.common.core.web.BaseController; -import com.gxwebsoft.common.system.entity.Company; +import com.gxwebsoft.common.core.web.BatchParam; +import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.common.system.entity.Order; import com.gxwebsoft.common.system.entity.OrderInfo; import com.gxwebsoft.common.system.entity.User; +import com.gxwebsoft.common.system.param.OrderParam; import com.gxwebsoft.common.system.service.CompanyService; import com.gxwebsoft.common.system.service.OrderInfoService; import com.gxwebsoft.common.system.service.OrderService; -import com.gxwebsoft.common.system.entity.Order; -import com.gxwebsoft.common.system.param.OrderParam; -import com.gxwebsoft.common.core.web.ApiResult; -import com.gxwebsoft.common.core.web.PageResult; -import com.gxwebsoft.common.core.web.PageParam; -import com.gxwebsoft.common.core.web.BatchParam; -import com.gxwebsoft.common.core.annotation.OperationLog; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.security.access.prepost.PreAuthorize; @@ -24,8 +22,9 @@ import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.math.BigDecimal; -import java.util.Date; +import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; /** * 订单控制器 @@ -103,9 +102,9 @@ public class OrderController extends BaseController { order.setUserId(loginUser.getUserId()); order.setOpenid(loginUser.getOpenid()); order.setRealName(loginUser.getRealName()); - order.setStartTime(timeMillis / 1000); - order.setAddTime(timeMillis / 1000); order.setPhone(loginUser.getPhone()); + final List collect = order.getOrderInfoList().stream().map(OrderInfo::getStartTime).collect(Collectors.toList()); + order.setStartTime(Collections.min(collect)); order.setVersion(10); if (orderService.save(order)) { order.getOrderInfoList().forEach(d -> { diff --git a/src/main/java/com/gxwebsoft/common/system/controller/WxNativePayController.java b/src/main/java/com/gxwebsoft/common/system/controller/WxNativePayController.java index dc8ec27..3072b4d 100644 --- a/src/main/java/com/gxwebsoft/common/system/controller/WxNativePayController.java +++ b/src/main/java/com/gxwebsoft/common/system/controller/WxNativePayController.java @@ -1,23 +1,19 @@ package com.gxwebsoft.common.system.controller; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSONObject; import com.gxwebsoft.common.core.config.ConfigProperties; import com.gxwebsoft.common.core.utils.CommonUtil; +import com.gxwebsoft.common.core.utils.RedisUtil; +import com.gxwebsoft.common.core.utils.WxNativeUtil; import com.gxwebsoft.common.core.web.ApiResult; import com.gxwebsoft.common.core.web.BaseController; import com.gxwebsoft.common.system.entity.Order; -import com.gxwebsoft.common.system.entity.Setting; -import com.gxwebsoft.common.system.entity.User; +import com.gxwebsoft.common.system.entity.Payment; import com.gxwebsoft.common.system.param.SettingParam; import com.gxwebsoft.common.system.service.OrderService; +import com.gxwebsoft.common.system.service.SettingService; import com.wechat.pay.java.core.Config; import com.wechat.pay.java.core.RSAAutoCertificateConfig; -import com.wechat.pay.java.core.notification.NotificationConfig; -import com.wechat.pay.java.core.notification.NotificationParser; -import com.wechat.pay.java.core.notification.RSANotificationConfig; -import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction; import com.wechat.pay.java.service.payments.nativepay.NativePayService; import com.wechat.pay.java.service.payments.nativepay.model.Amount; import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest; @@ -25,69 +21,59 @@ import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse; import io.swagger.annotations.Api; import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiOperation; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; -import java.io.File; import java.math.BigDecimal; -import java.text.DecimalFormat; import java.util.Map; - -import static com.gxwebsoft.common.core.constants.OrderConstants.PAY_STATUS_NO_PAY; -import static com.gxwebsoft.common.core.constants.OrderConstants.PAY_STATUS_SUCCESS; +import java.util.Set; @Api(tags = "微信Native支付接口") @RestController @RequestMapping("/api/system/wx-native-pay") public class WxNativePayController extends BaseController { - /** 商户号 */ + @Value("${spring.profiles.active}") + String active; public static String merchantId = "1246610101"; - /** 商户API私钥路径 */ public static String privateKeyPath = "/Users/gxwebsoft/JAVA/com.gxwebsoft.core/src/main/resources/cert/apiclient_key.pem"; -// public static String privateKeyPath = "/Users/gxwebsoft/cert/1246610101_20240511_cert/apiclient_cert.pem"; - /** 商户证书序列号 */ public static String merchantSerialNumber = "48749613B40AA8F1D768583FC352358E13EB5AF0"; - /** 商户APIV3密钥 */ public static String apiV3Key = "zGufUcqa7ovgxRL0kF5OlPr482EZwtn9"; - // 公众号的AppID - public static String appId = "wx541db955e7a62709"; - private final Config wxPayConfig; @Resource - private OrderService orderService; + private RedisUtil redisUtil; + private Config wxPayConfig; @Resource private ConfigProperties config; + @Resource + private SettingService settingService; + @Resource + private OrderService orderService; - public WxNativePayController() { - File file = new File(privateKeyPath); - if (file.exists()) { - this.wxPayConfig = - new RSAAutoCertificateConfig.Builder() - .merchantId(merchantId) - .privateKeyFromPath(privateKeyPath) - .merchantSerialNumber(merchantSerialNumber) - .apiV3Key(apiV3Key) - .build(); - }else { - System.out.println("无法访问文件"); - this.wxPayConfig = null ; - } - } - @ApiOperation("生成付款码") @PostMapping("/codeUrl") public ApiResult getCodeUrl(@RequestBody Order order) { - order.setMoney(new BigDecimal(0.01)); - order.setOrderNo(CommonUtil.createOrderNo()); + String key = "Payment:wxPay:".concat(getTenantId().toString()); + final Payment payment = redisUtil.get(key, Payment.class); + if (payment == null) { + return fail("微信支付未配置"); + } + // 获取微信小程序配置信息 + JSONObject setting = settingService.getBySettingKey("mp-weixin"); + final String appId = setting.getString("appId"); + final String appSecret = setting.getString("appSecret"); + // 使用自动更新平台证书的RSA配置 // 一个商户号只能初始化一个配置,否则会因为重复的下载任务报错 // 构建service - NativePayService service = new NativePayService.Builder().config(wxPayConfig).build(); + NativePayService service = new NativePayService.Builder().config(this.getWxPayConfig()).build(); // request.setXxx(val)设置所需参数,具体参数可见Request定义 PrepayRequest request = new PrepayRequest(); // 计算金额 + order.setMoney(new BigDecimal(0.01)); + order.setOrderNo(CommonUtil.createOrderNo()); BigDecimal decimal = order.getMoney(); final BigDecimal multiply = decimal.multiply(new BigDecimal(100)); // 将 BigDecimal 转换为 Integer @@ -96,9 +82,9 @@ public class WxNativePayController extends BaseController { amount.setTotal(money); request.setAmount(amount); request.setAppid(appId); - request.setMchid(merchantId); - request.setDescription("网宿软件企业版-1年授权"); - request.setNotifyUrl("https://server.gxwebsoft.com/api/system/wx-native-pay/notify/10150"); + request.setMchid(payment.getMchId()); + request.setDescription("企业版-1年授权"); + request.setNotifyUrl("https://server.gxwebsoft.com/api/system/wx-native-pay/notify/" + getTenantId()); request.setOutTradeNo(order.getOrderNo()); // 调用下单方法,得到应答 PrepayResponse response = service.prepay(request); @@ -106,10 +92,36 @@ public class WxNativePayController extends BaseController { // System.out.println(response.getCodeUrl()); // 生成指定url对应的二维码到文件,宽和高都是300像素 // QrCodeUtil.generate(response.getCodeUrl(), 300, 300, FileUtil.file("/Users/gxwebsoft/Documents/uploads/wx-native-qrcode.jpg")); - return success("生成付款码",response.getCodeUrl()); + return success("生成付款码", response.getCodeUrl()); } + private Config getWxPayConfig() { + // 获取租户ID + final Integer tenantId = getTenantId(); + Config build = WxNativeUtil.getConfig(tenantId); + if (build != null) { + return build; + } + String key = "Payment:wxPay:".concat(tenantId.toString()); + final Payment payment = redisUtil.get(key, Payment.class); + System.out.println("payment = " + payment); + String apiclientKey = config.getUploadPath().concat("/file").concat(payment.getApiclientKey()); + // 开发环境配置 + if(active.equals("dev")){ + apiclientKey = privateKeyPath; + } + Config config = new RSAAutoCertificateConfig.Builder() + .merchantId(payment.getMchId()) + .privateKeyFromPath(apiclientKey) + .merchantSerialNumber(payment.getMerchantSerialNumber()) + .apiV3Key(payment.getApiKey()) + .build(); + System.out.println("config = " + config); + WxNativeUtil.addConfig(tenantId, config); + return config; + } + @ApiModelProperty("异步通知") @PostMapping("/notify/{tenantId}") public String wxNotify(@RequestHeader Map header, @RequestBody String body, @PathVariable("tenantId") Integer tenantId) { @@ -126,7 +138,4 @@ public class WxNativePayController extends BaseController { return "fail"; } - - - } diff --git a/src/main/java/com/gxwebsoft/common/system/controller/WxPayNotifyController.java b/src/main/java/com/gxwebsoft/common/system/controller/WxPayNotifyController.java index 0915a86..6f194f5 100644 --- a/src/main/java/com/gxwebsoft/common/system/controller/WxPayNotifyController.java +++ b/src/main/java/com/gxwebsoft/common/system/controller/WxPayNotifyController.java @@ -2,43 +2,25 @@ package com.gxwebsoft.common.system.controller; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; -import com.alibaba.fastjson.JSONObject; -import com.alipay.api.AlipayApiException; -import com.alipay.api.DefaultAlipayClient; -import com.alipay.api.request.AlipayTradeAppPayRequest; -import com.alipay.api.response.AlipayTradeAppPayResponse; import com.gxwebsoft.common.core.config.ConfigProperties; -import com.gxwebsoft.common.core.utils.AlipayConfigUtil; -import com.gxwebsoft.common.core.utils.CommonUtil; +import com.gxwebsoft.common.core.utils.RedisUtil; import com.gxwebsoft.common.core.web.BaseController; import com.gxwebsoft.common.system.entity.Order; -import com.gxwebsoft.common.system.entity.Setting; -import com.gxwebsoft.common.system.entity.User; +import com.gxwebsoft.common.system.entity.Payment; import com.gxwebsoft.common.system.mapper.SettingMapper; -import com.gxwebsoft.common.system.param.SettingParam; -import com.gxwebsoft.common.system.service.OrderInfoService; import com.gxwebsoft.common.system.service.OrderService; -import com.gxwebsoft.common.system.service.SettingService; -import com.gxwebsoft.common.system.service.UserService; -import com.wechat.pay.java.core.Config; -import com.wechat.pay.java.core.RSAConfig; import com.wechat.pay.java.core.notification.NotificationConfig; import com.wechat.pay.java.core.notification.NotificationParser; import com.wechat.pay.java.core.notification.RSANotificationConfig; import com.wechat.pay.java.service.partnerpayments.jsapi.model.Transaction; -import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension; -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 io.swagger.annotations.Api; import io.swagger.annotations.ApiModelProperty; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; import javax.annotation.Resource; import java.math.BigDecimal; import java.text.DecimalFormat; -import java.util.HashMap; import java.util.Map; import static com.gxwebsoft.common.core.constants.OrderConstants.PAY_STATUS_NO_PAY; @@ -52,35 +34,36 @@ import static com.gxwebsoft.common.core.constants.OrderConstants.PAY_STATUS_SUCC */ @Api(tags = "会员特权购买记录表管理") @RestController -@RequestMapping("/api/system/notify") +@RequestMapping("/api/system/wx-pay") public class WxPayNotifyController extends BaseController { + @Value("${spring.profiles.active}") + String active; @Resource private OrderService orderService; @Resource - private ConfigProperties config; + private RedisUtil redisUtil; @Resource - private SettingMapper settingMapper; + private ConfigProperties conf; + // 本地证书 + public static String wechatpayCertPath = "/Users/gxwebsoft/JAVA/com.gxwebsoft.core/src/main/resources/cert/wechatpay_4A3231584E93B6AE77820074D07EADEACCB7E223.pem"; // 平台证书 @ApiModelProperty("异步通知") - @PostMapping("/wx-pay/notify/{tenantId}") + @PostMapping("/notify/{tenantId}") public String wxNotify(@RequestHeader Map header, @RequestBody String body, @PathVariable("tenantId") Integer tenantId) { System.out.println("异步通知*************** = "); - System.out.println("request header = " + header); - System.out.println("request body = " + body); - System.out.println("tenantId = " + tenantId); +// System.out.println("request header = " + header); +// System.out.println("request body = " + body); +// System.out.println("tenantId = " + tenantId); // 获取支付配置信息用于解密 - final SettingParam param = new SettingParam(); - param.setSettingKey("payment"); - param.setTenantId(tenantId); - final String uploadPath = config.getUploadPath(); // 服务器本地路径 - final Setting payment = settingMapper.getBySettingKeyIgnore(param); - final JSONObject jsonObject = JSONObject.parseObject(payment.getContent()); - final String apiV3key = jsonObject.getString("wechatApiKey"); - final String apiclientCert = uploadPath.concat("file").concat(jsonObject.getString("apiclientCert")); - -// System.out.println("apiV3key ====== " + apiV3key); -// System.out.println("payment ===== " + payment); + String key = "Payment:wxPay:".concat(tenantId.toString()); + final Payment payment = redisUtil.get(key, Payment.class); + String privateKey = payment.getApiKey(); + String apiclientCert = conf.getUploadPath().concat("/file").concat(payment.getApiclientCert()); + // 开发环境配置 + if(active.equals("dev")){ + apiclientCert = wechatpayCertPath; + } com.wechat.pay.java.core.notification.RequestParam requestParam = new com.wechat.pay.java.core.notification.RequestParam.Builder() .serialNumber(header.get("wechatpay-serial")) @@ -93,10 +76,11 @@ public class WxPayNotifyController extends BaseController { // 如果已经初始化了 RSAAutoCertificateConfig,可直接使用 // 没有的话,则构造一个 NotificationConfig config = new RSANotificationConfig.Builder() - .apiV3Key(apiV3key) + .apiV3Key(privateKey) .certificatesFromPath(apiclientCert) .build(); + // 初始化 NotificationParser NotificationParser parser = new NotificationParser(config); @@ -107,18 +91,19 @@ public class WxPayNotifyController extends BaseController { final String transactionId = transaction.getTransactionId(); final Integer total = transaction.getAmount().getTotal(); final String tradeStateDesc = transaction.getTradeStateDesc(); -// final Transaction.TradeStateEnum tradeState = transaction.getTradeState(); -// final Transaction.TradeTypeEnum tradeType = transaction.getTradeType(); -// System.out.println("transaction = " + transaction); -// System.out.println("tradeStateDesc = " + tradeStateDesc); -// System.out.println("tradeType = " + tradeType); -// System.out.println("tradeState = " + tradeState); -// System.out.println("outTradeNo = " + outTradeNo); -// System.out.println("amount = " + total); + final Transaction.TradeStateEnum tradeState = transaction.getTradeState(); + final Transaction.TradeTypeEnum tradeType = transaction.getTradeType(); + System.out.println("transaction = " + transaction); + System.out.println("tradeStateDesc = " + tradeStateDesc); + System.out.println("tradeType = " + tradeType); + System.out.println("tradeState = " + tradeState); + System.out.println("outTradeNo = " + outTradeNo); + System.out.println("amount = " + total); if (StrUtil.equals("支付成功", tradeStateDesc)) { // 1. 查询要处理的订单 Order order = orderService.getByOutTradeNo(outTradeNo); + System.out.println("order = " + order); // 2. 已支付则跳过 if (order.getPayStatus().equals(PAY_STATUS_SUCCESS)) { return "SUCCESS"; @@ -126,8 +111,8 @@ public class WxPayNotifyController extends BaseController { // 2. 未支付则处理更新订单状态 if (order.getPayStatus().equals(PAY_STATUS_NO_PAY)) { // 5. TODO 处理订单状态 - order.setPayStatus(PAY_STATUS_SUCCESS); order.setPayTime(DateUtil.date()); + order.setPayStatus(PAY_STATUS_SUCCESS); order.setTransactionId(transactionId); // 实际付款金额:Integer除以100后转BigDecimal DecimalFormat df = new DecimalFormat("0.00"); diff --git a/src/main/java/com/gxwebsoft/common/system/entity/Order.java b/src/main/java/com/gxwebsoft/common/system/entity/Order.java index 717ffb5..ea71528 100644 --- a/src/main/java/com/gxwebsoft/common/system/entity/Order.java +++ b/src/main/java/com/gxwebsoft/common/system/entity/Order.java @@ -2,9 +2,8 @@ package com.gxwebsoft.common.system.entity; import java.math.BigDecimal; +import cn.hutool.core.util.DesensitizedUtil; import com.baomidou.mybatisplus.annotation.*; - -import java.time.LocalDateTime; import java.io.Serializable; import java.util.Date; import java.util.List; @@ -25,141 +24,222 @@ import lombok.EqualsAndHashCode; @ApiModel(value = "Order对象", description = "订单") @TableName("sys_order") public class Order implements Serializable { - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 1L; - @ApiModelProperty(value = "ID") - @TableId(value = "order_id", type = IdType.AUTO) - private Integer orderId; + @ApiModelProperty(value = "订单号") + @TableId(value = "order_id", type = IdType.AUTO) + private Integer orderId; - @ApiModelProperty(value = "订单号") - private String orderNo; + @ApiModelProperty(value = "订单编号") + private String orderNo; - @ApiModelProperty(value = "类型") - private Integer type; + @ApiModelProperty(value = "微信支付订单号") + private String wechatOrder; - @ApiModelProperty(value = "订单金额") - private BigDecimal money; + @ApiModelProperty(value = "微信退款订单号") + private String refundOrder; - @ApiModelProperty(value = "实际付款金额(包含运费)") - private BigDecimal payPrice; + @ApiModelProperty(value = "场馆id用于权限判断") + private Integer merchantId; - @ApiModelProperty(value = "套餐ID") - private Integer planId; + @ApiModelProperty(value = "用户id") + private Integer userId; - @ApiModelProperty(value = "卡ID") - private Integer priceId; + @ApiModelProperty(value = "使用的优惠券id") + private Integer couponId; - @ApiModelProperty(value = "获得的会员等级") - private Integer gradeId; + @ApiModelProperty(value = "使用的会员卡id") + private Integer cardId; - @ApiModelProperty(value = "卡名称") - private String priceName; + @ApiModelProperty(value = "关联管理员id") + private Integer aid; - @ApiModelProperty(value = "用户ID") - private Integer userId; + @ApiModelProperty(value = "核销管理员id") + private Integer adminId; - @ApiModelProperty(value = "持有者ID") - private Integer memberId; + @ApiModelProperty(value = "IC卡号") + private String code; - @ApiModelProperty(value = "真实姓名") - private String realName; + @ApiModelProperty(value = "真实姓名") + private String name; - @ApiModelProperty(value = "联系电话") - private String phone; + @ApiModelProperty(value = "手机号码") + private String phone; - @ApiModelProperty(value = "订单总额") - private BigDecimal totalPrice; + @ApiModelProperty(value = "订单总额") + private BigDecimal totalPrice; - @ApiModelProperty(value = "付款时间") - private Date payTime; + @ApiModelProperty(value = "减少的金额,使用VIP会员折扣、优惠券抵扣、优惠券折扣后减去的价格") + private BigDecimal reducePrice; - @ApiModelProperty(value = "1微信支付,2积分,3支付宝,4现金,5POS机,6VIP月卡,7VIP年卡,8VIP次卡,9IC月卡,10IC年卡,11IC次卡,12免费,13VIP充值卡,14IC充值卡,15积分支付,16VIP季卡,17IC季卡") - private Integer payType; + @ApiModelProperty(value = "实际付款") + private BigDecimal payPrice; - @ApiModelProperty(value = "支付流水号") - private String transactionId; + @ApiModelProperty(value = "用于统计") + private BigDecimal price; - @ApiModelProperty(value = "付款状态(10未付款 20已付款)") - private Integer payStatus; + @ApiModelProperty(value = "价钱,用于积分赠送") + private BigDecimal money; - @ApiModelProperty(value = "支付方式(余额/微信/支付宝)") - private String payMethod; + @ApiModelProperty(value = "退款金额") + private BigDecimal refundMoney; - @ApiModelProperty(value = "到期时间") - private Date expirationTime; + @ApiModelProperty(value = "教练价格") + private BigDecimal coachPrice; - @ApiModelProperty(value = "所在省份") - private String province; + @ApiModelProperty(value = "教练id") + private Integer coachId; - @ApiModelProperty(value = "所在城市") - private String city; + @ApiModelProperty(value = "1微信支付,2积分,3支付宝,4现金,5POS机,6VIP月卡,7VIP年卡,8VIP次卡,9IC月卡,10IC年卡,11IC次卡,12免费,13VIP充值卡,14IC充值卡,15积分支付,16VIP季卡,17IC季卡") + private Integer payType; - @ApiModelProperty(value = "所在辖区") - private String region; + @ApiModelProperty(value = "1已付款,2未付款") + private Integer payStatus; - @ApiModelProperty(value = "所在地区") - private String area; + @ApiModelProperty(value = "1已完成,2未使用,3已取消,4退款申请中,5退款被拒绝,6退款成功,7客户端申请退款") + private Integer orderStatus; - @ApiModelProperty(value = "街道地址") - private String address; + @ApiModelProperty(value = "优惠类型:0无、1抵扣优惠券、2折扣优惠券、3、VIP月卡、4VIP年卡,5VIP次卡、6VIP会员卡、7IC月卡、8IC年卡、9IC次卡、10IC会员卡、11免费订单、12VIP充值卡、13IC充值卡、14VIP季卡、15IC季卡") + private Integer type; - @ApiModelProperty(value = "退款凭证") - private String refundImage; + @ApiModelProperty(value = "二维码地址,保存订单号,支付成功后才生成") + private String qrcode; - @ApiModelProperty(value = "退款理由") - private String refundContent; + @ApiModelProperty(value = "优惠说明") + private String desc; - @ApiModelProperty(value = "订单是否已结算(0未结算 1已结算)") - private Integer isSettled; + @ApiModelProperty(value = "vip月卡年卡、ic月卡年卡回退次数") + private Integer returnNum; - @ApiModelProperty(value = "排序(数字越小越靠前)") - private Integer sortNumber; + @ApiModelProperty(value = "vip充值回退金额") + private BigDecimal returnMoney; - @ApiModelProperty(value = "商户ID") - private Integer merchantId; + @ApiModelProperty(value = "预约详情开始时间数组") + private Integer startTime; - @ApiModelProperty(value = "备注") - private String comments; + @ApiModelProperty(value = "是否已开具发票:1已开发票,2未开发票,3不能开具发票") + private Integer isInvoice; - @ApiModelProperty(value = "状态, 0正常, 1冻结") - private Integer status; + @ApiModelProperty(value = "付款时间") + private Date payTime; - @ApiModelProperty(value = "是否删除, 0否, 1是") - @TableLogic - private Integer deleted; + @ApiModelProperty(value = "退款时间") + private Integer refundTime; - @ApiModelProperty(value = "租户id") - private Integer tenantId; + @ApiModelProperty(value = "申请退款时间") + private Integer refundApplyTime; - @ApiModelProperty(value = "创建时间") - private Date createTime; + @ApiModelProperty(value = "对账情况:1=已对账;2=未对账;3=已对账,金额对不上;4=未查询到该订单") + private Integer checkBill; - @ApiModelProperty(value = "修改时间") - private Date updateTime; + @ApiModelProperty(value = "系统版本") + private Integer version; - @ApiModelProperty(value = "用于微信支付") - @TableField(exist = false) - private String openid; + @ApiModelProperty(value = "备注") + private String comments; - @ApiModelProperty(value = "预约详情开始时间数组") - private Long startTime; + @ApiModelProperty(value = "是否删除, 0否, 1是") + @TableLogic + private Integer deleted; - @ApiModelProperty(value = "下单时间") - private Long addTime; + @ApiModelProperty(value = "创建时间") + private Date createTime; - @ApiModelProperty(value = "系统版本") - private Integer version; + @ApiModelProperty(value = "更新时间") + private Date updateTime; - @ApiModelProperty(value = "租户名称") - @TableField(exist = false) - private String tenantName; + @ApiModelProperty(value = "租户id") + private Integer tenantId; - @ApiModelProperty(value = "企业ID") - @TableField(exist = false) - private Integer companyId; + @ApiModelProperty(value = "商户名称") + @TableField(exist = false) + private String merchantName; - @ApiModelProperty(value = "订单详情") - @TableField(exist = false) - private List orderInfoList; + @ApiModelProperty(value = "商户图标") + @TableField(exist = false) + private String merchantAvatar; + + @ApiModelProperty(value = "微信OPENID,用于微信支付") + @TableField(exist = false) + private String openid; + + @ApiModelProperty(value = "订单详情") + @TableField(exist = false) + private List orderInfoList; + + @ApiModelProperty(value = "套餐ID") + private Integer planId; + + @ApiModelProperty(value = "卡ID") + private Integer priceId; + + @ApiModelProperty(value = "获得的会员等级") + private Integer gradeId; + + @ApiModelProperty(value = "卡名称") + private String priceName; + + @ApiModelProperty(value = "持有者ID") + private Integer memberId; + + @ApiModelProperty(value = "真实姓名") + private String realName; + + + + @ApiModelProperty(value = "支付流水号") + private String transactionId; + + @ApiModelProperty(value = "支付方式(余额/微信/支付宝)") + private String payMethod; + + @ApiModelProperty(value = "到期时间") + private Date expirationTime; + + @ApiModelProperty(value = "所在省份") + private String province; + + @ApiModelProperty(value = "所在城市") + private String city; + + @ApiModelProperty(value = "所在辖区") + private String region; + + @ApiModelProperty(value = "所在地区") + private String area; + + @ApiModelProperty(value = "街道地址") + private String address; + + @ApiModelProperty(value = "退款凭证") + private String refundImage; + + @ApiModelProperty(value = "退款理由") + private String refundContent; + + @ApiModelProperty(value = "订单是否已结算(0未结算 1已结算)") + private Integer isSettled; + + @ApiModelProperty(value = "排序(数字越小越靠前)") + private Integer sortNumber; + + @ApiModelProperty(value = "状态, 0正常, 1冻结") + private Integer status; + + @ApiModelProperty(value = "租户名称") + @TableField(exist = false) + private String tenantName; + + @ApiModelProperty(value = "企业ID") + @TableField(exist = false) + private Integer companyId; + + @ApiModelProperty("手机号(脱敏)") + @TableField(exist = false) + private String mobile; + + public String getMobile() { + return DesensitizedUtil.mobilePhone(this.phone); + } } diff --git a/src/main/java/com/gxwebsoft/common/system/entity/OrderInfo.java b/src/main/java/com/gxwebsoft/common/system/entity/OrderInfo.java index 8bd4217..05d8f5e 100644 --- a/src/main/java/com/gxwebsoft/common/system/entity/OrderInfo.java +++ b/src/main/java/com/gxwebsoft/common/system/entity/OrderInfo.java @@ -6,6 +6,8 @@ import com.baomidou.mybatisplus.annotation.IdType; import com.baomidou.mybatisplus.annotation.TableId; import com.baomidou.mybatisplus.annotation.TableLogic; import java.io.Serializable; +import java.util.Date; + import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import lombok.Data; @@ -91,4 +93,10 @@ public class OrderInfo implements Serializable { @ApiModelProperty(value = "租户id") private Integer tenantId; + @ApiModelProperty(value = "创建时间") + private Date createTime; + + @ApiModelProperty(value = "修改时间") + private Date updateTime; + } diff --git a/src/main/java/com/gxwebsoft/common/system/entity/Payment.java b/src/main/java/com/gxwebsoft/common/system/entity/Payment.java index 5dcda1d..edb2bb8 100644 --- a/src/main/java/com/gxwebsoft/common/system/entity/Payment.java +++ b/src/main/java/com/gxwebsoft/common/system/entity/Payment.java @@ -60,6 +60,9 @@ public class Payment implements Serializable { @ApiModelProperty(value = "商户证书序列号") private String merchantSerialNumber; + @ApiModelProperty(value = "支付结果通知") + private String notifyUrl; + @ApiModelProperty(value = "备注") private String comments; diff --git a/src/main/java/com/gxwebsoft/common/system/mapper/xml/OrderMapper.xml b/src/main/java/com/gxwebsoft/common/system/mapper/xml/OrderMapper.xml index c1f7aec..58a713f 100644 --- a/src/main/java/com/gxwebsoft/common/system/mapper/xml/OrderMapper.xml +++ b/src/main/java/com/gxwebsoft/common/system/mapper/xml/OrderMapper.xml @@ -4,9 +4,8 @@ - SELECT a.*,b.tenant_name + SELECT a.* FROM sys_order a - LEFT JOIN sys_tenant b ON a.tenant_id = b.tenant_id AND a.order_id = #{param.orderId} diff --git a/src/main/java/com/gxwebsoft/common/system/service/impl/OrderServiceImpl.java b/src/main/java/com/gxwebsoft/common/system/service/impl/OrderServiceImpl.java index 908a612..e164357 100644 --- a/src/main/java/com/gxwebsoft/common/system/service/impl/OrderServiceImpl.java +++ b/src/main/java/com/gxwebsoft/common/system/service/impl/OrderServiceImpl.java @@ -1,17 +1,17 @@ package com.gxwebsoft.common.system.service.impl; +import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSONObject; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.gxwebsoft.common.core.config.ConfigProperties; import com.gxwebsoft.common.core.utils.RedisUtil; -import com.gxwebsoft.common.system.entity.OrderInfo; -import com.gxwebsoft.common.system.entity.Payment; -import com.gxwebsoft.common.system.mapper.OrderMapper; -import com.gxwebsoft.common.system.service.OrderService; -import com.gxwebsoft.common.system.entity.Order; -import com.gxwebsoft.common.system.param.OrderParam; import com.gxwebsoft.common.core.web.PageParam; import com.gxwebsoft.common.core.web.PageResult; +import com.gxwebsoft.common.system.entity.Order; +import com.gxwebsoft.common.system.entity.Payment; +import com.gxwebsoft.common.system.mapper.OrderMapper; +import com.gxwebsoft.common.system.param.OrderParam; +import com.gxwebsoft.common.system.service.OrderService; import com.gxwebsoft.common.system.service.PaymentService; import com.gxwebsoft.common.system.service.SettingService; import com.wechat.pay.java.core.Config; @@ -21,6 +21,7 @@ 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 org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import javax.annotation.Resource; @@ -36,6 +37,8 @@ import java.util.List; */ @Service public class OrderServiceImpl extends ServiceImpl implements OrderService { + @Value("${spring.profiles.active}") + String active; @Resource private ConfigProperties config; @Resource @@ -44,6 +47,19 @@ public class OrderServiceImpl extends ServiceImpl implements private RedisUtil redisUtil; @Resource private PaymentService paymentService; + public static String privateKeyPath = "/Users/gxwebsoft/JAVA/com.gxwebsoft.core/src/main/resources/cert/apiclient_key.pem"; + public static String privateCertPath = "/Users/gxwebsoft/JAVA/com.gxwebsoft.core/src/main/resources/cert/apiclient_cert.pem"; + public static String serialNumber = "48749613B40AA8F1D768583FC352358E13EB5AF0"; + public static String merchantId = "1246610101"; + public static String appId = "wx541db955e7a62709"; + public static String apiV3Key = "zGufUcqa7ovgxRL0kF5OlPr482EZwtn9"; + + /** + * 平台证书生成命令(勿删) + * java -jar CertificateDownloader.jar -k zGufUcqa7ovgxRL0kF5OlPr482EZwtn9 -m 1246610101 -f /www/wwwroot/file.ws//file/20240511/280fb6cf7eec4c2d9661c2508123c6a8.pem -s 48749613B40AA8F1D768583FC352358E13EB5AF0 -o /www/cert/ + */ + public static String wechatpayCertPath = "/Users/gxwebsoft/JAVA/com.gxwebsoft.core/src/main/resources/cert/wechatpay_4A3231584E93B6AE77820074D07EADEACCB7E223.pem"; // 平台证书 + @Override public PageResult pageRel(OrderParam param) { PageParam page = new PageParam<>(param); @@ -57,7 +73,6 @@ public class OrderServiceImpl extends ServiceImpl implements List list = baseMapper.selectListRel(param); // 排序 PageParam page = new PageParam<>(); - //page.setDefaultOrder("create_time desc"); return page.sortRecords(list); } @@ -68,6 +83,19 @@ public class OrderServiceImpl extends ServiceImpl implements return param.getOne(baseMapper.selectListRel(param)); } + @Override + public Order getByOutTradeNo(String outTradeNo) { + System.out.println("outTradeNo = " + outTradeNo); + OrderParam param = new OrderParam(); + param.setOrderNo(outTradeNo); + return param.getOne(baseMapper.getByOutTradeNo(param)); + } + + @Override + public void updateByOutTradeNo(Order order) { + baseMapper.updateByOutTradeNo(order); + } + /** * 创建微信支付 * @@ -76,44 +104,32 @@ public class OrderServiceImpl extends ServiceImpl implements */ @Override public HashMap createWxOrder(Order order) { - final String uploadPath = config.getUploadPath(); // 服务器本地路径 + // 服务器本地路径 + final String uploadPath = config.getUploadPath(); final HashMap orderInfo = new HashMap<>(); // 微信小程序(微信支付) String key = "Payment:wxPay:".concat(order.getTenantId().toString()); final Payment payment = redisUtil.get(key, Payment.class); - System.out.println("payment1 = " + payment); final JSONObject mpWx = settingService.getBySettingKey("mp-weixin"); -// final JSONObject payment = settingService.getBySettingKey("payment"); - // 计算金额 BigDecimal decimal = order.getTotalPrice(); final BigDecimal multiply = decimal.multiply(new BigDecimal(100)); // 将 BigDecimal 转换为 Integer Integer money = multiply.intValue(); - - final String appId = mpWx.getString("appId"); - final String mchId = payment.getMchId(); - final String openid = order.getOpenid(); // openid - final String notifyUrl = config.getServerUrl() + "/system/notify/" + order.getTenantId(); // 异步通知地址 - final String privateKey = uploadPath.concat("file").concat(payment.getApiclientKey()); // 秘钥证书 - final String apiclientCert = uploadPath.concat("file").concat(payment.getApiclientCert()); - final String merchantSerialNumber = payment.getMerchantSerialNumber(); // 证书序列号 - final String apiV3key = payment.getApiKey(); - - System.out.println("privateKey = " + privateKey); - System.out.println("apiclientCert = " + apiclientCert); - System.out.println("merchantSerialNumber = " + merchantSerialNumber); - System.out.println("apiV3key = " + apiV3key); - System.out.println("mchId = " + mchId); - System.out.println("appId = " + appId); - + String privateKey = uploadPath.concat("/file").concat(payment.getApiclientKey()); // 秘钥证书 + String apiclientCert = uploadPath.concat("/file").concat(payment.getApiclientCert()); + // 开发环境配置 + if(active.equals("dev")){ + privateKey = privateKeyPath; + apiclientCert = wechatpayCertPath; + } Config config = new RSAConfig.Builder() - .merchantId(mchId) + .merchantId(payment.getMchId()) .privateKeyFromPath(privateKey) - .merchantSerialNumber(merchantSerialNumber) + .merchantSerialNumber(payment.getMerchantSerialNumber()) .wechatPayCertificatesFromPath(apiclientCert) .build(); @@ -125,18 +141,25 @@ public class OrderServiceImpl extends ServiceImpl implements amount.setTotal(money); amount.setCurrency("CNY"); request.setAmount(amount); - request.setAppid(appId); - request.setMchid(mchId); + request.setAppid(mpWx.getString("appId")); + request.setMchid(payment.getMchId()); request.setDescription(order.getComments()); - request.setNotifyUrl(notifyUrl); + request.setNotifyUrl("https://server.gxwebsoft.com/api/system/wx-pay/notify/" + order.getTenantId()); request.setOutTradeNo(order.getOrderNo()); request.setAttach(order.getTenantId().toString()); final Payer payer = new Payer(); - payer.setOpenid(openid); + payer.setOpenid(order.getOpenid()); request.setPayer(payer); - System.out.println("request2 = " + request); + if (StrUtil.isNotBlank(payment.getNotifyUrl())) { + request.setNotifyUrl(payment.getNotifyUrl().concat("/").concat(order.getTenantId().toString())); + } + // 测试金额 + if(active.equals("dev")){ + amount.setTotal(1); + request.setAmount(amount); + } + System.out.println("request = " + request); PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(request); - System.out.println("response = " + response); orderInfo.put("provider", "wxpay"); orderInfo.put("timeStamp", response.getTimeStamp()); orderInfo.put("nonceStr", response.getNonceStr()); @@ -145,16 +168,4 @@ public class OrderServiceImpl extends ServiceImpl implements orderInfo.put("paySign", response.getPaySign()); return orderInfo; } - - @Override - public Order getByOutTradeNo(String outTradeNo) { - OrderParam param = new OrderParam(); - param.setOrderNo(outTradeNo); - return param.getOne(baseMapper.getByOutTradeNo(param)); - } - - @Override - public void updateByOutTradeNo(Order order) { - baseMapper.updateByOutTradeNo(order); - } } diff --git a/src/main/resources/cert/wechatpay_4A3231584E93B6AE77820074D07EADEACCB7E223.pem b/src/main/resources/cert/wechatpay_4A3231584E93B6AE77820074D07EADEACCB7E223.pem new file mode 100644 index 0000000..50afe22 --- /dev/null +++ b/src/main/resources/cert/wechatpay_4A3231584E93B6AE77820074D07EADEACCB7E223.pem @@ -0,0 +1,24 @@ +-----BEGIN CERTIFICATE----- +MIIEFDCCAvygAwIBAgIUSjIxWE6Ttq53ggB00H6t6sy34iMwDQYJKoZIhvcNAQEL +BQAwXjELMAkGA1UEBhMCQ04xEzARBgNVBAoTClRlbnBheS5jb20xHTAbBgNVBAsT +FFRlbnBheS5jb20gQ0EgQ2VudGVyMRswGQYDVQQDExJUZW5wYXkuY29tIFJvb3Qg +Q0EwHhcNMjMwOTE5MTUxNTQ4WhcNMjgwOTE3MTUxNTQ4WjBuMRgwFgYDVQQDDA9U +ZW5wYXkuY29tIHNpZ24xEzARBgNVBAoMClRlbnBheS5jb20xHTAbBgNVBAsMFFRl +bnBheS5jb20gQ0EgQ2VudGVyMQswCQYDVQQGDAJDTjERMA8GA1UEBwwIU2hlblpo +ZW4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC5AOeoNuRReVPfOodE +TTE/wq96OK/7MI1lkwgtRlOIqwFGbGPxPx+wiLweWOWAeGrd0h+YuktIZezLhMhB +8pkJBzxz4U+zoQ9n3yY4CgDUBeAO8eNHhEQTTOTFUIvIlRxyr0EVuTHNP7pIkxA6 +gUvajWjJFbvU393vDHA9RflWrBNw1qVjkPPUx6axizD3wS8f77bwuspEWGTza/pS +g96HWUwFh9OSlQNPOjPG4km2YRQwGhoRMlLeZsUB+a0PtV0HI/B0TbY5u2EmPt0R +uNZLmcwImtiANYmySww6gp5uo4+im0P/kHKynPrW1SkS+8M9JP5T7Xck3bnHNv4S +9UOPAgMBAAGjgbkwgbYwCQYDVR0TBAIwADALBgNVHQ8EBAMCA/gwgZsGA1UdHwSB +kzCBkDCBjaCBiqCBh4aBhGh0dHA6Ly9ldmNhLml0cnVzLmNvbS5jbi9wdWJsaWMv +aXRydXNjcmw/Q0E9MUJENDIyMEU1MERCQzA0QjA2QUQzOTc1NDk4NDZDMDFDM0U4 +RUJEMiZzZz1IQUNDNDcxQjY1NDIyRTEyQjI3QTlEMzNBODdBRDFDREY1OTI2RTE0 +MDM3MTANBgkqhkiG9w0BAQsFAAOCAQEAHd42YyvxvvjYEIkCURHweCSQpWQrdsnP +AU2rcVzOx0kDxjq7+W2HkIkYeAZfE8pyBs5c39tgK+qospiDsKa9WYeBNyIRcCvN +q9ObLqSV4Cy/8lQMNh4Q37cdc3X+pAlTr7MtKka8ZcXYvbMBqus0dfJZayZvW7Ak +nnaXXJ4k7urgHOGYsZlZ+HC+DC/sYoN3DXzvg3XPlL0SNEQH0cWrRbaQnpOKVMk5 +TptbeNHKJfUxVW5jh4GtBFqvLeiOruY+gFYg7UkCKWo9ZIYe7/mEvJeHYh3acTTl +DOl3L0fR5KR7vqFMwZEUZxVOs6K2ANSCr57OQPBV++MG4DPr3yOhCQ== +-----END CERTIFICATE----- \ No newline at end of file