2023-06-22 09:36:43 +00:00
package callback
import (
"context"
2024-05-09 18:14:53 +00:00
"fmt"
2025-07-23 14:50:22 +00:00
"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"
2025-07-24 07:39:22 +00:00
"gitea.pena/PenaSide/tariffs/pkg/grpc_clients"
2025-07-23 14:50:22 +00:00
"google.golang.org/protobuf/types/known/timestamppb"
"log"
"sync"
"time"
2023-06-22 09:36:43 +00:00
2024-11-18 07:23:41 +00:00
"gitea.pena/PenaSide/customer/internal/errors"
"gitea.pena/PenaSide/customer/internal/models"
2025-07-23 14:50:22 +00:00
"go.uber.org/zap"
2024-05-09 18:14:53 +00:00
tb "gopkg.in/tucnak/telebot.v2"
2023-06-22 09:36:43 +00:00
)
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
2025-07-23 14:50:22 +00:00
AccountRepository * repository . AccountRepository
2023-06-22 09:36:43 +00:00
WalletService walletService
HistoryService historyService
2025-07-23 14:50:22 +00:00
Notifier * tb . Bot
NotifyChannel int64
AdminURL string
HistoryRepo * repository . HistoryRepository
2025-07-24 07:39:22 +00:00
TariffGrpcClient * grpc_clients . TariffGRPCClient
2025-07-23 14:50:22 +00:00
DiscountClient * client . DiscountClient
CurrencyClient * client . CurrencyClient
Producer * tariff . Producer
2023-06-22 09:36:43 +00:00
}
type PaymentCallbackService struct {
logger * zap . Logger
2025-07-23 14:50:22 +00:00
accountRepository * repository . AccountRepository
2023-06-22 09:36:43 +00:00
walletService walletService
historyService historyService
2025-07-23 14:50:22 +00:00
notifier * tb . Bot
notifyChannel int64
adminURL string
historyRepo * repository . HistoryRepository
2025-07-24 07:39:22 +00:00
tariffGrpcClient * grpc_clients . TariffGRPCClient
2025-07-23 14:50:22 +00:00
discountClient * client . DiscountClient
currencyClient * client . CurrencyClient
producer * tariff . Producer
2023-06-22 09:36:43 +00:00
}
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 ,
2025-07-23 14:50:22 +00:00
notifier : deps . Notifier ,
notifyChannel : deps . NotifyChannel ,
adminURL : deps . AdminURL ,
2023-06-22 09:36:43 +00:00
}
}
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
}
2024-03-17 22:13:56 +00:00
//go func() {
2024-07-12 09:37:56 +00:00
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
}
2024-03-17 22:13:56 +00:00
//}()
2025-07-23 14:50:22 +00:00
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 )
}
} ( )
2023-06-22 09:36:43 +00:00
return nil
}
func ( receiver * PaymentCallbackService ) FailureEvent ( ctx context . Context , event * models . PaymentEvent ) errors . Error {
if _ , err := receiver . historyService . CreateHistory ( ctx , & models . History {
2024-07-12 09:37:56 +00:00
UserID : event . UserID ,
Comment : event . Message ,
Key : event . Key ,
// RawDetails: fmt.Sprintf("%d%s", event.Amount, event.Currency),
2023-06-22 09:36:43 +00:00
} ) ; err != nil {
receiver . logger . Error ( "failed to create history on <FailureEvent> of <PaymentCallbackService>" , zap . Error ( err ) )
return err
}
return nil
}
2025-07-23 14:50:22 +00:00
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
}
2025-07-24 07:39:22 +00:00
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 ,
}
2025-07-23 14:50:22 +00:00
}
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 , & currentTariff ) ; 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
}