package handler import ( "encoding/json" "errors" "fmt" "server/call" "server/common" "server/config" "server/db" "server/modules/web/app" "server/modules/web/values" "server/pb" "server/util" "time" "github.com/gin-gonic/gin" "github.com/liangdas/mqant/log" "github.com/mitchellh/mapstructure" "gorm.io/gorm" ) func WithdrawInfo(c *gin.Context) { a := app.NewApp(c) defer func() { a.Response() }() info := new(common.RechargeInfo) info.UID = a.UID if err := db.Mysql().Get(info); err != nil && err != gorm.ErrRecordNotFound { log.Error("err:%v", err) a.Code = values.CodeRetry return } resp := values.WithDrawInfoResp{Tips: call.GetConfigPlatform().WithdrawTips} if util.IsSameDayTimeStamp(time.Now().Unix(), info.LastWithdraw) { resp.WithDrawCount = info.WithdrawCount } else { resp.WithDrawCount = 0 } con := call.GetVipCon(a.UID) if con != nil { resp.TotalWithdrawCount = con.WithdrawCount } resp.Fees = append(resp.Fees, con.Fee, con.UFee) resp.Channels = call.GetConfigWithdrawChannels() list := call.GetConfigWithdrawProduct() for _, v := range list { if v.IfSell == 2 { continue } one := *v if v.Type == common.CurrencyUSDT { for _, j := range resp.Channels { if j.CurrencyType == common.CurrencyUSDT { if j.PayDown <= v.Amount && j.PayUp >= v.Amount { one.Channels = append(one.Channels, j.ChannelID) } } } resp.List = append(resp.List, one) } else if v.Type == common.CurrencyINR { for _, j := range resp.Channels { if j.CurrencyType == common.CurrencyINR { if j.PayDown <= v.Amount && j.PayUp >= v.Amount { one.Channels = append(one.Channels, j.ChannelID) } } } resp.List = append(resp.List, one) } } for i := common.CurrencyTypeZero + 1; i < common.CurrencyAll; i++ { // bet := call.GetPlayerProfileByCurrency(a.UID, i).TotalBet info := call.GetPlayerRechargeInfoByCurrency(a.UID, i) canWithdraw := call.GetUserCurrency(a.UID, i) if canWithdraw < 0 || info.TotalRecharge == 0 { canWithdraw = 0 } resp.Bets = append(resp.Bets, canWithdraw) } a.Data = resp } func WithdrawHistory(c *gin.Context) { a := app.NewApp(c) defer func() { a.Response() }() req := new(values.WithdrawHistoryReq) if !a.S(req) { return } if req.Num > 100 { req.Num = 100 } // log.Debug("withdraw history req:%+v", *req) ret := []common.WithdrawOrder{} var count int64 var err error count, err = db.Mysql().QueryListW(req.Page, req.Num, "create_time desc", &common.WithdrawOrder{UID: a.UID}, &ret, "uid = ? and event = ?", a.UID, common.CurrencyEventWithDraw) if err != nil { log.Error("err:%v", err) a.Code = values.CodeRetry return } for i, v := range ret { if v.Status == common.StatusROrderCreate || v.Status == common.StatusROrderWaitting { ret[i].Status = common.StatusROrderPay } else if v.Status == common.StatusROrderRefuse { ret[i].Status = common.StatusROrderFail } } resp := values.WithdrawHistoryResp{ Count: count, List: ret, } a.Data = resp } func PlayerWithdrawBlock(c *gin.Context) { a := app.NewApp(c) resp := &values.WithdrawResp{} defer func() { a.Data = resp log.Debug("player %v PlayerWithdraw code:%v", a.UID, a.Code) a.Response() }() req := new(values.WithdrawBlockReq) if !a.S(req) { return } if req.CurrencyType != common.CurrencyUSDT { a.Code = values.CodeParam return } uid := a.UID log.Debug("player %v withdrawblock %+v", uid, *req) req.Amount = common.RoundCurrency(req.CurrencyType, req.Amount) // 退出条件限制 if !a.CheckWithdrawCondition(req.Amount, req.CurrencyType) { return } has := call.GetUserCurrency(a.UID, req.CurrencyType) need := req.Amount con := call.GetVipCon(uid) if con != nil && con.UFee > 0 { need += con.UFee } if has < need { log.Error("err not enough cash:%v,%v", has, need) a.Code = values.CodeWithdrawNotEnough return } // 拉取玩家退出信息 re := call.GetRechargeInfo(a.UID) now := time.Now().Unix() if re.ID == 0 { re.LastWithdraw = now if err := db.Mysql().Create(re); err != nil { a.Code = values.CodeRetry return } } else { u := map[string]interface{}{"last_withdraw": now} if !util.IsSameDayTimeStamp(now, re.LastWithdraw) { u["withdraw_count"] = 0 u["day_withdraw"] = 0 } rows, err := db.Mysql().UpdateRes(&common.RechargeInfo{UID: uid, LastWithdraw: re.LastWithdraw}, u) if err != nil || rows == 0 { a.Code = values.CodeRetry return } } orderID := "USDT" + util.NewOrderID(int(uid)) // 第一步,先扣钱 err := call.MineCurrencyProReal(&common.UpdateCurrency{ CurrencyBalance: &common.CurrencyBalance{ UID: a.UID, Event: common.CurrencyEventWithDraw, Type: common.CurrencyUSDT, Value: -need, Exs1: orderID, ChannelID: a.Channel, }, }).Err if err != nil { log.Error("player %v mines cash err:%v", uid, err) a.Code = values.CodeRetry return } // 直接发起退出 shouldAuto := call.CanAutoWithdraw(uid, re.TotalRecharge, re.TotalWithdraw+re.TotalWithdrawing+need) // 第二步,创建订单 orderStatus := common.StatusROrderCreate if shouldAuto { orderStatus = common.StatusROrderWaitting } order := &common.WithdrawOrder{ UID: int(uid), OrderID: orderID, APIPayID: "", // ProductID: one.ID, CreateTime: time.Now().Unix(), Amount: req.Amount, WithdrawCash: need, Status: uint8(orderStatus), PaySource: common.PaySourceBlockPay, CurrencyType: req.CurrencyType, Event: int(common.CurrencyEventWithDraw), PayAccount: req.Address, ChannelID: a.Channel, } if err := db.Mysql().Create(order); err != nil { log.Error("player %v create WithdrawBlock order fail err:%v", uid, err) a.Code = values.CodeRetry return } u := map[string]interface{}{} u["withdraw_count"] = gorm.Expr("withdraw_count + ?", 1) db.Mysql().Update(&common.RechargeInfo{UID: uid}, u) call.UpdatePlayerRechargeInfoCurrency(uid, req.CurrencyType, map[string]interface{}{"total_withdrawing": gorm.Expr("total_withdrawing + ?", need)}) // 直接发起退出 // util.Go(func() { // call.SendWithdrawMail(uid, call.MailWithdrawType2) // }) } func PlayerWithdrawCheck(c *gin.Context) { a := app.NewApp(c) defer func() { log.Debug("player %v PlayerWithdrawCheck code:%v,msg:%v", a.UID, a.Code, a.Msg) a.Response() }() req := new(values.WithdrawCheckReq) if !a.S(req) { return } a.CheckWithdrawCondition(req.Amount, common.CurrencyINR) } func PlayerWithdraw(c *gin.Context) { a := app.NewApp(c) resp := &values.WithdrawResp{} a.Data = resp defer func() { log.Debug("player %v PlayerWithdraw code:%v", a.UID, a.Code) a.Response() }() req := new(values.WithdrawReq) if !a.S(req) { return } if req.CurrencyType != common.CurrencyINR { log.Error("invalid type:%v", req.CurrencyType) req.CurrencyType = common.CurrencyINR } uid := a.UID log.Debug("player %v withdraw %+v", uid, *req) req.Amount = common.RoundCurrency(req.CurrencyType, req.Amount) // 退出条件限制 if !a.CheckWithdrawCondition(req.Amount, req.CurrencyType) { return } ip := a.GetRemoteIP() payInfo, code := NewWithdraw(req, uid, ip) if code != values.CodeOK { a.Code = code a.Msg = payInfo return } if len(payInfo) > 500 { a.Code = values.CodeParam a.Msg = "Withdrawal information too long." return } user := new(common.PlayerDBInfo) user.Id = uid if err := db.Mysql().Get(user); err != nil { log.Error("err:%v", err) a.Code = values.CodeRetry return } need := req.Amount has := call.GetUserCurrency(a.UID, req.CurrencyType) if need > has { log.Error("err not enough cash:%v,%v", has, need) a.Code = values.CodeWithdrawNotEnough return } re := call.GetRechargeInfo(a.UID) now := time.Now().Unix() if re.ID == 0 { re.LastWithdraw = now if err := db.Mysql().Create(re); err != nil { a.Code = values.CodeRetry return } } else { u := map[string]interface{}{"last_withdraw": now} if !util.IsSameDayTimeStamp(now, re.LastWithdraw) { u["withdraw_count"] = 0 u["day_withdraw"] = 0 } rows, err := db.Mysql().UpdateRes(&common.RechargeInfo{UID: uid, LastWithdraw: re.LastWithdraw}, u) if err != nil || rows == 0 { a.Code = values.CodeRetry return } } orderID := util.NewOrderID(int(uid)) // 第一步,先扣钱 pro := call.MineCurrencyProReal(&common.UpdateCurrency{ CurrencyBalance: &common.CurrencyBalance{ UID: uid, Value: -need, Event: common.CurrencyEventWithDraw, Type: req.CurrencyType, Exs1: orderID, ChannelID: a.Channel, }, }) if pro.Err != nil { log.Error("err:%v", pro.Err) a.Code = values.CodeRetry return } con := call.GetVipCon(uid) realAmount := need // 实际打款 if con != nil && con.Fee > 0 { realAmount = common.RoundCurrency(req.CurrencyType, (1000-int64(con.Fee))*realAmount/1000) } var shouldAuto = false // 在总赠送比配置比例小时才判断个人 if call.GetTotalRechargePer(realAmount) < config.GetConfig().Web.TotalWithdrawPer { // 直接发起退出 shouldAuto = call.CanAutoWithdraw(uid, re.TotalRecharge, re.TotalWithdraw+re.TotalWithdrawing+realAmount) } // 第二步,创建订单 withdrawChannel := -1 if req.ChannelID != nil { withdrawChannel = *req.ChannelID } orderStatus := common.StatusROrderCreate if shouldAuto { orderStatus = common.StatusROrderWaitting } order := &common.WithdrawOrder{ UID: int(uid), OrderID: orderID, APIPayID: "", // ProductID: one.ID, CreateTime: time.Now().Unix(), Amount: realAmount, WithdrawCash: need, Status: uint8(orderStatus), PaySource: common.PaySourceModulePay, Event: int(common.CurrencyEventWithDraw), CurrencyType: req.CurrencyType, PayAccount: payInfo, ChannelID: a.Channel, UPI: withdrawChannel, } if err := db.Mysql().Create(order); err != nil { log.Error("player %v create withdraw order fail err:%v", uid, err) a.Code = values.CodeRetry return } resp.Balance = pro.Balance resp.WithdrawBalance = has - need u := map[string]interface{}{} u["withdrawing_cash"] = gorm.Expr("withdrawing_cash + ?", need) u["withdraw_count"] = gorm.Expr("withdraw_count + ?", 1) u["total_withdrawing"] = gorm.Expr("total_withdrawing + ?", realAmount) u["day_withdraw"] = gorm.Expr("day_withdraw + ?", realAmount) db.Mysql().Update(&common.RechargeInfo{UID: uid}, u) // call.UpdatePlayerRechargeInfoCurrency(uid, req.CurrencyType, map[string]interface{}{"total_withdrawing": gorm.Expr("total_withdrawing + ?", need)}) // 直接发起退出 // util.Go(func() { // call.SendWithdrawMail(uid, call.MailWithdrawType2) // }) } // 返回值在code不为0的时候,代表错误msg func NewWithdraw(req *values.WithdrawReq, uid int, ip string) (string, int) { one := common.WithdrawCommon{} err := mapstructure.Decode(req.PayAccount, &one) if err != nil || !one.PayType.Isvalid() { log.Error("NewWithdrawImp err:%v,one:%+v", err, one) return "", values.CodeParam } one.IP = ip // one.AccountName = strings.TrimSpace(one.AccountName) // one.BankCardNo = strings.TrimSpace(one.BankCardNo) // one.BankCode = strings.TrimSpace(one.BankCode) // if one.DrawType == common.WithdrawTypeBank && len(one.BankCode) != 11 { // return "The IFSC Code shall be 11 digital letters.", values.CodeParam // } // if one.DrawType == common.WithdrawTypeBank && one.BankCardNo == "" && one.AccountName == "" && one.Email == "" { // log.Error("NewWithdrawImp 银行卡支付,银行卡号不能为空 one:%+v", one) // return "", values.CodeParam // } // if one.DrawType != common.WithdrawTypeUPI && one.DrawType != common.WithdrawTypeBank { // log.Error("NewWithdrawImp unknown draw type one:%+v", one) // return "", values.CodeParam // } // user, _ := call.GetUserXInfo(uid, "mobile") // if user.Mobile == "" { // return "", values.CodeParam // } // mapstructure.Decode(req.PayAccount, &one) // if one.Mobile == "" { // one.Mobile = user.Mobile // } // 判断是否是拉黑用户,拉黑用户不让代付 // blackData := &common.BlackList{Phone: one.Mobile, PayAccount: one.BankCardNo, Email: one.Email, Name: one.AccountName} // if one.DrawType == common.WithdrawTypeUPI { // blackData.PayAccount = one.BankCode // } // if call.BlackListAndKick(uid, blackData) { // return "", values.CodeRetry // } // 如果是银行那卡代付,验证ifsc // if one.DrawType == common.WithdrawTypeBank { // if !call.CheckIFSC(one.BankCode) { // return "The IFSC Code is invalid.", values.CodeParam // } // } // 查询该银行卡是否已经绑定其他账号 // pi := &common.PayInfo{UID: uid} // db.Mysql().Get(pi) // if one.DrawType == common.WithdrawTypeBank { // if pi.BankCardNo != one.BankCardNo { // sql := fmt.Sprintf("bank_card_no = '%v'", one.BankCardNo) // if db.Mysql().Count(&common.PayInfo{}, sql) >= int64(config.GetConfig().Web.MaxBankCardCount) { // return "", values.CodeBankCardNoLimit // } // } // } else if one.DrawType == common.WithdrawTypeUPI { // UPI // if pi.BankCode != one.BankCode { // sql := fmt.Sprintf("bank_code = '%v'", one.BankCode) // if db.Mysql().Count(&common.PayInfo{}, sql) >= int64(config.GetConfig().Web.MaxBankCardCount) { // return "", values.CodeBankCardNoLimit // } // } // } info := &common.PayInfo{ UID: uid, Name: one.Name, Mobile: one.Mobile, Email: one.Email, PayType: one.PayType, Number: one.Number, } if _, err := db.Mysql().Upsert(fmt.Sprintf("uid = %v", uid), info); err != nil { return "", values.CodeParam } ret, err := json.Marshal(one) if err != nil { log.Error("err:%v", err) return "", values.CodeParam } return string(ret), values.CodeOK } func NewWithdrawImp(order *common.WithdrawOrder) *WithdrawImp { base := new(WithdrawImp) // uid := order.UID cid := order.ChannelID base.Channel = call.GetChannelByID(cid) if base.Channel == nil { log.Error("invalid cid:%v", cid) return nil } base.order = order one := new(PayWithdraw) base.SubWithdraw = one one.base = base return base } // WithdrawImp 发起退出对象 type WithdrawImp struct { order *common.WithdrawOrder // tx *gorm.DB SubWithdraw WithdrawInter Channel *common.Channel PayChannel int } func (w *WithdrawImp) BaseWithdraw() error { var err error // uid := w.order.UID defer func() { if err != nil { call.ReturnBackWithdraw(w.order, common.StatusROrderPay, common.StatusROrderFail, w.PayChannel) } }() res := db.Mysql().C().Model(w.order).Where("status = ?", common.StatusROrderCreate).Updates(map[string]interface{}{"status": common.StatusROrderPay}) if res.Error != nil { // w.tx.Rollback() log.Error("sub withdraw err:%v", err) return res.Error } if res.RowsAffected == 0 { // w.tx.Rollback() log.Error("sub withdraw err:%v", err) return errors.New("invalid order") } err = w.SubWithdraw.Withdraw() if err != nil { // w.tx.Rollback() log.Error("sub withdraw err:%v", err) return err } // err = w.tx.Commit().Error // if err != nil { // log.Error("commit BaseWithdraw err:%v", err) // return err // } return nil } func (w *WithdrawImp) AutoWithdraw() error { err := w.SubWithdraw.AutoWithdraw() if err != nil { return err } return nil } type WithdrawInter interface { Withdraw() error AutoWithdraw() error } // PayWithdraw pay模块退出 type PayWithdraw struct { base *WithdrawImp } func (p *PayWithdraw) Withdraw() error { order := p.base.order req := &pb.InnerWithdrawReq{ OrderID: order.OrderID, Amount: order.Amount, UID: uint32(order.UID), Channel: int64(order.UPI), PaySource: uint32(order.PaySource), } if req.PaySource == common.PaySourceModulePay { send := new(common.WithdrawCommon) err := json.Unmarshal([]byte(order.PayAccount), &send) if err != nil { log.Error("withdraw unmarshal err %v", err) return err } req.Phone = send.Mobile req.Name = send.Name req.Email = send.Email req.PayType = int64(send.PayType) req.Address = send.Address } ret, err := call.Withdraw(req) if ret != nil { p.base.PayChannel = int(ret.Channel) } if err != nil { log.Error("err:%v", err) return err } or := &common.RechargeOrder{Status: common.StatusROrderPay, APIPayID: ret.APIOrderID, PaySource: common.PaySourceModulePay, PayChannel: int(ret.Channel)} or.ID = order.ID res := db.Mysql().C().Model(order).Updates(or) err = res.Error if err != nil { log.Error("update order err:%v", err) return err } if res.RowsAffected == 0 { log.Error("update order fail orderid:%v", order.ID) return errors.New("update order fail") } return nil } func (p *PayWithdraw) AutoWithdraw() error { order := p.base.order req := &pb.InnerWithdrawReq{ OrderID: order.OrderID, Amount: order.Amount, UID: uint32(order.UID), Channel: int64(order.UPI), PaySource: uint32(order.PaySource), } if req.PaySource == common.PaySourceModulePay { send := new(common.WithdrawCommon) err := json.Unmarshal([]byte(order.PayAccount), &send) if err != nil { log.Error("withdraw unmarshal err %v", err) return err } req.Phone = send.Mobile req.Name = send.Name req.Email = send.Email req.PayType = int64(send.PayType) req.Address = send.Address // req.Number = send.Number } else { req.Address = order.PayAccount } ret, err := call.Withdraw(req) if ret != nil { p.base.PayChannel = int(ret.Channel) } if err != nil { return err } p.base.order.APIPayID = ret.APIOrderID p.base.order.Status = common.StatusROrderPay p.base.order.PayChannel = int(ret.Channel) return nil }