diff --git a/ruoyi-admin/src/main/resources/application.yml b/ruoyi-admin/src/main/resources/application.yml
index fc47e9b..06f49cf 100644
--- a/ruoyi-admin/src/main/resources/application.yml
+++ b/ruoyi-admin/src/main/resources/application.yml
@@ -255,10 +255,19 @@ management:
--- # mail 邮件发送
mail:
enabled: false
-agora:
- app-id: 13e7c8066b9a4b2488eeffd66a9ab29c
- key: 9087bf18d142410ea113b60e8fbed8c9
- secret: f8c048e0b2f247409b42395efb846cba·
yunxin:
app-key: 748496374b04344194c68b53e411e09c123
app-secret: 77c09917d2de
+wx:
+ pay:
+ app-id: wxefe4087f9c1e0fd7
+ mch-id: 1673939172
+ partner-key: dhsajidua9sd8sahdj1h289dhjkashd8
+ notify-url: "http://xq.mubai8888.com"
+ mp:
+ app-id: wxefe4087f9c1e0fd7
+ secret: f11d09d4024c0ca18e895eabe99df549
+ aes-key:
+ token:
+ config-storage:
+ type: RedisTemplate
diff --git a/ruoyi-xq/pom.xml b/ruoyi-xq/pom.xml
index 462f906..a81a31e 100644
--- a/ruoyi-xq/pom.xml
+++ b/ruoyi-xq/pom.xml
@@ -63,5 +63,10 @@
javase
3.4.1
+
+ com.github.binarywang
+ wx-java-mp-spring-boot-starter
+ 4.4.0
+
diff --git a/ruoyi-xq/src/main/java/com/ruoyi/xq/config/WxPayProperties.java b/ruoyi-xq/src/main/java/com/ruoyi/xq/config/WxPayProperties.java
new file mode 100644
index 0000000..a6dca1f
--- /dev/null
+++ b/ruoyi-xq/src/main/java/com/ruoyi/xq/config/WxPayProperties.java
@@ -0,0 +1,33 @@
+package com.ruoyi.xq.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.io.Serializable;
+
+@Data
+@Component
+@ConfigurationProperties(prefix = "wx.pay")
+public class WxPayProperties implements Serializable {
+
+ private static final long serialVersionUID=1L;
+
+ /**
+ *
+ */
+ private String appId;
+ /**
+ *
+ */
+ private String mchId;
+ /**
+ *
+ */
+ private String partnerKey;
+ /**
+ *
+ */
+ private String notifyUrl;
+
+}
diff --git a/ruoyi-xq/src/main/java/com/ruoyi/xq/controller/app/MpController.java b/ruoyi-xq/src/main/java/com/ruoyi/xq/controller/app/MpController.java
new file mode 100644
index 0000000..e8bd017
--- /dev/null
+++ b/ruoyi-xq/src/main/java/com/ruoyi/xq/controller/app/MpController.java
@@ -0,0 +1,50 @@
+package com.ruoyi.xq.controller.app;
+
+import com.ruoyi.common.core.domain.R;
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import me.chanjar.weixin.common.bean.WxJsapiSignature;
+import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken;
+import me.chanjar.weixin.common.error.WxErrorException;
+import me.chanjar.weixin.mp.api.WxMpService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequestMapping("/api/mp")
+@Tag(name = "公众号相关接口")
+@Validated
+@Slf4j
+public class MpController {
+
+ @Autowired
+ private WxMpService wxMpService;
+
+ @Operation(summary = "公众号静默获取基础数据")
+ @GetMapping("/auth/access_token")
+ public R getAccessToken(@RequestParam(value = "code") String code) {
+ try {
+ return R.ok(wxMpService.getOAuth2Service().getAccessToken(code));
+ } catch (WxErrorException e) {
+ log.error("error", e);
+ return R.fail("获取失败");
+ }
+ }
+
+ @Operation(summary = "jsapi的授权")
+ @GetMapping("/jsapi/token")
+ public R jsApiToken(@RequestParam(value = "url") String url) {
+ try {
+ WxJsapiSignature jsapiSignature = wxMpService.createJsapiSignature(url);
+ return R.fail(jsapiSignature);
+ } catch (WxErrorException e) {
+ log.error("error", e);
+ return R.fail("获取失败");
+ }
+ }
+}
diff --git a/ruoyi-xq/src/main/java/com/ruoyi/xq/controller/app/PayController.java b/ruoyi-xq/src/main/java/com/ruoyi/xq/controller/app/PayController.java
index 38fd87d..92293c9 100644
--- a/ruoyi-xq/src/main/java/com/ruoyi/xq/controller/app/PayController.java
+++ b/ruoyi-xq/src/main/java/com/ruoyi/xq/controller/app/PayController.java
@@ -1,11 +1,179 @@
package com.ruoyi.xq.controller.app;
+import cn.dev33.satoken.annotation.SaIgnore;
+import cn.hutool.core.util.StrUtil;
+import com.alibaba.fastjson2.JSON;
+import com.ijpay.core.enums.SignType;
+import com.ijpay.core.enums.TradeType;
+import com.ijpay.core.kit.HttpKit;
+import com.ijpay.core.kit.WxPayKit;
+import com.ijpay.wxpay.WxPayApi;
+import com.ijpay.wxpay.model.UnifiedOrderModel;
+import com.ruoyi.common.annotation.Log;
+import com.ruoyi.common.core.domain.R;
+import com.ruoyi.common.enums.BusinessType;
+import com.ruoyi.common.utils.ServletUtils;
+import com.ruoyi.xq.config.WxPayProperties;
+import com.ruoyi.xq.dto.app.pay.PayControllerDTO;
+import com.ruoyi.xq.dto.app.pay.PayOrderInfoDTO;
+import com.ruoyi.xq.dto.app.pay.PayReturnResp;
+import com.ruoyi.xq.enums.pay.PlatformTypeEnum;
+import com.ruoyi.xq.manager.PayManager;
+import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+
@RestController
@RequestMapping("/api/pay")
-@Tag(name = "支付接口")
+@Tag(name = "支付相关的接口")
+@Validated
+@Slf4j
public class PayController {
+
+ @Autowired
+ private WxPayProperties wxPayProperties;
+ @Autowired
+ private PayManager payManager;
+
+ private static final String NOTIFY_WX_URL = "/api/pay/wx/notify";
+ private static final String NOTIFY_ALI_URL = "/api/pay/ali/notify";
+
+ @PostMapping(value = "/jsapi/wx/test")
+ @Operation(summary = "微信支付-公众号-测试")
+ @Log(title = "微信支付-公众号-测试", businessType = BusinessType.OTHER, isSaveDb = true)
+ public R jsapiPayTest(@RequestBody PayControllerDTO payController){
+ String openId = payController.getOpenId();
+ if (StrUtil.isEmpty(openId)) {
+ return R.fail("openid is null");
+ }
+ String ip = ServletUtils.getClientIP();
+ Map params = UnifiedOrderModel
+ .builder()
+ .appid(wxPayProperties.getAppId())
+ .mch_id(wxPayProperties.getMchId())
+ .nonce_str(WxPayKit.generateStr())
+ .body("测试发起支付")
+ .out_trade_no(WxPayKit.generateStr())
+ .total_fee("1000")
+ .spbill_create_ip(ip)
+ .notify_url(wxPayProperties.getNotifyUrl()+NOTIFY_WX_URL)
+ .trade_type(TradeType.JSAPI.getTradeType())
+ .openid(openId)
+ .build()
+ .createSign(wxPayProperties.getPartnerKey(), SignType.HMACSHA256);
+ String xmlResult = WxPayApi.pushOrder(false, params);
+ log.info(xmlResult);
+ Map resultMap = WxPayKit.xmlToMap(xmlResult);
+ String returnCode = resultMap.get("return_code");
+ String returnMsg = resultMap.get("return_msg");
+ if (!WxPayKit.codeIsOk(returnCode)) {
+ log.error("j微信支付失败 returnMsg={}",returnMsg);
+ return R.fail("微信支付失败,请联系客服");
+ }
+ String resultCode = resultMap.get("result_code");
+ if (!WxPayKit.codeIsOk(resultCode)) {
+ log.error("j微信支付失败 returnMsg={}",returnMsg);
+ return R.fail("微信支付失败,请联系客服");
+ }
+ // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
+ String prepayId = resultMap.get("prepay_id");
+ Map packageParams = WxPayKit.prepayIdCreateSign(prepayId, wxPayProperties.getAppId(),
+ wxPayProperties.getPartnerKey(), SignType.HMACSHA256);
+ String jsonStr = JSON.toJSONString(packageParams);
+ PayReturnResp resp = new PayReturnResp();
+ resp.setData(jsonStr);
+ return R.ok(resp);
+ }
+
+ @PostMapping(value = "/jsapi/wx")
+ @Operation(summary = "微信支付-公众号")
+ @Log(title = "微信支付-公众号", businessType = BusinessType.OTHER, isSaveDb = true)
+ public R jsapiPay(@RequestBody PayControllerDTO payController){
+ PayOrderInfoDTO payOrderInfo = payManager.getOrderInfo(payController.getOrderNo());
+ String openId = payController.getOpenId();
+ if (StrUtil.isEmpty(openId)) {
+ return R.fail("openid is null");
+ }
+ String ip = ServletUtils.getClientIP();
+ Map params = UnifiedOrderModel
+ .builder()
+ .appid(wxPayProperties.getAppId())
+ .mch_id(wxPayProperties.getMchId())
+ .nonce_str(WxPayKit.generateStr())
+ .body(payOrderInfo.getBody())
+ .out_trade_no(WxPayKit.generateStr())
+ .total_fee(payOrderInfo.getPriceFenStr())
+ .spbill_create_ip(ip)
+ .notify_url(wxPayProperties.getNotifyUrl()+NOTIFY_WX_URL)
+ .trade_type(TradeType.JSAPI.getTradeType())
+ .openid(openId)
+ .build()
+ .createSign(wxPayProperties.getPartnerKey(), SignType.HMACSHA256);
+ String xmlResult = WxPayApi.pushOrder(false, params);
+ log.info(xmlResult);
+ Map resultMap = WxPayKit.xmlToMap(xmlResult);
+ String returnCode = resultMap.get("return_code");
+ String returnMsg = resultMap.get("return_msg");
+ if (!WxPayKit.codeIsOk(returnCode)) {
+ log.error("j微信支付失败 returnMsg={}",returnMsg);
+ return R.fail("微信支付失败,请联系客服");
+ }
+ String resultCode = resultMap.get("result_code");
+ if (!WxPayKit.codeIsOk(resultCode)) {
+ log.error("j微信支付失败 returnMsg={}",returnMsg);
+ return R.fail("微信支付失败,请联系客服");
+ }
+ // 以下字段在 return_code 和 result_code 都为 SUCCESS 的时候有返回
+ String prepayId = resultMap.get("prepay_id");
+ Map packageParams = WxPayKit.prepayIdCreateSign(prepayId, wxPayProperties.getAppId(),
+ wxPayProperties.getPartnerKey(), SignType.HMACSHA256);
+ String jsonStr = JSON.toJSONString(packageParams);
+ PayReturnResp resp = new PayReturnResp();
+ resp.setData(jsonStr);
+ return R.ok(resp);
+ }
+
+
+ @PostMapping(value = "/wx/notify")
+ @Operation(hidden = true)
+ @Log(title = "微信回调", businessType = BusinessType.OTHER, isSaveDb = false)
+ @SaIgnore
+ public String payNotify(HttpServletRequest request) {
+ String xmlMsg = HttpKit.readData(request);
+ log.info("支付通知=" + xmlMsg);
+ Map params = WxPayKit.xmlToMap(xmlMsg);
+ String returnCode = params.get("return_code");
+ String mchId = params.get("mch_id");
+ if(!wxPayProperties.getMchId().equals(mchId)){
+ log.error("未找到微信配置 mchId={}",mchId);
+ return null;
+ }
+ // 注意重复通知的情况,同一订单号可能收到多次通知,请注意一定先判断订单状态
+ // 注意此处签名方式需与统一下单的签名类型一致
+ if (WxPayKit.verifyNotify(params, wxPayProperties.getPartnerKey(), SignType.MD5)) {
+ if (WxPayKit.codeIsOk(returnCode)) {
+ String outTradeNo = params.get("out_trade_no");
+ String transactionId = params.get("transaction_id");
+ payManager.callBack(outTradeNo,params,mchId, PlatformTypeEnum.WX);
+ Map xml = new HashMap<>(2);
+ xml.put("return_code", "SUCCESS");
+ xml.put("return_msg", "OK");
+ return WxPayKit.toXml(xml);
+ }
+ }
+ return null;
+ }
+
+
+
}
diff --git a/ruoyi-xq/src/main/java/com/ruoyi/xq/domain/VipOrder.java b/ruoyi-xq/src/main/java/com/ruoyi/xq/domain/VipOrder.java
index 9ab377a..edbcc42 100644
--- a/ruoyi-xq/src/main/java/com/ruoyi/xq/domain/VipOrder.java
+++ b/ruoyi-xq/src/main/java/com/ruoyi/xq/domain/VipOrder.java
@@ -54,6 +54,7 @@ public class VipOrder implements Serializable {
* 会员价格
*/
private BigDecimal vipPrice;
+ private String body;
/**
* 订单号
*/
diff --git a/ruoyi-xq/src/main/java/com/ruoyi/xq/domain/WxTransOrder.java b/ruoyi-xq/src/main/java/com/ruoyi/xq/domain/WxTransOrder.java
index e575559..a87bda1 100644
--- a/ruoyi-xq/src/main/java/com/ruoyi/xq/domain/WxTransOrder.java
+++ b/ruoyi-xq/src/main/java/com/ruoyi/xq/domain/WxTransOrder.java
@@ -42,6 +42,8 @@ public class WxTransOrder implements Serializable {
* 跟踪ID
*/
private String traceId;
+
+ private String body;
/**
* WX-ID
*/
diff --git a/ruoyi-xq/src/main/java/com/ruoyi/xq/dto/app/pay/PayControllerDTO.java b/ruoyi-xq/src/main/java/com/ruoyi/xq/dto/app/pay/PayControllerDTO.java
new file mode 100644
index 0000000..85ffda3
--- /dev/null
+++ b/ruoyi-xq/src/main/java/com/ruoyi/xq/dto/app/pay/PayControllerDTO.java
@@ -0,0 +1,14 @@
+package com.ruoyi.xq.dto.app.pay;
+
+import io.swagger.v3.oas.annotations.media.Schema;
+import lombok.Data;
+
+@Data
+public class PayControllerDTO {
+ @Schema(description = "订单号")
+ private String orderNo;
+ @Schema(description = "微信OpenId jsapi支付必传")
+ private String openId;
+// @Schema(description = "微信支付的时候需要传")
+// private String wxAppId;
+}
diff --git a/ruoyi-xq/src/main/java/com/ruoyi/xq/dto/app/pay/PayOrderInfoDTO.java b/ruoyi-xq/src/main/java/com/ruoyi/xq/dto/app/pay/PayOrderInfoDTO.java
new file mode 100644
index 0000000..e175cff
--- /dev/null
+++ b/ruoyi-xq/src/main/java/com/ruoyi/xq/dto/app/pay/PayOrderInfoDTO.java
@@ -0,0 +1,19 @@
+
+package com.ruoyi.xq.dto.app.pay;
+
+import cn.hutool.core.util.NumberUtil;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+@Data
+public class PayOrderInfoDTO {
+ private String body;
+ private String subject;
+ private BigDecimal price;
+ private String orderNo;
+
+ public String getPriceFenStr(){
+ return NumberUtil.mul(price,100).longValue()+"";
+ }
+}
diff --git a/ruoyi-xq/src/main/java/com/ruoyi/xq/dto/app/pay/PayReturnResp.java b/ruoyi-xq/src/main/java/com/ruoyi/xq/dto/app/pay/PayReturnResp.java
new file mode 100644
index 0000000..0045293
--- /dev/null
+++ b/ruoyi-xq/src/main/java/com/ruoyi/xq/dto/app/pay/PayReturnResp.java
@@ -0,0 +1,8 @@
+package com.ruoyi.xq.dto.app.pay;
+
+import lombok.Data;
+
+@Data
+public class PayReturnResp {
+ private String data;
+}
diff --git a/ruoyi-xq/src/main/java/com/ruoyi/xq/manager/PayManager.java b/ruoyi-xq/src/main/java/com/ruoyi/xq/manager/PayManager.java
index 0a9b7c8..c9ca533 100644
--- a/ruoyi-xq/src/main/java/com/ruoyi/xq/manager/PayManager.java
+++ b/ruoyi-xq/src/main/java/com/ruoyi/xq/manager/PayManager.java
@@ -1,8 +1,13 @@
package com.ruoyi.xq.manager;
+import com.ruoyi.common.exception.ServiceException;
+import com.ruoyi.xq.domain.VipOrder;
import com.ruoyi.xq.domain.VipPrice;
+import com.ruoyi.xq.domain.WxTransOrder;
import com.ruoyi.xq.dto.app.pay.ConsumeResp;
+import com.ruoyi.xq.dto.app.pay.PayOrderInfoDTO;
import com.ruoyi.xq.enums.common.OrderTypeEnum;
+import com.ruoyi.xq.enums.pay.PayStatusEnum;
import com.ruoyi.xq.enums.pay.PlatformTypeEnum;
import com.ruoyi.xq.mq.AmqpProducer;
import com.ruoyi.xq.mq.handle.dto.CalculateSalesHandleDTO;
@@ -30,6 +35,48 @@ public class PayManager {
@Autowired
private WxTransOrderService wxTransOrderService;
+ public PayOrderInfoDTO getOrderInfo(String orderNo){
+ OrderTypeEnum orderTypeEnum = OrderNoUtil.getType(orderNo);
+ if(orderTypeEnum == null){
+ log.error("订单类型有误!orderNo={}",orderNo);
+ throw new ServiceException("支付失败,请检查订单号");
+ }
+ PayOrderInfoDTO dto = null;
+ switch (orderTypeEnum) {
+ case VIP:
+ VipOrder vipOrder = vipOrderService.getByOrderNo(orderNo);
+ if(vipOrder == null){
+ throw new ServiceException("订单不存在,请重新下单支付");
+ }
+ if(!PayStatusEnum.READY_PAY.getCode().equals(vipOrder.getPayStatus())){
+ throw new ServiceException("订单状态有误,请重新下单支付");
+ }
+ dto = new PayOrderInfoDTO();
+ dto.setBody(vipOrder.getBody());
+ dto.setSubject(vipOrder.getBody());
+ dto.setPrice(vipOrder.getVipPrice());
+ dto.setOrderNo(vipOrder.getOrderNo());
+ break;
+ case WX_TRANS:
+ WxTransOrder wxTransOrder = wxTransOrderService.getByOrderNo(orderNo);
+ if(wxTransOrder == null){
+ throw new ServiceException("订单不存在,请重新下单支付");
+ }
+ if(!PayStatusEnum.READY_PAY.getCode().equals(wxTransOrder.getPayStatus())){
+ throw new ServiceException("订单状态有误,请重新下单支付");
+ }
+ dto = new PayOrderInfoDTO();
+ dto.setBody(wxTransOrder.getBody());
+ dto.setSubject(wxTransOrder.getBody());
+ dto.setPrice(wxTransOrder.getWxPrice());
+ dto.setOrderNo(wxTransOrder.getOrderNo());
+ break;
+ default:
+ break;
+ }
+ return dto;
+ }
+
public void callBack(String orderNo, Map params, String appId, PlatformTypeEnum payTypeEnum){
OrderTypeEnum orderTypeEnum = OrderNoUtil.getType(orderNo);