2025-06-27 13:01:39 +07:00

139 lines
4.0 KiB
Go

package balance
import (
"context"
"enaklo-pos-be/internal/common/logger"
"enaklo-pos-be/internal/common/mycontext"
"enaklo-pos-be/internal/entity"
"enaklo-pos-be/internal/repository"
"errors"
"go.uber.org/zap"
)
type Config interface {
GetPlatformFee() int64
}
type BalanceService struct {
repo repository.WalletRepository
trx repository.Trx
crypt repository.Crypto
transaction repository.TransactionRepository
cfg Config
}
func NewBalanceService(repo repository.WalletRepository,
trx repository.Trx,
crypt repository.Crypto, cfg Config,
transaction repository.TransactionRepository) *BalanceService {
return &BalanceService{
repo: repo,
trx: trx,
crypt: crypt,
cfg: cfg,
transaction: transaction,
}
}
func (s *BalanceService) GetByID(ctx context.Context, id int64) (*entity.Balance, error) {
balanceDB, err := s.repo.GetByPartnerID(ctx, nil, id)
if err != nil {
logger.ContextLogger(ctx).Error("error when get branch by id", zap.Error(err))
return nil, err
}
return &entity.Balance{
PartnerID: id,
Balance: balanceDB.Balance,
AuthBalance: balanceDB.AuthBalance,
}, nil
}
func (s *BalanceService) WithdrawInquiry(ctx context.Context, req *entity.BalanceWithdrawInquiry) (*entity.BalanceWithdrawInquiryResponse, error) {
balanceDB, err := s.repo.GetForUpdate(ctx, nil, req.PartnerID)
if err != nil {
logger.ContextLogger(ctx).Error("error when get branch by id", zap.Error(err))
return nil, err
}
if float64(req.Amount) > balanceDB.Balance {
logger.ContextLogger(ctx).Error("requested amount exceeds available balance")
return nil, errors.New("insufficient balance")
}
token, err := s.crypt.GenerateJWTWithdraw(&entity.WalletWithdrawRequest{
ID: balanceDB.ID,
PartnerID: req.PartnerID,
Amount: req.Amount - s.cfg.GetPlatformFee(),
Fee: s.cfg.GetPlatformFee(),
Total: req.Amount,
})
return &entity.BalanceWithdrawInquiryResponse{
PartnerID: req.PartnerID,
Amount: req.Amount - s.cfg.GetPlatformFee(),
Token: token,
Fee: s.cfg.GetPlatformFee(),
Total: req.Amount,
}, nil
}
func (s *BalanceService) WithdrawExecute(ctx mycontext.Context, req *entity.WalletWithdrawRequest) (*entity.WalletWithdrawResponse, error) {
decodedReq, err := s.crypt.ValidateJWTWithdraw(req.Token)
if err != nil || decodedReq.PartnerID != req.PartnerID {
logger.ContextLogger(ctx).Error("invalid withdrawal token", zap.Error(err))
return nil, errors.New("invalid withdrawal token")
}
trx, _ := s.trx.Begin(ctx)
wallet, err := s.repo.GetForUpdate(ctx, trx, decodedReq.PartnerID)
if err != nil {
logger.ContextLogger(ctx).Error("error retrieving wallet by partner ID", zap.Error(err))
trx.Rollback()
return nil, err
}
totalAmount := float64(decodedReq.Total)
if totalAmount > wallet.Balance {
logger.ContextLogger(ctx).Error("insufficient balance for withdrawal", zap.Float64("available", wallet.Balance), zap.Float64("requested", totalAmount))
trx.Rollback()
return nil, errors.New("insufficient balance")
}
wallet.Balance -= totalAmount
wallet.AuthBalance += totalAmount
if _, err := s.repo.Update(ctx, trx, wallet); err != nil {
logger.ContextLogger(ctx).Error("error updating wallet balance", zap.Error(err))
trx.Rollback()
return nil, err
}
transaction := &entity.Transaction{
PartnerID: wallet.PartnerID,
TransactionType: "WITHDRAW",
Status: "WAITING_APPROVAL",
CreatedBy: ctx.RequestedBy(),
Amount: totalAmount,
}
transaction, err = s.transaction.Create(ctx, trx, transaction)
if err != nil {
logger.ContextLogger(ctx).Error("error creating transaction record", zap.Error(err))
trx.Rollback()
return nil, err
}
if err := trx.Commit().Error; err != nil {
logger.ContextLogger(ctx).Error("error committing transaction", zap.Error(err))
return nil, err
}
response := &entity.WalletWithdrawResponse{
TransactionID: transaction.ID,
Status: "WAITING_APPROVAL",
}
return response, nil
}