package swagger import ( "fmt" "github.com/gofiber/fiber/v2" "math" "net/http" "net/url" "os" "sync" "time" "github.com/labstack/echo/v4" "go.mongodb.org/mongo-driver/mongo" "go.uber.org/zap" "google.golang.org/protobuf/types/known/timestamppb" qutils "penahub.gitlab.yandexcloud.net/backend/quiz/common.git/utils" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/broker/tariff" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/client" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/repository" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/proto/discount" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/service/history" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/utils/transfer" "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/echotools" "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate" ) const defaultCurrency = "RUB" // TODO move type API2 struct { logger *zap.Logger history repository.HistoryRepository account repository.AccountRepository currency repository.CurrencyRepository producer *tariff.Producer consumer *tariff.Consumer clients clients grpc models.ConfigurationGRPC encrypt *qutils.Encrypt } type clients struct { auth *client.AuthClient hubadmin *client.HubadminClient currency *client.CurrencyClient discount *client.DiscountClient payment *client.PaymentClient verify *client.VerificationClient template *client.TemplateClient mail *client.MailClient codeword *client.CodewordClient } var _ ServerInterface = (*API2)(nil) func NewAPI2(logger *zap.Logger, db *mongo.Database, config *models.Config, consumer *tariff.Consumer, producer *tariff.Producer, encrypt *qutils.Encrypt) API2 { return API2{ logger: logger, history: repository.NewHistoryRepository2(logger, db.Collection("histories")), currency: repository.NewCurrencyRepository2(logger, db.Collection("currency_lists")), account: repository.NewAccountRepository2(logger, db.Collection("accounts")), consumer: consumer, producer: producer, encrypt: encrypt, grpc: config.GRPC, clients: clients{ auth: client.NewAuthClient(client.AuthClientDeps{Logger: logger, URLs: &config.Service.AuthMicroservice.URL}), hubadmin: client.NewHubadminClient(client.HubadminClientDeps{Logger: logger, URLs: &config.Service.HubadminMicroservice.URL}), currency: client.NewCurrencyClient(client.CurrencyClientDeps{Logger: logger, URLs: &config.Service.CurrencyMicroservice.URL}), discount: client.NewDiscountClient(client.DiscountClientDeps{Logger: logger, DiscountServiceHost: config.Service.DiscountMicroservice.HostGRPC}), payment: client.NewPaymentClient(client.PaymentClientDeps{Logger: logger, PaymentServiceHost: config.Service.PaymentMicroservice.HostGRPC}), verify: client.NewVerificationClient(client.VerificationClientDeps{Logger: logger, URLs: &config.Service.VerificationMicroservice.URL}), template: client.NewTemplateClient(client.TemplateClientDeps{Logger: logger, URLs: &config.Service.TemplategenMicroserviceURL.URL}), mail: client.NewMailClient(client.MailClientDeps{Logger: logger, ApiUrl: config.Service.Mail.ApiUrl, Sender: config.Service.Mail.Sender, Auth: &models.PlainAuth{ Identity: config.Service.Mail.Auth.Identity, Username: config.Service.Mail.Auth.Username, Password: config.Service.Mail.Auth.Password, }, ApiKey: config.Service.Mail.ApiKey, FiberClient: fiber.AcquireClient(), MailAddress: config.Service.Mail.MailAddress, }), codeword: client.NewCodewordClient(client.CodewordClientDeps{Logger: logger, CodewordServiceHost: config.Service.CodewordMicroservice.HostGRPC}), }, } } func (api *API2) error(ctx echo.Context, status int, message string, rest ...any) error { if len(rest) > 0 { message = fmt.Sprintf(message, rest...) } api.logger.Error(message) return ctx.JSON(status, models.ResponseErrorHTTP{ StatusCode: status, Message: message, }) } func (api *API2) errorOld(ctx echo.Context, err errors.Error) error { api.logger.Error("error:", zap.Error(err)) return errors.HTTP(ctx, err) } func (api *API2) noauth(ctx echo.Context) error { return api.error(ctx, http.StatusUnauthorized, "failed to get jwt payload") } // Health func (api *API2) GetHealth(ctx echo.Context) error { return ctx.String(http.StatusOK, "OK") } // Account func (api *API2) DeleteAccount(ctx echo.Context) error { userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) if !ok { return api.noauth(ctx) } account, err := api.account.Remove(ctx.Request().Context(), userID) if err != nil { return api.errorOld(ctx, err) } return ctx.JSON(http.StatusOK, account) } func (api *API2) ChangeAccount(ctx echo.Context) error { userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) if !ok { return api.noauth(ctx) } request, bindErr := echotools.Bind[models.Name](ctx) if bindErr != nil { return api.error(ctx, http.StatusBadRequest, "failed to bind json: %w", bindErr) } account, err := api.account.UpdateName(ctx.Request().Context(), userID, request) if err != nil { return api.errorOld(ctx, err) } return ctx.JSON(http.StatusOK, account) } func (api *API2) SetAccountVerificationStatus(ctx echo.Context, userID string) error { request, bindErr := echotools.Bind[models.SetAccountStatus](ctx) if bindErr != nil { return api.error(ctx, http.StatusBadRequest, "failed to bind json: %w", bindErr) } account, err := api.account.SetStatus(ctx.Request().Context(), userID, request.Status) if err != nil { api.logger.Error("failed to set status on of ", zap.Error(err)) return api.errorOld(ctx, err) } return ctx.JSON(http.StatusOK, account) } func (api *API2) GetAccount(ctx echo.Context) error { userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) if !ok { return api.noauth(ctx) } account, err := api.account.FindByUserID(ctx.Request().Context(), userID) if err != nil { return api.errorOld(ctx, err) } return ctx.JSON(http.StatusOK, account) } func (api *API2) AddAccount(ctx echo.Context) error { userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) if !ok { return api.noauth(ctx) } var quizFrom, quizUser string cookie, er := ctx.Request().Cookie("quizFrom") if er == nil { quizFrom = cookie.Value } cookie, er = ctx.Request().Cookie("quizUser") if er == nil { quizUser, er = url.QueryUnescape(cookie.Value) if er == nil { quizUser, er = api.encrypt.DecryptStr([]byte(quizUser)) } } account, err := api.account.FindByUserID(ctx.Request().Context(), userID) if err != nil && err.Type() != errors.ErrNotFound { return api.errorOld(ctx, err) } if account != nil { return api.error(ctx, http.StatusBadRequest, "account exists") } user, err := api.clients.auth.GetUser(ctx.Request().Context(), userID) if err != nil { return api.errorOld(ctx, err) } account, err = api.account.Insert(ctx.Request().Context(), &models.Account{ UserID: user.ID, Wallet: models.Wallet{Currency: defaultCurrency}, From: quizFrom, Partner: quizUser}) if err != nil { return api.errorOld(ctx, err) } return ctx.JSON(http.StatusOK, account) } func (api *API2) DeleteDirectAccount(ctx echo.Context, userID string) error { account, err := api.account.Remove(ctx.Request().Context(), userID) if err != nil { return api.errorOld(ctx, err) } return ctx.JSON(http.StatusOK, account) } func (api *API2) GetDirectAccount(ctx echo.Context, userID string) error { account, err := api.account.FindByUserID(ctx.Request().Context(), userID) if err != nil { return api.errorOld(ctx, err) } return ctx.JSON(http.StatusOK, account) } func (api *API2) PaginationAccounts(ctx echo.Context, params PaginationAccountsParams) error { if params.Page == nil || params.Limit == nil { return api.error(ctx, http.StatusInternalServerError, "default values missing for PaginationAccounts") } page := int64(math.Max(float64(*params.Page), 1)) limit := int64(math.Max(float64(*params.Limit), 1)) limit = int64(math.Min(float64(limit), float64(models.DefaultLimit))) count, err := api.account.CountAll(ctx.Request().Context()) if err != nil { return api.errorOld(ctx, err) } if count == 0 { response := models.PaginationResponse[models.Account]{TotalPages: 0, Records: []models.Account{}} return ctx.JSON(http.StatusOK, response) } totalPages := int64(math.Ceil(float64(count) / float64(limit))) accounts, err := api.account.FindMany(ctx.Request().Context(), page, limit) if err != nil { return api.errorOld(ctx, err) } response := models.PaginationResponse[models.Account]{ TotalPages: totalPages, Records: accounts, } return ctx.JSON(http.StatusOK, response) } // Cart func (api *API2) RemoveFromCart(ctx echo.Context, params RemoveFromCartParams) error { userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) if !ok { return api.noauth(ctx) } if validate.IsStringEmpty(params.Id) { return api.error(ctx, http.StatusBadRequest, "empty item id") } cartItems, err := api.account.RemoveItemFromCart(ctx.Request().Context(), userID, params.Id) if err != nil { return api.errorOld(ctx, err) } return ctx.JSON(http.StatusOK, cartItems) } func (api *API2) Add2cart(ctx echo.Context, params Add2cartParams) error { userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) if !ok { return api.noauth(ctx) } token, ok := ctx.Get(models.AuthJWTDecodedAccessTokenKey).(string) if !ok { return api.noauth(ctx) } if validate.IsStringEmpty(params.Id) { return api.error(ctx, http.StatusBadRequest, "empty item id") } tariffID := params.Id tariff, err := api.clients.hubadmin.GetTariff(ctx.Request().Context(), token, tariffID) if err != nil { return api.errorOld(ctx, err) } if tariff == nil { return api.error(ctx, http.StatusNotFound, "tariff not found") } cartItems, err := api.account.AddItemToCart(ctx.Request().Context(), userID, tariffID) if err != nil { return api.errorOld(ctx, err) } return ctx.JSON(http.StatusOK, cartItems) } func (api *API2) PayCart(ctx echo.Context) error { userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) if !ok { return api.noauth(ctx) } accessToken, ok := ctx.Get(models.AuthJWTDecodedAccessTokenKey).(string) if !ok { return api.noauth(ctx) } account, err := api.account.FindByUserID(ctx.Request().Context(), userID) if err != nil { return api.errorOld(ctx, err) } api.logger.Info("account for pay", zap.Any("acc", account)) tariffs, err := api.clients.hubadmin.GetTariffs(ctx.Request().Context(), accessToken, account.Cart) if err != nil { return api.errorOld(ctx, err) } api.logger.Info("tariffs for pay", zap.Any("acc", tariffs)) tariffsAmount := utils.CalculateCartPurchasesAmount(tariffs) discountResponse, err := api.clients.discount.Apply(ctx.Request().Context(), &discount.ApplyDiscountRequest{ UserInformation: &discount.UserInformation{ ID: account.UserID, Type: string(account.Status), PurchasesAmount: uint64(account.Wallet.Spent), CartPurchasesAmount: tariffsAmount, }, Products: transfer.TariffsToProductInformations(tariffs), Date: timestamppb.New(time.Now()), }) if err != nil { return api.errorOld(ctx, err) } api.logger.Info("discountResponse for pay", zap.Any("acc", discount.ApplyDiscountRequest{ UserInformation: &discount.UserInformation{ ID: account.UserID, Type: string(account.Status), PurchasesAmount: uint64(account.Wallet.Spent), CartPurchasesAmount: tariffsAmount, }, Products: transfer.TariffsToProductInformations(tariffs), Date: timestamppb.New(time.Now()), })) if account.Wallet.Money < int64(discountResponse.Price) { return api.error(ctx, http.StatusPaymentRequired, "insufficient funds: %d", int64(discountResponse.Price)-account.Wallet.Money) } for _, applied := range discountResponse.AppliedDiscounts { if applied.Condition.User != nil && *applied.Condition.User != "" { _, err := api.clients.discount.DeleteDiscount(ctx.Request().Context(), &discount.GetDiscountByIDRequest{ ID: applied.ID, }) if err != nil { return api.error(ctx, http.StatusInternalServerError, "failed delete discount by id:%s", applied.ID) } } } // WithdrawAccountWalletMoney request := models.WithdrawAccountWallet{ Money: int64(discountResponse.Price), Account: account, } if validate.IsStringEmpty(request.Account.Wallet.Currency) { request.Account.Wallet.Currency = models.InternalCurrencyKey } var updatedAccount *models.Account if request.Account.Wallet.Currency == models.InternalCurrencyKey { accountx, err := api.account.ChangeWallet(ctx.Request().Context(), request.Account.UserID, &models.Wallet{ Cash: request.Account.Wallet.Cash - request.Money, Money: request.Account.Wallet.Money - request.Money, Spent: request.Account.Wallet.Spent + request.Money, PurchasesAmount: request.Account.Wallet.PurchasesAmount, Currency: request.Account.Wallet.Currency, }) if err != nil { return api.errorOld(ctx, err) } updatedAccount = accountx } else { cash, err := api.clients.currency.Translate(ctx.Request().Context(), &models.TranslateCurrency{ Money: request.Money, From: models.InternalCurrencyKey, To: request.Account.Wallet.Currency, }) if err != nil { return api.errorOld(ctx, err) } accountx, err := api.account.ChangeWallet(ctx.Request().Context(), request.Account.UserID, &models.Wallet{ Cash: request.Account.Wallet.Cash - cash, Money: request.Account.Wallet.Money - request.Money, Spent: request.Account.Wallet.Spent + request.Money, PurchasesAmount: request.Account.Wallet.PurchasesAmount, Currency: request.Account.Wallet.Currency, }) if err != nil { return api.errorOld(ctx, err) } updatedAccount = accountx } if _, err := api.history.Insert(ctx.Request().Context(), &models.History{ Key: models.CustomerHistoryKeyPayCart, UserID: account.UserID, Comment: "Успешная оплата корзины", RawDetails: models.RawDetails{ Tariffs: tariffs, Price: int64(discountResponse.Price), }, }); err != nil { return api.errorOld(ctx, err) } // TODO: обработать ошибки при отправке сообщений sendErrors := make([]errors.Error, 0) waitGroup := sync.WaitGroup{} mutex := sync.Mutex{} for _, tariff := range tariffs { waitGroup.Add(1) go func(currentTariff models.Tariff) { defer waitGroup.Done() if err := api.producer.Send(ctx.Request().Context(), userID, ¤tTariff); err != nil { mutex.Lock() defer mutex.Unlock() sendErrors = append(sendErrors, err) } }(tariff) } waitGroup.Wait() if len(sendErrors) > 0 { for _, err := range sendErrors { api.logger.Error("failed to send tariffs to broker on of ", zap.Error(err)) } return api.error(ctx, http.StatusInternalServerError, "failed to send tariffs to broker") } if _, err := api.account.ClearCart(ctx.Request().Context(), account.UserID); err != nil { return api.errorOld(ctx, err) } updatedAccount.Cart = []string{} return ctx.JSON(http.StatusOK, updatedAccount) } // Currency func (api *API2) GetCurrencies(ctx echo.Context) error { currencyList, err := api.currency.FindCurrenciesList(ctx.Request().Context(), models.DefaultCurrencyListName) if err != nil && err.Type() != errors.ErrNotFound { return api.errorOld(ctx, err) } if err != nil && err.Type() == errors.ErrNotFound { return ctx.JSON(http.StatusOK, []string{}) } return ctx.JSON(http.StatusOK, currencyList) } func (api *API2) UpdateCurrencies(ctx echo.Context) error { currenciesPtr, bindErr := echotools.Bind[[]string](ctx) if bindErr != nil { return api.error(ctx, http.StatusBadRequest, "faild to bind currencies") } currencies := *currenciesPtr currencyList, err := api.currency.ReplaceCurrencies(ctx.Request().Context(), &models.CurrencyList{ Name: models.DefaultCurrencyListName, Currencies: currencies, }) if err != nil && err.Type() != errors.ErrNotFound { return api.errorOld(ctx, err) } if err != nil && err.Type() == errors.ErrNotFound { newCurrencyList, err := api.currency.Insert(ctx.Request().Context(), &models.CurrencyList{ Name: models.DefaultCurrencyListName, Currencies: currencies, }) if err != nil && err.Type() != errors.ErrNotFound { return api.errorOld(ctx, err) } return ctx.JSON(http.StatusOK, newCurrencyList.Currencies) } return ctx.JSON(http.StatusOK, currencyList.Currencies) } // History func (api *API2) GetHistory(ctx echo.Context, params GetHistoryParams) error { var userID string if params.AccountID != nil && *params.AccountID != "" { userID = *params.AccountID } else { userID, _ = ctx.Get(models.AuthJWTDecodedUserIDKey).(string) } dto := &history.GetHistories{ UserID: userID, Type: params.Type, Pagination: &models.Pagination{ Page: int64(*params.Page), Limit: int64(*params.Limit), }, } count, err := api.history.CountAll(ctx.Request().Context(), dto) if err != nil { return api.errorOld(ctx, err) } if count == 0 { returnHistories := models.PaginationResponse[models.History]{TotalPages: 0, Records: []models.History{}} return ctx.JSON(http.StatusOK, returnHistories) } totalPages := int64(math.Ceil(float64(count) / float64(dto.Pagination.Limit))) histories, err := api.history.FindMany(ctx.Request().Context(), dto) if err != nil { return api.errorOld(ctx, err) } returnHistories := models.PaginationResponse[models.History]{ TotalPages: totalPages, Records: histories, } return ctx.JSON(http.StatusOK, returnHistories) } // Wallet func (api *API2) RequestMoney(ctx echo.Context) error { userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) if !ok { return api.noauth(ctx) } request, bindErr := echotools.Bind[models.GetPaymentLinkBody](ctx) if bindErr != nil { return api.error(ctx, http.StatusBadRequest, "faild to bind payment link") } if err := utils.ValidateGetPaymentLinkBody(request); err != nil { return api.errorOld(ctx, err) } link, err := api.GetPaymentLink(ctx.Request().Context(), &models.GetPaymentLinkRequest{ Body: request, UserID: userID, ClientIP: ctx.RealIP(), }) if err != nil { return api.errorOld(ctx, err) } return ctx.JSON(http.StatusOK, &models.GetPaymentLinkResponse{Link: link}) } func (api *API2) ChangeCurrency(ctx echo.Context) error { userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) if !ok { return api.noauth(ctx) } request, bindErr := echotools.Bind[models.ChangeCurrency](ctx) if bindErr != nil { return api.error(ctx, http.StatusBadRequest, "faild to bind currency") } if validate.IsStringEmpty(request.Currency) { return api.error(ctx, http.StatusBadRequest, "empty currency") } currency := request.Currency account, err := api.account.FindByUserID(ctx.Request().Context(), userID) if err != nil { return api.errorOld(ctx, err) } cash, err := api.clients.currency.Translate(ctx.Request().Context(), &models.TranslateCurrency{ Money: account.Wallet.Cash, From: account.Wallet.Currency, To: currency, }) if err != nil { return api.errorOld(ctx, err) } updatedAccount, err := api.account.ChangeWallet(ctx.Request().Context(), account.UserID, &models.Wallet{ Cash: cash, Currency: currency, Money: account.Wallet.Money, }) if err != nil { return api.errorOld(ctx, err) } return ctx.JSON(http.StatusOK, updatedAccount) } func (api *API2) CalculateLTV(ctx echo.Context) error { var req CalculateLTVJSONBody if err := ctx.Bind(&req); err != nil { api.logger.Error("failed to bind request", zap.Error(err)) return api.error(ctx, http.StatusBadRequest, "failed to bind request") } if req.From > req.To && req.To != 0 { api.logger.Error("From timestamp must be less than To timestamp unless To is 0") return api.error(ctx, http.StatusBadRequest, "From timestamp must be less than To timestamp unless To is 0") } ltv, err := api.history.CalculateCustomerLTV(ctx.Request().Context(), req.From, req.To) if err != nil { api.logger.Error("failed to calculate LTV", zap.Error(err)) return api.errorOld(ctx, err) } response := struct { LTV int64 `json:"LTV"` }{ LTV: ltv, } return ctx.JSON(http.StatusOK, response) } func (api *API2) GetRecentTariffs(ctx echo.Context) error { userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) if !ok { api.logger.Error("failed to convert jwt payload to string on of ") return api.error(ctx, http.StatusBadRequest, "failed to convert jwt payload to string") } if userID == "" { api.logger.Error("user id is missing in of ") return api.error(ctx, http.StatusBadRequest, "user id is missing") } tariffs, err := api.history.GetRecentTariffs(ctx.Request().Context(), userID) if err != nil { api.logger.Error("failed to get recent tariffs on of ", zap.String("userId", userID), zap.Error(err), ) return api.errorOld(ctx, err) } return ctx.JSON(http.StatusOK, tariffs) } func (api *API2) SendReport(ctx echo.Context) error { var req SendReportJSONBody if err := ctx.Bind(&req); err != nil { api.logger.Error("failed to bind request", zap.Error(err)) return api.error(ctx, http.StatusBadRequest, "failed to bind request") } if req.Id == "" { api.logger.Error("history id is missing in of ") return api.error(ctx, http.StatusBadRequest, "history id is missing") } tariffs, err := api.history.GetHistoryByID(ctx.Request().Context(), req.Id) if err != nil { api.logger.Error( "failed to get history by id in of ", zap.String("historyID", req.Id), zap.Error(err), ) return api.errorOld(ctx, err) } if tariffs.Key != models.CustomerHistoryKeyPayCart { api.logger.Error( "invalid history record key", zap.String("historyID", req.Id), zap.Error(err), ) return api.error(ctx, http.StatusBadRequest, "invalid history record key") } historyMap, err := api.history.GetDocNumber(ctx.Request().Context(), tariffs.UserID) if err != nil { api.logger.Error( "failed to get history of sorting by date created in of ", zap.String("historyID", req.Id), zap.Error(err), ) return api.errorOld(ctx, err) } token := ctx.Request().Header.Get("Authorization") fmt.Println("HEADERS", ctx.Request().Header) verifuser, err := api.clients.verify.GetVerification(ctx.Request().Context(), token, tariffs.UserID) if err != nil { api.logger.Error("failed to get user verification on of ", zap.Error(err), zap.String("userID", tariffs.UserID), ) return api.errorOld(ctx, err) } if !verifuser.Accepted { api.logger.Error( "verification not accepted", zap.String("userID", tariffs.UserID), zap.Error(err), ) return api.error(ctx, http.StatusBadRequest, "verification not accepted") } authuser, err := api.clients.auth.GetUser(ctx.Request().Context(), tariffs.UserID) if err != nil { api.logger.Error("failed to get user on of ", zap.Error(err), zap.String("userID", tariffs.UserID), ) return api.errorOld(ctx, err) } fileContents, readerr := os.ReadFile("./report.docx") if readerr != nil { return api.error(ctx, http.StatusInternalServerError, "failed to read file") } for _, tariff := range tariffs.RawDetails.Tariffs { totalAmount := uint64(0) for _, privilege := range tariff.Privileges { totalAmount += privilege.Amount } data := models.RespGeneratorService{ DocNumber: historyMap[req.Id] + 1, Date: time.Now().Format("2006-01-02"), OrgTaxNum: verifuser.TaxNumber, OrgName: models.Name{Orgname: "Orgname"}, Name: tariff.Name, Amount: totalAmount, Price: tariffs.RawDetails.Price, Sum: tariffs.RawDetails.Price, } err = api.clients.template.SendData(ctx.Request().Context(), data, fileContents, authuser.Email) if err != nil { api.logger.Error("failed to send report to user on of ", zap.Error(err), zap.String("userID", tariffs.UserID), ) return api.errorOld(ctx, err) } } return ctx.NoContent(http.StatusOK) } func (api *API2) PostWalletRspay(ctx echo.Context) error { userID, ok := ctx.Get(models.AuthJWTDecodedUserIDKey).(string) if !ok { return api.noauth(ctx) } var req PostWalletRspayJSONBody if err := ctx.Bind(&req); err != nil { api.logger.Error("failed to bind request", zap.Error(err)) return api.error(ctx, http.StatusBadRequest, "failed to bind request") } user, err := api.account.FindByUserID(ctx.Request().Context(), userID) if err != nil { return api.errorOld(ctx, err) } if user.Status != models.AccountStatusNko && user.Status != models.AccountStatusOrg { return api.error(ctx, http.StatusForbidden, "not allowed for non organizations") } token := ctx.Request().Header.Get("Authorization") fmt.Println("HEADERS", ctx.Request().Header) verification, err := api.clients.verify.GetVerification(ctx.Request().Context(), token, userID) if err == errors.ErrNotFound { return api.error(ctx, http.StatusForbidden, "no verification data found") } if (user.Status == models.AccountStatusOrg && len(verification.Files) != 3) || (user.Status == models.AccountStatusNko && len(verification.Files) != 4) { return api.error(ctx, http.StatusForbidden, "not enough verification files") } authData, err := api.clients.auth.GetUser(ctx.Request().Context(), userID) if err != nil { return api.errorOld(ctx, err) } err = api.clients.mail.SendMessage(authData.Login, verification, *req.Money) if err != nil { return api.errorOld(ctx, err) } return ctx.NoContent(http.StatusOK) } type QuizLogoStat2 struct { Count int64 `json:"count,omitempty"` Items map[string]Item `json:"items,omitempty"` } type Item struct { Money int64 `json:"money,omitempty"` Quizes map[string][2]int `json:"quizes,omitempty"` Regs int `json:"regs,omitempty"` } func (api *API2) QuizLogoStat(ctx echo.Context) error { var req QuizLogoStatJSONRequestBody if err := ctx.Bind(&req); err != nil { api.logger.Error("failed to bind request", zap.Error(err)) return api.error(ctx, http.StatusBadRequest, "failed to bind request") } result, err := api.account.QuizLogoStat(ctx.Request().Context(), repository.QuizLogoStatDeps{ Page: req.Page, Limit: req.Limit, From: req.From, To: req.To, }) if err != nil { return api.error(ctx, http.StatusInternalServerError, fmt.Sprint("failed getting quiz logo stat", err.Error())) } return ctx.JSON(http.StatusOK, result) } func (api *API2) PromoCodeLtvStat(ctx echo.Context) error { var req PromocodeLTVJSONRequestBody if err := ctx.Bind(&req); err != nil { api.logger.Error("failed to bind request", zap.Error(err)) return api.error(ctx, http.StatusBadRequest, "failed to bind request") } codewordData, err := api.clients.codeword.GetAllPromoActivations(ctx.Request().Context()) if err != nil { return api.error(ctx, http.StatusInternalServerError, fmt.Sprint("failed getting codeword data", err.Error())) } }