local skynet = require "skynet" local oo = require "Class" local gameCmd = require "GameCmd" local json =require "json" local log = require "Log" local sqlUrl = require "SqlUrl" local errorInfo = require "ErrorInfo" local serverId = tonumber(skynet.getenv "serverId") local redisKeyUrl = require "RedisKeyUrl" local clusterServer = require "ClusterServer" local crypt = require "skynet.crypt" local dataType = require "DataType" local serverManage = require "ServerManage" local mail = require "Mail" local personal = require "Personal" local sha1 = crypt.sha1 local PayServer = oo.class(clusterServer) PayServer.apiKey = "407dcc74075cee33f40e76f0ec2f5817" --初始化 function PayServer:Init() --self.orderList = {} --记录订单,防止重复 废除 self.diamondOrderList = {} --官网充值 钻石订单 self.WXOrderList = {} --微信通知 end --跨天 function PayServer:OnNewDay() end --1秒Timer function PayServer:On1SecTimer() end --5秒Timer function PayServer:On5SecTimer() --[[废除 local t1 = skynet.GetTime() for k, v in pairs( self.orderList ) do --删除6小时的订单记录 if t1 >= v + 21600 then self.orderList[ k ] = nil end end ]] end --接收集群数据 function PayServer:ClusterRecv(...) local cmd , c2sData = ... local s2cData = {} s2cData.code = errorInfo.Suc if self.Center2All_ServerManageCmd == cmd then --中心服集群消息 skynet.server.serverManage:DoManageCmd( c2sData , s2cData ) elseif self.Center2All_SyncClusterInfo == cmd then self:RecvSyncClusterInfo( c2sData ) elseif self.Center2All_SyncServerConfig == cmd then --同步服务器配置 self:RecvSyncServerConfig( c2sData ) else log.warning(string.format("集群服务器 消息接口 %d 不存在", cmd)) s2cData.code = errorInfo.ErrorCode.NoExistInterface end log.info("集群服务器 消息接口", cmd , "返回信息",s2cData.code) return s2cData end --[[ [addr]=192.168.50.188, [sign]=8513b67c21afc1238adb734771cebdfc43b9c917, [paymentMethod]=$%7BpaymentMethod%7D, [timestamp]=1566793027, [rebate]=$%7BurlEncode%28rebateInfo%29%7D, [provider]=$%7Bprovider%7D, [currency]=CNY, [gameId]=h5_test, [price]=60.0, [extra]=$%7BurlEncode%28userdata%29%7D, [gameAccountId]=12345, [gameOrderId]=15667930060415003, [cpOrderId]=960198, [itemId]=com_1, [minPrice]=6000, } ]] --接收HTTP数据 function PayServer:HttpRecv( c2sData , url , addr ) local s2cData = {} s2cData.state = {} --组装签名参数{post方式可用,目前只有微信游戏圈发货采用} local signParam = {} if c2sData.bodyStr ~=nil and c2sData.bodyStr ~= "" then signParam["_body"]=c2sData.bodyStr:gsub("%s+", "") --删除多余参数{影响签名} c2sData.bodyStr = nil end if c2sData.getData ~= nil and type(c2sData.getData) == "string" then local getData = skynet.server.httpServer:Translate(c2sData.getData) if next(getData) then --合并参数 for key, value in pairs(getData) do c2sData[key]=value signParam[key]=value end end --删除多余参数{影响签名} c2sData.getData = nil end s2cData.state.code = errorInfo.Suc c2sData.addr = addr if "AddPayInfo" == url then self:AddPayInfo( c2sData , s2cData ) elseif "AddOfficialPayInfo" == url then self:AddOfficialPayInfo( c2sData , s2cData ) else log.info(string.format("Http服务器 消息接口 %s 不存在", url)) s2cData.code = errorInfo.ErrorCode.NoExistInterface end log.info("Http服务器 消息接口 ",url ,"返回信息",s2cData.code) return s2cData end --新增充值信息 function PayServer:AddPayInfo( c2sData , s2cData ) local clusterReturn = {} --c2sData.extra = skynet.server.common:UrlDecode( c2sData.extra or "" ) --先进行URL解码 --c2sData.extra = json:decode(c2sData.extra) --把JSON再转成表 --[[废除 if self.orderList[ c2sData.cpOrderId ] then s2cData.state.code = 1 --重复订单 return else self.orderList[ c2sData.cpOrderId ] = skynet.GetTime() end ]] --根据发的数据查找对应的配置 local cpOrderId = skynet.server.common:Split( c2sData.cpOrderId ,"_" ) cpOrderId = tonumber(cpOrderId[3]) local cfgStorePrice = skynet.server.gameConfig.StorePrice local cfgCur = nil for k, v in pairs(cfgStorePrice) do if cpOrderId == v.storePackId then cfgCur = v break end end --充值账号 local account = tostring(c2sData.gameAccountId) if not self:CheckPayInfo( c2sData , s2cData , cfgCur , cpOrderId ) then return else s2cData.state.code = 1 --进这里面都能表示成功 --充值信息 local sendData = {} sendData.account = account sendData.storeId = cfgCur.storePackId sendData.count = 1 --缓存失败的充值 local function CacheFailedAccount() --将失败数据插入到redis中去 --[[ local redisKey = redisKeyUrl.PayServerPayFailedAccountSet skynet.server.redis:sadd( redisKey , account ) redisKey = string.format( redisKeyUrl.PayServerPayFailedAccountList , account ) skynet.server.redis:rpush( redisKey , json:encode(sendData) ) --通知道其它游戏服 local tmpData = {} tmpData.account = account self:SendMsgToGameServer( self.Pay2Game_CacheFailedAccount , tmpData , s2cData ) log.info(string.format("充值 玩家 %s 充值失败,缓存到充值失败队列中 充值平台id %s", account , cfgCur.storePackId)) ]] local userDBInfo = skynet.server.redisCenter:GetRedisDBInfo( account ) if userDBInfo then --将充值失败的数据保存到离线消息中 local myPartnerId = userDBInfo.PartnerId skynet.server.personal:AddOfflineMsg( myPartnerId , dataType.OfflineMsgType_PayFailed , sendData , 45 , 0) else log.info(string.format("充值 玩家 %s 缓存失败的充值未成功 充值平台id %s", account , cfgCur.storePackId)) end end --获取玩家登陆信息 local redisKey = string.format( redisKeyUrl.RouteServerLoginInfoHash , account ) local isExistLogin = skynet.server.redis:exists( redisKey ) if isExistLogin then --存在玩家的登录信息 local gameServerId = tonumber(skynet.server.redis:hget( redisKey , "GameServerID")) if not gameServerId or not self.clusterInfo[ gameServerId ] then --并没有相关的游戏服 log.info(string.format("充值 玩家 %s 未找到该游戏服信息", account )) CacheFailedAccount() else log.info(string.format("充值 玩家 %s 开始发放充值 充值平台id %s", account , sendData.storeId)) local callData = {} local isSuc = self:SendMsgToOneGameServer( self.Pay2Game_AddPayInfo , gameServerId , sendData , callData ) if not isSuc then if errorInfo.ErrorCode.NoExistUser == callData.code then log.info(string.format("充值 玩家 %s 玩家未在线1", account )) CacheFailedAccount() else log.info(string.format("充值 玩家 %s 可能某个条件限制,发货失败 错误码 %d", account , callData.code)) s2cData.state.code = errorInfo.ErrorCode.PayInfoStoreLimit end end end else log.info(string.format("充值 玩家 %s 玩家未在线2", account )) CacheFailedAccount() end --将充值数据保存到DB local sql = string.format(sqlUrl.insertPayRecord , json:encode( c2sData )) local insertData = skynet.server.db:Query("game" , sql ) if 1 ~= insertData.affected_rows then log.info(string.format("充值 玩家 %s 插入服务器配置失败 %s", account , sql )) end end end --新增官网充值信息 function PayServer:AddOfficialPayInfo( c2sData , s2cData ) --测试代码注意取消注释 if self.diamondOrderList[ c2sData.paymentId ] then s2cData.state.code = 1 --重复订单 return end --参数 --local serverId = c2sData.serverId local account = tostring(c2sData.gameAccountId) --获取商品列表 local items = json:decode(skynet.server.common:UrlDecode(c2sData.items)) --判断配置是否为空 if items == nil or next(items) == nil then log.debug("官网充值 items is nil") s2cData.state.code = 2 --参数错误 return end --根据发的数据查找对应的配置 local cfgDiamondPrices = skynet.server.gameConfig.DiamondPrice local cfgEffective = {} for _, v in ipairs(cfgDiamondPrices) do for _, item in ipairs(items) do if tonumber(item.itemId) == v.diamondPackId then table.insert(cfgEffective, v) break end end end --检查配置 不准确 if next(cfgEffective) == nil or #cfgEffective ~= #items then s2cData.state.code = 2 --参数错误 log.info(string.format("官网充值 配置数量不正确 #cfgEffective :%d #items :%d",#cfgEffective , #items)) return end --验证签名 local sign = self:GenerateSign( c2sData ) log.info(string.format("充值 玩家 %s 生成的Sign %s 收到的Sign %s", account , sign , c2sData.sign)) if sign ~= c2sData.sign then s2cData.state.code = 2 --发货失败 log.info(string.format("充值 玩家 %s Sign不一致" , account )) return end s2cData.state.code = 1 --进这里面都能表示成功 --充值信息 local sendData = {} sendData.account = account sendData.items = items --缓存失败的充值 local function OfficialPayFailedAccount() --将失败数据插入到redis中去 local redisKey = redisKeyUrl.PayServerOfficialPayFailedAccountSet skynet.server.redis:sadd( redisKey , account ) redisKey = string.format( redisKeyUrl.PayServerOfficialPayFailedAccountList , account ) skynet.server.redis:rpush( redisKey , json:encode(sendData) ) --通知道其它游戏服 local tmpData = {} tmpData.account = account self:SendMsgToGameServer( self.Pay2Game_OfficialPayFailedAccount , tmpData , s2cData ) end local redisKey = string.format( redisKeyUrl.RouteServerLoginInfoHash , account ) local isExistLogin = skynet.server.redis:exists( redisKey ) if isExistLogin then --存在玩家的登录信息 local gameServerId = tonumber(skynet.server.redis:hget( redisKey , "GameServerID")) if not gameServerId or not self.clusterInfo[ gameServerId ] then --并没有相关的游戏服 log.info(string.format("官网充值 玩家 %s 未找到该游戏服信息", account )) OfficialPayFailedAccount() else local callData = {} local isSuc = self:SendMsgToOneGameServer( self.Pay2Game_OfficialAddPayInfo , gameServerId , sendData , callData ) if not isSuc then if errorInfo.ErrorCode.NoExistUser == callData.code then log.info(string.format("官网充值 玩家 %s 玩家未在线1", account )) OfficialPayFailedAccount() else log.info(string.format("官网充值 玩家 %s 可能某个条件限制,发货失败 错误码 %d", account , callData.code)) s2cData.state.code = 2 --发货失败 end end end else log.info(string.format("官网充值 玩家 %s 玩家未在线2", account )) OfficialPayFailedAccount() end --将充值数据保存到DB local sql = string.format(sqlUrl.insertPayRecord , json:encode( c2sData )) local insertData = skynet.server.db:Query("game" , sql ) if 1 ~= insertData.affected_rows then log.info(string.format("官网充值 玩家 %s 插入服务器配置失败 %s", account , sql )) end self.diamondOrderList[ c2sData.paymentId ] = skynet.GetTime() end --对充值信息的合法性检测 function PayServer:CheckPayInfo( c2sData , s2cData , cfgCur , cpOrderId) local account = tostring(c2sData.gameAccountId) local minPrice = c2sData.minPrice local buyPrice = cfgCur.price if not cfgCur then s2cData.state.code = errorInfo.ErrorCode.PayInfoNoCfg log.info(string.format("充值 玩家 %s 不存在充值礼包 %s 配置" , account , cpOrderId)) return false elseif minPrice ~= buyPrice then s2cData.state.code = errorInfo.ErrorCode.PayInfoPriceUnlike log.info(string.format("充值 玩家 %s 礼包 %d 价格不一致配置 %d %d" , account , cfgCur.storePackId , buyPrice , minPrice)) return false else local sign = self:GenerateSign( c2sData ) log.info(string.format("充值 玩家 %s 生成的Sign %s 收到的Sign %s", account , sign , c2sData.sign)) if sign ~= c2sData.sign then s2cData.state.code = errorInfo.ErrorCode.PayInfoSignUnlike log.info(string.format("充值 玩家 %s Sign不一致" , account )) return false end return true end end --生成签名 function PayServer:GenerateSign( c2sData ) local param = "" local allData = {} for k, v in pairs( c2sData ) do table.insert( allData , { key = k , value = v }) end --对参数进行一个升序 table.sort( allData , function(a,b) return a.key < b.key end) for k, v in pairs( allData ) do if "sign" ~= v.key and "addr" ~= v.key then log.debug("签名参数:",v.key,"值:",v.value) local tmpText = "" if "table" == type(v.value) then --如果是表需要进行转JSON,URL编码 tmpText = json:encode(v.value) tmpText = skynet.server.common:UrlEncode(tmpText) elseif "extra" == v.key and not skynet.server.common:IsUrlEncode( v.value ) then --extra如果未url编码就进行一次编码 tmpText = skynet.server.common:UrlEncode( v.value ) else tmpText = v.value end local tmp = v.key.."="..tmpText if "" == param then param = param .. tmp else param = param .. "&" param = param .. tmp end end end param = param..(skynet.server.gameConfig.BasicConfig.payApiKey or self.apiKey) --param = "cpOrderId=960198¤cy=CNY&gameAccountId=12345&gameId=h5_test&gameOrderId=15667930060415003&itemId=com_1&minPrice=6000&paymentMethod=${paymentMethod}&price=60.0&provider=${provider}&rebate=${urlEncode(rebateInfo)}×tamp=1566793027407dcc74075cee33f40e76f0ec2f5817" local sign = string.lower(skynet.server.common:HexToStr(sha1(param))) log.debug("加密前生成的参数组合:",param) log.debug("加密后的数据:",sign) return sign end --发送消息到GS function PayServer:SendMsgToGameServer( cmd , c2sData , s2cData) for k, v in pairs(self.clusterInfo) do if self:IsGameServer( v.serverId ) and v.status == self.Status_Running or v.status == self.Status_Stop then self:SendMsgToServer(v.serverId , cmd , c2sData ) end end end --发送消息到GS function PayServer:SendMsgToOneGameServer( cmd , gameServerId , sendData , callData ) local clusterReturn = {} local account = tostring(sendData.account) for k, v in pairs(self.clusterInfo) do if gameServerId == v.serverId and self:IsGameServer( v.serverId ) then log.info(string.format("充值 玩家 %s 充值服向游戏服 %d 发送消息 %d 开始" , account , v.serverId , cmd )) clusterReturn = self:CallMsgToServer( v.serverId , cmd , sendData ) if clusterReturn and errorInfo.Suc == clusterReturn.code then log.info(string.format("充值 玩家 %s 充值服向游戏服 %d 发送消息 %d 成功" , account , v.serverId , cmd )) callData.code = clusterReturn.code return true else log.info(string.format("充值 玩家 %s 充值服向游戏服 %d 发送消息 %d 失败" , account , v.serverId , cmd )) callData.code = clusterReturn.code return false end end end return false end skynet.server.payServer = PayServer return PayServer