Преглед на файлове

修复ResourceInvalidException异常编码不正确问题;优化微信支付逻辑;

woody преди 1 година
родител
ревизия
a28bf291b6

+ 1 - 1
framework-base/src/main/java/com/chelvc/framework/base/exception/ResourceInvalidException.java

@@ -20,7 +20,7 @@ public class ResourceInvalidException extends PlatformException {
     }
 
     public ResourceInvalidException(String message) {
-        this(message, null, null);
+        this(null, message);
     }
 
     public ResourceInvalidException(String code, String message) {

+ 73 - 116
framework-wechat/src/main/java/com/chelvc/framework/wechat/DefaultWechatHandler.java

@@ -11,7 +11,6 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpSession;
 
 import com.chelvc.framework.base.context.SessionContextHolder;
-import com.chelvc.framework.base.model.Terminal;
 import com.chelvc.framework.base.util.DecimalUtils;
 import com.chelvc.framework.base.util.HostUtils;
 import com.chelvc.framework.base.util.ObjectUtils;
@@ -130,49 +129,30 @@ public class DefaultWechatHandler implements WechatHandler {
     }
 
     /**
-     * 根据支付交易类型获取Appid
+     * 获取支付配置
      *
-     * @param type    交易类型
-     * @param payment 支付配置
-     * @return Appid
+     * @param mode 支付方式
+     * @return 支付配置
      */
-    private String getAppid(TradeType type, WechatProperties.Payment payment) {
-        if (type == TradeType.APP) {
-            // 兼容老版本
-            if (SessionContextHolder.isBeforeVersion(Terminal.APP, "1.4.4")) {
-                return "wx297be7a59f0eaf28";
-            }
-            return Objects.requireNonNull(payment.getPayid(), "Wechat payid unspecified");
-        }
-        return Objects.requireNonNull(this.properties.getAppid(), "Wechat appid unspecified");
-    }
-
-    /**
-     * 参数签名
-     *
-     * @param payment    支付配置
-     * @param parameters 签名参数
-     * @return 签名信息
-     */
-    private String sign(WechatProperties.Payment payment, Map<String, String> parameters) {
-        String mchkey = Objects.requireNonNull(payment.getMchkey(), "Wechat mchkey unspecified");
-        return WechatUtils.sign(mchkey, parameters);
+    private WechatProperties.Payment getPayment(PayMode mode) {
+        List<WechatProperties.Payment> payments = this.properties.getPayments();
+        WechatProperties.Payment payment = CollectionUtils.isEmpty(payments) ? null :
+                payments.stream().filter(config -> config.getMode() == mode).findAny().orElse(null);
+        return Objects.requireNonNull(payment, "Not support pay mode: " + mode);
     }
 
     /**
-     * 构建支付参数
+     * 生成支付参数
      *
-     * @param type    交易类型
      * @param payment 支付配置
      * @param request 支付请求参数
      * @return 支付参数键/值对
      */
-    private Map<String, String> buildPaymentParameters(TradeType type, WechatProperties.Payment payment,
-                                                       WechatPayRequest request) {
+    private Map<String, String> generatePaymentParameters(WechatProperties.Payment payment, WechatPayRequest request) {
         String mchid = Objects.requireNonNull(payment.getMchid(), "Wechat mchid unspecified");
         String callback = Objects.requireNonNull(payment.getCallback(), "Wechat callback unspecified");
         Map<String, String> parameters = Maps.newHashMap();
-        parameters.put("appid", this.getAppid(type, payment));
+        parameters.put("appid", this.getAppid(payment));
         parameters.put("mch_id", mchid);
         parameters.put("body", request.getComment());
         parameters.put("out_trade_no", request.getOrder());
@@ -183,46 +163,34 @@ public class DefaultWechatHandler implements WechatHandler {
         parameters.put("total_fee", String.valueOf(request.getAmount().multiply(DecimalUtils.HUNDRED).intValue()));
         parameters.put("spbill_create_ip", HostUtils.LOCAL_ADDRESS);
         parameters.put("notify_url", callback);
-        parameters.put("trade_type", type.name());
+        parameters.put("trade_type", payment.getMode().name());
         parameters.put("nonce_str", WXPayUtil.generateNonceStr());
         return parameters;
     }
 
     /**
-     * 微信统一下单
+     * 根据支付交易类型获取Appid
      *
-     * @param payment    支付配置
-     * @param parameters 下单参数
-     * @return 下单结果
+     * @param payment 支付配置
+     * @return Appid
      */
-    private Map<String, String> unifiedorder(WechatProperties.Payment payment, Map<String, String> parameters) {
-        boolean debug = log.isDebugEnabled();
-        if (debug) {
-            log.debug("Wechat parameters: {}", parameters);
-        }
-
-        // 请求参数签名
-        parameters.put("sign", this.sign(payment, parameters));
-
-        // 将请求参数转换成xml请求体
-        String body = WechatUtils.map2xml(parameters);
-        if (debug) {
-            log.debug("Wechat request: {}, {}", UNIFIEDORDER_URL, body);
+    private String getAppid(WechatProperties.Payment payment) {
+        if (payment.getMode() == PayMode.APP || payment.getMode() == PayMode.NATIVE) {
+            return Objects.requireNonNull(payment.getPayid(), "Wechat payid unspecified");
         }
+        return Objects.requireNonNull(this.properties.getAppid(), "Wechat appid unspecified");
+    }
 
-        // 发起下单请求
-        HttpHeaders headers = new HttpHeaders();
-        headers.setContentType(MediaType.TEXT_XML);
-        HttpEntity<?> entity = new HttpEntity<>(body, headers);
-        String response = this.restTemplate.exchange(UNIFIEDORDER_URL, HttpMethod.POST, entity, String.class).getBody();
-        if (debug) {
-            log.debug("Wechat response: {}", response);
-        }
-        if (StringUtils.isEmpty(response) || !response.contains("SUCCESS")) {
-            log.error("Wechat unifiedorder failed: {}", response);
-            throw new RuntimeException("Wechat unifiedorder failed");
-        }
-        return WechatUtils.xml2map(response);
+    /**
+     * 参数签名
+     *
+     * @param payment    支付配置
+     * @param parameters 签名参数
+     * @return 签名信息
+     */
+    private String sign(WechatProperties.Payment payment, Map<String, String> parameters) {
+        String mchkey = Objects.requireNonNull(payment.getMchkey(), "Wechat mchkey unspecified");
+        return WechatUtils.sign(mchkey, parameters);
     }
 
     @Override
@@ -394,69 +362,58 @@ public class DefaultWechatHandler implements WechatHandler {
     }
 
     @Override
-    public WechatProperties.Payment getPayment(@NonNull String type) {
-        List<WechatProperties.Payment> payments = this.properties.getPayments();
-        WechatProperties.Payment payment = CollectionUtils.isEmpty(payments) ? null :
-                payments.stream().filter(config -> Objects.equals(config.getType(), type)).findAny().orElse(null);
-        return Objects.requireNonNull(payment, "Not support payment type: " + type);
-    }
-
-    @Override
-    public String sign(@NonNull String type, @NonNull Map<String, String> parameters) {
-        return this.sign(this.getPayment(type), parameters);
+    public String sign(@NonNull PayMode mode, @NonNull Map<String, String> parameters) {
+        return this.sign(this.getPayment(mode), parameters);
     }
 
     @Override
-    public Map<String, String> pay(@NonNull String type, @NonNull WechatPayRequest request) {
+    public WechatUnifiedOrder unifiedorder(@NonNull PayMode mode, @NonNull WechatPayRequest request) {
         // 获取支付配置
-        WechatProperties.Payment payment = this.getPayment(type);
+        WechatProperties.Payment payment = this.getPayment(mode);
 
         // 构建请求参数
-        Map<String, String> parameters = this.buildPaymentParameters(TradeType.APP, payment, request);
-
-        // 统一下单
-        Map<String, String> result = this.unifiedorder(payment, parameters);
-
-        // 构建支付结果
-        Map<String, String> returns = Maps.newHashMapWithExpectedSize(8);
-        returns.put("appid", this.getAppid(TradeType.APP, payment));
-        returns.put("partnerid", payment.getMchid());
-        returns.put("prepayid", result.get("prepay_id"));
-        returns.put("noncestr", WXPayUtil.generateNonceStr());
-        returns.put("package", "Sign=WXPay");
-        returns.put("timestamp", String.valueOf(System.currentTimeMillis() / 1000).substring(0, 10));
-        returns.put("sign", this.sign(payment, returns));
-        returns.put("orderId", String.valueOf(request.getOrder()));
-        if (log.isDebugEnabled()) {
-            log.debug("App payment: {}", returns);
+        Map<String, String> parameters = this.generatePaymentParameters(payment, request);
+
+        // 如果是小程序支付则需要指定openid
+        if (mode == PayMode.JSAPI) {
+            parameters.put("openid", Objects.requireNonNull(request.getOpenid(), "openid unspecified"));
         }
-        return returns;
-    }
 
-    @Override
-    public Map<String, String> appletPay(@NonNull String type, @NonNull String token, WechatPayRequest request) {
-        // 获取支付配置
-        WechatProperties.Payment payment = this.getPayment(type);
+        boolean debug = log.isDebugEnabled();
+        if (debug) {
+            log.debug("Wechat parameters: {}", parameters);
+        }
 
-        // 构建请求参数
-        Map<String, String> parameters = this.buildPaymentParameters(TradeType.JSAPI, payment, request);
-        parameters.put("openid", this.code2session(token).getOpenid());
-
-        // 统一下单
-        Map<String, String> result = this.unifiedorder(payment, parameters);
-
-        // 构建支付结果
-        Map<String, String> returns = Maps.newHashMapWithExpectedSize(7);
-        returns.put("appId", this.getAppid(TradeType.JSAPI, payment));
-        returns.put("nonceStr", WXPayUtil.generateNonceStr());
-        returns.put("package", "prepay_id=" + result.get("prepay_id"));
-        returns.put("signType", "MD5");
-        returns.put("timeStamp", String.valueOf(System.currentTimeMillis() / 1000).substring(0, 10));
-        returns.put("paySign", this.sign(payment, returns));
-        returns.put("orderId", String.valueOf(request.getOrder()));
-        if (log.isDebugEnabled()) {
-            log.debug("Applet payment: {}", returns);
+        // 请求参数签名
+        parameters.put("sign", this.sign(payment, parameters));
+
+        // 将请求参数转换成xml请求体
+        String body = WechatUtils.map2xml(parameters);
+        if (debug) {
+            log.debug("Wechat request: {}, {}", UNIFIEDORDER_URL, body);
+        }
+
+        // 发起下单请求
+        HttpHeaders headers = new HttpHeaders();
+        headers.setContentType(MediaType.TEXT_XML);
+        HttpEntity<?> entity = new HttpEntity<>(body, headers);
+        String response = this.restTemplate.exchange(UNIFIEDORDER_URL, HttpMethod.POST, entity, String.class).getBody();
+        if (debug) {
+            log.debug("Wechat response: {}", response);
+        }
+
+        // 验证相应结果
+        Map<String, String> result = ObjectUtils.ifNull(response, WechatUtils::xml2map);
+        if (StringUtils.isEmpty(response) || !Objects.equals(result.get("return_code"), "SUCCESS")
+                || !Objects.equals(result.get("result_code"), "SUCCESS")
+                || !Objects.equals(result.remove("sign"), this.sign(payment, result))) {
+            log.error("Wechat unifiedorder failed: {}", response);
+            throw new RuntimeException("Wechat unifiedorder failed");
         }
-        return returns;
+
+        // 构建微信统一下单信息
+        return WechatUnifiedOrder.builder().mode(mode).appid(result.get("appid")).mchid(result.get("mch_id"))
+                .nonce(result.get("nonce_str")).prepayid(result.get("prepay_id"))
+                .qrcode(result.get("code_url")).build();
     }
 }

+ 8 - 3
framework-wechat/src/main/java/com/chelvc/framework/wechat/TradeType.java → framework-wechat/src/main/java/com/chelvc/framework/wechat/PayMode.java

@@ -1,12 +1,12 @@
 package com.chelvc.framework.wechat;
 
 /**
- * 微信支付交易类型枚举
+ * 微信支付方式枚举
  *
  * @author Woody
  * @date 2023/4/5
  */
-public enum TradeType {
+public enum PayMode {
     /**
      * APP
      */
@@ -15,5 +15,10 @@ public enum TradeType {
     /**
      * JSAPI
      */
-    JSAPI
+    JSAPI,
+
+    /**
+     * NATIVE
+     */
+    NATIVE;
 }

+ 6 - 26
framework-wechat/src/main/java/com/chelvc/framework/wechat/WechatHandler.java

@@ -3,8 +3,6 @@ package com.chelvc.framework.wechat;
 import java.util.Map;
 import java.util.function.Supplier;
 
-import com.chelvc.framework.wechat.config.WechatProperties;
-
 /**
  * 微信操作接口
  *
@@ -103,39 +101,21 @@ public interface WechatHandler {
      */
     WechatSession code2session(String code);
 
-    /**
-     * 获取支付配置
-     *
-     * @param type 支付类型
-     * @return 支付配置
-     */
-    WechatProperties.Payment getPayment(String type);
-
     /**
      * 参数签名
      *
-     * @param type       支付类型
+     * @param mode       支付方式
      * @param parameters 签名参数
      * @return 签名信息
      */
-    String sign(String type, Map<String, String> parameters);
-
-    /**
-     * App支付
-     *
-     * @param type    支付类型
-     * @param request 支付请求参数
-     * @return 支付结果
-     */
-    Map<String, String> pay(String type, WechatPayRequest request);
+    String sign(PayMode mode, Map<String, String> parameters);
 
     /**
-     * 小程序支付
+     * 微信统一下单
      *
-     * @param type    支付类型
-     * @param token   临时令牌
+     * @param mode    支付方式
      * @param request 支付请求参数
-     * @return 支付结果
+     * @return 微信统一下单信息
      */
-    Map<String, String> appletPay(String type, String token, WechatPayRequest request);
+    WechatUnifiedOrder unifiedorder(PayMode mode, WechatPayRequest request);
 }

+ 5 - 0
framework-wechat/src/main/java/com/chelvc/framework/wechat/WechatPayRequest.java

@@ -34,6 +34,11 @@ public class WechatPayRequest implements Serializable {
      */
     private BigDecimal amount;
 
+    /**
+     * 微信openid
+     */
+    private String openid;
+
     /**
      * 上下文参数
      */

+ 50 - 0
framework-wechat/src/main/java/com/chelvc/framework/wechat/WechatUnifiedOrder.java

@@ -0,0 +1,50 @@
+package com.chelvc.framework.wechat;
+
+import java.io.Serializable;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+
+/**
+ * 微信统一下单信息
+ *
+ * @author Woody
+ * @date 2023/6/21
+ */
+@Data
+@SuperBuilder
+@NoArgsConstructor
+@AllArgsConstructor
+public class WechatUnifiedOrder implements Serializable {
+    /**
+     * 支付方式
+     */
+    private PayMode mode;
+
+    /**
+     * AppID
+     */
+    private String appid;
+
+    /**
+     * 商户标识
+     */
+    private String mchid;
+
+    /**
+     * 随机串
+     */
+    private String nonce;
+
+    /**
+     * 预支付ID
+     */
+    private String prepayid;
+
+    /**
+     * 二维码地址
+     */
+    private String qrcode;
+}

+ 3 - 2
framework-wechat/src/main/java/com/chelvc/framework/wechat/config/WechatProperties.java

@@ -3,6 +3,7 @@ package com.chelvc.framework.wechat.config;
 import java.util.Collections;
 import java.util.List;
 
+import com.chelvc.framework.wechat.PayMode;
 import lombok.Data;
 import org.springframework.boot.context.properties.ConfigurationProperties;
 import org.springframework.context.annotation.Configuration;
@@ -79,9 +80,9 @@ public class WechatProperties {
     @Data
     public static class Payment {
         /**
-         * 支付类型
+         * 支付方式
          */
-        private String type;
+        private PayMode mode;
 
         /**
          * 支付ID