import {ApiCall} from 'tsrpc'; import {YangChengMuBiaofun} from '../api_s2c/event/yangchengmubiao/fun'; import {MsgPayChange} from '../shared/protocols/msg_s2c/MsgPayChange'; import {payLog} from '../shared/protocols/pay/PtlGetList'; import {player} from '../shared/protocols/user/type'; import {PublicShared} from '../shared/public/public'; import {EmailFun} from './email'; import {HuoDongFun} from './huodongfun'; import {call, PlayerFun} from './player'; import {number} from "mathjs"; import {getGud} from './gud'; import {getConf as zmlbGetConf} from '../api_s2c/event/zhoumolibao/ApiOpen'; async function checkPayIsActive(payId: string, logs: payLog[], payArgs) { let conf: any = await this.getConf(payId, payArgs); if (!conf) return false; let lastLog = logs.last(); if (!lastLog) return false; if (conf.time == -1) return true; return G.time < lastLog.eTime; } export class PayFun { static async addPayLog(uid: string, payId: string, type: "system" | "user" | "legu", payArgs) { let conf: any = await this.getConf(payId, payArgs); let time = PublicShared.getToDayZeroTime(G.time); let obj: payLog = {time: G.time, type: type}; if (conf.time != -1) obj.eTime = time + conf.time; if (payId == 'G123SendGift') { obj.popup_id = payArgs.popup_id obj.template_id = payArgs.template_id obj.buyNumber = conf.buyNumber } let result = await G.mongodb.collection('payLogNew').findOneAndUpdate( {uid: uid, key: payId, del_time: {$exists: false}}, {$push: {values: obj}}, {upsert: true, returnDocument: 'after'} ); let msg: MsgPayChange = {[payId]: result.value?.values || []}; if (payId == 'G123SendGift') { G.server.sendMsgByUid(uid, 'msg_s2c/PayChange', R.groupBy(i => i.popup_id)(result.value?.values)); } else { G.server.sendMsgByUid(uid, 'msg_s2c/PayChange', msg); } } static changeG123GiftLog(uid: string, popupId: string) { G.mongodb.collection('giftLog').updateOne({popup_id: popupId}, {$inc: {buyNumber: 1}}, {upsert: true}); } static async delPayLog(uid: string, ...args: { payId: string, val: payLog[]; }[]) { args.map(a => { G.mongodb.collection('payLogNew').updateOne( {uid: uid, key: a.payId, del_time: {$exists: false}}, {$set: {del_time: G.time}}, {upsert: true} ); }) G.mongodb.collection('payLogNew').insertMany(args.map(i => ({key: i.payId, uid: uid, values: i.val}))); G.server.sendMsgByUid(uid, 'msg_s2c/PayChange', Object.fromEntries(args.map(a => [a.payId, a.val]))); } static async getPayLog(uid: string, payId: string): Promise; static async getPayLog(uid: string, payId: string) { // let allPlayerPayLog = await G.redis.hGet('player:payLog', uid) let val = await G.mongodb.collection("payLogNew").findOne({uid: uid, key: payId, del_time: {$exists: false}}, { projection: { _id: 0, uid: 0 } }); let allPlayerPayLog = {[payId]: val?.values || []} return allPlayerPayLog?.[payId] || []; } static async getPayLogs(uid: string, payIds?: string[]) { let where = { uid: uid, del_time: {$exists: false} } if (payIds?.length) where['key'] = {$in: payIds} let logs = await G.mongodb.collection("payLogNew").find(where, { projection: { _id: 0, uid: 0 } }).toArray(); if (payIds?.length) { return Object.fromEntries(payIds.map(id => [id, logs[id] || []])); } let allLogs = {} logs.map(i => allLogs[i.key] = i.values) return allLogs } /** * 获取支付配置,主要是针对G123礼包、GM后台内部充值的特殊处理 * 让这两个不在预设充值挡位内的,可以正常发奖 * @param payId * @param payArgs */ static async getConf(payId, payArgs) { let conf: any = G.gc.pay[payId] if (payId == 'G123SendGift') { let giftInfo = await G.mongodb.collection('giftLog').findOne({popup_id: payArgs.popup_id}); let gPrize = giftInfo.items let bPrize = [] for (let index in gPrize) { let args = index.split('^') bPrize.push({a: args[0], t: args[1], n: gPrize[index]}) } conf = { id: 'G123SendGift', money: giftInfo.price, payExp: [{"a": "attr", "t": "payExp", "n": giftInfo.vipPoints}], prize: bPrize, firstPayPrize: [], name: 'G123SendGift', time: -1, buys: giftInfo.purchaseLimitAmount, needVip: 0, front: {}, buyNumber: giftInfo.buyNumber } } else if (payId == 'LeguSendGift') { conf = { id: 'LeguSendGift', money: payArgs.money, payExp: payArgs.payExp, prize: payArgs.prize, firstPayPrize: [], name: 'LeguSendGift', time: -1, buys: 0, needVip: 0, front: {} } } return conf } /** * 实际作用是支付后发奖 * 针对G123礼包有特殊处理,不在游戏配置内预设的充值挡位 * todo 目前还缺验证通道,内部充值可以直接调用 * @param uid * @param payId * @param payArgs * @param type * @param orderNo */ static async pay(uid: string, payId: string, payArgs?: any, type?: "system" | "user" | "legu", orderNo: string = '') { if (orderNo) { //判断订单号是否已经支付过 let payed = await G.mongodb.collection('payOrderLog' as any).count({"orderNo": orderNo}); if (payed) { console.log(uid, payId, payArgs, orderNo); console.log('订单号已存在'); } } let conf: any = await this.getConf(payId, payArgs); let isReplaceConf = await this.checkBuysAfterPay(uid, payId, conf, payArgs) if (isReplaceConf) { let prizePayId = `zuanshi_${conf.money}` payArgs.toPrizePayId = prizePayId conf = this.replacePrizeToChongzhi(prizePayId, conf) } let prize = [...conf.prize]; //let player = await G.redis.get('user', uid); let player = await getGud(uid); if (conf.firstPayPrize.length > 0) { let logs = await this.getPayLog(uid, payId); if (!logs || logs.length < 1) { prize = PublicShared.mergePrize([].concat(conf.prize, conf.firstPayPrize)); } } let call = this.getCall(player); call.req = { uid: uid, payId: payId, payArgs: payArgs, orderNo: orderNo } if (payId.indexOf('zixuanlibao') != -1) { // 后端唯一标识 htype 4 自选/定制 礼包 // @ts-ignore let _hdList = await HuoDongFun.gethdList(call, 4) _hdList.forEach(ele => { let selectPrize = ele.data.gift.find(v => v.payId == payId)?.prize; if (prize) { let index = payArgs instanceof Array ? payArgs : []; let select = selectPrize.map((v, i) => v[index[i]] ? v[index[i]] : v[0]); prize.push(...select); } }) } if (payId.indexOf('ycmb') != -1) { // @ts-ignore // 养成活动充值礼包 let _hdids = await YangChengMuBiaofun.getCon(call); Object.values(_hdids).forEach(ele => { let selectPrize = ele.data.gift.find(v => v.payId == payId)?.prize; if (selectPrize) { prize.push(...selectPrize); } }); } await PlayerFun.sendPrize(call, prize); await PlayerFun.addAttr(call, conf.payExp); if (payId == 'G123SendGift') { this.changeG123GiftLog(uid, payArgs.popup_id) } await this.addPayLog(player.uid, payId, type, payArgs); // this.addPayLogNew(player.uid, payId, type); //记录支付信息log if (orderNo) { await G.mongodb.collection('payOrderLog' as any).insertOne({ "orderNo": orderNo, "payId": payId, "uid": uid, "payArgs": payArgs, "type": type, "ctime": new Date().getTime() }); } G.server.sendMsgByUid(uid, 'msg_s2c/Collection', { fromApi: `pay_${payId}`, msg: call.eventMsg }); G.server.sendMsgByUid(uid, 'msg_s2c/PayResult', { code: 1, data: { id: payId, prize: prize, price: payArgs?.price } }); G.emit('PLAYER_PAY', player, payId, payArgs); G.emit("Class_task_116", 'Class_task_116', call, 1, 0); } /** * 转换订单奖励成直冲钻石奖励 * @param payId * @param conf */ static async replacePrizeToChongzhi(payId, conf) { return { id: payId, money: conf.money, payExp: [ { "a": "attr", "t": "payExp", "n": conf.money * 10 } ], prize: [ { "a": "attr", "t": "rmbmoney", "n": conf.money * 10 } ], firstPayPrize: [], name: `replacePrize`, time: -1, buys: 0, needVip: 0, front: {} } } /** * 支付成功后,某些情况下会有超出限购次数的限购订单 * 这种情况需检查,返回true是需要转换成钻石的订单 * @param uid * @param payId * @param conf * @param payArgs */ static async checkBuysAfterPay(uid, payId, conf, payArgs) { if (payId == 'G123SendGift') { let giftInfo = await G.mongodb.collection('giftLog').findOne({popup_id: payArgs.popup_id}); if (giftInfo.purchaseLimitAmount && giftInfo.buyNumber >= giftInfo.purchaseLimitAmount && number(giftInfo.price) > 0) { return true } } else { let buyLog = await this.getPayLog(uid, payId) || []; if (conf.buys > 0 && conf.time > 0) { buyLog = buyLog.filter(v => v.time >= PublicShared.getToDayZeroTime(G.time)); } if (conf.buys > 0 && buyLog.length >= conf.buys) return true; } return false } /** * 拉起订单前检查购买前置条件,如符合,通知前端拉起支付 * 针对G123礼包有特殊处理,不在游戏配置内预设的充值挡位 * todo 目前没有订单锁,所以会造成限购商品可以二次拉起支付 * @param player * @param payId * @param payArgs */ static async check(player: player, payId: string, payArgs?: any) { let conf: any = await this.getConf(payId, payArgs); if (!conf) return G.server.sendMsgByUid(player.uid, 'msg_s2c/PayResult', {code: 0}); if (player.vip < conf.needVip) return G.server.sendMsgByUid(player.uid, 'msg_s2c/PayResult', {code: -3}); if (conf.front.cond) { let pays = await this.getPayLogs(player.uid, conf.front.pays); //@ts-ignore let actives = Object.entries(pays).map(v => checkPayIsActive(v[0], v[1], payArgs)).filter(v => v); if (conf.front.cond == '|' && actives.length < 1) return G.server.sendMsgByUid(player.uid, 'msg_s2c/PayResult', {code: -4}); if (conf.front.cond == '&' && actives.length < conf.front.pays.length) return G.server.sendMsgByUid(player.uid, 'msg_s2c/PayResult', {code: -4}); } if (payId == 'G123SendGift') { let giftInfo = await G.mongodb.collection('giftLog').findOne({popup_id: payArgs.popup_id}); if (giftInfo.purchaseLimitAmount && giftInfo.buyNumber >= giftInfo.purchaseLimitAmount) { return G.server.sendMsgByUid(player.uid, 'msg_s2c/PayResult', {code: -1}); } if (number(giftInfo.price) <= 0) { await this.pay(player.uid, payId, payArgs, 'user'); return } } else { let buyLog = await this.getPayLog(player.uid, payId) || []; if (conf.buys > 0 && conf.time > 0) { buyLog = buyLog.filter(v => v.time >= PublicShared.getToDayZeroTime(G.time)); } if (conf.buys > 0 && buyLog.length >= conf.buys) return G.server.sendMsgByUid(player.uid, 'msg_s2c/PayResult', {code: -1}); if (conf.time != -1 && buyLog.slice(-1)[0]?.eTime > G.time && conf.buys == 0) return G.server.sendMsgByUid(player.uid, 'msg_s2c/PayResult', {code: -2}); if (payId.indexOf('136Gift') != -1 && payId != '136Gift1') { buyLog = await this.getPayLog(player.uid, '136Gift1'); if (buyLog.slice(-1)[0]?.eTime > G.time) return G.server.sendMsgByUid(player.uid, 'msg_s2c/PayResult', {code: -3}); } if (payId.indexOf('wkdlibao') != -1) { let call = this.getCall(player) let conf = await zmlbGetConf(call, {payId}) buyLog = await this.getPayLog(player.uid, payId); if (buyLog.length && buyLog.length >= conf.buyNum) return G.server.sendMsgByUid(player.uid, 'msg_s2c/PayResult', {code: -1}); } } if (G.config.debug) { await this.pay(player.uid, payId, payArgs, 'user', "debuger_" + new Date().getTime()); } else { G.server.sendMsgByUid(player.uid, 'msg_s2c/PayResult', {code: 200}); } } static getCall(player: player): call { return { get otherBuff() { return this.conn.otherBuff; }, uid: player.uid, conn: G.server.uid_connections[player.uid] ? G.server.uid_connections[player.uid] : { id: null, uid: player.uid, gud: player, item: null, otherBuff: null, heroPos: null, refreshPower: null, sendMsg: null, }, service: {name: "pay"}, req: {}, eventMsg: null, addEventMsg() { return ApiCall.prototype.addEventMsg.call(this, ...arguments); } }; } /** * 当玩家购买过有时间期效性的礼包 并且在生效期内每天有邮件奖励时 每天首次登陆时根据时间补发邮件 玩家每天首次登陆会进入检查 * @param lastTime 上一次登陆时间 * @param curTime 每日首次登陆时间 */ static async checkGiftDayEmail(player: player, lastTime: number, curTime: number) { const buyLogs = await this.getPayLogs(player.uid); const keys = Object.keys(buyLogs).filter(payId => G.gc.payEmail[payId]?.length > 0); const curZeroTime = PublicShared.getToDayZeroTime(curTime); const lastZeroTime = PublicShared.getToDayZeroTime(lastTime); keys.forEach(payId => { let payEmailConf = G.gc.payEmail[payId] as _gcType['payEmail']['caifutequan']; let latelyLog = buyLogs[payId].slice(-1)[0]; if (!latelyLog || (latelyLog.eTime && lastZeroTime >= latelyLog.eTime)) return; let countDay = 1; let payZeroTime = PublicShared.getToDayZeroTime(latelyLog.time); let payZeroEtime = PublicShared.getToDayZeroTime(latelyLog.eTime); let days: number if (curZeroTime > payZeroEtime) { days = (payZeroEtime - lastZeroTime) / (24 * 3600); } else { days = (curZeroTime - lastZeroTime) / (24 * 3600); } for (let n = 1; n <= days; n++) { countDay++; // if (payZeroTime + n * 24 * 3600 <= payZeroEtime) continue; payEmailConf.forEach(conf => { if (conf.day == 1 || countDay % conf.day == 0) { EmailFun.addEmail({ uid: player.uid, type: 'system', title: conf.title, content: conf.content, prize: conf.prize, createTime: lastZeroTime + n * 24 * 3600 }); } }); } }); } /** * 获取某个时间段内 玩家充值x元以上的天数 */ static async getPayDaysBuyPayNum(uid: string, sTime: number, eTime: number, ckeckNeed: number) { let logs = await G.mongodb.collection('dayPay').find({uid: uid}).toArray() || []; return logs.filter(log => log.time >= sTime && log.time <= eTime && log.payNum >= ckeckNeed).length; } /** * 获取某个时间段内 玩家的充值总金额 */ static async getPayDaysAllPayNum(uid: string, sTime: number, eTime: number) { let logs = await G.mongodb.collection('dayPay').find({uid: uid}).toArray() || []; logs = logs.filter(log => log.time >= sTime && log.time < eTime); if (logs.length <= 0) return 0; return logs.map(log => log.payNum).reduce((a, b) => a + b); } }