generated from PenaSide/GolangTemplate
322 lines
11 KiB
Go
322 lines
11 KiB
Go
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"
|
|
"gitea.pena/PenaSide/tariffs/pkg/grpc_clients"
|
|
"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
|
|
TariffGrpcClient *grpc_clients.TariffGRPCClient
|
|
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
|
|
tariffGrpcClient *grpc_clients.TariffGRPCClient
|
|
discountClient *client.DiscountClient
|
|
currencyClient *client.CurrencyClient
|
|
producer *tariff.Producer
|
|
}
|
|
|
|
func NewPaymentCallbackService(deps PaymentCallbackServiceDeps) *PaymentCallbackService {
|
|
if deps.Logger == nil {
|
|
log.Panicln("logger is nil on <New (payment callback service)>")
|
|
}
|
|
|
|
if deps.AccountRepository == nil {
|
|
log.Panicln("AccountRepository is nil on <New (payment callback service)>")
|
|
}
|
|
|
|
if deps.WalletService == nil {
|
|
log.Panicln("WalletService is nil on <New (payment callback service)>")
|
|
}
|
|
|
|
if deps.HistoryService == nil {
|
|
log.Panicln("HistoryService is nil on <New (payment callback service)>")
|
|
}
|
|
|
|
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 <SuccessEvent> of <PaymentCallbackService>", zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
if account.Wallet.LastPaymentID == event.PaymentID {
|
|
receiver.logger.Info("payment already was executed on <SuccessEvent> of <PaymentCallbackService>", 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 <SuccessEvent> of <PaymentCallbackService>", 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 <SuccessEvent> of <PaymentCallbackService>", 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 <FailureEvent> of <PaymentCallbackService>", 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 <CartPurchaseEvent> of <PaymentCallbackService>", zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
if len(account.Cart) == 0 {
|
|
return nil
|
|
}
|
|
|
|
tariffs := make([]models.Tariff, len(account.Cart))
|
|
for index, tariffID := range account.Cart {
|
|
resp, err := receiver.tariffGrpcClient.GetTariff(ctx, tariffID)
|
|
if err != nil {
|
|
receiver.logger.Error("failed to get tariff on <GetTariffs> of <PaymentCallbackService>", zap.Error(err), zap.String("tariffID", tariffID))
|
|
return errors.New(
|
|
fmt.Errorf("failed to get tariff on <FindByUserID> of <PaymentCallbackService>: %w", err),
|
|
errors.ErrInternalError,
|
|
)
|
|
}
|
|
|
|
pbTariff := resp.Tariff
|
|
privileges := make([]models.Privilege, 0, len(pbTariff.Privileges))
|
|
for _, pbPriv := range pbTariff.Privileges {
|
|
privileges = append(privileges, models.Privilege{
|
|
ID: pbPriv.Id,
|
|
Name: pbPriv.Name,
|
|
PrivilegeID: pbPriv.PrivilegeId,
|
|
ServiceKey: pbPriv.ServiceKey,
|
|
Description: pbPriv.Description,
|
|
Amount: uint64(pbPriv.Amount),
|
|
Type: models.PrivilegeType(pbPriv.Type),
|
|
Value: pbPriv.Value,
|
|
Price: uint64(pbPriv.Price),
|
|
})
|
|
}
|
|
|
|
tariffs[index] = models.Tariff{
|
|
ID: pbTariff.Id,
|
|
Name: pbTariff.Name,
|
|
Price: uint64(pbTariff.Price),
|
|
IsCustom: pbTariff.IsCustom,
|
|
Privileges: privileges,
|
|
Deleted: pbTariff.IsDeleted,
|
|
}
|
|
}
|
|
|
|
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 <CartPurchaseEvent> of <PaymentCallbackService>", 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 <CartPurchaseEvent> of <PaymentCallbackService>", 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 <CartPurchaseEvent> of <PaymentCallbackService>", 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 <CartPurchaseEvent> of <PaymentCallbackService>", 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 <CartPurchaseEvent> of <PaymentCallbackService>", 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 <CartPurchaseEvent> of <PaymentCallbackService>", 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 <CartPurchaseEvent> of <PaymentCallbackService>", 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 <CartPurchaseEvent> of <PaymentCallbackService>", zap.Error(err))
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|