package values import ( "encoding/json" "fmt" "io/ioutil" "math/rand" "net/http" "server/call" "server/common" "server/config" "server/db" "server/util" "sort" "strings" "sync" "github.com/gin-gonic/gin" "github.com/liangdas/mqant/log" "gorm.io/gorm" ) // 支付方式 type PayWay int const ( FunzonePay PayWay = iota WellPay OctroPay IGeekPay CloudPay VSPay JoyPay FFPay BestPay HXPay MGPay OOPay ZWPay FastPay HaoPay QPPay OwlPay SkyPay GrePay MoonPay AcePay MccPay YoduPay WordPay HWPay JJPay AntPay MLPay RojPay QuantaPay InnoPay PePay FF8Pay FlaPay DidaPay CYGGPay ZPay HappyPay FastPlusPay GoPay LemonPay CamelPay MoonPay2 PayPlus LuckyinPay PayAll ) // 错误码 const ( CodeOk = iota ) var ( PayWeightLock = new(sync.RWMutex) WithdrawWeightLock = new(sync.RWMutex) ) // SelectPayWay 特定条件为玩家优先选择渠道 // func SelectPayWay(uid int, amount int64) PayWay { // if !config.GetConfig().Pay.SelectPayWay { // return -1 // } // banList := []int{} // zlist := db.Redis().ZRange(common.GetRedisKeyPlayerPay(uid)) // for _, v := range zlist { // banList = append(banList, util.GetInt(v.Member)) // } // lastSuccess := &common.RechargeOrder{UID: uid, Event: int(common.CurrencyEventReCharge), Status: common.StatusROrderPay} // err := db.Mysql().GetLast(lastSuccess) // if err == nil { // 直接按上次成功的渠道作为首选推荐渠道 // if !util.SliceContain(banList, lastSuccess.PayChannel) && IsTopChannel(lastSuccess.PayChannel, 3, amount) { // return PayWay(lastSuccess.PayChannel) // } // } // banAll := GetValidPayChannelsWithBanList(amount, banList) // if PayStatus == PayStatusChannel { // for _, v := range banAll { // return PayWay(v.ChannelID) // } // } // // 尝试当前权重最高的 // all := GetValidPayChannels(amount) // if len(all) > 0 { // if !util.SliceContain(banList, all[0].ChannelID) { // return PayWay(all[0].ChannelID) // } // } // return GetPayWayWeight(banAll) // } // 手动选择模式获取支付渠道 func GetPayWayChannel(index int, all []*common.ConfigPayChannels) (w PayWay) { if len(all) == 0 { w = -1 return } if index > len(all)-1 || index < 0 { index = 0 } w = PayWay(all[index].ChannelID) return } // 权重模式获取支付渠道 func GetPayWayWeight(all []*common.ConfigPayChannels) PayWay { total := 0 for _, v := range all { total += v.PayPer } if total > 0 { ran := rand.Intn(total) rans := 0 for i := 0; i < len(all); i++ { rans += all[i].PayPer if ran < rans { return PayWay(all[i].ChannelID) } } } return -1 } // ChoosePayWay 选择充值渠道 // func ChoosePayWay(uid, index int, amount int64) PayWay { // pw := SelectPayWay(uid, amount) // if pw >= 0 { // return pw // } // if PayStatus == PayStatusChannel { // return GetPayWayChannel(index, GetValidPayChannels(amount)) // } // return GetPayWayWeight(GetValidPayChannels(amount)) // } func PayFail(w PayWay) { failWeight := config.GetConfig().Pay.PayFailWeight if failWeight <= 0 { return } PayWeightLock.Lock() for _, v := range call.ConfigPayChannels { if v.ChannelID == int(w) { v.PayPer -= failWeight break } } sort.Slice(call.ConfigPayChannels, func(i, j int) bool { return call.ConfigPayChannels[i].PayPer > call.ConfigPayChannels[j].PayPer }) PayWeightLock.Unlock() AddPayChannel(int(w), -failWeight) } func PayCallback(w PayWay) { successWeight := config.GetConfig().Pay.PaySuccessWeight if successWeight <= 0 { return } PayWeightLock.Lock() for _, v := range call.ConfigPayChannels { if v.ChannelID == int(w) { v.PayPer += successWeight break } } sort.Slice(call.ConfigPayChannels, func(i, j int) bool { return call.ConfigPayChannels[i].PayPer > call.ConfigPayChannels[j].PayPer }) PayWeightLock.Unlock() AddPayChannel(int(w), successWeight) } // 获取支付回调路径 func GetPayCallback(way PayWay) string { return config.GetConfig().Pay.CallbackURL + config.GetConfig().Pay.Addr + "/callback/pay/" + fmt.Sprintf("%d", way) } // 获取退出回调路径 func GetWithdrawCallback(way PayWay) string { return config.GetConfig().Pay.CallbackURL + config.GetConfig().Pay.Addr + "/callback/withdraw/" + fmt.Sprintf("%d", way) } func GetFrontCallback() string { return config.GetConfig().Pay.CallbackURL + config.GetConfig().Pay.Addr + "/common/front/payCallback" } func GetFrontCallbackFail() string { return config.GetConfig().Pay.CallbackURL + config.GetConfig().Pay.Addr + "/common/front/payCallback/fail" } // 根据body里的字段直接拼接出签名字符串 func GetSignStr(str string, pass ...string) string { m := map[string]json.RawMessage{} sortStrs := []string{} json.Unmarshal([]byte(str), &m) for i := range m { sortStrs = append(sortStrs, i) } signStr := "" sort.Strings(sortStrs) for _, v := range sortStrs { shouldPass := false for _, s := range pass { if v == s { shouldPass = true break } } if shouldPass { continue } if len(m[v]) > 1 && m[v][0] == 34 { m[v] = m[v][1 : len(m[v])-1] } if len(m[v]) == 0 { continue } signStr += fmt.Sprintf("%v=%v", v, string(m[v])) signStr += "&" } signStr = signStr[:len(signStr)-1] log.Debug("signStr:%v", signStr) return signStr } // 根据body里的字段直接拼接出签名字符串(去除null) func GetSignStrNull(str string, pass ...string) string { m := map[string]json.RawMessage{} sortStrs := []string{} json.Unmarshal([]byte(str), &m) for i := range m { sortStrs = append(sortStrs, i) } signStr := "" sort.Strings(sortStrs) for _, v := range sortStrs { shouldPass := false for _, s := range pass { if v == s { shouldPass = true break } } if shouldPass { continue } if len(m[v]) > 1 && m[v][0] == 34 { m[v] = m[v][1 : len(m[v])-1] } if len(m[v]) == 0 || string(m[v]) == "null" { continue } signStr += fmt.Sprintf("%v=%v", v, string(m[v])) signStr += "&" } signStr = signStr[:len(signStr)-1] log.Debug("signStr:%v", signStr) return signStr } // 根据body里的字段直接拼接出签名字符串(包括空字符串) func GetSignStrAll(str string, pass ...string) string { m := map[string]json.RawMessage{} sortStrs := []string{} json.Unmarshal([]byte(str), &m) for i := range m { sortStrs = append(sortStrs, i) } signStr := "" sort.Strings(sortStrs) for _, v := range sortStrs { shouldPass := false for _, s := range pass { if v == s { shouldPass = true break } } if shouldPass { continue } if len(m[v]) > 1 && m[v][0] == 34 { m[v] = m[v][1 : len(m[v])-1] } signStr += fmt.Sprintf("%v=%v", v, string(m[v])) signStr += "&" } signStr = signStr[:len(signStr)-1] log.Debug("signStr:%v", signStr) return signStr } // 根据body里的字段直接拼接出签名字符串(包括空字符串,unicode转码) func GetSignStrAllUnicode(str string, pass ...string) string { m := map[string]json.RawMessage{} sortStrs := []string{} json.Unmarshal([]byte(str), &m) for i := range m { sortStrs = append(sortStrs, i) } signStr := "" sort.Strings(sortStrs) for _, v := range sortStrs { shouldPass := false for _, s := range pass { if v == s { shouldPass = true break } } if shouldPass { continue } str := string(m[v]) if len(str) > 1 && str[0] == 34 { str = str[1 : len(str)-1] } tmp, err := util.ZhToUnicode(str) if err == nil { str = tmp } str = strings.ReplaceAll(str, `\`, "") signStr += fmt.Sprintf("%v=%v", v, str) signStr += "&" } signStr = signStr[:len(signStr)-1] log.Debug("signStr:%v", signStr) return signStr } func GetSignStrForm(c *gin.Context, pass ...string) string { sortStrs := []string{} for k := range c.Request.PostForm { sortStrs = append(sortStrs, k) } signStr := "" sort.Strings(sortStrs) for _, v := range sortStrs { shouldPass := false for _, s := range pass { if v == s { shouldPass = true break } } if shouldPass { continue } if len(c.PostForm(v)) == 0 { continue } signStr += fmt.Sprintf("%v=%v", v, c.PostForm(v)) signStr += "&" } signStr = signStr[:len(signStr)-1] log.Debug("signStr:%v", signStr) return signStr } // status=1时,属于不知道第三方是什么情况的报错,不能当成业务中断 // status=0时,可以根据返回判断是否是业务错误 func Post(req *http.Request, ret interface{}) int { client := &http.Client{} log.Debug("req:%+v", req) resp, err := client.Do(req) if err != nil { log.Error("http post call err:%v", err) return 1 } defer resp.Body.Close() body, _ := ioutil.ReadAll(resp.Body) log.Debug("response Body%v:", string(body)) if err := json.Unmarshal(body, ret); err != nil { log.Error("unmarshal fail err:%v", err) return 1 } return 0 } func AddPayChannel(p int, amount int) { if err := db.Mysql().C().Model(&common.ConfigPayChannels{}).Where("channel_id = ?", p).Updates(map[string]interface{}{"pay_per": gorm.Expr("pay_per + ?", amount)}).Error; err != nil { log.Error("err:%v", err) } } func SetPayChannel(p int, amount int) { if err := db.Mysql().C().Model(&common.ConfigPayChannels{}).Where("channel_id = ?", p).Updates(map[string]interface{}{"pay_per": amount}).Error; err != nil { log.Error("err:%v", err) } } func GetValidPayChannels(amount int64) []*common.ConfigPayChannels { PayWeightLock.RLock() defer PayWeightLock.RUnlock() all := []*common.ConfigPayChannels{} for _, v := range call.ConfigPayChannels { if v.PayPer > 0 && amount >= v.PayDown && (amount <= v.PayUp || v.PayUp <= 0) { all = append(all, v) } } return all } func GetValidPayChannelsWithBanList(amount int64, banList []int) []*common.ConfigPayChannels { all := []*common.ConfigPayChannels{} PayWeightLock.RLock() defer PayWeightLock.RUnlock() for _, v := range call.ConfigPayChannels { if v.PayPer > 0 && amount >= v.PayDown && (amount <= v.PayUp || v.PayUp <= 0) { if !util.SliceContain(banList, v.ChannelID) { all = append(all, v) } } } return all } func IsPayChannelValid(cid int, amount int64) bool { PayWeightLock.RLock() defer PayWeightLock.RUnlock() for _, v := range call.ConfigPayChannels { if cid == v.ChannelID { if v.PayPer > 0 && amount >= v.PayDown && (amount <= v.PayUp || v.PayUp <= 0) { return true } break } } return false } // IsTopChannel 判断渠道是否是排名前几的可用渠道 func IsTopChannel(cid, top int, amount int64) bool { PayWeightLock.RLock() defer PayWeightLock.RUnlock() for i := 0; i < top; i++ { v := call.ConfigPayChannels[i] if cid == v.ChannelID { if v.PayPer > 0 && amount >= v.PayDown && (amount <= v.PayUp || v.PayUp <= 0) { return true } } } return false } func WithdrawAmount(w PayWay, success bool, amount int64) { WithdrawWeightLock.Lock() defer WithdrawWeightLock.Unlock() con := call.GetConfigWithdrawChannelsByID(int(w), common.CurrencyINR) if con == nil { return } fee := con.CalAmount(amount) // 成功扣除余额,失败的情况,余额退回 if success { fee = -fee } con.Amount += fee util.Go(func() { db.Mysql().UpdateW(&common.ConfigWithdrawChannels{}, map[string]interface{}{"amount": gorm.Expr("amount + ?", fee)}, fmt.Sprintf("channel_id = %d", int(w))) }) }