package wallet import ( "context" "fmt" "log" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/interface/client" "go.uber.org/zap" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/errors" "penahub.gitlab.yandexcloud.net/pena-services/customer/internal/models" "penahub.gitlab.yandexcloud.net/pena-services/customer/pkg/validate" ) type accountRepository interface { ChangeWallet(ctx context.Context, userID string, wallet *models.Wallet) (*models.Account, errors.Error) FindByUserID(ctx context.Context, id string) (*models.Account, errors.Error) } type currencyClient interface { Translate(context.Context, *models.TranslateCurrency) (int64, errors.Error) } type verificationClient interface { GetVerification(ctx context.Context, userID string) (*models.Verification, errors.Error) } type authClient interface { GetUser(ctx context.Context, userID string) (*models.User, errors.Error) } type historyService interface { CreateHistory(ctx context.Context, history *models.History) (*models.History, errors.Error) } type Deps struct { Logger *zap.Logger Repository accountRepository CurrencyClient currencyClient HistoryService historyService VerificationClient verificationClient AuthClient authClient MailClient *client.MailClient } type Service struct { logger *zap.Logger repository accountRepository currencyClient currencyClient historyService historyService verificationClient verificationClient authClient authClient mailClient *client.MailClient } func New(deps Deps) *Service { if deps.Logger == nil { log.Panicln("logger is nil on ") } if deps.Repository == nil { log.Panicln("repository is nil on ") } if deps.CurrencyClient == nil { log.Panicln("CurrencyClient is nil on ") } if deps.VerificationClient == nil { log.Panicln("VerificationClient is nil on ") } if deps.AuthClient == nil { log.Panicln("AuthClient is nil on ") } if deps.MailClient == nil { log.Panicln("MailClient is nil on ") } if deps.HistoryService == nil { log.Panicln("HistoryService is nil on ") } return &Service{ logger: deps.Logger, repository: deps.Repository, currencyClient: deps.CurrencyClient, verificationClient: deps.VerificationClient, authClient: deps.AuthClient, mailClient: deps.MailClient, historyService: deps.HistoryService, } } func (receiver *Service) ReplenishAccountWallet(ctx context.Context, request *models.ReplenishAccountWallet) (*models.Account, errors.Error) { if validate.IsStringEmpty(request.Account.Wallet.Currency) { request.Account.Wallet.Currency = models.InternalCurrencyKey } cash := request.Cash if request.Currency != request.Account.Wallet.Currency { translatedCash, translateErr := receiver.currencyClient.Translate(ctx, &models.TranslateCurrency{ Money: request.Cash, From: request.Currency, To: request.Account.Wallet.Currency, }) if translateErr != nil { receiver.logger.Error("failed to translate cash on of ", zap.Error(translateErr)) return nil, translateErr } cash = translatedCash } if request.Currency == models.InternalCurrencyKey { updatedAccount, changeErr := receiver.repository.ChangeWallet(ctx, request.Account.UserID, &models.Wallet{ Cash: request.Account.Wallet.Cash + cash, Money: request.Account.Wallet.Money + request.Cash, PurchasesAmount: request.Account.Wallet.PurchasesAmount + request.Cash, Currency: request.Account.Wallet.Currency, LastPaymentID: request.PaymentID, }) if changeErr != nil { receiver.logger.Error("failed to replenish wallet on of ", zap.Error(changeErr), zap.String("Currency", request.Account.Wallet.Currency), zap.Int64("Money", request.Account.Wallet.Money+request.Cash), zap.Int64("Cash", request.Account.Wallet.Cash+cash), ) return nil, changeErr } go func() { if _, historyErr := receiver.historyService.CreateHistory(ctx, &models.History{ Key: models.CustomerHistoryKeyReplenish, UserID: request.Account.UserID, Comment: "Успешное пополнение средств (Без конвертации валюты)", RawDetails: fmt.Sprintf("%d%s", cash, models.InternalCurrencyKey), }); historyErr != nil { receiver.logger.Error("failed to insert history on of ", zap.Error(historyErr)) } }() return updatedAccount, nil } money, err := receiver.currencyClient.Translate(ctx, &models.TranslateCurrency{ Money: request.Cash, From: request.Currency, To: models.InternalCurrencyKey, }) if err != nil { receiver.logger.Error("failed to translate money on of ", zap.Error(err)) return nil, err } updatedAccount, err := receiver.repository.ChangeWallet(ctx, request.Account.UserID, &models.Wallet{ Cash: request.Account.Wallet.Cash + cash, Money: request.Account.Wallet.Money + money, PurchasesAmount: request.Account.Wallet.PurchasesAmount + money, Spent: request.Account.Wallet.Spent, Currency: request.Account.Wallet.Currency, LastPaymentID: request.PaymentID, }) if err != nil { receiver.logger.Error("failed to replenish wallet on of ", zap.Error(err), zap.String("Currency", request.Account.Wallet.Currency), zap.Int64("Money", request.Account.Wallet.Money+money), zap.Int64("Cash", request.Account.Wallet.Cash+cash), zap.Bool("Is currensy equal internal", request.Currency == models.InternalCurrencyKey), ) return nil, err } go func() { if _, historyErr := receiver.historyService.CreateHistory(ctx, &models.History{ Key: models.CustomerHistoryKeyReplenish, UserID: request.Account.UserID, Comment: "Успешное пополнение средств (C конвертацией валюты)", RawDetails: fmt.Sprintf("%d%s", cash, request.Currency), }); historyErr != nil { receiver.logger.Error("failed to insert history on of ", zap.Error(historyErr)) } }() return updatedAccount, nil } func (receiver *Service) WithdrawAccountWalletMoney(ctx context.Context, request *models.WithdrawAccountWallet) (*models.Account, errors.Error) { if validate.IsStringEmpty(request.Account.Wallet.Currency) { request.Account.Wallet.Currency = models.InternalCurrencyKey } if request.Account.Wallet.Currency == models.InternalCurrencyKey { updatedAccount, err := receiver.repository.ChangeWallet(ctx, 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 { receiver.logger.Error("failed to replenish wallet on of ", zap.Error(err), zap.String("Currency", request.Account.Wallet.Currency), zap.Int64("Money", request.Account.Wallet.Money-request.Money), zap.Int64("Cash", request.Account.Wallet.Cash+request.Money), ) return nil, err } return updatedAccount, nil } cash, err := receiver.currencyClient.Translate(ctx, &models.TranslateCurrency{ Money: request.Money, From: models.InternalCurrencyKey, To: request.Account.Wallet.Currency, }) if err != nil { receiver.logger.Error("failed to translate money on of ", zap.Error(err)) return nil, err } updatedAccount, err := receiver.repository.ChangeWallet(ctx, 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 { receiver.logger.Error("failed to replenish wallet on of ", zap.Error(err), zap.String("Currency", request.Account.Wallet.Currency), zap.Int64("Money", request.Account.Wallet.Money-request.Money), zap.Int64("Cash", request.Account.Wallet.Cash+cash), ) return nil, err } return updatedAccount, nil } func (receiver *Service) ChangeCurrency(ctx context.Context, userID string, currency models.CurrencyKey) (*models.Account, errors.Error) { account, err := receiver.repository.FindByUserID(ctx, userID) if err != nil { receiver.logger.Error("failed to find account on of ", zap.Error(err), zap.String("userID", userID), zap.Any("currency", currency), ) return nil, err } cash, err := receiver.currencyClient.Translate(ctx, &models.TranslateCurrency{ Money: account.Wallet.Cash, From: account.Wallet.Currency, To: currency, }) if err != nil { receiver.logger.Error("failed to translate currency on of ", zap.Error(err)) return nil, err } updatedAccount, err := receiver.repository.ChangeWallet(ctx, account.UserID, &models.Wallet{ Cash: cash, Currency: currency, Money: account.Wallet.Money, }) if err != nil { receiver.logger.Error("failed to update wallet on of ", zap.Error(err)) return nil, err } return updatedAccount, nil } func (receiver *Service) PostWalletRspay(ctx context.Context, userID string) errors.Error { user, err := receiver.repository.FindByUserID(ctx, userID) if err != nil { return err } if user.Status != models.AccountStatusNko && user.Status != models.AccountStatusOrg { return errors.New( fmt.Errorf("not allowed for non organizations"), errors.ErrNoAccess, ) } verification, err := receiver.verificationClient.GetVerification(ctx, userID) if err == errors.ErrNotFound { return errors.New( fmt.Errorf("no verification data found"), errors.ErrNoAccess, ) } if user.Status == models.AccountStatusOrg && len(verification.Files) != 3 { return errors.New( fmt.Errorf("not enough verification files"), errors.ErrNoAccess, ) } else if user.Status == models.AccountStatusNko && len(verification.Files) != 4 { return errors.New( fmt.Errorf("not enough verification files"), errors.ErrNoAccess, ) } authData, err := receiver.authClient.GetUser(ctx, userID) if err != nil { return err } err = receiver.mailClient.SendMessage(authData.Login, verification) if err != nil { return err } return nil }