Browse Source

优化微信支付和消息推送

lighter 1 year ago
parent
commit
7d4c16c992

+ 13 - 12
src/main/java/thyyxxk/wxservice_server/api/WxAppletApi.java

@@ -133,7 +133,7 @@ public class WxAppletApi {
         request.setDescription(order.getDescription());
         request.setAppid(appId);
         request.setMchid(mchId);
-        request.setNotifyUrl(PropertiesUtil.getLocalProperty("notifyUrl"));
+        request.setNotifyUrl(PropertiesUtil.getLocalProperty("jsapiNotifyUrl"));
         request.setOutTradeNo(tradeNo);
         Payer payer = new Payer();
         payer.setOpenid(order.getOpenId());
@@ -652,15 +652,16 @@ public class WxAppletApi {
         return collectionsService.getMyCollections(openId);
     }
 
-//    @GetMapping("/test")
-//    public ResultVo<Object> test() {
-//        WxAppletOrder order = new WxAppletOrder();
-//        order.setPatientId("317452-0");
-//        order.setCartIds(",2289,2290,2293,");
-//        order.setTradeNo(SnowFlakeId.instance().nextWxAppletTradeNo());
-//        order.setTotalAmount(335100);
-//        order.setSerialNo(String.valueOf(System.currentTimeMillis()));
-//        order.setPayDatetime(new Date());
-//        return saveAppletPayResultService.saveTemplateInfo(order);
-//    }
+    @GetMapping("/test")
+    public ResultVo<Object> test() {
+        WxAppletOrder order = new WxAppletOrder();
+        order.setPatientId("317452-0");
+        order.setCartIds(",2289,2290,2293,");
+        order.setTradeNo(SnowFlakeId.instance().nextWxAppletTradeNo());
+        order.setTotalAmount(335100);
+        order.setSerialNo(String.valueOf(System.currentTimeMillis()));
+        order.setPayDatetime(new Date());
+        return saveAppletPayResultService.saveTemplateInfo(order);
+    }
+
 }

+ 3 - 5
src/main/java/thyyxxk/wxservice_server/controller/WxApiController.java

@@ -16,7 +16,6 @@ import thyyxxk.wxservice_server.utils.PropertiesUtil;
 import thyyxxk.wxservice_server.utils.ResultVoUtil;
 
 import javax.servlet.http.HttpServletRequest;
-import java.io.IOException;
 import java.util.Map;
 
 /**
@@ -63,12 +62,12 @@ public class WxApiController {
     }
 
     @GetMapping("/queryOrderStateOnly")
-    public ResultVo<JSONObject> queryOrderStateOnly(@RequestParam("tradeNo") String tradeNo) throws IOException {
+    public ResultVo<JSONObject> queryOrderStateOnly(@RequestParam("tradeNo") String tradeNo) {
         return service.queryOrderStateOnly(tradeNo);
     }
 
     @PostMapping("/genMzPayQrcode")
-    public ResultVo<String> genMzPayQrcode(@RequestBody @Validated GenMzPayQrcodeParam param) throws Exception {
+    public ResultVo<String> genMzPayQrcode(@RequestBody @Validated GenMzPayQrcodeParam param) {
         return service.generateMzGuideBillPayQrcode(param);
     }
 
@@ -79,8 +78,7 @@ public class WxApiController {
 
     @PostMapping("/pushMessage2")
     public String pushMessage2(@RequestBody JSONObject param) {
-        boolean result = pushWxMessageService.pushMessage2(param);
-        return result ? "SUCCESS" : "FAIL";
+        return pushWxMessageService.pushMessage2(param);
     }
 
     @PostMapping("/getWxPayQrcode")

+ 7 - 9
src/main/java/thyyxxk/wxservice_server/controller/WxPayNotifyController.java

@@ -3,13 +3,11 @@ package thyyxxk.wxservice_server.controller;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
-import thyyxxk.wxservice_server.entity.wxapi.PaymentNotify;
 import thyyxxk.wxservice_server.service.WxPayNotifyService;
 
-import java.security.GeneralSecurityException;
+import javax.servlet.http.HttpServletRequest;
 
 /**
  * @author dj
@@ -25,13 +23,13 @@ public class WxPayNotifyController {
         this.service = service;
     }
 
-    @PostMapping("/notify")
-    public void paymentNotify(@RequestBody PaymentNotify param) throws Exception {
-        service.paymentNotify(param);
+    @PostMapping("/native")
+    public void paymentNotify(HttpServletRequest request) throws Exception {
+        service.nativeNotify(request);
     }
 
-    @PostMapping("/notify2")
-    public void paymentNotify2(@RequestBody PaymentNotify param) throws Exception {
-        service.paymentNotify2(param);
+    @PostMapping("/jsapi")
+    public void paymentNotify3(HttpServletRequest request) throws Exception {
+        service.jsapiNotify(request);
     }
 }

+ 2 - 2
src/main/java/thyyxxk/wxservice_server/dao/WxApiDao.java

@@ -50,7 +50,7 @@ public interface WxApiDao {
     @Update("update t_wechat_pay_order set trade_state=#{tradeState},pay_datetime=#{successTime},open_id=#{openId}, " +
             "query_state_times=(query_state_times+1),last_query_state=getdate() where trade_no=#{tradeNo}")
     void updatePayStatusAndPayTimeAndOpenId(@Param("tradeNo") String tradeNo,
-                                            @Param("tradeState") String tradeState,
+                                            @Param("tradeState") Transaction.TradeStateEnum tradeState,
                                             @Param("successTime") String successTime,
                                             @Param("openId") String openId);
 
@@ -62,7 +62,7 @@ public interface WxApiDao {
     @Update("update t_wechat_pay_order set trade_state=#{tradeState},query_state_times=(query_state_times+1), " +
             "open_id=#{openId},last_query_state=getdate() where trade_no=#{tradeNo}")
     void updatePayStatusAndQueryTimesAndOpenId(@Param("tradeNo") String tradeNo,
-                                               @Param("tradeState") String tradeState,
+                                               @Param("tradeState") Transaction.TradeStateEnum tradeState,
                                                @Param("openId") String openId);
 
     @Update("update t_wechat_pay_order set trade_state=#{tradeState} where trade_no=#{tradeNo}")

+ 1 - 1
src/main/java/thyyxxk/wxservice_server/scheduled/QuestionnaireAfterVisit.java

@@ -101,7 +101,7 @@ public class QuestionnaireAfterVisit {
                 "\"template_id\":\"G4YAN56RmDjEPpNyP5fpCdr5TghyqspDeWlWaD5Eg2o\"," +
                 "\"url\":\"" + url + "\"}";
         JSONObject message = JSONObject.parseObject(msgContent);
-        if (messageService.pushMessage2(message)) {
+        if (messageService.pushMessage2(message).equals("SUCCESS")) {
             if (visit.getType() == 1) {
                 dao.updateQuestionnaireTime(visit.getPatientId());
             } else {

+ 12 - 3
src/main/java/thyyxxk/wxservice_server/service/PushWxMessageService.java

@@ -48,17 +48,26 @@ public class PushWxMessageService {
         }
     }
 
-    public boolean pushMessage2(JSONObject msgContent) {
+    public String pushMessage2(JSONObject msgContent) {
         RestTemplate template = new RestTemplate();
         String wxUrl = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=" +
                 PropertiesUtil.getLocalProperty("access_token");
         String res = template.postForObject(wxUrl, msgContent, String.class);
         log.info("推送消息:内容:{},结果:{}", msgContent, res);
         if (StringUtil.isBlank(res)) {
-            return false;
+            return "推送失败,未知的异常。";
         }
         JSONObject resobj = JSONObject.parseObject(res);
         Integer errcode = resobj.getInteger("errcode");
-        return (null != errcode && 0 == errcode);
+        if (null == errcode) {
+            return "推送失败,未知的异常。";
+        }
+        if (0 == errcode) {
+            return "SUCCESS";
+        }
+        if (43004 == errcode) {
+            return "患者未关注公众号,无法推送。";
+        }
+        return resobj.getString("errmsg");
     }
 }

+ 2 - 2
src/main/java/thyyxxk/wxservice_server/service/WxApiService.java

@@ -83,7 +83,7 @@ public class WxApiService {
         String merchantId = PropertiesUtil.getLocalProperty("mchId");
         String tradeNo = SnowFlakeId.instance().nextId();
         int totalFee = DecimalTool.moneyYuanToFen(param.getTotalFee()).intValue();
-        String notifyUrl = "http://staticweb.hnthyy.cn/wxserver/wxPayNotify/notify2";
+        String notifyUrl = "http://staticweb.hnthyy.cn/wxserver/wxPayNotify/jsapi";
         Config config = WxPayConfigUtil.getInstance().getConfig();
         JsapiServiceExtension service = new JsapiServiceExtension .Builder().config(config).build();
         PrepayRequest request = new PrepayRequest();
@@ -320,7 +320,7 @@ public class WxApiService {
         request.setDescription(description);
         request.setAppid(PropertiesUtil.getLocalProperty("appId"));
         request.setMchid(PropertiesUtil.getLocalProperty("mchId"));
-        request.setNotifyUrl(PropertiesUtil.getLocalProperty("notifyUrl"));
+        request.setNotifyUrl(PropertiesUtil.getLocalProperty("nativeNotifyUrl"));
         request.setOutTradeNo(SnowFlakeId.instance().nextNativePayTradeNo());
         com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse response = service.prepay(request);
         log.info("获取微信支付二维码:\n参数:{}\n结果:{}", JSONObject.toJSON(request), response.getCodeUrl());

+ 46 - 34
src/main/java/thyyxxk/wxservice_server/service/WxPayNotifyService.java

@@ -1,18 +1,20 @@
 package thyyxxk.wxservice_server.service;
 
-import com.alibaba.fastjson.JSONObject;
+import com.wechat.pay.java.core.RSAAutoCertificateConfig;
+import com.wechat.pay.java.core.notification.NotificationParser;
+import com.wechat.pay.java.core.notification.RequestParam;
 import com.wechat.pay.java.service.payments.model.Transaction;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import thyyxxk.wxservice_server.constant.OrderType;
 import thyyxxk.wxservice_server.dao.WxApiDao;
-import thyyxxk.wxservice_server.entity.wxapi.PaymentNotify;
 import thyyxxk.wxservice_server.entity.wxapi.WxPayOrder;
 import thyyxxk.wxservice_server.utils.TradeVectorUtil;
-import thyyxxk.wxservice_server.utils.WxPaySignUtil;
+import thyyxxk.wxservice_server.utils.wxpay.WxPayConfigUtil;
 
-import java.nio.charset.StandardCharsets;
+import javax.servlet.http.HttpServletRequest;
+import java.io.BufferedReader;
 
 /**
  * @author dj
@@ -29,40 +31,30 @@ public class WxPayNotifyService {
         this.savePayResultService = savePayResultService;
     }
 
-    public void paymentNotify(PaymentNotify param) throws Exception {
-        log.info("微信扫码支付通知: {}", param);
-        String nonce = param.getResource().getNonce();
-        String associatedData = param.getResource().getAssociated_data();
-        String ciphertext = param.getResource().getCiphertext();
-        ciphertext = WxPaySignUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),
-                nonce.getBytes(StandardCharsets.UTF_8), ciphertext);
-        JSONObject cipherObj = JSONObject.parseObject(ciphertext);
-        log.info("微信扫码支付通知解密:{}", ciphertext);
-        JSONObject payer = cipherObj.getJSONObject("payer");
-        String tradeNo = cipherObj.getString("out_trade_no");
+    public void nativeNotify(HttpServletRequest request) throws Exception {
+        Transaction transaction = decryptNotifyBody(request);
+        String tradeNo = transaction.getOutTradeNo();
         WxPayOrder order = dao.selectOrderByTradeNo(tradeNo);
-        if (null != order) {
-            Transaction.TradeStateEnum tradeState = order.getTradeState();
-            if (tradeState == Transaction.TradeStateEnum.SUCCESS ||
-                    tradeState == Transaction.TradeStateEnum.REFUND) {
-                log.info("订单号【{}】已解密过,无需继续解密。", tradeNo);
-                return;
-            }
+        Transaction.TradeStateEnum tradeState = order.getTradeState();
+        if (tradeState == Transaction.TradeStateEnum.SUCCESS ||
+                tradeState == Transaction.TradeStateEnum.REFUND) {
+            log.info("订单号【{}】已解密过,无需继续解密。", tradeNo);
+            return;
         }
         if (TradeVectorUtil.tradeNoBeingQuery(tradeNo)) {
             log.info("订单号【{}】正在业务中,无需继续解密。", tradeNo);
             return;
         }
-        String openId = payer.getString("openid");
-        String tradeStateStr = cipherObj.getString("trade_state");
-        if (!tradeStateStr.equals("SUCCESS")) {
-            dao.updatePayStatusAndQueryTimesAndOpenId(tradeNo, tradeStateStr, openId);
+        String openId = transaction.getPayer().getOpenid();
+        tradeState = transaction.getTradeState();
+        if (tradeState != Transaction.TradeStateEnum.SUCCESS) {
+            dao.updatePayStatusAndQueryTimesAndOpenId(tradeNo, tradeState, openId);
             return;
         }
-        String successTime = cipherObj.getString("success_time")
+        order.setTradeState(Transaction.TradeStateEnum.SUCCESS);
+        String successTime = transaction.getSuccessTime()
                 .split("\\+")[0].replace("T", " ");
-        dao.updatePayStatusAndPayTimeAndOpenId(tradeNo, tradeStateStr, successTime, openId);
-        order = dao.selectOrderByTradeNo(tradeNo);
+        dao.updatePayStatusAndPayTimeAndOpenId(tradeNo, tradeState, successTime, openId);
         OrderType orderType = OrderType.get(order.getOrderType());
         switch (orderType) {
             case OUTPATIENT:
@@ -74,10 +66,30 @@ public class WxPayNotifyService {
         }
     }
 
-    public void paymentNotify2(PaymentNotify param) throws Exception {
-        log.info("微信支付回调2:{}", param);
-        String ciphertext = WxPaySignUtil.decryptToString(param.getResource().getAssociated_data().getBytes(StandardCharsets.UTF_8),
-                param.getResource().getNonce().getBytes(StandardCharsets.UTF_8), param.getResource().getCiphertext());
-        log.info("微信支付回调解密2:{}", ciphertext);
+    public void jsapiNotify(HttpServletRequest request) throws Exception {
+        decryptNotifyBody(request);
+    }
+
+    private Transaction decryptNotifyBody(HttpServletRequest request) throws Exception {
+        BufferedReader reader = request.getReader();
+        StringBuilder requestBody = new StringBuilder();
+        String line;
+        while ((line = reader.readLine()) != null) {
+            requestBody.append(line);
+        }
+        RequestParam requestParam = new RequestParam.Builder()
+                .serialNumber(request.getHeader("Wechatpay-Serial"))
+                .nonce(request.getHeader("Wechatpay-Nonce"))
+                .signature(request.getHeader("Wechatpay-Signature"))
+                .timestamp(request.getHeader("Wechatpay-Timestamp"))
+                .body(requestBody.toString())
+                .build();
+
+        log.info("微信回调SDK解密开始:{}", requestParam);
+        RSAAutoCertificateConfig config = WxPayConfigUtil.getInstance().getConfig();
+        NotificationParser parser = new NotificationParser(config);
+        Transaction transaction = parser.parse(requestParam, Transaction.class);
+        log.info("微信回调SDK解密结束:{}", transaction);
+        return transaction;
     }
 }

+ 0 - 16
src/main/java/thyyxxk/wxservice_server/utils/WxPaySignUtil.java

@@ -1,11 +1,6 @@
 package thyyxxk.wxservice_server.utils;
 
-import javax.crypto.Cipher;
-import javax.crypto.spec.GCMParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-import java.nio.charset.StandardCharsets;
 import java.security.*;
-import java.util.*;
 
 /**
  * @author dj
@@ -13,8 +8,6 @@ import java.util.*;
 public class WxPaySignUtil {
     private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5',
             '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
-    private static final int TAG_LENGTH_BIT = 128;
-    private static final byte[] AES_KEY = PropertiesUtil.getLocalProperty("mchApiV3Key").getBytes(StandardCharsets.UTF_8);
 
     /**
      * Takes the raw bytes from the digest and formats them correct.
@@ -41,13 +34,4 @@ public class WxPaySignUtil {
         messageDigest.update(str.getBytes());
         return getFormattedText(messageDigest.digest());
     }
-
-    public static String decryptToString(byte[] associatedData, byte[] nonce, String ciphertext) throws Exception {
-        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
-        SecretKeySpec key = new SecretKeySpec(AES_KEY, "AES");
-        GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);
-        cipher.init(Cipher.DECRYPT_MODE, key, spec);
-        cipher.updateAAD(associatedData);
-        return new String(cipher.doFinal(Base64.getDecoder().decode(ciphertext)), StandardCharsets.UTF_8);
-    }
 }

+ 14 - 4
src/main/java/thyyxxk/wxservice_server/utils/wxpay/WxPayConfigUtil.java

@@ -7,7 +7,11 @@ import thyyxxk.wxservice_server.utils.PropertiesUtil;
 
 public class WxPayConfigUtil {
     private final static String PRIVATE_KEY_PATH;
-    private Config config = null;
+
+//    private Config config = null;
+
+    private RSAAutoCertificateConfig config = null;
+
 
     static {
         if (Constants.WINDOWS_10.equals(PropertiesUtil.getLocalProperty(Constants.OS_NAME))) {
@@ -32,15 +36,21 @@ public class WxPayConfigUtil {
         return INSTANCE;
     }
 
-    public Config getConfig() {
+//    public Config getConfig() {
+//        if (null == config) {
+//            config = initAutoCertificateConfig();
+//        }
+//        return config;
+//    }
+
+    public RSAAutoCertificateConfig getConfig() {
         if (null == config) {
             config = initConfig();
         }
         return config;
     }
 
-
-    private Config initConfig() {
+    private RSAAutoCertificateConfig initConfig() {
         return new RSAAutoCertificateConfig.Builder()
                 .merchantId(PropertiesUtil.getLocalProperty("mchId"))
                 .privateKeyFromPath(PRIVATE_KEY_PATH)

+ 4 - 2
src/main/resources/weChatOfficialAccounts.properties

@@ -1,4 +1,4 @@
-osName=
+osName=5
 access_token=
 ticket=
 
@@ -8,7 +8,9 @@ mchId=1574204121
 mchKey=lilaiwflzIOLJI2320JLZL2Llisd02ak
 mchApiV3Key=zxkkOIL9Z909lkjl2lzczi2KLsol2wct
 mchSerialNo=6574E696C4DB0B93EBD0D29B5DE809F31E04FC6D
-notifyUrl=http://staticweb.hnthyy.cn/wxserver/wxPayNotify/notify
+
+jsapiNotifyUrl=http://staticweb.hnthyy.cn/wxserver/wxPayNotify/jsapi
+nativeNotifyUrl=http://staticweb.hnthyy.cn/wxserver/wxPayNotify/native
 
 qywxCorpId=wwf0b23c8b36012b34
 qywxSecret=wpHuNePfiDyotmpXjy5hUYGF0w8Ks5OPHSQp22z8oBk