Explorar o código

Merge branch 'package' of GameCenter/game-center into dev

zhimo hai 1 ano
pai
achega
5984b4dc5c

+ 6 - 0
game-module/game-module-mybatis/src/main/java/com/zanxiang/game/module/mybatis/entity/Order.java

@@ -131,6 +131,12 @@ public class Order implements Serializable {
      */
     private String merchantOrderNo;
 
+    /**
+     * 米大师商户订单号
+     * PS : 微信和支付宝都是以业务订单号作为商户订单号, 只有米大师个狗东西不一样, 独有字段
+     */
+    private String miPayMerchantNo;
+
     /**
      * 收款商户号
      */

+ 1 - 1
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/SDKApplication.java

@@ -23,7 +23,7 @@ public class SDKApplication {
 
     public static void main(String[] args) {
         SpringApplication.run(SDKApplication.class, args);
-        System.out.println("赞象SDK服务启动成功 <dubbo升级3.0, 米大师支付逻辑bug修改> ( ´・・)ノ(._.`) \n" +
+        System.out.println("赞象SDK服务启动成功 <dubbo升级3.0, 米大师支付回调上线> ( ´・・)ノ(._.`) \n" +
                 " ___________ _   __\n" +
                 "/  ___|  _  \\ | / /\n" +
                 "\\ `--.| | | | |/ / \n" +

+ 127 - 0
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/pojo/dto/AppletMsgDTO.java

@@ -10,6 +10,36 @@ import lombok.Data;
 @Data
 public class AppletMsgDTO {
 
+    /**
+     * 客户文本消息
+     */
+    public static final String MSG_TYPE_TEXT = "text";
+
+    /**
+     * 回调事件消息
+     */
+    public static final String MSG_TYPE_EVENT = "event";
+
+    /**
+     * 米大师支付回调事件
+     */
+    public static final String EVENT_MI_PAY_CALL_BACK = "minigame_coin_deliver_completed";
+
+    /**
+     * 用户进入会话事件
+     */
+    public static final String EVENT_USER_ENTER_TEMP_SESSION = "user_enter_tempsession";
+
+    /**
+     * 客服进入会话事件
+     */
+    public static final String EVENT_USER_KF_CREATE_SESSION = "kf_create_session";
+
+    /**
+     * 客服支付约定消息文本
+     */
+    public static final String MSG_CONTENT_PAY = "2";
+
     /**
      * 开发者微信号
      */
@@ -30,6 +60,11 @@ public class AppletMsgDTO {
      */
     private String MsgType;
 
+    /**
+     * 事件类型
+     */
+    private String Event;
+
     /**
      * 文本消息内容
      */
@@ -39,4 +74,96 @@ public class AppletMsgDTO {
      * 消息id,64位整型
      */
     private Long MsgId;
+
+    /**
+     * 米大师支付回调通知内容
+     */
+    private MiniGameBean MiniGame;
+
+    @Data
+    public static class MiniGameBean {
+
+        /**
+         * 支付参数信息
+         */
+        private PayloadBean Payload;
+
+        /**
+         * 支付参数的签名算法
+         */
+        private String PayEventSig;
+
+        /**
+         * 是否是模拟数据
+         */
+        private Boolean IsMock;
+    }
+
+    @Data
+    public static class PayloadBean {
+
+        /**
+         * 用户openid
+         */
+        private String OpenId;
+
+        /**
+         * 业务订单号
+         */
+        private String OutTradeNo;
+
+        /**
+         * 微信支付信息
+         */
+        private WeChatPayInfoBean WeChatPayInfo;
+
+        /**
+         * 环境配置, 0:现网环境(也叫正式环境), 1:沙箱环境
+         */
+        private Long Env;
+
+        /**
+         * 游戏币信息
+         */
+        private CoinInfoBean CoinInfo;
+    }
+
+    @Data
+    public static class WeChatPayInfoBean {
+
+        /**
+         * 微信支付商户单号
+         */
+        private String MchOrderNo;
+
+        /**
+         * 交易单号(微信支付订单号)
+         */
+        private String TransactionId;
+
+    }
+
+    @Data
+    public static class CoinInfoBean {
+
+        /**
+         * 游戏道具id标识
+         */
+        private String ZoneId;
+
+        /**
+         * 实际支付价格(分)
+         */
+        private Long ActualPrice;
+
+        /**
+         * 购买数量
+         */
+        private Long BuyQuantity;
+
+        /**
+         * 原始价格(分)
+         */
+        private Long OrigPrice;
+    }
 }

+ 74 - 23
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/impl/GameAppletServiceImpl.java

@@ -17,6 +17,7 @@ import com.zanxiang.game.module.sdk.pojo.param.UserData;
 import com.zanxiang.game.module.sdk.pojo.vo.GameInitVO;
 import com.zanxiang.game.module.sdk.service.*;
 import com.zanxiang.game.module.sdk.service.api.WxApiService;
+import com.zanxiang.game.module.sdk.service.pay.MiPayService;
 import com.zanxiang.game.module.sdk.util.SignUtil;
 import com.zanxiang.module.util.JsonUtil;
 import com.zanxiang.module.util.bean.BeanUtil;
@@ -28,9 +29,11 @@ import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.web.client.RestTemplate;
 import org.springframework.web.util.UriComponentsBuilder;
+import reactor.util.function.Tuples;
 
 import java.math.BigDecimal;
 import java.net.URI;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.Objects;
@@ -68,6 +71,9 @@ public class GameAppletServiceImpl extends ServiceImpl<GameAppletMapper, GameApp
     @Autowired
     private IGameExtService gameExtService;
 
+    @Autowired
+    private MiPayService miPayService;
+
     @Value("${payConfig.wxPay.customH5Url}")
     private String customH5Url;
 
@@ -108,32 +114,77 @@ public class GameAppletServiceImpl extends ServiceImpl<GameAppletMapper, GameApp
         }
         //消息内容
         AppletMsgDTO appletMsgDTO = JsonUtil.toObj(postData, AppletMsgDTO.class);
+        //用户客服支付会话
+        if (Objects.equals(appletMsgDTO.getMsgType(), AppletMsgDTO.MSG_TYPE_TEXT)
+                && Objects.equals(appletMsgDTO.getContent(), AppletMsgDTO.MSG_CONTENT_PAY)) {
+            return this.customPayMessage(gameAppletDTO, appletMsgDTO);
+        }
+        //米大师支付回调事件
+        if (Objects.equals(appletMsgDTO.getMsgType(), AppletMsgDTO.MSG_TYPE_EVENT)
+                && Objects.equals(appletMsgDTO.getEvent(), AppletMsgDTO.EVENT_MI_PAY_CALL_BACK)) {
+            return this.miPayMessage(appletMsgDTO, gameAppletDTO);
+        }
+        //其他消息不处理, 直接返回成功
+        return result;
+    }
+
+    private String miPayMessage(AppletMsgDTO appletMsgDTO, GameAppletDTO gameAppletDTO) {
+        log.error("米大师支付回调 appletMsgDTO : {}", JsonUtil.toString(appletMsgDTO));
+        String failResult = JsonUtil.toString(Collections.singletonMap(99999L, "internal error"));
+        String successResult = JsonUtil.toString(Collections.singletonMap(0L, "Success"));
+        //通知内容
+        AppletMsgDTO.MiniGameBean miniGame = appletMsgDTO.getMiniGame();
+        //判断是否为模拟数据, 模拟数据直接返回, 不进行业务处理
+        if (Objects.equals(miniGame.getIsMock(), Boolean.TRUE)) {
+            return successResult;
+        }
+        //支付内容
+        AppletMsgDTO.PayloadBean payload = miniGame.getPayload();
+        if (payload == null) {
+            log.error("米大师支付回调参数错误, 没有支付信息 appletMsgDTO : {}", JsonUtil.toString(appletMsgDTO));
+            return failResult;
+        }
+        //商户订单号和交易订单号判断
+        AppletMsgDTO.WeChatPayInfoBean weChatPayInfo = payload.getWeChatPayInfo();
+        if (weChatPayInfo == null || Strings.isBlank(weChatPayInfo.getMchOrderNo())
+                || Strings.isBlank(weChatPayInfo.getTransactionId())) {
+            log.error("米大师支付回调参数错误, 没有支付信息 appletMsgDTO : {}", JsonUtil.toString(appletMsgDTO));
+            return failResult;
+        }
+        //查询游戏小程序信息
+        String appKey = gameAppletDTO.getMiPayConfigBean().getAppKey();
+        //计算签名
+        String mySign = miPayService.calcSignature(appletMsgDTO.getEvent() + "&" + miniGame.getPayload(), appKey);
+        //签名不匹配
+        if (Objects.equals(mySign, miniGame.getPayEventSig())) {
+            log.error("米大师支付回调签名不匹配, mySign : {}, PayEventSig : {}, appKey : {}",
+                    mySign, miniGame.getPayEventSig(), appKey);
+            return failResult;
+        }
+        log.error("米大师支付回调签名匹配成功, 开始计算业务数据--------------->");
+        //更新订单信息
+        Boolean miPayNotifyResult = miPayService.miPayNotify(payload.getOutTradeNo(),
+                Tuples.of(weChatPayInfo.getMchOrderNo(), weChatPayInfo.getTransactionId()));
+        //放业务更新结果
+        return Objects.equals(miPayNotifyResult, Boolean.TRUE) ? successResult : failResult;
+    }
+
+    private String customPayMessage(GameAppletDTO gameAppletDTO, AppletMsgDTO appletMsgDTO) {
         //用户信息
         UserDTO userDTO = userService.getUserByOpenId(gameAppletDTO.getGameId(), appletMsgDTO.getFromUserName());
-        //客服支付
-        if (Objects.equals(appletMsgDTO.getMsgType(), "text") && Objects.equals(appletMsgDTO.getContent(), "2")) {
-            //查询用户最新客服支付订单
-            Order order = orderService.getOne(new LambdaQueryWrapper<Order>()
-                    .eq(Order::getUserId, userDTO.getId())
-                    .eq(Order::getStatus, OrderStateEnum.READY_PAY.getCode())
-                    .eq(Order::getPayWayId, PayWayEnum.WX_PAY.getPayWayId())
-                    .eq(Order::getPayDeviceId, PayDeviceEnum.CUSTOM_PAY.getPayDeviceId())
-                    .orderByDesc(Order::getCreateTime)
-                    .last("limit 1"));
-            if (order != null) {
-                //发送客服消息
-                return this.sendCustomMessage(gameAppletDTO, userDTO.getOpenId(), order);
-            }
+        //查询用户最新客服支付订单
+        Order order = orderService.getOne(new LambdaQueryWrapper<Order>()
+                .eq(Order::getUserId, userDTO.getId())
+                .eq(Order::getStatus, OrderStateEnum.READY_PAY.getCode())
+                .eq(Order::getPayWayId, PayWayEnum.WX_PAY.getPayWayId())
+                .eq(Order::getPayDeviceId, PayDeviceEnum.CUSTOM_PAY.getPayDeviceId())
+                .orderByDesc(Order::getCreateTime)
+                .last("limit 1"));
+        if (order != null) {
+            //发送客服消息
+            return this.sendCustomMessage(gameAppletDTO, userDTO.getOpenId(), order);
         }
-        return result;
-//        //非客服支付, 回复通用消息
-//        GameExt gameExt = gameExtService.getByGameId(userDTO.getGameId());
-//        String text = "尊敬的玩家您好,请您用QQ添加:" + gameExt.getCustomerQq()
-//                + ",联系官方客服哦,客服目前排队比较多,请您耐心等待,请您备注好区服角色,方便客服第一时间为您处理问题";
-//        //客服消息参数构造
-//        Map<String, Object> textMap = new HashMap<>(1);
-//        textMap.put("content", text);
-//        return this.sendCustomMessageApi(gameAppletDTO, userDTO.getOpenId(), "text", textMap);
+        return HttpStatusEnum.SUCCESS.getMsg();
     }
 
     private String sendCustomMessage(GameAppletDTO gameAppletDTO, String openId, Order order) {

+ 54 - 1
game-module/game-module-sdk/src/main/java/com/zanxiang/game/module/sdk/service/pay/MiPayService.java

@@ -1,9 +1,11 @@
 package com.zanxiang.game.module.sdk.service.pay;
 
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
 import com.zanxiang.game.module.base.pojo.enums.HttpStatusEnum;
 import com.zanxiang.game.module.mybatis.entity.Order;
 import com.zanxiang.game.module.sdk.enums.OrderStateEnum;
+import com.zanxiang.game.module.sdk.listener.OrderPaySuccessEvent;
 import com.zanxiang.game.module.sdk.pojo.dto.*;
 import com.zanxiang.game.module.sdk.service.IGameAppletService;
 import com.zanxiang.game.module.sdk.service.IOrderService;
@@ -16,6 +18,7 @@ import lombok.extern.slf4j.Slf4j;
 import org.apache.logging.log4j.util.Strings;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.ApplicationContext;
 import org.springframework.stereotype.Service;
 import org.springframework.web.client.RestTemplate;
 import reactor.util.function.Tuple2;
@@ -67,6 +70,9 @@ public class MiPayService extends PayBaseService {
     @Autowired
     private IUserService userService;
 
+    @Autowired
+    private ApplicationContext applicationContext;
+
     /**
      * 支付配置
      */
@@ -211,7 +217,7 @@ public class MiPayService extends PayBaseService {
         return this.calcSignature(needSignMsg, appKey);
     }
 
-    private String calcSignature(String postBody, String sessionKey) {
+    public String calcSignature(String postBody, String sessionKey) {
         try {
             Mac hmacSha256 = Mac.getInstance(MiPayService.SIGN_HMACSHA256);
             SecretKeySpec secretKey = new SecretKeySpec(sessionKey.getBytes(StandardCharsets.UTF_8), MiPayService.SIGN_HMACSHA256);
@@ -267,6 +273,53 @@ public class MiPayService extends PayBaseService {
         return payParamMap;
     }
 
+    public boolean miPayNotify(String orderId, Tuple2<String, String> weChatPayInfoTuple2) {
+        //查询订单
+        PlatformOrderDTO platformOrderDTO = orderService.getByOrderId(orderId);
+        if (platformOrderDTO == null) {
+            log.error("参数错误, 订单信息不存在! orderId : {}", orderId);
+            return Boolean.FALSE;
+        }
+        this.configInit(platformOrderDTO.getGameId());
+        //获取用户openId
+        UserDTO userDTO = userService.getUserByUserId(platformOrderDTO.getUserId());
+        String openId = userDTO.getOpenId();
+        String sessionKey = userDTO.getSessionKey();
+        //获取米大师钱包余额
+        Long balance = this.midasGetBalance(openId, sessionKey);
+        //余额不足, 无法扣除
+        if (balance < platformOrderDTO.getAmount().longValue()) {
+            log.error("米大师游戏币不足, 无法扣除! balance : {}, orderId : {}", balance, orderId);
+            return Boolean.FALSE;
+        }
+        //余额充足, 直接扣除
+        Tuple2<Long, String> tuple2 = this.midasPay(openId, sessionKey, platformOrderDTO.getAmount(), platformOrderDTO.getOrderId());
+        log.error("米大师余额扣除返回, orderId : {}, tuple2 : {}", orderId, JsonUtil.toString(tuple2));
+        //支付成功逻辑
+        this.miPaySuccess(platformOrderDTO.getOrderId(), platformOrderDTO.getAmount().toString(), weChatPayInfoTuple2);
+        //返回成功
+        return Boolean.TRUE;
+    }
+
+    private void miPaySuccess(String orderNo, String realAmount, Tuple2<String, String> weChatPayInfoTuple2) {
+        Order order = orderService.getOne(new LambdaQueryWrapper<Order>().eq(Order::getOrderId, orderNo));
+        //订单不存在或者是已经支付成功的状态,直接返回成功
+        if (order == null || order.getStatus().equals(OrderStateEnum.SUCCESS_PAY.getCode())) {
+            return;
+        }
+        //订单更新
+        orderService.update(new LambdaUpdateWrapper<Order>()
+                .set(Order::getRealAmount, new BigDecimal(realAmount))
+                .set(Order::getMiPayMerchantNo, weChatPayInfoTuple2.getT1())
+                .set(Order::getMerchantOrderNo, weChatPayInfoTuple2.getT2())
+                .set(Order::getStatus, OrderStateEnum.SUCCESS_PAY.getCode())
+                .set(Order::getPayTime, LocalDateTime.now())
+                .eq(Order::getOrderId, orderNo));
+        //发送订单履约消息
+        OrderPaySuccessEvent orderPaySuccessEvent = new OrderPaySuccessEvent(this, order.getOrderId());
+        applicationContext.publishEvent(orderPaySuccessEvent);
+    }
+
     @Override
     public String notify(HttpServletRequest request, HttpServletResponse response) {
         throw new BaseException("米大师支付不存在通用异步回调");