Bläddra i källkod

对接支付开发

hejinbo 2 år sedan
förälder
incheckning
4d2d8b0976
47 ändrade filer med 2503 tillägg och 2 borttagningar
  1. 59
    0
      pay/pom.xml
  2. 32
    0
      pay/src/main/java/com/ruoyi/CallBackService.java
  3. 66
    0
      pay/src/main/java/com/ruoyi/ElegentPay.java
  4. 40
    0
      pay/src/main/java/com/ruoyi/ali/AlipayConfig.java
  5. 30
    0
      pay/src/main/java/com/ruoyi/ali/AlipayConstant.java
  6. 461
    0
      pay/src/main/java/com/ruoyi/ali/AlipayElegentTrade.java
  7. 124
    0
      pay/src/main/java/com/ruoyi/ali/AlipayElegentValid.java
  8. 20
    0
      pay/src/main/java/com/ruoyi/annotation/TradePlatform.java
  9. 23
    0
      pay/src/main/java/com/ruoyi/config/CallbackConfig.java
  10. 11
    0
      pay/src/main/java/com/ruoyi/constant/PayConstant.java
  11. 16
    0
      pay/src/main/java/com/ruoyi/constant/Platform.java
  12. 33
    0
      pay/src/main/java/com/ruoyi/constant/TradeType.java
  13. 35
    0
      pay/src/main/java/com/ruoyi/core/CallBackServiceImpl.java
  14. 90
    0
      pay/src/main/java/com/ruoyi/core/CallbackController.java
  15. 73
    0
      pay/src/main/java/com/ruoyi/core/CallbackWatch.java
  16. 27
    0
      pay/src/main/java/com/ruoyi/core/ElegentConfig.java
  17. 82
    0
      pay/src/main/java/com/ruoyi/core/ElegentLoader.java
  18. 136
    0
      pay/src/main/java/com/ruoyi/core/ElegentPayImpl.java
  19. 54
    0
      pay/src/main/java/com/ruoyi/core/ElegentTrade.java
  20. 48
    0
      pay/src/main/java/com/ruoyi/core/ElegentValid.java
  21. 20
    0
      pay/src/main/java/com/ruoyi/core/WatchList.java
  22. 20
    0
      pay/src/main/java/com/ruoyi/dto/PayRequest.java
  23. 29
    0
      pay/src/main/java/com/ruoyi/dto/PayResponse.java
  24. 24
    0
      pay/src/main/java/com/ruoyi/dto/QueryRefundResponse.java
  25. 25
    0
      pay/src/main/java/com/ruoyi/dto/QueryResponse.java
  26. 16
    0
      pay/src/main/java/com/ruoyi/dto/RefundRequest.java
  27. 15
    0
      pay/src/main/java/com/ruoyi/dto/ValidResponse.java
  28. 12
    0
      pay/src/main/java/com/ruoyi/dto/WatchDTO.java
  29. 24
    0
      pay/src/main/java/com/ruoyi/exceptions/TradeException.java
  30. 16
    0
      pay/src/main/java/com/ruoyi/key/KeyManager.java
  31. 19
    0
      pay/src/main/java/com/ruoyi/key/impl/DefaultKeyManager.java
  32. 37
    0
      pay/src/main/java/com/ruoyi/util/FileUtil.java
  33. 459
    0
      pay/src/main/java/com/ruoyi/wx/WxPayElegentTrade.java
  34. 153
    0
      pay/src/main/java/com/ruoyi/wx/WxPayElegentValid.java
  35. 20
    0
      pay/src/main/java/com/ruoyi/wx/WxpayConfig.java
  36. 48
    0
      pay/src/main/java/com/ruoyi/wx/WxpayConstant.java
  37. 14
    0
      pay/src/main/resources/META-INF/spring.factories
  38. 1
    0
      pom.xml
  39. 5
    1
      ruoyi-admin/pom.xml
  40. 29
    0
      ruoyi-admin/src/main/java/com/ruoyi/web/controller/payment/CasePaymentController.java
  41. 1
    0
      ruoyi-admin/src/main/resources/alipay_private.key
  42. 1
    0
      ruoyi-admin/src/main/resources/alipay_public.key
  43. 15
    0
      ruoyi-admin/src/main/resources/application.yml
  44. 1
    0
      ruoyi-admin/src/main/resources/wxpay_private.key
  45. 5
    1
      ruoyi-system/pom.xml
  46. 7
    0
      ruoyi-system/src/main/java/com/ruoyi/system/service/ICasePaymentService.java
  47. 27
    0
      ruoyi-system/src/main/java/com/ruoyi/system/service/impl/CasePaymentServiceImpl.java

+ 59
- 0
pay/pom.xml Visa fil

@@ -0,0 +1,59 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<project xmlns="http://maven.apache.org/POM/4.0.0"
3
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5
+    <parent>
6
+        <artifactId>ruoyi</artifactId>
7
+        <groupId>com.ruoyi</groupId>
8
+        <version>3.8.6</version>
9
+    </parent>
10
+    <modelVersion>4.0.0</modelVersion>
11
+    <artifactId>pay</artifactId>
12
+    <version>1.0.0-SNAPSHOT</version>
13
+
14
+    <properties>
15
+        <maven.compiler.source>8</maven.compiler.source>
16
+        <maven.compiler.target>8</maven.compiler.target>
17
+    </properties>
18
+
19
+    <dependencies>
20
+        <dependency>
21
+            <groupId>org.springframework.boot</groupId>
22
+            <artifactId>spring-boot-starter-web</artifactId>
23
+        </dependency>
24
+        <dependency>
25
+            <groupId>org.springframework.boot</groupId>
26
+            <artifactId>spring-boot-autoconfigure</artifactId>
27
+            <version>2.7.10</version>
28
+            <optional>true</optional>
29
+        </dependency>
30
+
31
+        <dependency>
32
+            <groupId>org.projectlombok</groupId>
33
+            <artifactId>lombok</artifactId>
34
+            <version>1.18.22</version>
35
+        </dependency>
36
+        <dependency>
37
+            <groupId>com.alibaba</groupId>
38
+            <artifactId>fastjson</artifactId>
39
+            <version>1.2.72</version>
40
+        </dependency>
41
+        <dependency>
42
+            <groupId>cn.hutool</groupId>
43
+            <artifactId>hutool-all</artifactId>
44
+            <version>5.7.12</version>
45
+        </dependency>
46
+        <dependency>
47
+            <groupId>com.github.wechatpay-apiv3</groupId>
48
+            <artifactId>wechatpay-apache-httpclient</artifactId>
49
+            <version>0.4.7</version>
50
+        </dependency>
51
+        <dependency>
52
+            <groupId>com.alipay.sdk</groupId>
53
+            <artifactId>alipay-sdk-java</artifactId>
54
+            <version>4.34.8.ALL</version>
55
+        </dependency>
56
+    </dependencies>
57
+
58
+
59
+</project>

+ 32
- 0
pay/src/main/java/com/ruoyi/CallBackService.java Visa fil

@@ -0,0 +1,32 @@
1
+package com.ruoyi;
2
+
3
+/**
4
+ * 业务回调处理接口
5
+ */
6
+public interface CallBackService {
7
+
8
+    /**
9
+     * 成功支付--处理业务逻辑
10
+     * @param orderSn 订单号
11
+     */
12
+    void successPay(String orderSn);
13
+
14
+    /**
15
+     * 失败支付-处理业务逻辑
16
+     * @param orderSn 订单号
17
+     */
18
+    void failPay(String orderSn);
19
+
20
+    /**
21
+     * 退款成功-处理业务逻辑
22
+     * @param orderSn 订单号
23
+     */
24
+    void successRefund(String orderSn);
25
+
26
+    /**
27
+     * 退款失败-处理业务逻辑
28
+     * @param orderSn 订单号
29
+     */
30
+    void failRefund(String orderSn);
31
+
32
+}

+ 66
- 0
pay/src/main/java/com/ruoyi/ElegentPay.java Visa fil

@@ -0,0 +1,66 @@
1
+package com.ruoyi;
2
+
3
+
4
+import com.ruoyi.dto.*;
5
+import com.ruoyi.exceptions.TradeException;
6
+
7
+public interface ElegentPay {
8
+
9
+
10
+    /**
11
+     * 统一下单接口
12
+     * @param payRequest 支付请求
13
+     * @param tradeType 交易类型
14
+     * @param platform 平台
15
+     * @return 支付响应
16
+     * @throws TradeException
17
+     */
18
+    PayResponse requestPay(PayRequest payRequest, String tradeType, String platform) throws TradeException;
19
+
20
+
21
+    /**
22
+     * 关闭订单
23
+     * @param orderSn 订单号
24
+     * @param platform 平台
25
+     * @return 是否成功关闭订单
26
+     * @throws TradeException
27
+     */
28
+    Boolean closePay(String orderSn, String platform) throws TradeException;
29
+
30
+    /**
31
+     * 退款
32
+     * @param refundRequest 退款请求封装对象
33
+     * @param platform 平台
34
+     * @return 是否成功退款
35
+     * @throws TradeException
36
+     */
37
+    Boolean refund(RefundRequest refundRequest, String platform) throws TradeException;
38
+
39
+    /**
40
+     * 根据订单号查询订单
41
+     * @param orderSn 订单号
42
+     * @param platform 平台
43
+     * @return 查询响应对象
44
+     * @throws TradeException
45
+     */
46
+    QueryResponse queryTradingOrderNo(String orderSn , String platform) throws TradeException;
47
+
48
+
49
+    /**
50
+     * 查询单笔退款API
51
+     * @param orderSn 订单号
52
+     * @param platform 平台
53
+     * @return 查询退款响应对象
54
+     * @throws TradeException
55
+     */
56
+    QueryRefundResponse queryRefundTrading(String orderSn , String platform) throws TradeException;
57
+
58
+
59
+    /**
60
+     * 获得openID
61
+     * @param code
62
+     * @return
63
+     */
64
+    public String getOpenid(String code, String platform);
65
+
66
+}

+ 40
- 0
pay/src/main/java/com/ruoyi/ali/AlipayConfig.java Visa fil

@@ -0,0 +1,40 @@
1
+package com.ruoyi.ali;
2
+
3
+import com.ruoyi.key.KeyManager;
4
+import lombok.Data;
5
+import org.springframework.beans.factory.annotation.Autowired;
6
+import org.springframework.beans.factory.annotation.Value;
7
+import org.springframework.boot.context.properties.ConfigurationProperties;
8
+import org.springframework.stereotype.Component;
9
+
10
+/**
11
+ * 支付宝权限对接类
12
+ */
13
+@Component
14
+@ConfigurationProperties("elegent.pay.alipay")
15
+@Data
16
+public class AlipayConfig {
17
+    /**
18
+     * 应用识别码
19
+     */
20
+    private String appId;
21
+
22
+    /**
23
+     * 密钥加密方式 RSA2
24
+     */
25
+    @Value("${elegent.pay.alipay.charset:RSA2}")
26
+    private String signType;
27
+
28
+    @Autowired
29
+    private KeyManager keyManager;
30
+
31
+    public String getPrivateKey(){
32
+        return keyManager.getKey("alipay_private.key");
33
+    }
34
+
35
+    public String getPublicKey(){
36
+        return keyManager.getKey("alipay_public.key");
37
+    }
38
+
39
+
40
+}

+ 30
- 0
pay/src/main/java/com/ruoyi/ali/AlipayConstant.java Visa fil

@@ -0,0 +1,30 @@
1
+package com.ruoyi.ali;
2
+
3
+import java.util.HashMap;
4
+import java.util.Map;
5
+
6
+/**
7
+ * ZFBConstant
8
+ * @description 支付宝相关的常量
9
+*/
10
+public class AlipayConstant {
11
+
12
+    public static final String SUCCESS = "SUCCESS";
13
+
14
+    public static final String  FAIL  = "FAIL";
15
+
16
+    /**
17
+     * 交易状态(用于转换)
18
+     */
19
+    public static final Map<String,String> TRADE_STATE = new HashMap<String,String>(){
20
+        {
21
+            put("TRADE_SUCCESS", "SUCCESS");
22
+            put("WAIT_BUYER_PAY", "NOTPAY");
23
+            put("TRADE_CLOSED", "CLOSED");
24
+            put("TRADE_FINISHED", "FINISHED");
25
+        }
26
+    };
27
+
28
+    public static final String domain="https://openapi.alipay.com/gateway.do";
29
+
30
+}

+ 461
- 0
pay/src/main/java/com/ruoyi/ali/AlipayElegentTrade.java Visa fil

@@ -0,0 +1,461 @@
1
+package com.ruoyi.ali;
2
+
3
+import com.alibaba.fastjson.JSON;
4
+import com.alibaba.fastjson.JSONObject;
5
+import com.alipay.api.AlipayApiException;
6
+import com.alipay.api.AlipayClient;
7
+import com.alipay.api.DefaultAlipayClient;
8
+import com.alipay.api.request.*;
9
+import com.alipay.api.response.*;
10
+import com.ruoyi.CallBackService;
11
+import com.ruoyi.annotation.TradePlatform;
12
+import com.ruoyi.config.CallbackConfig;
13
+import com.ruoyi.constant.PayConstant;
14
+import com.ruoyi.constant.Platform;
15
+import com.ruoyi.constant.TradeType;
16
+import com.ruoyi.core.ElegentTrade;
17
+import com.ruoyi.dto.*;
18
+import com.ruoyi.exceptions.TradeException;
19
+import lombok.extern.slf4j.Slf4j;
20
+import org.springframework.beans.factory.annotation.Autowired;
21
+import org.springframework.stereotype.Service;
22
+
23
+import java.math.BigDecimal;
24
+import java.util.Map;
25
+
26
+/**
27
+ * 支付宝支付的策略类
28
+ * @author wgl
29
+ */
30
+@Service
31
+@TradePlatform(Platform.ALI)
32
+@Slf4j
33
+public class AlipayElegentTrade implements ElegentTrade {
34
+
35
+    @Autowired
36
+    private AlipayConfig alipayConfig;
37
+
38
+    @Autowired
39
+    private CallbackConfig callbackConfig;
40
+
41
+    @Autowired
42
+    private CallBackService callBackService;
43
+
44
+
45
+    /**
46
+     * 获取回调地址
47
+     * @return
48
+     */
49
+    private String getPayNotifyUrl(){
50
+        return callbackConfig.getDomain()+ PayConstant.CALLBACK_PATH+ PayConstant.NOTIFY +"/"+ Platform.ALI;
51
+    }
52
+
53
+    /**
54
+     * 获取退款回调
55
+     * @return
56
+     */
57
+    private String getRefundNotifyUrl(){
58
+        return callbackConfig.getDomain()+ PayConstant.CALLBACK_PATH+ PayConstant.REFUND_NOTIFY +"/"+ Platform.ALI;
59
+    }
60
+
61
+
62
+
63
+    /**
64
+     * 创建支付订单
65
+      * @param payRequest
66
+     * @return
67
+     * @throws TradeException
68
+     */
69
+    @Override
70
+    public PayResponse requestPay(PayRequest payRequest, String tradeType) throws TradeException {
71
+        if(TradeType.NATIVE.equals( tradeType )){
72
+            return createNativeOrder(payRequest);
73
+        }
74
+        if(TradeType.JSAPI.equals( tradeType )){
75
+            return createJsApiOrder(payRequest);
76
+        }
77
+        if(TradeType.H5.equals( tradeType )){
78
+            return createH5Order(payRequest);
79
+        }
80
+        if(TradeType.APP.equals( tradeType )){
81
+            return createAPPOrder(payRequest);
82
+        }
83
+        return createNativeOrder(payRequest);
84
+    }
85
+
86
+
87
+    /**
88
+     * 本地支付(扫码)
89
+     * https://opendocs.alipay.com/open/194/106078?ref=api#预下单
90
+     * @param payRequest
91
+     * @return
92
+     * @throws TradeException
93
+     */
94
+    private PayResponse createNativeOrder(PayRequest payRequest) throws TradeException {
95
+        try {
96
+            AlipayClient alipayClient = getAliHttpClient();
97
+            AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
98
+
99
+            request.setNotifyUrl(getPayNotifyUrl());
100
+
101
+            JSONObject bizContent = new JSONObject();
102
+            bizContent.put("out_trade_no", payRequest.getOrderSn());
103
+            //转换
104
+            //String totalFee= BigDecimal.valueOf(payRequest.getTotalFee()).divide(new BigDecimal(100)  ).toString();
105
+            bizContent.put("total_amount", fenToYuan(payRequest.getTotalFee()));
106
+            bizContent.put("subject", payRequest.getBody());
107
+            request.setBizContent(bizContent.toString());
108
+            AlipayTradePrecreateResponse response = alipayClient.execute(request);
109
+
110
+            if (response.isSuccess()) {
111
+                PayResponse payResponse =new PayResponse();
112
+                payResponse.setSuccess(true);
113
+                payResponse.setCode_url(response.getQrCode()); //本地支付二维码
114
+                payResponse.setOrder_sn(payRequest.getOrderSn());
115
+                return payResponse;
116
+            } else {
117
+                log.error("调用失败");
118
+                return null;
119
+            }
120
+        }catch (Exception e){
121
+            e.printStackTrace();
122
+            throw new TradeException("订单创建失败,订单号:"+ payRequest.getOrderSn());
123
+        }
124
+    }
125
+
126
+
127
+    /**
128
+     * 小程序
129
+     * https://opendocs.alipay.com/mini/03l5wn
130
+     * @param payRequest
131
+     * @return
132
+     * @throws TradeException
133
+     */
134
+    private PayResponse createJsApiOrder(PayRequest payRequest) throws TradeException {
135
+        AlipayClient alipayClient = getAliHttpClient();
136
+        try {
137
+            AlipayTradeCreateRequest request = new AlipayTradeCreateRequest();
138
+            request.setNotifyUrl(getPayNotifyUrl());
139
+            JSONObject bizContent = new JSONObject();
140
+            bizContent.put("out_trade_no", payRequest.getOrderSn());
141
+            bizContent.put("total_amount", fenToYuan( payRequest.getTotalFee() ));
142
+            bizContent.put("subject", payRequest.getBody());
143
+            bizContent.put("buyer_id", payRequest.getOpenid());
144
+            bizContent.put("timeout_express", "10m");
145
+            request.setBizContent(bizContent.toString());
146
+            AlipayTradeCreateResponse response = alipayClient.sdkExecute(request);
147
+            if (response.isSuccess()) {
148
+                PayResponse payResponse =new PayResponse();
149
+                payResponse.setSuccess(true);
150
+                payResponse.setPrepay_id(response.getTradeNo());
151
+                payResponse.setOrder_sn(response.getOutTradeNo());
152
+                return payResponse;
153
+            } else {
154
+                log.error("调用失败");
155
+                return null;
156
+            }
157
+        }catch (Exception e){
158
+            throw new TradeException("订单创建失败,订单号:"+ payRequest.getOrderSn());
159
+        }
160
+    }
161
+
162
+
163
+    /**
164
+     * H5
165
+     * https://opendocs.alipay.com/mini/03l5wn
166
+     * @param payRequest
167
+     * @return
168
+     * @throws TradeException
169
+     */
170
+    private PayResponse createH5Order(PayRequest payRequest) throws TradeException {
171
+        AlipayClient alipayClient = getAliHttpClient();
172
+        try {
173
+            AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();
174
+            request.setNotifyUrl(getPayNotifyUrl());
175
+            request.setReturnUrl("");
176
+            JSONObject bizContent = new JSONObject();
177
+            bizContent.put("out_trade_no", payRequest.getOrderSn());
178
+            bizContent.put("total_amount", fenToYuan( payRequest.getTotalFee() ));
179
+            bizContent.put("subject", payRequest.getBody());
180
+            bizContent.put("product_code", "QUICK_WAP_WAY");
181
+
182
+            request.setBizContent(bizContent.toString());
183
+            AlipayTradeWapPayResponse response = alipayClient.pageExecute(request);
184
+            if (response.isSuccess()) {
185
+                PayResponse payResponse =new PayResponse();
186
+                payResponse.setSuccess(true);
187
+                payResponse.setPrepay_id(response.getTradeNo());
188
+                payResponse.setOrder_sn(response.getOutTradeNo());
189
+                return payResponse;
190
+            } else {
191
+                log.error("调用失败");
192
+                return null;
193
+            }
194
+        }catch (Exception e){
195
+            throw new TradeException("订单创建失败,订单号:"+ payRequest.getOrderSn());
196
+        }
197
+    }
198
+
199
+
200
+    /**
201
+     * APP
202
+     * https://opendocs.alipay.com/open/02e7gq?ref=api&scene=20
203
+     * @param payRequest
204
+     * @return
205
+     * @throws TradeException
206
+     */
207
+    private PayResponse createAPPOrder(PayRequest payRequest) throws TradeException {
208
+        AlipayClient alipayClient = getAliHttpClient();
209
+        try {
210
+            AlipayTradeAppPayRequest request = new AlipayTradeAppPayRequest();
211
+            request.setNotifyUrl(getPayNotifyUrl());
212
+            JSONObject bizContent = new JSONObject();
213
+            bizContent.put("out_trade_no", payRequest.getOrderSn());
214
+            bizContent.put("total_amount", fenToYuan( payRequest.getTotalFee() ));
215
+            bizContent.put("subject", payRequest.getBody());
216
+            bizContent.put("product_code", "QUICK_MSECURITY_PAY");
217
+
218
+            request.setBizContent(bizContent.toString());
219
+            AlipayTradeAppPayResponse response = alipayClient.sdkExecute(request);
220
+            if (response.isSuccess()) {
221
+                PayResponse payResponse =new PayResponse();
222
+                payResponse.setSuccess(true);
223
+                payResponse.setPrepay_id(response.getTradeNo());
224
+                payResponse.setOrder_sn(response.getOutTradeNo());
225
+                return payResponse;
226
+            } else {
227
+                log.error("调用失败");
228
+                return null;
229
+            }
230
+        }catch (Exception e){
231
+            throw new TradeException("订单创建失败,订单号:"+ payRequest.getOrderSn());
232
+        }
233
+    }
234
+
235
+
236
+
237
+    /**
238
+     * 关闭订单
239
+     * @param orderSn
240
+     * @return
241
+     * @throws TradeException
242
+     */
243
+    @Override
244
+    public Boolean closePay(String orderSn) throws TradeException {
245
+        try {
246
+            AlipayClient alipayClient = getAliHttpClient();
247
+            AlipayTradeCloseRequest request = new AlipayTradeCloseRequest();
248
+            JSONObject bizContent = new JSONObject();
249
+            bizContent.put("trade_no", orderSn);
250
+            request.setBizContent(bizContent.toString());
251
+            AlipayTradeCloseResponse response = alipayClient.execute(request);
252
+            if (response.isSuccess()) {
253
+                log.info("调用成功");
254
+                return true;
255
+            } else {
256
+                log.error("调用失败");
257
+                return false;
258
+            }
259
+        }catch (Exception e){
260
+            throw new TradeException("订单关闭失败,订单号:"+orderSn);
261
+        }
262
+    }
263
+
264
+    /**
265
+     * 退款接口
266
+     * alipay.trade.refund(统一收单交易退款接口)
267
+     * https://opendocs.alipay.com/open/02ekfk
268
+     * @param refundRequest
269
+     * @return
270
+     * @throws TradeException
271
+     */
272
+    @Override
273
+    public Boolean refund(RefundRequest refundRequest) throws TradeException {
274
+        try {
275
+            AlipayClient alipayClient = getAliHttpClient();
276
+            AlipayTradeRefundRequest request = new AlipayTradeRefundRequest();
277
+            request.setNotifyUrl(getRefundNotifyUrl()); //退款回调
278
+            JSONObject bizContent = new JSONObject();
279
+            bizContent.put("refund_amount", fenToYuan(refundRequest.getRefundAmount() ));
280
+            bizContent.put("out_trade_no", refundRequest.getOrderSn());
281
+            //退款请求号,做幂等性校验
282
+            if(refundRequest.getRequestNo()!=null){
283
+                bizContent.put("out_request_no", refundRequest.getRequestNo());
284
+            }else{
285
+                bizContent.put("out_request_no", refundRequest.getOrderSn());
286
+            }
287
+            request.setBizContent(bizContent.toString());
288
+            AlipayTradeRefundResponse response = alipayClient.execute(request);
289
+            if (response.isSuccess()) {
290
+                if("Y".equals(response.getFundChange())) {
291
+                    log.info("退款成功{}",refundRequest.getOrderSn());
292
+                    callBackService.successRefund(refundRequest.getOrderSn());
293
+                    return true;
294
+                }else{
295
+                    //退款失败
296
+                    log.error("退款失败{}",refundRequest.getOrderSn());
297
+                    callBackService.failRefund(refundRequest.getOrderSn());
298
+                    return false;
299
+                }
300
+            } else {
301
+                log.error("退款调用失败{}",refundRequest.getOrderSn());
302
+                return false;
303
+            }
304
+        }catch (Exception e){
305
+            e.printStackTrace();
306
+            throw new TradeException("订单退款失败,订单号:"+ refundRequest.getOrderSn());
307
+        }
308
+    }
309
+
310
+    /**
311
+     * 查询单笔交易订单
312
+     * 参考官网: https://opendocs.alipay.com/open/02e7gm?ref=api#请求示例
313
+     *
314
+     * @param orderSn
315
+     * @return
316
+     * @throws TradeException
317
+     */
318
+    @Override
319
+    public QueryResponse queryTradingOrderNo(String orderSn) throws TradeException {
320
+        AlipayClient alipayClient = getAliHttpClient();
321
+        try {
322
+            AlipayTradeQueryRequest request = new AlipayTradeQueryRequest();
323
+            JSONObject bizContent = new JSONObject();
324
+            bizContent.put("out_trade_no", orderSn);
325
+            request.setBizContent(bizContent.toString());
326
+            AlipayTradeQueryResponse response = alipayClient.execute(request);
327
+            QueryResponse queryResponse=new QueryResponse();
328
+            queryResponse.setOrder_sn(orderSn );
329
+            if (response.isSuccess()) {
330
+                queryResponse.setTransaction_id( response.getTradeNo() );
331
+                queryResponse.setTrade_state(AlipayConstant.TRADE_STATE.get(  response.getTradeStatus()) );//交易状态
332
+
333
+                //int total = BigDecimal.valueOf(Double.valueOf(response.getTotalAmount())).multiply(new BigDecimal(100)).intValue();
334
+                queryResponse.setTotal(  yuanToFen(response.getTotalAmount())   ); //总金额
335
+
336
+                //int buyer_pay_amount = BigDecimal.valueOf(Double.valueOf(response.getBuyerPayAmount())).multiply(new BigDecimal(100)).intValue();
337
+                //queryResponse.setPayer_total( yuanToFen(response.getBuyerPayAmount()) );//支付金额
338
+
339
+                queryResponse.setOpenid( response.getBuyerUserId());
340
+                Map map = JSON.parseObject(response.getBody(), Map.class  ) ;
341
+                queryResponse.setExpand(map);//全部数据
342
+                return queryResponse;
343
+            } else {
344
+                queryResponse.setTrade_state("NOTPAY");
345
+                return queryResponse;
346
+            }
347
+        }catch (Exception e){
348
+            e.printStackTrace();
349
+            //throw new TradeException("订单查询失败,订单号:"+orderSn);
350
+            return null;
351
+        }
352
+    }
353
+
354
+    /**
355
+     * 查询退款订单
356
+     * @param orderSn
357
+     * @return
358
+     * @throws TradeException
359
+     */
360
+    @Override
361
+    public QueryRefundResponse queryRefundTrading(String orderSn) throws TradeException {
362
+        try {
363
+            AlipayClient alipayClient = getAliHttpClient();
364
+            AlipayTradeFastpayRefundQueryRequest request = new AlipayTradeFastpayRefundQueryRequest();
365
+            JSONObject bizContent = new JSONObject();
366
+            bizContent.put("out_request_no", orderSn);
367
+            request.setBizContent(bizContent.toString());
368
+            AlipayTradeFastpayRefundQueryResponse response = alipayClient.execute(request);
369
+            if (response.isSuccess()) {
370
+                log.info("调用成功");
371
+                Map map = JSON.parseObject(response.getBody(), Map.class   );
372
+
373
+                QueryRefundResponse queryRefundResponse=new QueryRefundResponse();
374
+
375
+                queryRefundResponse.setOrder_sn( (String) map.get("out_trade_no") );
376
+                queryRefundResponse.setTransaction_id(   (String) map.get("trade_no") );
377
+                queryRefundResponse.setTotal( (Integer) map.get("total_amount")  ); //总金额
378
+                //queryRefundResponse.setPayer_total( (Integer) map.get("total_amount")  );//支付金额
379
+                queryRefundResponse.setRefund((Integer) map.get("refund_amount")   ); //退款金额
380
+                queryRefundResponse.setRefund_id((String) map.get("trade_no")  );  //退款单号
381
+                queryRefundResponse.setOut_refund_no( (String) map.get("out_request_no")    );//退款订单号
382
+                //queryRefundResponse.setChannel(  (String) map.get("channel")  );  //通道
383
+                //queryRefundResponse.setUser_received_account(  (String) map.get("user_received_account") ); //账号
384
+                queryRefundResponse.setStatus(   (String) map.get("refund_status")  ); //状态
385
+                queryRefundResponse.setSuccess_time(  (String) map.get("gmt_refund_pay")  );
386
+                //queryRefundResponse.setCreate_time(  (String) map.get("gmt_refund_pay") );
387
+
388
+                queryRefundResponse.setExpand(map);
389
+                return queryRefundResponse;
390
+
391
+            } else {
392
+                log.error("调用失败");
393
+                return null;
394
+            }
395
+        }catch (Exception e){
396
+            e.printStackTrace();
397
+            //throw new TradeException("退款订单查询失败,订单号:"+ orderSn);
398
+            return null;
399
+        }
400
+    }
401
+
402
+    @Override
403
+    public String getOpenid(String code) {
404
+        AlipayClient alipayClient = new DefaultAlipayClient("https://openapi.alipay.com/gateway.do",
405
+                alipayConfig.getAppId(), alipayConfig.getPrivateKey(), "json", "utf-8", alipayConfig.getPublicKey(), alipayConfig.getSignType());
406
+        AlipaySystemOauthTokenRequest request = new AlipaySystemOauthTokenRequest();
407
+        request.setCode(code);
408
+        request.setGrantType("authorization_code");
409
+        try {
410
+            AlipaySystemOauthTokenResponse oauthTokenResponse = alipayClient.execute(request);
411
+            return  oauthTokenResponse.getUserId();
412
+        } catch (AlipayApiException e) {
413
+            //处理异常
414
+            e.printStackTrace();
415
+            return "";
416
+        }
417
+    }
418
+
419
+
420
+    /**
421
+     * 获取支付宝连接
422
+     * 参考官网:https://opendocs.alipay.com/open/01csp3?ref=api#公钥模式加签
423
+     * 公钥模式加签
424
+     * @return
425
+     */
426
+    private AlipayClient getAliHttpClient(){
427
+        try {
428
+            com.alipay.api.AlipayConfig alipayConfig = new com.alipay.api.AlipayConfig();
429
+            alipayConfig.setServerUrl(AlipayConstant.domain);
430
+            alipayConfig.setAppId(this.alipayConfig.getAppId());
431
+            alipayConfig.setPrivateKey(this.alipayConfig.getPrivateKey());
432
+            alipayConfig.setFormat("json");
433
+            alipayConfig.setCharset("utf-8");
434
+            alipayConfig.setAlipayPublicKey(this.alipayConfig.getPublicKey());
435
+            alipayConfig.setSignType(this.alipayConfig.getSignType());
436
+            //构造client
437
+            AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig);
438
+            return alipayClient;
439
+        }catch (Exception e){
440
+            e.printStackTrace();
441
+            throw new TradeException("支付宝支付--初始化,校验系统参数失败");
442
+        }
443
+
444
+    }
445
+
446
+    /**
447
+     * 分转换为元
448
+     * @param fen
449
+     * @return
450
+     */
451
+    private String fenToYuan(int fen){
452
+        //转换为元
453
+        return BigDecimal.valueOf(fen).divide(new BigDecimal(100)  ).toString();
454
+    }
455
+
456
+    private int yuanToFen(String yuan){
457
+        return BigDecimal.valueOf(Double.valueOf(yuan)).multiply(new BigDecimal(100)).intValue();
458
+    }
459
+
460
+
461
+}

+ 124
- 0
pay/src/main/java/com/ruoyi/ali/AlipayElegentValid.java Visa fil

@@ -0,0 +1,124 @@
1
+package com.ruoyi.ali;
2
+
3
+
4
+import com.alipay.api.internal.util.AlipaySignature;
5
+import com.ruoyi.annotation.TradePlatform;
6
+import com.ruoyi.constant.Platform;
7
+import com.ruoyi.core.ElegentValid;
8
+import com.ruoyi.dto.ValidResponse;
9
+import com.ruoyi.exceptions.TradeException;
10
+import lombok.extern.slf4j.Slf4j;
11
+import org.springframework.beans.factory.annotation.Autowired;
12
+import org.springframework.http.HttpEntity;
13
+import org.springframework.stereotype.Service;
14
+
15
+import javax.servlet.http.HttpServletRequest;
16
+import java.util.HashMap;
17
+import java.util.Iterator;
18
+import java.util.Map;
19
+
20
+@Service
21
+@TradePlatform(Platform.ALI)
22
+@Slf4j
23
+public class AlipayElegentValid implements ElegentValid {
24
+
25
+
26
+    @Autowired
27
+    private AlipayConfig alipayConfig;
28
+
29
+
30
+    /**
31
+     * 订单回调结果通知验签
32
+     * 参考代码: https://opendocs.alipay.com/open/194/103296?ref=api  异步返回结果的验签
33
+     * @param httpEntity
34
+     * @param httpRequest
35
+     * @return
36
+     */
37
+    @Override
38
+    public ValidResponse validPay(HttpEntity<String> httpEntity, HttpServletRequest httpRequest) throws TradeException {
39
+        ValidResponse validResponse=new ValidResponse();
40
+        try {
41
+            Map<String, String> params = getParams(httpRequest);
42
+            //获取支付宝POST过来反馈信息,将异步通知中收到的待验证所有参数都存放到map中
43
+            //String body = httpEntity.getBody();
44
+            //调用SDK验证签名
45
+            //公钥验签示例代码
46
+            boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayConfig.getPublicKey(), "utf-8", alipayConfig.getSignType());
47
+            if (signVerified) {
48
+                validResponse.setValid(true);
49
+                validResponse.setOrderSn((String) params.get("out_trade_no"));
50
+                return validResponse;
51
+            } else {
52
+                validResponse.setValid(false);
53
+                validResponse.setOrderSn( (String) params.get("out_trade_no") );
54
+                return validResponse;
55
+            }
56
+        }catch (Exception e){
57
+            e.printStackTrace();
58
+            throw new TradeException("验签异常");
59
+        }
60
+    }
61
+
62
+    /**
63
+     * 退款结果通知验签
64
+     * 参考官网 https://opendocs.alipay.com/support/01ravh
65
+     * @param httpEntity
66
+     * @param httpRequest
67
+     * @return
68
+     * @throws TradeException
69
+     */
70
+    @Override
71
+    public ValidResponse validRefund(HttpEntity<String> httpEntity, HttpServletRequest httpRequest) throws TradeException {
72
+        ValidResponse validResponse=new ValidResponse();
73
+
74
+        try {
75
+            //获取支付宝POST过来反馈信息,将异步通知中收到的待验证所有参数都存放到map中
76
+            Map<String, String> params = getParams(httpRequest);
77
+            //调用SDK验证签名
78
+            //公钥验签示例代码
79
+            boolean signVerified = AlipaySignature.rsaCheckV1(params, alipayConfig.getPublicKey(), "utf-8", alipayConfig.getSignType());
80
+            if (signVerified) {
81
+                validResponse.setValid(true);
82
+                validResponse.setOrderSn( (String) params.get("out_trade_no") );
83
+                return validResponse;
84
+            } else {
85
+                validResponse.setValid(false);
86
+                validResponse.setOrderSn( (String) params.get("out_trade_no") );
87
+                return validResponse;
88
+            }
89
+        }catch (Exception e){
90
+            e.printStackTrace();
91
+            validResponse.setValid(false);
92
+            return validResponse;
93
+        }
94
+    }
95
+
96
+    private Map<String,String> getParams(HttpServletRequest httpServletRequest){
97
+        Map<String,String> params = new HashMap< String , String >();
98
+        Map requestParams = httpServletRequest.getParameterMap();
99
+
100
+        for(Iterator iter = requestParams.keySet().iterator(); iter.hasNext();){
101
+            String name = (String)iter.next();
102
+            String[] values = (String [])requestParams.get(name);
103
+            String valueStr = "";
104
+            for(int i = 0;i < values.length;i ++ ){
105
+                valueStr =  (i==values.length-1)?valueStr + values [i]:valueStr + values[i] + ",";
106
+            }
107
+            //乱码解决,这段代码在出现乱码时使用。
108
+            //valueStr = new String(valueStr.getBytes("ISO-8859-1"), "utf-8");
109
+            params.put (name,valueStr);
110
+        }
111
+        log.info("params:{}",params);
112
+        return params;
113
+    }
114
+
115
+    @Override
116
+    public String successResult() {
117
+        return AlipayConstant.SUCCESS;
118
+    }
119
+
120
+    @Override
121
+    public String failResult() {
122
+        return AlipayConstant.FAIL;
123
+    }
124
+}

+ 20
- 0
pay/src/main/java/com/ruoyi/annotation/TradePlatform.java Visa fil

@@ -0,0 +1,20 @@
1
+package com.ruoyi.annotation;
2
+
3
+import java.lang.annotation.*;
4
+
5
+/**
6
+ * create by: wgl
7
+ * desc: 自定义支付平台注解,支付平台 微信:wx 支付宝:zfb
8
+ */
9
+@Documented
10
+@Retention(RetentionPolicy.RUNTIME)
11
+@Target({ElementType.TYPE})
12
+public @interface TradePlatform {
13
+
14
+    /**
15
+     * 平台的id
16
+     * Platform.WX
17
+     * @return
18
+     */
19
+    String value();
20
+}

+ 23
- 0
pay/src/main/java/com/ruoyi/config/CallbackConfig.java Visa fil

@@ -0,0 +1,23 @@
1
+package com.ruoyi.config;
2
+import lombok.Data;
3
+import org.springframework.beans.factory.annotation.Value;
4
+import org.springframework.boot.context.properties.ConfigurationProperties;
5
+import org.springframework.stereotype.Component;
6
+
7
+/**
8
+ * 微信权限对接类
9
+ */
10
+@Component
11
+@ConfigurationProperties("elegent.pay.callback")
12
+@Data
13
+public class CallbackConfig {
14
+
15
+    private String domain; //回调域名
16
+
17
+    @Value("${elegent.pay.callback.watch:false}")
18
+    private boolean watch; //是否开启监听
19
+
20
+    @Value("${elegent.pay.callback.cycle:10}")
21
+    private int cycle;//检查周期
22
+
23
+}

+ 11
- 0
pay/src/main/java/com/ruoyi/constant/PayConstant.java Visa fil

@@ -0,0 +1,11 @@
1
+package com.ruoyi.constant;
2
+
3
+public class PayConstant {
4
+
5
+    public final static String CALLBACK_PATH = "/payCallBack";
6
+
7
+    public final static String NOTIFY = "/notify";
8
+
9
+    public final static String  REFUND_NOTIFY = "/refund_notify";
10
+
11
+}

+ 16
- 0
pay/src/main/java/com/ruoyi/constant/Platform.java Visa fil

@@ -0,0 +1,16 @@
1
+package com.ruoyi.constant;
2
+
3
+/**
4
+ * Platform
5
+ * 支付方式
6
+*/
7
+public class Platform {
8
+    /**
9
+     * 微信
10
+     */
11
+    public final static String WX = "wxpay";
12
+    /**
13
+     * 支付宝
14
+     */
15
+    public final static String ALI = "alipay";
16
+}

+ 33
- 0
pay/src/main/java/com/ruoyi/constant/TradeType.java Visa fil

@@ -0,0 +1,33 @@
1
+package com.ruoyi.constant;
2
+
3
+import lombok.Data;
4
+
5
+/**
6
+ * 交易类型
7
+ */
8
+@Data
9
+public class TradeType {
10
+
11
+    /**
12
+     * native(扫码)
13
+     */
14
+    public final static String NATIVE = "native";
15
+
16
+    /**
17
+     * jsapi(小程序)
18
+     */
19
+    public final static String JSAPI = "jsapi";
20
+
21
+
22
+    /**
23
+     * app
24
+     */
25
+    public final static String APP = "app";
26
+
27
+
28
+    /**
29
+     * h5
30
+     */
31
+    public final static String H5 = "h5";
32
+
33
+}

+ 35
- 0
pay/src/main/java/com/ruoyi/core/CallBackServiceImpl.java Visa fil

@@ -0,0 +1,35 @@
1
+package com.ruoyi.core;
2
+
3
+import com.ruoyi.CallBackService;
4
+import lombok.extern.slf4j.Slf4j;
5
+
6
+/**
7
+ * 回调类
8
+ */
9
+@Slf4j
10
+public class CallBackServiceImpl implements CallBackService {
11
+
12
+
13
+    @Override
14
+    public void successPay(String orderSn) {
15
+        log.info("支付成功回调!"+orderSn);
16
+    }
17
+
18
+    @Override
19
+    public void failPay(String orderSn) {
20
+        log.info("支付失败回调!"+orderSn);
21
+    }
22
+
23
+
24
+    @Override
25
+    public void successRefund(String orderSn) {
26
+        log.info("退款成功回调!"+orderSn);
27
+    }
28
+
29
+    @Override
30
+    public void failRefund(String orderSn) {
31
+        log.info("退款失败回调!"+orderSn);
32
+    }
33
+
34
+
35
+}

+ 90
- 0
pay/src/main/java/com/ruoyi/core/CallbackController.java Visa fil

@@ -0,0 +1,90 @@
1
+package com.ruoyi.core;
2
+
3
+import com.ruoyi.CallBackService;
4
+import com.ruoyi.constant.PayConstant;
5
+import com.ruoyi.dto.ValidResponse;
6
+import lombok.extern.slf4j.Slf4j;
7
+import org.springframework.beans.factory.annotation.Autowired;
8
+import org.springframework.http.HttpEntity;
9
+import org.springframework.web.bind.annotation.PathVariable;
10
+import org.springframework.web.bind.annotation.RequestMapping;
11
+import org.springframework.web.bind.annotation.RestController;
12
+
13
+import javax.servlet.http.HttpServletRequest;
14
+import javax.servlet.http.HttpServletResponse;
15
+
16
+/**
17
+ * CallbackController
18
+ * @description 系统默认提供的微信回调的Controller
19
+*/
20
+@RestController
21
+@Slf4j
22
+@RequestMapping(PayConstant.CALLBACK_PATH)
23
+public class CallbackController {
24
+
25
+    @Autowired
26
+    private CallBackService callBackService;
27
+
28
+    /**
29
+     * 系统提供的默认的微信回调的接口(支付回调)
30
+     * @param httpEntity
31
+     * @param response
32
+     * @return
33
+     */
34
+    @RequestMapping( PayConstant.NOTIFY + "/{platform}")
35
+    public String notify(HttpEntity<String> httpEntity, HttpServletRequest request, HttpServletResponse response, @PathVariable("platform") String platform){
36
+
37
+        ElegentValid elegentValid = ElegentLoader.getElegentValid(platform);  //获取验证器
38
+        try {
39
+            ValidResponse validResponse = elegentValid.validPay(httpEntity, request);//验证支付通知
40
+            String orderSn =validResponse.getOrderSn(); //订单号
41
+            if(validResponse.isValid()){  //返回码成功
42
+                callBackService.successPay(orderSn);
43
+                //返回成功消费
44
+                return elegentValid.successResult();
45
+            }else{
46
+                callBackService.failPay(orderSn);
47
+                return elegentValid.failResult();
48
+            }
49
+        }catch (Exception e){
50
+            log.error("支付回调处理失败",e);
51
+            //微信返回的状态非正常
52
+            return elegentValid.failResult();
53
+        }
54
+    }
55
+
56
+
57
+    /**
58
+     * 系统提供的默认的微信回调的接口(退款通知)
59
+     *
60
+     * @param httpEntity
61
+     * @param response
62
+     * @return
63
+     */
64
+    @RequestMapping( PayConstant.REFUND_NOTIFY + "/{platform}")
65
+    public String refundNotify(HttpEntity<String> httpEntity, HttpServletRequest request, HttpServletResponse response,@PathVariable("platform") String platform) {
66
+
67
+        ElegentValid elegentValid = ElegentLoader.getElegentValid(platform);  //获取验证器
68
+
69
+        try {
70
+            ValidResponse validResponse = elegentValid.validRefund(httpEntity, request);
71
+            String orderSn = validResponse.getOrderSn();
72
+            //订单号
73
+            if (validResponse.isValid()) {  //返回码成功
74
+                callBackService.successRefund(orderSn);
75
+                //返回成功消费
76
+                return elegentValid.successResult();
77
+            } else {
78
+                callBackService.failRefund(orderSn);
79
+                return elegentValid.failResult();
80
+            }
81
+        } catch (Exception e) {
82
+            log.error("退款回调处理失败", e);
83
+            //微信返回的状态非正常
84
+            return elegentValid.failResult();
85
+        }
86
+
87
+    }
88
+
89
+
90
+}

+ 73
- 0
pay/src/main/java/com/ruoyi/core/CallbackWatch.java Visa fil

@@ -0,0 +1,73 @@
1
+package com.ruoyi.core;
2
+
3
+
4
+
5
+import com.ruoyi.CallBackService;
6
+import com.ruoyi.ElegentPay;
7
+import com.ruoyi.config.CallbackConfig;
8
+import com.ruoyi.dto.QueryRefundResponse;
9
+import com.ruoyi.dto.QueryResponse;
10
+import com.ruoyi.dto.WatchDTO;
11
+import lombok.extern.slf4j.Slf4j;
12
+import org.springframework.beans.factory.annotation.Autowired;
13
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
14
+import org.springframework.stereotype.Component;
15
+import javax.annotation.PostConstruct;
16
+import java.util.Timer;
17
+import java.util.TimerTask;
18
+
19
+@Component
20
+@ConditionalOnProperty(prefix = "elegent.pay.callback",name = "watch",havingValue = "true")
21
+@Slf4j
22
+public class CallbackWatch {
23
+
24
+    @Autowired
25
+    private CallBackService callBackService;
26
+
27
+    @Autowired
28
+    private CallbackConfig callbackConfig;
29
+
30
+    @Autowired
31
+    private ElegentPay elegentPay;
32
+
33
+    @PostConstruct
34
+    public void queryWatch(){
35
+        if(callbackConfig.getCycle()<=0){
36
+            return;
37
+        }
38
+        //log.info("开启支付结果定期巡检");
39
+        Timer timer = new Timer();
40
+        // 2、创建 TimerTask 任务线程
41
+        TimerTask task=new TimerTask() {
42
+            @Override
43
+            public void run() {
44
+                try{
45
+                    //查询支付状态
46
+                    //log.info("支付状态定期巡检,{}",WatchList.payList);
47
+                    for( WatchDTO watchDTO: WatchList.payList ){
48
+                        //查询订单是否支付成功
49
+                        QueryResponse queryResponse = elegentPay.queryTradingOrderNo(watchDTO.getOrderSn(),watchDTO.getPlatform());
50
+                        if("SUCCESS".equals(queryResponse.getTrade_state())){
51
+                            callBackService.successPay(queryResponse.getOrder_sn());
52
+                            WatchList.payList.remove(watchDTO );
53
+                        }
54
+                    }
55
+                    //log.info("退款状态定期巡检,{}",WatchList.refundList);
56
+                    //查询退款状态
57
+                    for( WatchDTO watchDTO: WatchList.refundList ){
58
+                        //查询退款中订单
59
+                        QueryRefundResponse queryResponse = elegentPay.queryRefundTrading( watchDTO.getOrderSn(),watchDTO.getPlatform());
60
+                        if("SUCCESS".equals(queryResponse.getStatus())){
61
+                            callBackService.successRefund(queryResponse.getOrder_sn());
62
+                            WatchList.refundList.remove(watchDTO);
63
+                        }
64
+                    }
65
+                }catch (Exception ex){
66
+                }
67
+            }
68
+        };
69
+        // 4、启动定时任务
70
+        timer.schedule(task, callbackConfig.getCycle()*1000, callbackConfig.getCycle()*1000);
71
+    }
72
+
73
+}

+ 27
- 0
pay/src/main/java/com/ruoyi/core/ElegentConfig.java Visa fil

@@ -0,0 +1,27 @@
1
+package com.ruoyi.core;
2
+
3
+
4
+import com.ruoyi.CallBackService;
5
+import com.ruoyi.key.KeyManager;
6
+import com.ruoyi.key.impl.DefaultKeyManager;
7
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
8
+import org.springframework.context.annotation.Bean;
9
+import org.springframework.context.annotation.Configuration;
10
+
11
+@Configuration
12
+public class ElegentConfig {
13
+
14
+    @Bean
15
+    @ConditionalOnMissingBean
16
+    public CallBackService callBackService(){
17
+        return new CallBackServiceImpl();
18
+    }
19
+
20
+
21
+    @Bean
22
+    @ConditionalOnMissingBean
23
+    public KeyManager keyManager(){
24
+        return new DefaultKeyManager();
25
+    }
26
+
27
+}

+ 82
- 0
pay/src/main/java/com/ruoyi/core/ElegentLoader.java Visa fil

@@ -0,0 +1,82 @@
1
+package com.ruoyi.core;
2
+
3
+import com.ruoyi.annotation.TradePlatform;
4
+import com.ruoyi.exceptions.TradeException;
5
+import lombok.extern.slf4j.Slf4j;
6
+import org.springframework.beans.BeansException;
7
+import org.springframework.context.ApplicationContext;
8
+import org.springframework.context.ApplicationContextAware;
9
+import org.springframework.stereotype.Component;
10
+
11
+import java.util.Collection;
12
+import java.util.HashMap;
13
+import java.util.Map;
14
+
15
+/**
16
+ * PlatformLoader
17
+ * @description 第三方支付平台类加载器
18
+ * 服务启动的时候自动加载第三方支付组件
19
+*/
20
+@Component
21
+@Slf4j
22
+public class ElegentLoader implements ApplicationContextAware {
23
+
24
+    private static Map<String, ElegentTrade> elegentTradeMap = new HashMap<>();
25
+
26
+    private static Map<String, ElegentValid> elegentValidMap = new HashMap<>();
27
+
28
+    @Override
29
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
30
+
31
+        //加载所有的交易实现类
32
+        Collection<ElegentTrade> elegentTrades = applicationContext.getBeansOfType(ElegentTrade.class).values();
33
+        elegentTrades.stream().forEach(e->{
34
+            //通过反射拿到类上的平台注解
35
+            TradePlatform annotation = e.getClass().getAnnotation(TradePlatform.class);
36
+            if (annotation != null) {
37
+                elegentTradeMap.put(annotation.value(), e);
38
+            }
39
+        });
40
+
41
+        //加载所有的验证实现类
42
+        Collection<ElegentValid> elegentValids = applicationContext.getBeansOfType(ElegentValid.class).values();
43
+        elegentValids.stream().forEach(e->{
44
+            //通过反射拿到类上的平台注解
45
+            TradePlatform annotation = e.getClass().getAnnotation(TradePlatform.class);
46
+            if (annotation != null) {
47
+                elegentValidMap.put(annotation.value(), e);
48
+            }
49
+        });
50
+
51
+    }
52
+
53
+    /**
54
+     * 根据平台id获取具体的第三方平台
55
+     * @param platform 平台id
56
+     * @return
57
+     */
58
+    public static ElegentTrade getElegentTrade(String platform){
59
+        ElegentTrade elegentPayTemplate = elegentTradeMap.get(platform);
60
+        if(elegentPayTemplate!=null){
61
+            return elegentPayTemplate;
62
+        }else{
63
+            throw new TradeException("未找到适配的交易类型,交易平台id:"+platform);
64
+        }
65
+    }
66
+
67
+
68
+    /**
69
+     * 根据平台id获取具体的验证类
70
+     * @param platform 平台id
71
+     * @return
72
+     */
73
+    public static ElegentValid getElegentValid(String platform){
74
+        ElegentValid elegentValidTemplate = elegentValidMap.get(platform);
75
+        if(elegentValidTemplate!=null){
76
+            return elegentValidTemplate;
77
+        }else{
78
+            throw new TradeException("未找到适配的交易类型,交易平台id:"+platform);
79
+        }
80
+    }
81
+
82
+}

+ 136
- 0
pay/src/main/java/com/ruoyi/core/ElegentPayImpl.java Visa fil

@@ -0,0 +1,136 @@
1
+package com.ruoyi.core;
2
+import com.ruoyi.ElegentPay;
3
+import com.ruoyi.config.CallbackConfig;
4
+import com.ruoyi.dto.*;
5
+import com.ruoyi.exceptions.TradeException;
6
+import org.springframework.beans.factory.annotation.Autowired;
7
+import org.springframework.stereotype.Component;
8
+
9
+/**
10
+ * ElegentPayTemplate
11
+ * @description 统一模板 在模板层进行第三方平台的选择
12
+*/
13
+@Component
14
+public class ElegentPayImpl implements ElegentPay {
15
+
16
+    @Autowired
17
+    private CallbackConfig callbackConfig;
18
+
19
+    /**
20
+     * 统一下单接口
21
+     * @param payRequest
22
+     * @return
23
+     */
24
+    @Override
25
+    public PayResponse requestPay(PayRequest payRequest, String tradeType, String platform) throws TradeException {
26
+        //获取交易策略
27
+        PayResponse payResponse = getPlatFormService(platform).requestPay(payRequest, tradeType);
28
+
29
+        //加入监听列表
30
+        if(callbackConfig.isWatch()){
31
+            WatchDTO watchDTO=new WatchDTO();
32
+            watchDTO.setOrderSn(payRequest.getOrderSn());
33
+            watchDTO.setPlatform(platform);
34
+            WatchList.payList.add( watchDTO );
35
+        }
36
+        return payResponse;
37
+    }
38
+
39
+
40
+    /**
41
+     * 关闭订单
42
+     * @param orderSn
43
+     * @param platform
44
+     * @return
45
+     * @throws Exception
46
+     */
47
+    @Override
48
+    public Boolean closePay(String orderSn, String platform) throws TradeException {
49
+        //调用对应第三方的创建订单接口
50
+        Boolean aBoolean = getPlatFormService(platform).closePay(orderSn);
51
+        //加入监听列表
52
+        if(callbackConfig.isWatch()){
53
+            WatchDTO watchDTO=new WatchDTO();
54
+            watchDTO.setOrderSn(orderSn);
55
+            watchDTO.setPlatform(platform);
56
+            WatchList.payList.remove( watchDTO );
57
+        }
58
+
59
+        return aBoolean;
60
+    }
61
+
62
+    /**
63
+     * 退款方法
64
+     * @param refundRequest
65
+     * @return
66
+     * @throws Exception
67
+     */
68
+    @Override
69
+    public Boolean refund(RefundRequest refundRequest, String platform) throws TradeException {
70
+        Boolean refund = getPlatFormService(platform).refund(refundRequest);
71
+        //加入监听列表
72
+        if(callbackConfig.isWatch()  && refund){
73
+            WatchDTO watchDTO=new WatchDTO();
74
+            watchDTO.setOrderSn(refundRequest.getRequestNo());
75
+            watchDTO.setPlatform(platform);
76
+            WatchList.refundList.add( watchDTO );
77
+        }
78
+        return refund;
79
+    }
80
+
81
+
82
+    /**
83
+     * 手动查询订单的方法
84
+     * 商户订单号查询 根据订单号查询订单
85
+     * 该接口基于生成的订单号来进行订单的查询
86
+     * 可以基于查询的结果判断用户订单是否支付成功
87
+     * @param orderSn
88
+     * @param Platform
89
+     * @return
90
+     * @throws Exception
91
+     */
92
+    @Override
93
+    public QueryResponse queryTradingOrderNo(String  orderSn , String Platform) throws TradeException {
94
+        //调用具体第三方的退款接口
95
+        return getPlatFormService(Platform).queryTradingOrderNo(orderSn);
96
+    }
97
+
98
+
99
+    /**
100
+     * 查询退款单号
101
+     *
102
+     * @return
103
+     * @throws Exception
104
+     */
105
+    @Override
106
+    public QueryRefundResponse queryRefundTrading(String orderSn , String platform) throws TradeException {
107
+        //获取请求参数
108
+        return getPlatFormService(platform).queryRefundTrading(orderSn);
109
+    }
110
+
111
+    /**
112
+     * 获得openId
113
+     * @param code
114
+     * @param platform
115
+     * @return
116
+     */
117
+    @Override
118
+    public String getOpenid(String code, String platform) {
119
+        return getPlatFormService(platform).getOpenid(code);
120
+    }
121
+
122
+/**
123
+ * ====================================提供的模板类需要使用的方法=====================================
124
+ */
125
+
126
+
127
+    /**
128
+     * 跟进具体内容获取实现类的方法
129
+     * @param platForm
130
+     * @return
131
+     */
132
+    private ElegentTrade getPlatFormService(String platForm) {
133
+        return ElegentLoader.getElegentTrade(platForm);
134
+    }
135
+
136
+}

+ 54
- 0
pay/src/main/java/com/ruoyi/core/ElegentTrade.java Visa fil

@@ -0,0 +1,54 @@
1
+package com.ruoyi.core;
2
+
3
+
4
+import com.ruoyi.dto.*;
5
+import com.ruoyi.exceptions.TradeException;
6
+
7
+public interface ElegentTrade {
8
+
9
+
10
+    /**
11
+     * 单独的创建本地支付订单接口
12
+     * 该接口基于设计模式实现,对接了微信V3版支付代码和支付宝支付sdk
13
+     * 业务编写人员仅仅只需要准备好请求参数调用该方法即可完成下单请求
14
+     * @return 交易数据对象 包含有native支付的二维码+jsApi支付的支付组件
15
+     */
16
+    PayResponse requestPay(PayRequest payRequest, String tradeType) throws TradeException;
17
+
18
+    /**
19
+     * 关闭订单
20
+     * 该结构基于设计模式实现,对接了微信V3版支付代码和支付宝支付sdk
21
+     * 业务编写人员在处理超时订单的时候需要 通知微信 由于是超时订单需要进行远程关闭
22
+     */
23
+    Boolean closePay(String orderSn) throws TradeException;
24
+
25
+    /**
26
+     * 退款s申请
27
+     * 该接口基于设计模式实现,对接了微信V3版支付代码和支付宝支付sdk
28
+     * 业务编写人员在处理业务执行失败时需要进行退款
29
+     */
30
+    Boolean refund(RefundRequest refundRequest) throws TradeException;
31
+
32
+    /**
33
+     * 商户订单号查询 根据订单号查询订单
34
+     * 该接口基于生成的订单号来进行订单的查询
35
+     * 可以基于查询的结果判断用户订单是否支付成功
36
+     */
37
+    QueryResponse queryTradingOrderNo(String orderSn) throws TradeException;
38
+
39
+
40
+    /**
41
+     * 查询单笔退款API
42
+     *
43
+     */
44
+    QueryRefundResponse queryRefundTrading(String orderSn) throws TradeException;
45
+
46
+
47
+    /**
48
+     * 获得openId
49
+     * @param code
50
+     * @return
51
+     */
52
+    String getOpenid(String code);
53
+
54
+}

+ 48
- 0
pay/src/main/java/com/ruoyi/core/ElegentValid.java Visa fil

@@ -0,0 +1,48 @@
1
+package com.ruoyi.core;
2
+
3
+
4
+import com.ruoyi.dto.ValidResponse;
5
+import com.ruoyi.exceptions.TradeException;
6
+import org.springframework.http.HttpEntity;
7
+
8
+import javax.servlet.http.HttpServletRequest;
9
+
10
+/**
11
+ * 验证器接口
12
+ *
13
+ */
14
+public interface ElegentValid {
15
+
16
+
17
+    /**
18
+     * 支付结果通知校验
19
+     * 该接口基于设计模式实现,对接了微信V3版支付代码和支付宝支付sdk
20
+     * 业务编写人员在接收到微信或支付宝回调的时候可以使用该方法验证回调是否成功,是否是伪回调
21
+     * @return 交易数据对象
22
+     */
23
+    ValidResponse validPay(HttpEntity<String> httpEntity, HttpServletRequest request) throws TradeException;
24
+
25
+
26
+
27
+    /**
28
+     * 退款结果通知校验
29
+     * 该接口基于设计模式实现,对接了微信V3版支付代码和支付宝支付sdk
30
+     * 业务编写人员在接收到微信或支付宝回调的时候可以使用该方法验证回调是否成功,是否是伪回调
31
+     */
32
+    ValidResponse validRefund(HttpEntity<String> httpEntity, HttpServletRequest request) throws TradeException;
33
+
34
+
35
+    /**
36
+     * 成功返回结构
37
+     * @return
38
+     */
39
+    String successResult();
40
+
41
+
42
+    /**
43
+     * 失败返回内容
44
+     * @return
45
+     */
46
+    String failResult();
47
+
48
+}

+ 20
- 0
pay/src/main/java/com/ruoyi/core/WatchList.java Visa fil

@@ -0,0 +1,20 @@
1
+package com.ruoyi.core;
2
+
3
+
4
+
5
+import com.ruoyi.dto.WatchDTO;
6
+
7
+import java.util.concurrent.CopyOnWriteArraySet;
8
+
9
+/**
10
+ * 监听列表
11
+ */
12
+public class WatchList {
13
+
14
+
15
+
16
+    public static CopyOnWriteArraySet<WatchDTO> payList=new CopyOnWriteArraySet<>(); //支付中列表
17
+
18
+    public static CopyOnWriteArraySet<WatchDTO> refundList=new CopyOnWriteArraySet<>();  //退款中列表
19
+
20
+}

+ 20
- 0
pay/src/main/java/com/ruoyi/dto/PayRequest.java Visa fil

@@ -0,0 +1,20 @@
1
+package com.ruoyi.dto;
2
+
3
+
4
+import lombok.Data;
5
+
6
+/**
7
+ * PayRequest
8
+ * 支付请求外观类
9
+*/
10
+@Data
11
+public class PayRequest {
12
+
13
+    private String body;//商品描述
14
+
15
+    private String orderSn; //订单号
16
+
17
+    private int totalFee; //订单金额
18
+
19
+    private String openid;//openId
20
+}

+ 29
- 0
pay/src/main/java/com/ruoyi/dto/PayResponse.java Visa fil

@@ -0,0 +1,29 @@
1
+package com.ruoyi.dto;
2
+
3
+import lombok.Data;
4
+
5
+import java.util.Map;
6
+
7
+/**
8
+ * 结果统一封装类
9
+ */
10
+@Data
11
+public class PayResponse {
12
+
13
+    private boolean success;  //是否成功
14
+
15
+    private String message;//信息
16
+
17
+    private String order_sn;//订单编号
18
+
19
+    private Map<String,String>  expand;//扩展属性
20
+
21
+    private String code_url;//二维码连接(native返回)
22
+
23
+    private String prepay_id;//预支付Id(小程序返回)
24
+
25
+    private String h5_url;//支付跳转链接
26
+
27
+    private Map<String,String>  jsapiData;//小程序返回
28
+
29
+}

+ 24
- 0
pay/src/main/java/com/ruoyi/dto/QueryRefundResponse.java Visa fil

@@ -0,0 +1,24 @@
1
+package com.ruoyi.dto;
2
+
3
+import lombok.Data;
4
+
5
+@Data
6
+public class QueryRefundResponse extends QueryResponse{
7
+
8
+
9
+    private String refund_id;  //微信支付退款单号
10
+
11
+    private String out_refund_no;//商户退款单号
12
+
13
+    private String channel;//退款渠道
14
+
15
+    private String user_received_account;//退款账号
16
+
17
+    private String success_time;//退款成功时间
18
+
19
+    private String status;//退款状态
20
+
21
+    private int refund;//退款金额
22
+
23
+
24
+}

+ 25
- 0
pay/src/main/java/com/ruoyi/dto/QueryResponse.java Visa fil

@@ -0,0 +1,25 @@
1
+package com.ruoyi.dto;
2
+
3
+import lombok.Data;
4
+
5
+import java.util.Map;
6
+
7
+/**
8
+ * 查询响应对象
9
+ */
10
+@Data
11
+public class QueryResponse {
12
+
13
+    private String openid;//用户id
14
+
15
+    private String trade_state;//交易状态
16
+
17
+    private String order_sn;//订单号
18
+
19
+    private String transaction_id;//交易单号
20
+
21
+    private int total;//金额
22
+
23
+    private Map expand;//扩展(全部的返回数据)
24
+
25
+}

+ 16
- 0
pay/src/main/java/com/ruoyi/dto/RefundRequest.java Visa fil

@@ -0,0 +1,16 @@
1
+package com.ruoyi.dto;
2
+
3
+import lombok.Data;
4
+
5
+@Data
6
+public class RefundRequest {
7
+
8
+    private int totalFee; //订单金额
9
+
10
+    private int refundAmount; //退款金额
11
+
12
+    private String orderSn; //订单号
13
+
14
+    private String requestNo; //退款请求号,做退款幂等性校验,当部分退款时必须给出
15
+
16
+}

+ 15
- 0
pay/src/main/java/com/ruoyi/dto/ValidResponse.java Visa fil

@@ -0,0 +1,15 @@
1
+package com.ruoyi.dto;
2
+
3
+import lombok.Data;
4
+
5
+/**
6
+ * 验证签名
7
+ */
8
+@Data
9
+public class ValidResponse {
10
+
11
+    private boolean isValid;// 是否通过验签
12
+
13
+    private String orderSn;// 订单号
14
+
15
+}

+ 12
- 0
pay/src/main/java/com/ruoyi/dto/WatchDTO.java Visa fil

@@ -0,0 +1,12 @@
1
+package com.ruoyi.dto;
2
+
3
+import lombok.Data;
4
+
5
+@Data
6
+public class WatchDTO {
7
+
8
+    private String orderSn; //订单号
9
+
10
+    private String platform;//平台
11
+
12
+}

+ 24
- 0
pay/src/main/java/com/ruoyi/exceptions/TradeException.java Visa fil

@@ -0,0 +1,24 @@
1
+package com.ruoyi.exceptions;
2
+
3
+import lombok.Getter;
4
+import lombok.Setter;
5
+
6
+/**
7
+ * 交易SDK提供的总的异常
8
+ */
9
+@Getter
10
+@Setter
11
+public class TradeException extends RuntimeException{
12
+    private String code;
13
+    private String msg;
14
+
15
+    public TradeException(String code, String msg) {
16
+        super(msg);
17
+        this.code = code;
18
+        this.msg = msg;
19
+    }
20
+
21
+    public TradeException(String msg) {
22
+        super(msg);
23
+    }
24
+}

+ 16
- 0
pay/src/main/java/com/ruoyi/key/KeyManager.java Visa fil

@@ -0,0 +1,16 @@
1
+package com.ruoyi.key;
2
+
3
+/**
4
+ * 密钥管理器接口
5
+ */
6
+public interface KeyManager {
7
+
8
+
9
+    /**
10
+     * 根据名字获取key字符串
11
+     * @param name
12
+     * @return
13
+     */
14
+    String getKey(String name);
15
+
16
+}

+ 19
- 0
pay/src/main/java/com/ruoyi/key/impl/DefaultKeyManager.java Visa fil

@@ -0,0 +1,19 @@
1
+package com.ruoyi.key.impl;
2
+
3
+
4
+import com.ruoyi.key.KeyManager;
5
+import com.ruoyi.util.FileUtil;
6
+
7
+/**
8
+ * 默认的key管理器-文件管理器
9
+ */
10
+public class DefaultKeyManager implements KeyManager {
11
+
12
+
13
+    @Override
14
+    public String getKey(String name) {
15
+        return FileUtil.readToStr(name);
16
+    }
17
+
18
+
19
+}

+ 37
- 0
pay/src/main/java/com/ruoyi/util/FileUtil.java Visa fil

@@ -0,0 +1,37 @@
1
+package com.ruoyi.util;
2
+
3
+import java.io.ByteArrayOutputStream;
4
+import java.io.IOException;
5
+import java.io.InputStream;
6
+
7
+/**
8
+ * 文件读取类
9
+ */
10
+public class FileUtil {
11
+
12
+
13
+    /**
14
+     * 获取文件内容
15
+     * @param fileName
16
+     * @return
17
+     */
18
+    public static String readToStr(String fileName){
19
+        InputStream is = FileUtil.class.getClassLoader().getResourceAsStream(fileName);
20
+        ByteArrayOutputStream os = new ByteArrayOutputStream(2048);
21
+        byte[] buffer = new byte[1024];
22
+
23
+        String str;
24
+        try {
25
+            int length;
26
+            while((length = is.read(buffer)) != -1) {
27
+                os.write(buffer, 0, length);
28
+            }
29
+
30
+            str = os.toString("UTF-8");
31
+        } catch (IOException var5) {
32
+            throw new IllegalArgumentException("无效的密钥", var5);
33
+        }
34
+        return str;
35
+    }
36
+
37
+}

+ 459
- 0
pay/src/main/java/com/ruoyi/wx/WxPayElegentTrade.java Visa fil

@@ -0,0 +1,459 @@
1
+package com.ruoyi.wx;
2
+
3
+
4
+import cn.hutool.core.util.CharsetUtil;
5
+import cn.hutool.core.util.IdUtil;
6
+import cn.hutool.core.util.StrUtil;
7
+import com.alibaba.fastjson.JSON;
8
+import com.ruoyi.annotation.TradePlatform;
9
+import com.ruoyi.config.CallbackConfig;
10
+import com.ruoyi.constant.PayConstant;
11
+import com.ruoyi.constant.Platform;
12
+import com.ruoyi.core.ElegentTrade;
13
+import com.ruoyi.dto.*;
14
+import com.ruoyi.exceptions.TradeException;
15
+import com.ruoyi.util.FileUtil;
16
+import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
17
+import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
18
+import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
19
+import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
20
+import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
21
+import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
22
+import lombok.extern.slf4j.Slf4j;
23
+import org.apache.http.client.methods.CloseableHttpResponse;
24
+import org.apache.http.client.methods.HttpGet;
25
+import org.apache.http.client.methods.HttpPost;
26
+import org.apache.http.client.utils.URIBuilder;
27
+import org.apache.http.entity.StringEntity;
28
+import org.apache.http.impl.client.CloseableHttpClient;
29
+import org.apache.http.util.EntityUtils;
30
+import org.springframework.beans.factory.annotation.Autowired;
31
+import org.springframework.stereotype.Component;
32
+import org.springframework.web.client.RestTemplate;
33
+
34
+import java.io.ByteArrayInputStream;
35
+import java.io.IOException;
36
+import java.net.URISyntaxException;
37
+import java.security.PrivateKey;
38
+import java.security.Signature;
39
+import java.util.Base64;
40
+import java.util.HashMap;
41
+import java.util.Map;
42
+
43
+@Component
44
+@Slf4j
45
+@TradePlatform(Platform.WX)
46
+public class WxPayElegentTrade implements ElegentTrade {
47
+
48
+    @Autowired
49
+    private WxpayConfig wxpayConfig;
50
+
51
+    @Autowired
52
+    private CallbackConfig callbackConfig;
53
+
54
+    /**
55
+     * 创建微信支付订单方法
56
+     * 这里参考官网代码:
57
+     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml   Native下单
58
+     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml   JSAPI 下单
59
+     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_1.shtml   H5下单
60
+     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_2_1.shtml   APP下单
61
+     * @param payRequest 支付请求
62
+     * @return 支付响应
63
+     * @throws IOException
64
+     */
65
+    @Override
66
+    public PayResponse requestPay(PayRequest payRequest, String tradeType)throws TradeException {
67
+        PayResponse payResponse = new PayResponse(); //返回结果
68
+        try {
69
+            // 请求body参数构建
70
+            Map<String, Object> params = new HashMap<String, Object>() {
71
+                {
72
+                    put("mchid", wxpayConfig.getMchId());
73
+                    put("appid", wxpayConfig.getAppId());
74
+                    put("notify_url", callbackConfig.getDomain()+ PayConstant.CALLBACK_PATH + PayConstant.NOTIFY +"/"+ Platform.WX );
75
+                    put("out_trade_no", payRequest.getOrderSn());
76
+                    put("amount", new HashMap<String, Object>() {
77
+                        {
78
+                            put("total", payRequest.getTotalFee());//金额,单位:分
79
+                            put("currency", "CNY");//人民币
80
+                        }
81
+                    });
82
+                    put("description", payRequest.getBody());
83
+                }
84
+            };
85
+
86
+            if("h5".equals(tradeType)){ //h5
87
+                params.put("scene_info", new HashMap<String, Object>() {
88
+                    {
89
+                        put("payer_client_ip", "127.0.0.1");
90
+                        put("h5_info", new HashMap<String, Object>() {
91
+                            {
92
+                                put("type", "Wap");
93
+                            }
94
+                        });
95
+                    }
96
+                });
97
+            }
98
+            if ("jsapi".equals(tradeType)) {  //如果是小程序支付
99
+                params.put("payer", new HashMap<String, Object>() {
100
+                    {
101
+                        put("openid", payRequest.getOpenid());
102
+                    }
103
+                });
104
+            }
105
+
106
+
107
+            String url = WxpayConstant.createOrder + tradeType; //创建订单
108
+            log.info("elegent-pay 请求参数{}",params);
109
+            Map<String, String> map = postApiTemplate(url, params);
110
+
111
+            if("SUCCESS".equals( map.get("code") )){
112
+                payResponse.setOrder_sn(payRequest.getOrderSn());
113
+                payResponse.setSuccess(true);
114
+                payResponse.setCode_url(map.get("code_url"));
115
+                payResponse.setMessage(map.get("message"));
116
+
117
+                payResponse.setPrepay_id(map.get("prepay_id"));
118
+                payResponse.setH5_url( map.get("h5_url") );
119
+
120
+                payResponse.setExpand(map); //全部数据
121
+
122
+                if("jsapi".equals(tradeType)){//如果是小程序,需要封装到Expand
123
+                    Map<String, String> data=new HashMap<>();
124
+                    String timeStamp = String.valueOf(System.currentTimeMillis() / 1000);
125
+                    String nonceStr = IdUtil.simpleUUID();
126
+                    String packages = "prepay_id=" + payResponse.getPrepay_id();
127
+                    String privateKey = FileUtil.readToStr("wxpay_private.key");
128
+                    String paySign = this.createPaySign(wxpayConfig.getAppId(), timeStamp, nonceStr, packages, privateKey);
129
+                    data.put("appId", wxpayConfig.getAppId());// appid
130
+                    data.put("timeStamp", timeStamp);// 时间戳
131
+                    data.put("nonceStr", nonceStr);// 随机字符串
132
+                    data.put("package","prepay_id="+payResponse.getPrepay_id());
133
+                    data.put("signType", "RSA");// 签名类型,默认为RSA,仅支持RSA
134
+                    data.put("paySign", paySign);// 签名
135
+                    data.put("orderNo",payRequest.getOrderSn());
136
+                    payResponse.setJsapiData(data);
137
+                }
138
+
139
+
140
+                log.info("createOrder: {}", payResponse);
141
+
142
+            }else{
143
+                payResponse.setSuccess(false);
144
+                payResponse.setMessage( map.get("message") );
145
+            }
146
+            return payResponse;
147
+        }catch (Exception e){
148
+            e.printStackTrace();
149
+            payResponse.setSuccess(false);
150
+        }
151
+        return payResponse;
152
+    }
153
+
154
+
155
+    /**
156
+     * 关闭订单
157
+     * 参考官网代码
158
+     * https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_7_2.shtml 3.2.5. 【服务端】关闭订单
159
+     * @return
160
+     */
161
+    @Override
162
+    public Boolean closePay(String orderSn) throws TradeException {
163
+        // 请求body参数构建
164
+        Map<String, Object> params = new HashMap<String, Object>();
165
+        params.put("mchid", wxpayConfig.getMchId());
166
+        String url = WxpayConstant.closeOrder.replaceAll("\\{out_trade_no\\}",orderSn);
167
+        Map map = postApiTemplate(url, params);
168
+        if("SUCCESS".equals(  map.get("code"))){
169
+            return true;
170
+        }else{
171
+            return false;
172
+        }
173
+    }
174
+
175
+
176
+    /**
177
+     * 退款
178
+     * 参考官网代码 https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_3_9.shtml
179
+     * @param refundRequest
180
+     * @return
181
+     */
182
+    @Override
183
+    public Boolean refund(RefundRequest refundRequest) {
184
+        // 请求body参数构建
185
+        // 请求body参数构建
186
+        Map<String, Object> params = new HashMap<String, Object>();
187
+        params.put("notify_url", callbackConfig.getDomain()+ PayConstant.CALLBACK_PATH +PayConstant.REFUND_NOTIFY +"/"+ Platform.WX    );  //回调地址
188
+        params.put("out_trade_no", refundRequest.getOrderSn());//订单编号
189
+        //out_refund_no
190
+        params.put("out_refund_no", refundRequest.getRequestNo());//退款申请单编号  (多次退款需要不一样才行)
191
+        params.put("amount", new HashMap<String, Object>() {
192
+            {
193
+                put("refund",refundRequest.getRefundAmount());//退款金额
194
+                put("total", refundRequest.getTotalFee());//原金额,单位:分
195
+                put("currency", "CNY");//人民币
196
+            }
197
+        });
198
+        String url = WxpayConstant.refund;  //创建订单
199
+        Map map = postApiTemplate(url, params);
200
+        if("SUCCESS".equals(  map.get("code"))){
201
+
202
+            return true;
203
+        }else{
204
+            return false;
205
+        }
206
+    }
207
+
208
+
209
+
210
+    /**
211
+     * 手动查询订单
212
+     * 参考官网代码: https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_6_2.shtml   3.2.4. 【服务端】查询订单
213
+     * @param orderSn
214
+     * @return
215
+     */
216
+    @Override
217
+    public QueryResponse queryTradingOrderNo(String orderSn) throws TradeException {
218
+        // 请求body参数构建
219
+        Map<String, String> params = new HashMap<String, String>();
220
+        params.put("mchid", wxpayConfig.getMchId());
221
+        String url = WxpayConstant.queryOrderNo+orderSn;;
222
+        try {
223
+            Map map = getApiTemplate(url, params);
224
+            QueryResponse queryResponse=new QueryResponse();
225
+            queryResponse.setOrder_sn((String)map.get("out_trade_no")  ); //订单号
226
+            queryResponse.setTransaction_id((String)map.get("transaction_id") ); //交易单类型
227
+            queryResponse.setTrade_state(   (String) map.get("trade_state") );//交易状态
228
+            Map amount = (Map)map.get("amount");
229
+            if(amount!=null){
230
+                queryResponse.setTotal( (Integer) amount.get("total")  ); //总金额
231
+            }
232
+            Map payer= (Map)map.get("payer");
233
+            if(payer!=null){
234
+                queryResponse.setOpenid(  (String)payer.get("openid")  );
235
+            }
236
+            queryResponse.setExpand(map);//全部数据
237
+            return queryResponse;
238
+
239
+        } catch (URISyntaxException e) {
240
+            throw new RuntimeException(e);
241
+        } catch (IOException e) {
242
+            throw new RuntimeException(e);
243
+        }
244
+
245
+    }
246
+
247
+
248
+    /**
249
+     * 查询退款订单
250
+     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_10.shtml
251
+     * @param out_refund_no
252
+     * @return
253
+     * @throws TradeException
254
+     */
255
+    @Override
256
+    public QueryRefundResponse queryRefundTrading(String out_refund_no) throws TradeException {
257
+
258
+        // 请求body参数构建
259
+        Map<String, String> params = new HashMap<String, String>();
260
+        String url = WxpayConstant.queryRufundOrderNo + out_refund_no;
261
+        try {
262
+            Map map = getApiTemplate(url, params);
263
+            QueryRefundResponse queryRefundResponse=new QueryRefundResponse();
264
+            queryRefundResponse.setOrder_sn( (String) map.get("out_trade_no") );
265
+            queryRefundResponse.setTransaction_id(   (String) map.get("transaction_id") );
266
+            Map amount = (Map)map.get("amount");
267
+            queryRefundResponse.setTotal( (Integer) amount.get("total")  ); //总金额
268
+            queryRefundResponse.setRefund((Integer) amount.get("payer_refund")   ); //退款金额
269
+            queryRefundResponse.setRefund_id((String) map.get("refund_id")  );  //退款单号
270
+            queryRefundResponse.setOut_refund_no( (String) map.get("out_refund_no")    );//退款订单号
271
+
272
+            queryRefundResponse.setChannel(  (String) map.get("channel")  );  //通道
273
+            queryRefundResponse.setUser_received_account(  (String) map.get("user_received_account") ); //账号
274
+            queryRefundResponse.setStatus(   (String) map.get("status")  ); //状态
275
+            queryRefundResponse.setSuccess_time(  (String) map.get("success_time")  );
276
+            queryRefundResponse.setExpand(map);
277
+            return queryRefundResponse;
278
+        } catch (URISyntaxException e) {
279
+            throw new RuntimeException(e);
280
+        } catch (IOException e) {
281
+            throw new RuntimeException(e);
282
+        }
283
+    }
284
+
285
+    @Override
286
+    public String getOpenid(String code) {
287
+        String getOpenIdUrl = "https://api.weixin.qq.com/sns/jscode2session?" +
288
+                "appid="+wxpayConfig.getAppId()
289
+                +"&secret="+wxpayConfig.getAppSecret()
290
+                +"&js_code="+code+"&grant_type=authorization_code";
291
+        RestTemplate restTemplate = new RestTemplate();
292
+        String respResult = restTemplate.getForObject(getOpenIdUrl,String.class);
293
+        log.info("获取openid的url:{},respResult:{}",getOpenIdUrl,respResult);
294
+        if( respResult==null || "".equals(respResult)  ) return "";
295
+        try{
296
+
297
+            Map<String,String> map = JSON.parseObject(respResult, Map.class);
298
+            String errorCode = map.get("errcode") ;
299
+            if(errorCode!=null && !"".equals(errorCode)){
300
+                int errorCodeInt = Integer.valueOf(errorCode).intValue();
301
+
302
+                log.info("获取openid的errorCode,{}",errorCodeInt);
303
+                if(errorCodeInt != 0) return "";
304
+            }
305
+            return map.get("openid");
306
+        }catch (Exception ex){
307
+            ex.printStackTrace();
308
+            return "";
309
+        }
310
+    }
311
+
312
+
313
+    private Map getApiTemplate(String url, Map<String, String> params) throws URISyntaxException, IOException {
314
+        URIBuilder uriBuilder = new URIBuilder(url);
315
+        //添加参数
316
+        for (String key : params.keySet()) {
317
+            uriBuilder.addParameter(key, params.get(key));
318
+        }
319
+        //完成签名并执行请求
320
+        HttpGet httpGet = new HttpGet(uriBuilder.build());
321
+        httpGet.addHeader("Accept", "application/json");
322
+        CloseableHttpClient httpClient = getWxHttpClient();
323
+        CloseableHttpResponse response = httpClient.execute(httpGet);
324
+        return responseTemplate(response);
325
+    }
326
+
327
+
328
+    private Map responseTemplate(CloseableHttpResponse response) {
329
+        Map result = null;
330
+        try {
331
+            int statusCode = response.getStatusLine().getStatusCode();
332
+            if (statusCode == 200) { //处理成功
333
+               // log.info("success,return body = " + EntityUtils.toString(response.getEntity()));
334
+                result = JSON.parseObject(EntityUtils.toString(response.getEntity()), Map.class);
335
+                result.put("code", "SUCCESS");
336
+            } else if (statusCode == 204) { //处理成功,无返回Body
337
+                log.info("success");
338
+                result = new HashMap<String, Object>() {
339
+                    {
340
+                        put("code", "SUCCESS");
341
+                    }
342
+                };
343
+            } else {
344
+                String returnBody = EntityUtils.toString(response.getEntity());
345
+                log.error("failed,resp code = " + statusCode + ",return body = " + returnBody);
346
+                //throw new TradeException("创建本地支付订单失败!"+payDTO.getOrderSn());
347
+                Map<String, String> map = JSON.parseObject(returnBody, Map.class);
348
+                result = new HashMap<String, Object>() {
349
+                    {
350
+                        put("code", "FAIL");
351
+                        put("message", map.get("message"));
352
+                    }
353
+                };
354
+            }
355
+        } catch (Exception e) {
356
+            result = new HashMap<String, Object>() {
357
+                {
358
+                    put("code", "FAIL");
359
+                    put("message", e.getMessage());
360
+                }
361
+            };
362
+        } finally {
363
+            closeConnect(response);
364
+            return result;
365
+        }
366
+    }
367
+
368
+    private Map postApiTemplate(String url, Map<String, Object> params) {
369
+        try {
370
+            HttpPost httpPost = new HttpPost(url);
371
+            StringEntity entity = new StringEntity(JSON.toJSONString(params));
372
+            entity.setContentType("application/json");
373
+            httpPost.setEntity(entity);
374
+            httpPost.setHeader("Accept", "application/json");
375
+            //完成签名并执行请求
376
+            CloseableHttpClient httpClient = getWxHttpClient();
377
+            CloseableHttpResponse response = httpClient.execute(httpPost);
378
+            return responseTemplate( response );
379
+        } catch (IOException e) {
380
+            throw new RuntimeException(e);
381
+        }
382
+
383
+    }
384
+
385
+
386
+    /**
387
+     * 获取微信HTTP连接
388
+     *
389
+     * @return
390
+     */
391
+    private CloseableHttpClient getWxHttpClient() {
392
+        try {
393
+            //这里对秘钥进行加密使用的完全是官网上的代码
394
+            //TODO 参考官网代码 https://pay.weixin.qq.com/wiki/doc/apiv3/open/pay/chapter2_6_2.shtml
395
+            // 加载商户私钥(privateKey:私钥字符串)
396
+            String key = FileUtil.readToStr("wxpay_private.key");
397
+
398
+            PrivateKey merchantPrivateKey = PemUtil
399
+                    .loadPrivateKey(new ByteArrayInputStream(key.getBytes("utf-8")));
400
+            // 加载平台证书(mchId:商户号,mchSerialNo:商户证书序列号,apiV3Key:V3密钥)
401
+            //PrivateKey merchantPrivateKey = keyManager.getPrivateKey("wxpay_private.key");//读取私钥
402
+            PrivateKeySigner privateKeySigner = new PrivateKeySigner(wxpayConfig.getMchSerialNo(), merchantPrivateKey);
403
+            WechatPay2Credentials wechatPay2Credentials = new WechatPay2Credentials(
404
+                    wxpayConfig.getMchId(), privateKeySigner);
405
+            // 向证书管理器增加需要自动更新平台证书的商户信息
406
+            CertificatesManager certificatesManager = CertificatesManager.getInstance();
407
+            certificatesManager.putMerchant(wxpayConfig.getMchId(), wechatPay2Credentials, wxpayConfig.getApiV3Key().getBytes("utf-8"));
408
+            // 初始化httpClient
409
+            return WechatPayHttpClientBuilder.create()
410
+                    .withMerchant(wxpayConfig.getMchId(), wxpayConfig.getMchSerialNo(), merchantPrivateKey)
411
+                    .withValidator(new WechatPay2Validator(certificatesManager.getVerifier(wxpayConfig.getMchId()))).build();
412
+        } catch (Exception e) {
413
+            e.printStackTrace();
414
+            throw new RuntimeException("微信支付--初始化,校验系统参数失败");
415
+        }
416
+
417
+    }
418
+
419
+
420
+    /**
421
+     * 关闭资源
422
+     */
423
+    private void closeConnect(CloseableHttpResponse response) {
424
+        try {
425
+            response.close();
426
+        } catch (Exception e) {
427
+            e.printStackTrace();
428
+            throw new RuntimeException("资源回收出错");
429
+        }
430
+    }
431
+
432
+
433
+    /**
434
+     *  创建支付签名
435
+     * @param appid
436
+     * @param timeStamp
437
+     * @param nonceStr
438
+     * @param packages
439
+     * @param privateKey
440
+     * @return
441
+     * @throws Exception
442
+     */
443
+    private String createPaySign(String appid, String timeStamp, String nonceStr, String packages,String  privateKey) throws Exception {
444
+        Signature sign = Signature.getInstance("SHA256withRSA");
445
+        // 加载商户私钥
446
+        PrivateKey key = PemUtil
447
+                .loadPrivateKey(new ByteArrayInputStream(privateKey.getBytes(CharsetUtil.CHARSET_UTF_8)));
448
+        sign.initSign(key);
449
+        String message = StrUtil.format("{}\n{}\n{}\n{}\n",
450
+                appid,
451
+                timeStamp,
452
+                nonceStr,
453
+                packages);
454
+        sign.update(message.getBytes());
455
+        return Base64.getEncoder().encodeToString(sign.sign());
456
+    }
457
+
458
+
459
+}

+ 153
- 0
pay/src/main/java/com/ruoyi/wx/WxPayElegentValid.java Visa fil

@@ -0,0 +1,153 @@
1
+package com.ruoyi.wx;
2
+
3
+
4
+import com.alibaba.fastjson.JSON;
5
+import com.ruoyi.annotation.TradePlatform;
6
+import com.ruoyi.constant.Platform;
7
+import com.ruoyi.core.ElegentValid;
8
+import com.ruoyi.dto.ValidResponse;
9
+import com.ruoyi.exceptions.TradeException;
10
+import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
11
+import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
12
+import com.wechat.pay.contrib.apache.httpclient.notification.Notification;
13
+import com.wechat.pay.contrib.apache.httpclient.notification.NotificationHandler;
14
+import com.wechat.pay.contrib.apache.httpclient.notification.NotificationRequest;
15
+import lombok.extern.slf4j.Slf4j;
16
+import org.springframework.beans.factory.annotation.Autowired;
17
+import org.springframework.http.HttpEntity;
18
+import org.springframework.http.HttpHeaders;
19
+import org.springframework.stereotype.Component;
20
+
21
+import javax.servlet.http.HttpServletRequest;
22
+import java.nio.charset.StandardCharsets;
23
+import java.util.Map;
24
+
25
+@Component
26
+@Slf4j
27
+@TradePlatform(Platform.WX)
28
+public class WxPayElegentValid implements ElegentValid {
29
+
30
+
31
+    @Autowired
32
+    private WxpayConfig wxpayConfig;
33
+
34
+    @Override
35
+    public ValidResponse validPay(HttpEntity<String> httpEntity, HttpServletRequest httpServletRequest) throws TradeException {
36
+        ValidResponse validResponse=new ValidResponse();
37
+
38
+        try {
39
+            //获取请求头
40
+            HttpHeaders headers = httpEntity.getHeaders();
41
+            //构建微信请求数据对象
42
+            NotificationRequest request = new NotificationRequest.Builder()
43
+                    .withSerialNumber(headers.getFirst("Wechatpay-Serial")) //证书序列号(微信平台)
44
+                    .withNonce(headers.getFirst("Wechatpay-Nonce"))  //随机串
45
+                    .withTimestamp(headers.getFirst("Wechatpay-Timestamp")) //时间戳
46
+                    .withSignature(headers.getFirst("Wechatpay-Signature")) //签名字符串
47
+                    .withBody(httpEntity.getBody())
48
+                    .build();
49
+
50
+
51
+            //微信通知的业务处理
52
+            //验证签名,确保请求来自微信
53
+            Map jsonData = null;
54
+            try {
55
+                //确保在管理器中存在自动更新的商户证书
56
+                CertificatesManager certificatesManager = CertificatesManager.getInstance();
57
+                Verifier verifier = certificatesManager.getVerifier(wxpayConfig.getMchId());
58
+
59
+                //验签和解析请求数据
60
+                NotificationHandler notificationHandler = new NotificationHandler(verifier, wxpayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
61
+                Notification notification = notificationHandler.parse(request);
62
+
63
+                if (!"TRANSACTION.SUCCESS".equals(notification.getEventType())) {
64
+                    validResponse.setValid(false);
65
+                    return validResponse;
66
+                }
67
+                //获取解密后的数据
68
+                jsonData =  JSON.parseObject(notification.getDecryptData(),Map.class   );
69
+                log.info("解密后的数据为:"+jsonData);
70
+            } catch (Exception e) {
71
+                throw new TradeException("验签失败");
72
+            }
73
+            if (!"SUCCESS".equals(jsonData.get("trade_state"))) {
74
+                validResponse.setValid(false);
75
+                return validResponse;
76
+            }
77
+            validResponse.setValid(true);
78
+            validResponse.setOrderSn(  (String) jsonData.get("out_trade_no")  ); //订单号
79
+            return validResponse;
80
+        } catch (Exception e) {
81
+            validResponse.setValid(false);
82
+            return validResponse;
83
+        }
84
+    }
85
+
86
+    @Override
87
+    public ValidResponse validRefund(HttpEntity<String> httpEntity, HttpServletRequest httpServletRequest) throws TradeException {
88
+
89
+        ValidResponse validResponse=new ValidResponse();
90
+        try {
91
+            //获取请求头
92
+            HttpHeaders headers = httpEntity.getHeaders();
93
+
94
+            //构建微信请求数据对象
95
+            NotificationRequest request = new NotificationRequest.Builder()
96
+                    .withSerialNumber(headers.getFirst("Wechatpay-Serial")) //证书序列号(微信平台)
97
+                    .withNonce(headers.getFirst("Wechatpay-Nonce"))  //随机串
98
+                    .withTimestamp(headers.getFirst("Wechatpay-Timestamp")) //时间戳
99
+                    .withSignature(headers.getFirst("Wechatpay-Signature")) //签名字符串
100
+                    .withBody(httpEntity.getBody())
101
+                    .build();
102
+
103
+            //微信通知的业务处理
104
+            Map jsonData = null;
105
+            //验证签名,确保请求来自微信
106
+            try {
107
+                //确保在管理器中存在自动更新的商户证书
108
+                CertificatesManager certificatesManager = CertificatesManager.getInstance();
109
+                Verifier verifier = certificatesManager.getVerifier(wxpayConfig.getMchId());
110
+
111
+                //验签和解析请求数据
112
+                NotificationHandler notificationHandler = new NotificationHandler(verifier, wxpayConfig.getApiV3Key().getBytes(StandardCharsets.UTF_8));
113
+                Notification notification = notificationHandler.parse(request);
114
+
115
+                if (!"REFUND.SUCCESS".equals(notification.getEventType())) {
116
+                    //非成功请求直接返回,理论上都是成功的请求
117
+                    validResponse.setValid(false);
118
+                    return validResponse;
119
+                }
120
+                //获取解密后的数据
121
+                jsonData = JSON.parseObject( notification.getDecryptData(),Map.class  );
122
+
123
+
124
+            } catch (Exception e) {
125
+                throw new TradeException("验签失败");
126
+            }
127
+            if (!"SUCCESS".equals(jsonData.get("refund_status"))) {
128
+                //非成功请求直接返回,理论上都是成功的请求
129
+                validResponse.setValid(false);
130
+                return validResponse;
131
+            }
132
+
133
+            //交易单号
134
+            validResponse.setValid(true);
135
+            validResponse.setOrderSn(  (String) jsonData.get("out_trade_no")  ); //订单号
136
+            return validResponse;
137
+        } catch (Exception e) {
138
+            //非成功请求直接返回,理论上都是成功的请求
139
+            validResponse.setValid(false);
140
+            return validResponse;
141
+        }
142
+    }
143
+
144
+    @Override
145
+    public String successResult() {
146
+        return  JSON.toJSONString(WxpayConstant.SUCCESS ) ;
147
+    }
148
+
149
+    @Override
150
+    public String failResult() {
151
+        return JSON.toJSONString( WxpayConstant.FAIL);
152
+    }
153
+}

+ 20
- 0
pay/src/main/java/com/ruoyi/wx/WxpayConfig.java Visa fil

@@ -0,0 +1,20 @@
1
+package com.ruoyi.wx;
2
+import lombok.Data;
3
+import org.springframework.boot.context.properties.ConfigurationProperties;
4
+import org.springframework.stereotype.Component;
5
+
6
+/**
7
+ * 微信权限对接类
8
+ */
9
+@Component
10
+@ConfigurationProperties("elegent.pay.wxpay")
11
+@Data
12
+public class WxpayConfig {
13
+
14
+    private String mchId; //商户号
15
+    private String appId; //APPID
16
+    private String appSecret;//app密钥
17
+    private String mchSerialNo; //商户证书序列号
18
+    private String apiV3Key; //V3密钥
19
+
20
+}

+ 48
- 0
pay/src/main/java/com/ruoyi/wx/WxpayConstant.java Visa fil

@@ -0,0 +1,48 @@
1
+package com.ruoyi.wx;
2
+
3
+import java.util.HashMap;
4
+import java.util.Map;
5
+
6
+public class WxpayConstant {
7
+
8
+    public static final Map SUCCESS = new HashMap<String,Object>(){
9
+        {
10
+            put("code", "SUCCESS");
11
+        }
12
+    };
13
+
14
+    public static final Map FAIL  = new HashMap<String,Object>(){
15
+        {
16
+            put("code", "FAIL");
17
+            put("message","微信回调错误结果");
18
+        }
19
+    };
20
+
21
+
22
+    public final static String domain ="https://api.mch.weixin.qq.com/v3";
23
+
24
+    /**
25
+     * 创建订单
26
+     */
27
+    public final static String createOrder = domain +"/pay/transactions/";
28
+
29
+    /**
30
+     * 关闭订单
31
+     */
32
+    public final static String closeOrder = domain+"/pay/transactions/out-trade-no/{out_trade_no}/close";
33
+
34
+    /**
35
+     * 查询订单编号
36
+     */
37
+    public static String queryOrderNo = domain+"/pay/transactions/out-trade-no/";
38
+
39
+    /**
40
+     * 查询退款订单
41
+     */
42
+    public static String queryRufundOrderNo =domain+ "/refund/domestic/refunds/";
43
+
44
+    /**
45
+     * 退款接口
46
+     */
47
+    public static String refund = domain+"/refund/domestic/refunds";
48
+}

+ 14
- 0
pay/src/main/resources/META-INF/spring.factories Visa fil

@@ -0,0 +1,14 @@
1
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
2
+  com.ruoyi.config.CallbackConfig,\
3
+  com.ruoyi.core.CallbackWatch,\
4
+  com.ruoyi.core.ElegentPayImpl,\
5
+  com.ruoyi.core.ElegentLoader,\
6
+  com.ruoyi.core.ElegentConfig,\
7
+  com.ruoyi.key.impl.DefaultKeyManager,\
8
+  com.ruoyi.core.CallbackController,\
9
+  com.ruoyi.wx.WxpayConfig,\
10
+  com.ruoyi.wx.WxPayElegentTrade,\
11
+  com.ruoyi.wx.WxPayElegentValid,\
12
+  com.ruoyi.ali.AlipayConfig,\
13
+  com.ruoyi.ali.AlipayElegentTrade,\
14
+  com.ruoyi.ali.AlipayElegentValid

+ 1
- 0
pom.xml Visa fil

@@ -180,6 +180,7 @@
180 180
         <module>ruoyi-quartz</module>
181 181
         <module>ruoyi-generator</module>
182 182
         <module>ruoyi-common</module>
183
+        <module>pay</module>
183 184
     </modules>
184 185
     <packaging>pom</packaging>
185 186
 

+ 5
- 1
ruoyi-admin/pom.xml Visa fil

@@ -60,7 +60,11 @@
60 60
             <groupId>com.ruoyi</groupId>
61 61
             <artifactId>ruoyi-generator</artifactId>
62 62
         </dependency>
63
-
63
+        <dependency>
64
+            <groupId>com.ruoyi</groupId>
65
+            <artifactId>pay</artifactId>
66
+            <version>1.0.0-SNAPSHOT</version>
67
+        </dependency>
64 68
     </dependencies>
65 69
 
66 70
     <build>

+ 29
- 0
ruoyi-admin/src/main/java/com/ruoyi/web/controller/payment/CasePaymentController.java Visa fil

@@ -0,0 +1,29 @@
1
+package com.ruoyi.web.controller.payment;
2
+
3
+import com.ruoyi.ElegentPay;
4
+import com.ruoyi.constant.Platform;
5
+import com.ruoyi.constant.TradeType;
6
+import com.ruoyi.dto.PayRequest;
7
+import com.ruoyi.dto.PayResponse;
8
+import com.ruoyi.system.service.ICasePaymentService;
9
+import org.springframework.beans.factory.annotation.Autowired;
10
+import org.springframework.web.bind.annotation.PostMapping;
11
+import org.springframework.web.bind.annotation.RequestMapping;
12
+import org.springframework.web.bind.annotation.RestController;
13
+
14
+/**
15
+ * 缴费支付
16
+ */
17
+@RestController
18
+@RequestMapping("/pay")
19
+public class CasePaymentController {
20
+    private final ICasePaymentService paymentService;
21
+    @Autowired
22
+    public CasePaymentController(ICasePaymentService paymentService){
23
+       this.paymentService=paymentService;
24
+    }
25
+    @PostMapping("/casePay")
26
+    public String casePay(PayRequest request) {
27
+        return paymentService.casePay(request);
28
+    }
29
+}

+ 1
- 0
ruoyi-admin/src/main/resources/alipay_private.key Visa fil

@@ -0,0 +1 @@
1
+MIIEuwIBADANBgkqhkiG9w0BAQEFAASCBKUwggShAgEAAoIBAQC12YM9mR+HFQYTx/fHKHZbgszVtDHDB0B/ysWl3MbcPpGtjcZlDr5aynRMRLaoduRHT++A98IaNVIVGj9RHdXrX2j9I/Uz6fYDH63cdu6FZ6Pk82yPwNZW7pebprbVHInR/7gzsKQWSWEST70BgjCRqlbfAE6xzUZFTeYxciCjptm0rUQ2MC24xRdkvZByIDIYFnQ/AdmSFqNtKDR2WpEV/M8aBjyuPPomRJZ1X8oudWuJIU4ySdas04fCbDxD10TY/wyQcDHXuG1IrQpXme4DOGQeJZ0/aOFphBkDFUyPGfYMmLshOPNdBKi2IqWHPPs4XsV4Rv6+tvTSnMF2uGqHAgMBAAECggEAPa1sifPpcZN74DGupGng2uDeQI1BY3iOM8m+h6b9+61tE4RGifgaMAkCsOuNWE4a1uURwphFyUXUdTvVxdlsuMw/e7w6akUsH5sbCO99rtmcCQdXBtrM1+dMnIpK8LUhOYyWGVIMFVMGDYPmAyD5AC7aEAC2sC+DafYl4RdoYpidq1YxeE7DVw1aQHCI2mKhYjZG+3RDDGDfNFvdyH61MgdYjoGkeXNvARzEXgfWvfiTrHZ3H1SYgvOEHofzKDTrWsQL2dvaEsc55Jiw0AgNUVcgby7al8PUekTJoK3ZvrE3pSWaUirBcqsqWISHjeR7Xx501CHIha8EnZwlnDoM4QKBgQDtnYzwQ5mHg7cRHD8Z6QdTpvBvYSEPesiUT/HeI+AKQKDCVJxKiLvJagc6zZkOzV9bZDS/WLgzXWMyxUb+OTjht0jLWAMcf7NfFp3tPKq9wkmQQ/vQSBQ1lFmO6A4Zq1eoGKeUCB4pKBG6cSM+t8+ruhm7s1ZUt+6EBwCVN/izrQKBgQDD62jIm+6NFErdidUaIrGiFUrzqdR13w6JOexfk+O6Aau3wRqsr7Wz4nqQvVVxGMRpXbOH06zpeiS+vMmjwgO973VoLAmH+hJ0GZz8qj3zA2GEOFWjD2V7tqeRvGkQvz0v46pl+8sBJkrRHLN7DWNYY5NDI+b7exwqcTc/LL19gwJ/a6r4MeZvqvgD+7zQ2uy8ZSs/xzg7wsfgG1QeRIn8+qhOL8AnEZ7jeGCS5hJDSHHGw6KkRA/vZ1bpnBfIE2naXGywj3NR9Zfnry6QYO8cbt+adcRYVghTH/QYoKiFuxvonEKPrIQBJqUBY3ngforLjwTEpEie1cSCT1Dc8sBp8QKBgQCaz8fqzRyBKknGKQXVMxj+JKknRUl3IpzP3o9jLu9BqdRQzSwQzH9d91Y2TQXY6mM5hys35xG5JCUo+vCyj7p5OWCiwjl90yMFzr93/+YXwtIpsoIo6R+d1EUxKZoz+4mT7+hT0dUlwWZZOr6wO3IHBBf3c8UvbqZg+zlWmDnblQKBgEs6jwMkb5zaG2fyBJ7PJUN/8nIz8V+X0SxQfcEqIX0J+EC+7MAgFjcdZFp+lca3Vd9z+8Ksd4rMzMa5y856ositL2NZ+K0fs8i8EBaQPny61OgCFUuXEuv5keB2YuGMSns5FYRWuByrtDXl4PxzKXvq05iKWLKCaCq9v4momvKZ

+ 1
- 0
ruoyi-admin/src/main/resources/alipay_public.key Visa fil

@@ -0,0 +1 @@
1
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAhUnjdAKwZApwZEcfq+5L0pa77Vg3mqcoXv+th8RR0SYotkPsH1f2JkbS48ySaSCM6YNWSMNfqp5qdOla2zUJOBnJ/yaBg7s7fVD6V3M2mEog8kCDYGKt/3P4VII3xYl8lFYMQ3IcFRELkxCBBCA8JDKmf5z2R4F/Z/jFFEuOwxaJvp+7Ke9OzZHYdWGNnU6QP8YYLYUeX7VNZLHEuly34ExAw6A+yJkNDsYEho2Lu31QjT2pLh9g+88MlRfiI92iN25O9NVdeM4f5RcpvBPrBQZQs9tlFmALYSFS3prIf3FAobWM+W7iwxT6J25nFIhst1DdJQfIBpaeRUJVTkn99QIDAQAB

+ 15
- 0
ruoyi-admin/src/main/resources/application.yml Visa fil

@@ -129,3 +129,18 @@ xss:
129 129
   excludes: /system/notice
130 130
   # 匹配链接
131 131
   urlPatterns: /system/*,/monitor/*,/tool/*
132
+#支付相关配置
133
+elegent:
134
+  pay:
135
+    wxpay:
136
+      mchId: 1561414331
137
+      appId: wx6592a2db3f85ed25
138
+      appSecret: d9a9ff00a633cd7353a8925119063b01
139
+      mchSerialNo: 25FBDE3EFD31B03A4377EB9A4A47C517969E6620
140
+      apiV3Key: CZBK51236435wxpay435434323FFDuv3
141
+    alipay:
142
+      appId: 2021003141676135
143
+    callback:
144
+      domain: https://2d3ac179.r5.cpolar.top
145
+      watch: true
146
+      cycle: 10

+ 1
- 0
ruoyi-admin/src/main/resources/wxpay_private.key Visa fil

@@ -0,0 +1 @@
1
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDBHGgIh80193GhdpD1LtMZfTRpcWI0fImyuBCyrd3gYb3rrsARebGcHdJsQA3mVjVqVp5ybhEZDPa4ecoK4Ye1hTppNpI/lmLt4/uUV/zhF5ahli7hi+116Ty6svHSbuMQBuUZeTFOwGrxjvofU/4pGIwh8ZvkcSnyOp9uX2177UVxDBkhgbZbJp9XF2b83vUa5eHo93CziPzn3hFdAlBCdTXB7DH+m0nN3Jou0szGukvq7cIgGpHku4ycKSTkIhhl9WRhN6OoSEJxq88MXzjkzTruc85PHN52aUTUifwg3T8Y4XqFQ61dTnEmgxeD2O6/pLdB9gLsp6yCGqN5Lqk7AgMBAAECggEBAL4X+WzUSbSjFS9NKNrCMjm4H1zgqTxjj6TnPkC1mGEltjAHwLgzJBw62wWGdGhWWpSIGccpBBm1wjTMZpAZfF66fEpP1t1Ta6UjtGZNyvfFIZmE3jdWZ/WXGBnsxtFQKKKBNwrBW0Fbdqq9BQjLxLitmlxbmwrgPttcy855j6vZqq4MBT1v8CtUT/gz4UWW2xWovVnmWOrRSScv7Nh0pMbRpPLkNHXrBwSSNz/keORzXB9JSm85wlkafa7n5/IJbdTml3A/uAgW3q3JZZQotHxQsYvD4Zb5Cnc9CPAXE5L2Yk877kVXZMGt5QPIVcPMj/72AMtaJT67Y0fN0RYHEGkCgYEA38BIGDY6pePgPbxB7N/l6Df0/OKPP0u8mqR4Q0aQD3VxeGiZUN1uWXEFKsKwlOxLfIFIFk1/6zQeC0xetNTKk0gTL8hpMUTNkE7vI9gFWws2LY6DE86Lm0bdFEIwh6d7Fr7zZtyQKPzMsesC3XV9sdSUExEi5o/VwAyf+xZlOXcCgYEA3PGZYlILjg3esPNkhDz2wxFw432i8l/BCPD8ZtqIV9eguu4fVtFYcUVfawBb0T11RamJkc4eiSOqayC+2ehgb+GyRLJNK4FqbFcsIT+CK0HlscZw51jrMR0MxTc4RzuOIMoYDeZqeGB6/YnNyG4pw2sD8bIwHm8406gtJsX/v10CgYAo8g3/aEUZQHcztPS3fU2cTkkl0ev24Ew2XGypmwsX2R0XtMSBuNPNyFHyvkgEKK2zrhDcC/ihuRraZHJcUyhzBViFgP5HBtk7VEaM36YzP/z9Hzw7bqu7kZ85atdoq6xpwC3Yn/o9le17jY8rqamD1mv2hUdGvAGYsHbCQxnpBwKBgHTkeaMUBzr7yZLS4p435tHje1dQVBJpaKaDYPZFrhbTZR0g+IGlNmaPLmFdCjbUjiPyA2+Znnwt227cHz0IfWUUAo3ny3419QkmwZlBkWuzbIO2mms7lwsf9G6uvV6qepKMeVd5TWEsokVbT/03k27pQmfwPxcK/wS0GFdIL/udAoGAOYdDqY5/aadWCyhzTGI6qXPLvC+fsJBPhK2RXyc+jYV0KmrEv4ewxlK5NksuFsNkyB7wlI1oMCa/xB3T/2vTBALgGFPi8BJqceUjtnTYtI4R2JIVEl08RtEJwyU5JZ2rvWcilsotVZYwfuLZ9KfdhkTrgNxlp/KKkr+UuKce4Vs=

+ 5
- 1
ruoyi-system/pom.xml Visa fil

@@ -22,7 +22,11 @@
22 22
             <groupId>com.ruoyi</groupId>
23 23
             <artifactId>ruoyi-common</artifactId>
24 24
         </dependency>
25
-
25
+        <dependency>
26
+            <groupId>com.ruoyi</groupId>
27
+            <artifactId>pay</artifactId>
28
+            <version>1.0.0-SNAPSHOT</version>
29
+        </dependency>
26 30
     </dependencies>
27 31
 
28 32
 </project>

+ 7
- 0
ruoyi-system/src/main/java/com/ruoyi/system/service/ICasePaymentService.java Visa fil

@@ -0,0 +1,7 @@
1
+package com.ruoyi.system.service;
2
+
3
+import com.ruoyi.dto.PayRequest;
4
+
5
+public interface ICasePaymentService {
6
+    String casePay(PayRequest request);
7
+}

+ 27
- 0
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/CasePaymentServiceImpl.java Visa fil

@@ -0,0 +1,27 @@
1
+package com.ruoyi.system.service.impl;
2
+
3
+import com.ruoyi.ElegentPay;
4
+import com.ruoyi.constant.Platform;
5
+import com.ruoyi.constant.TradeType;
6
+import com.ruoyi.dto.PayRequest;
7
+import com.ruoyi.dto.PayResponse;
8
+import com.ruoyi.system.service.ICasePaymentService;
9
+import org.springframework.beans.factory.annotation.Autowired;
10
+import org.springframework.stereotype.Service;
11
+
12
+@Service
13
+public class CasePaymentServiceImpl implements ICasePaymentService {
14
+    private final ElegentPay elegentPay;
15
+    @Autowired
16
+    public CasePaymentServiceImpl(ElegentPay elegentPay){
17
+        this.elegentPay=elegentPay;
18
+    }
19
+    @Override
20
+    public String casePay(PayRequest request) {
21
+        PayResponse payResponse = elegentPay.requestPay(request, TradeType.NATIVE, Platform.WX);
22
+        if (payResponse!=null){
23
+            return payResponse.getCode_url();
24
+        }
25
+        return null;
26
+    }
27
+}