package callback import ( "context" "fmt" "gitea.pena/PenaSide/customer/internal/interface/broker/tariff" "gitea.pena/PenaSide/customer/internal/interface/client" "gitea.pena/PenaSide/customer/internal/interface/repository" "gitea.pena/PenaSide/customer/internal/proto/discount" "gitea.pena/PenaSide/customer/internal/utils" "gitea.pena/PenaSide/customer/internal/utils/transfer" "gitea.pena/PenaSide/customer/pkg/validate" "google.golang.org/protobuf/types/known/timestamppb" "log" "sync" "time" "gitea.pena/PenaSide/customer/internal/errors" "gitea.pena/PenaSide/customer/internal/models" "go.uber.org/zap" tb "gopkg.in/tucnak/telebot.v2" ) type accountRepository interface { FindByUserID(context.Context, string) (*models.Account, errors.Error) } type historyService interface { CreateHistory(ctx context.Context, history *models.History) (*models.History, errors.Error) } type walletService interface { ReplenishAccountWallet(context.Context, *models.ReplenishAccountWallet) (*models.Account, errors.Error) } type PaymentCallbackServiceDeps struct { Logger *zap.Logger AccountRepository *repository.AccountRepository WalletService walletService HistoryService historyService Notifier *tb.Bot NotifyChannel int64 AdminURL string HistoryRepo *repository.HistoryRepository HubAdminClient *client.HubadminClient DiscountClient *client.DiscountClient CurrencyClient *client.CurrencyClient Producer *tariff.Producer } type PaymentCallbackService struct { logger *zap.Logger accountRepository *repository.AccountRepository walletService walletService historyService historyService notifier *tb.Bot notifyChannel int64 adminURL string historyRepo *repository.HistoryRepository hubAdminClient *client.HubadminClient discountClient *client.DiscountClient currencyClient *client.CurrencyClient producer *tariff.Producer } func NewPaymentCallbackService(deps PaymentCallbackServiceDeps) *PaymentCallbackService { if deps.Logger == nil { log.Panicln("logger is nil on ") } if deps.AccountRepository == nil { log.Panicln("AccountRepository is nil on ") } if deps.WalletService == nil { log.Panicln("WalletService is nil on ") } if deps.HistoryService == nil { log.Panicln("HistoryService is nil on ") } return &PaymentCallbackService{ logger: deps.Logger, accountRepository: deps.AccountRepository, walletService: deps.WalletService, historyService: deps.HistoryService, notifier: deps.Notifier, notifyChannel: deps.NotifyChannel, adminURL: deps.AdminURL, } } func (receiver *PaymentCallbackService) SuccessEvent(ctx context.Context, event *models.PaymentEvent) errors.Error { account, err := receiver.accountRepository.FindByUserID(ctx, event.UserID) if err != nil { receiver.logger.Error("failed to get account on of ", zap.Error(err)) return err } if account.Wallet.LastPaymentID == event.PaymentID { receiver.logger.Info("payment already was executed on of ", zap.String("paymentID", event.PaymentID)) return nil } if _, err := receiver.walletService.ReplenishAccountWallet(ctx, &models.ReplenishAccountWallet{ Cash: event.Amount, Currency: event.Currency, PaymentID: event.PaymentID, Account: account, }); err != nil { receiver.logger.Error("failed to replenish wallet on of ", zap.Error(err)) return err } //go func() { if _, err := receiver.historyService.CreateHistory(ctx, &models.History{ UserID: account.UserID, Comment: event.Message, Key: event.Key, RawDetails: models.RawDetails{ Price: event.Amount, Comment: event.Type + ":" + event.Currency, }, }); err != nil { receiver.logger.Error("failed to create history on of ", zap.Error(err)) return err } //}() go func() { if _, err := receiver.notifier.Send(tb.ChatID(receiver.notifyChannel), fmt.Sprintf(`Внесены деньги %.2f, пользователем %s`, float64(event.Amount/100), receiver.adminURL+"/users/"+account.UserID)); err != nil { fmt.Println("TBOER", err) } }() return nil } func (receiver *PaymentCallbackService) FailureEvent(ctx context.Context, event *models.PaymentEvent) errors.Error { if _, err := receiver.historyService.CreateHistory(ctx, &models.History{ UserID: event.UserID, Comment: event.Message, Key: event.Key, // RawDetails: fmt.Sprintf("%d%s", event.Amount, event.Currency), }); err != nil { receiver.logger.Error("failed to create history on of ", zap.Error(err)) return err } return nil } func (receiver *PaymentCallbackService) CartPurchaseEvent(ctx context.Context, event *models.PaymentEvent) errors.Error { account, err := receiver.accountRepository.FindByUserID(ctx, event.UserID) if err != nil { receiver.logger.Error("failed to get account on of ", zap.Error(err)) return err } if len(account.Cart) == 0 { return nil } tariffs, err := receiver.hubAdminClient.GetTariffs(ctx, accesToken, account.Cart) if err != nil { receiver.logger.Error("failed to get tariffs on of ", zap.Error(err)) return err } tariffsAmount := utils.CalculateCartPurchasesAmount(tariffs) discountResponse, err := receiver.discountClient.Apply(ctx, &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 { receiver.logger.Error("failed to apply discount on of ", zap.Error(err)) return err } for _, applied := range discountResponse.AppliedDiscounts { if applied.Condition.User != nil && *applied.Condition.User != "" { _, err := receiver.discountClient.DeleteDiscount(ctx, &discount.GetDiscountByIDRequest{ ID: applied.ID, }) if err != nil { receiver.logger.Error("failed to delete discount on of ", zap.Error(err)) return err } } } request := models.WithdrawAccountWallet{ Money: int64(discountResponse.Price), Account: account, } if validate.IsStringEmpty(request.Account.Wallet.Currency) { request.Account.Wallet.Currency = models.InternalCurrencyKey } if request.Account.Wallet.Currency == models.InternalCurrencyKey { _, err = receiver.accountRepository.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 change wallet on of ", zap.Error(err)) return err } } else { 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 currency on of ", zap.Error(err)) return err } _, err = receiver.accountRepository.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 change wallet (non-internal currency) on of ", zap.Error(err)) return err } } if _, err := receiver.historyRepo.Insert(ctx, &models.History{ Key: models.CustomerHistoryKeyPayCart, UserID: account.UserID, Comment: "Успешная оплата корзины", RawDetails: models.RawDetails{ Tariffs: tariffs, Price: int64(discountResponse.Price), }, }); err != nil { receiver.logger.Error("failed to insert history on of ", zap.Error(err)) return err } 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 := receiver.producer.Send(ctx, account.UserID, ¤tTariff); err != nil { mutex.Lock() defer mutex.Unlock() sendErrors = append(sendErrors, err) } }(tariff) } waitGroup.Wait() if len(sendErrors) > 0 { for _, err := range sendErrors { receiver.logger.Error("failed to send tariffs to broker on of ", zap.Error(err)) } return errors.New(fmt.Errorf("failed to send tariffs to broker"), errors.ErrInternalError) } if _, err := receiver.accountRepository.ClearCart(ctx, account.UserID); err != nil { receiver.logger.Error("failed to clear cart on of ", zap.Error(err)) return err } return nil }