diff --git a/config/configs.go b/config/configs.go index 177e169..f1c30db 100644 --- a/config/configs.go +++ b/config/configs.go @@ -31,6 +31,7 @@ type Config struct { Midtrans Midtrans `mapstructure:"midtrans"` Brevo Brevo `mapstructure:"brevo"` Email Email `mapstructure:"email"` + Withdraw Withdraw `mapstructure:"withdrawal"` } var ( @@ -73,5 +74,9 @@ func (c *Config) Auth() *AuthConfig { secret: c.Jwt.TokenResetPassword.Secret, expireTTL: c.Jwt.TokenResetPassword.ExpiresTTL, }, + jwtWithdraw: JWT{ + secret: c.Jwt.TokenWithdraw.Secret, + expireTTL: c.Jwt.TokenWithdraw.ExpiresTTL, + }, } } diff --git a/config/crypto.go b/config/crypto.go index abab087..c740a22 100644 --- a/config/crypto.go +++ b/config/crypto.go @@ -8,6 +8,7 @@ type AuthConfig struct { jwtOrderSecret string jwtOrderExpiresTTL int jwtSecretResetPassword JWT + jwtWithdraw JWT } type JWT struct { @@ -23,6 +24,15 @@ func (c *AuthConfig) AccessTokenOrderSecret() string { return c.jwtOrderSecret } +func (c *AuthConfig) AccessTokenWithdrawSecret() string { + return c.jwtWithdraw.secret +} + +func (c *AuthConfig) AccessTokenWithdrawExpire() time.Time { + duration := time.Duration(c.jwtWithdraw.expireTTL) + return time.Now().UTC().Add(time.Minute * duration) +} + func (c *AuthConfig) AccessTokenOrderExpiresDate() time.Time { duration := time.Duration(c.jwtOrderExpiresTTL) return time.Now().UTC().Add(time.Minute * duration) diff --git a/config/jwt.go b/config/jwt.go index f7a59ef..f8441c1 100644 --- a/config/jwt.go +++ b/config/jwt.go @@ -4,6 +4,7 @@ type Jwt struct { Token Token `mapstructure:"token"` TokenOrder Token `mapstructure:"token-order"` TokenResetPassword Token `mapstructure:"token-reset-password"` + TokenWithdraw Token `mapstructure:"token-withdraw"` } type Token struct { diff --git a/config/withdraw.go b/config/withdraw.go new file mode 100644 index 0000000..6014d63 --- /dev/null +++ b/config/withdraw.go @@ -0,0 +1,9 @@ +package config + +type Withdraw struct { + PlatformFee int64 `mapstructure:"platform_fee"` +} + +func (w *Withdraw) GetPlatformFee() int64 { + return w.PlatformFee +} diff --git a/infra/furtuna.development.yaml b/infra/furtuna.development.yaml index 2bf3e87..464c7cd 100644 --- a/infra/furtuna.development.yaml +++ b/infra/furtuna.development.yaml @@ -10,6 +10,9 @@ jwt: token-order: expires-ttl: 2 secret: "123Lm25V3Qd7aut8dr4QUxm5PZUrSFs" + token-withdraw: + expires-ttl: 2 + secret: "909Lm25V3Qd7aut8dr4QUxm5PZUrSFs" postgresql: host: 103.96.146.124 @@ -47,4 +50,7 @@ email: template_path: "templates/reset_password.html" subject: "Reset Password" opening_word: "Terima kasih sudah menjadi bagian dari Furtuna. Anda telah berhasil melakukan reset password, silakan masukan unik password yang dibuat oleh sistem dibawah ini:" - closing_word: "Silakan login kembali menggunakan email dan password anda diatas, sistem akan secara otomatis meminta anda untuk membuat password baru setelah berhasil login. Mohon maaf atas kendala yang dialami." \ No newline at end of file + closing_word: "Silakan login kembali menggunakan email dan password anda diatas, sistem akan secara otomatis meminta anda untuk membuat password baru setelah berhasil login. Mohon maaf atas kendala yang dialami." + +withdrawal: + platform_fee: 5000 \ No newline at end of file diff --git a/internal/entity/balance.go b/internal/entity/balance.go index 980e935..94e6777 100644 --- a/internal/entity/balance.go +++ b/internal/entity/balance.go @@ -5,3 +5,21 @@ type Balance struct { Balance float64 AuthBalance float64 } + +type BalanceWithdrawInquiry struct { + PartnerID int64 + Amount int64 +} + +type BalanceWithdrawInquiryResponse struct { + PartnerID int64 + Amount int64 + Total int64 + Fee int64 + Token string +} + +type WalletWithdrawResponse struct { + TransactionID string + Status string +} diff --git a/internal/entity/jwt.go b/internal/entity/jwt.go index 103a1a0..ccf7855 100644 --- a/internal/entity/jwt.go +++ b/internal/entity/jwt.go @@ -18,3 +18,13 @@ type JWTOrderClaims struct { OrderID int64 `json:"order_id"` jwt.StandardClaims } + +type JWTWithdrawClaims struct { + ID int64 `json:"id"` + PartnerID int64 `json:"partner_id"` + OrderID int64 `json:"order_id"` + Amount int64 `json:"amount"` + Fee int64 `json:"fee"` + Total int64 `json:"total"` + jwt.StandardClaims +} diff --git a/internal/entity/transaction.go b/internal/entity/transaction.go index 12a52bb..cdf21ed 100644 --- a/internal/entity/transaction.go +++ b/internal/entity/transaction.go @@ -12,6 +12,7 @@ type Transaction struct { Status string `gorm:"size:255"` CreatedBy int64 `gorm:"not null"` UpdatedBy int64 `gorm:"not null"` + Amount float64 `gorm:"not null"` CreatedAt time.Time `gorm:"autoCreateTime"` UpdatedAt time.Time `gorm:"autoUpdateTime"` } diff --git a/internal/entity/wallet.go b/internal/entity/wallet.go index 55d7876..e7e1b5d 100644 --- a/internal/entity/wallet.go +++ b/internal/entity/wallet.go @@ -16,3 +16,12 @@ type Wallet struct { func (Wallet) TableName() string { return "wallets" } + +type WalletWithdrawRequest struct { + ID int64 + Token string + PartnerID int64 + Amount int64 + Fee int64 + Total int64 +} diff --git a/internal/handlers/http/balance/balance.go b/internal/handlers/http/balance/balance.go new file mode 100644 index 0000000..1455abf --- /dev/null +++ b/internal/handlers/http/balance/balance.go @@ -0,0 +1,129 @@ +package balance + +import ( + "furtuna-be/internal/common/errors" + "furtuna-be/internal/entity" + "furtuna-be/internal/handlers/request" + "furtuna-be/internal/handlers/response" + "furtuna-be/internal/services" + "github.com/gin-gonic/gin" + "net/http" +) + +type Handler struct { + service services.Balance +} + +func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) { + route := group.Group("/balance") + + route.GET("/partner", jwt, h.GetPartnerBalance) + route.POST("/withdraw/inquiry", jwt, h.WithdrawBalanceInquiry) + route.POST("/withdraw/execute", jwt, h.WithdrawBalanceExecute) +} + +func NewHandler(service services.Balance) *Handler { + return &Handler{ + service: service, + } +} + +func (h *Handler) GetPartnerBalance(c *gin.Context) { + ctx := request.GetMyContext(c) + + if !ctx.IsPartnerAdmin() { + response.ErrorWrapper(c, errors.ErrorBadRequest) + return + } + + updatedBranch, err := h.service.GetByID(ctx, *ctx.GetPartnerID()) + if err != nil { + response.ErrorWrapper(c, err) + return + } + + c.JSON(http.StatusOK, response.BaseResponse{ + Success: true, + Status: http.StatusOK, + Data: h.toBalanceResponse(updatedBranch), + }) +} + +func (h *Handler) WithdrawBalanceInquiry(c *gin.Context) { + var req request.BalanceReq + if err := c.ShouldBindJSON(&req); err != nil { + response.ErrorWrapper(c, errors.ErrorBadRequest) + return + } + + ctx := request.GetMyContext(c) + + if !ctx.IsPartnerAdmin() { + response.ErrorWrapper(c, errors.ErrorBadRequest) + return + } + + inquiryResp, err := h.service.WithdrawInquiry(ctx, req.ToEntity(*ctx.GetPartnerID())) + if err != nil { + response.ErrorWrapper(c, err) + return + } + + c.JSON(http.StatusOK, response.BaseResponse{ + Success: true, + Status: http.StatusOK, + Data: h.toBalanceInquiryResp(inquiryResp), + }) +} + +func (h *Handler) WithdrawBalanceExecute(c *gin.Context) { + var req request.BalanceReq + if err := c.ShouldBindJSON(&req); err != nil { + response.ErrorWrapper(c, errors.ErrorBadRequest) + return + } + + ctx := request.GetMyContext(c) + + if !ctx.IsPartnerAdmin() { + response.ErrorWrapper(c, errors.ErrorBadRequest) + return + } + + inquiryResp, err := h.service.WithdrawExecute(ctx, req.ToEntityReq(*ctx.GetPartnerID())) + if err != nil { + response.ErrorWrapper(c, err) + return + } + + c.JSON(http.StatusOK, response.BaseResponse{ + Success: true, + Status: http.StatusOK, + Data: h.toBalanceExecuteResp(inquiryResp), + }) +} + +func (h *Handler) toBalanceResponse(resp *entity.Balance) response.Balance { + return response.Balance{ + PartnerID: resp.PartnerID, + Balance: resp.Balance, + AuthBalance: resp.AuthBalance, + } +} + +func (h *Handler) toBalanceInquiryResp(resp *entity.BalanceWithdrawInquiryResponse) response.BalanceInquiryResponse { + return response.BalanceInquiryResponse{ + PartnerID: resp.PartnerID, + Amount: resp.Amount, + Token: resp.Token, + Total: resp.Total, + Fee: resp.Fee, + } +} + +func (h *Handler) toBalanceExecuteResp(resp *entity.WalletWithdrawResponse) response.BalanceExecuteResponse { + return response.BalanceExecuteResponse{ + TransactionID: resp.TransactionID, + Status: resp.Status, + } +} diff --git a/internal/handlers/http/balance/branch.go b/internal/handlers/http/balance/branch.go deleted file mode 100644 index e27175d..0000000 --- a/internal/handlers/http/balance/branch.go +++ /dev/null @@ -1,56 +0,0 @@ -package balance - -import ( - "furtuna-be/internal/common/errors" - "furtuna-be/internal/entity" - "furtuna-be/internal/handlers/request" - "furtuna-be/internal/handlers/response" - "furtuna-be/internal/services" - "github.com/gin-gonic/gin" - "net/http" -) - -type Handler struct { - service services.Balance -} - -func (h *Handler) Route(group *gin.RouterGroup, jwt gin.HandlerFunc) { - route := group.Group("/balance") - - route.GET("/partner", jwt, h.GetPartnerBalance) -} - -func NewHandler(service services.Balance) *Handler { - return &Handler{ - service: service, - } -} - -func (h *Handler) GetPartnerBalance(c *gin.Context) { - ctx := request.GetMyContext(c) - - if !ctx.IsPartnerAdmin() { - response.ErrorWrapper(c, errors.ErrorBadRequest) - return - } - - updatedBranch, err := h.service.GetByID(ctx, *ctx.GetPartnerID()) - if err != nil { - response.ErrorWrapper(c, err) - return - } - - c.JSON(http.StatusOK, response.BaseResponse{ - Success: true, - Status: http.StatusOK, - Data: h.toBalanceResponse(updatedBranch), - }) -} - -func (h *Handler) toBalanceResponse(resp *entity.Balance) response.Balance { - return response.Balance{ - PartnerID: resp.PartnerID, - Balance: resp.Balance, - AuthBalance: resp.AuthBalance, - } -} diff --git a/internal/handlers/request/balance.go b/internal/handlers/request/balance.go new file mode 100644 index 0000000..4fdab4e --- /dev/null +++ b/internal/handlers/request/balance.go @@ -0,0 +1,23 @@ +package request + +import "furtuna-be/internal/entity" + +type BalanceReq struct { + Amount int64 `json:"amount"` + Token string `json:"token"` +} + +func (b *BalanceReq) ToEntity(partnerID int64) *entity.BalanceWithdrawInquiry { + return &entity.BalanceWithdrawInquiry{ + PartnerID: partnerID, + Amount: b.Amount, + } +} + +func (b *BalanceReq) ToEntityReq(partnerID int64) *entity.WalletWithdrawRequest { + return &entity.WalletWithdrawRequest{ + PartnerID: partnerID, + Amount: b.Amount, + Token: b.Token, + } +} diff --git a/internal/handlers/response/branch.go b/internal/handlers/response/branch.go index 03768b8..6130b6a 100644 --- a/internal/handlers/response/branch.go +++ b/internal/handlers/response/branch.go @@ -5,3 +5,16 @@ type Balance struct { Balance float64 `json:"balance"` AuthBalance float64 `json:"auth_balance"` } + +type BalanceInquiryResponse struct { + PartnerID int64 `json:"partner_id"` + Total int64 `json:"total"` + Amount int64 `json:"amount"` + Fee int64 `json:"fee"` + Token string `json:"token"` +} + +type BalanceExecuteResponse struct { + TransactionID string `json:"transaction_id"` + Status string `json:"status"` +} diff --git a/internal/repository/crypto/init.go b/internal/repository/crypto/init.go index b55a4d0..ea2d312 100644 --- a/internal/repository/crypto/init.go +++ b/internal/repository/crypto/init.go @@ -25,6 +25,8 @@ type CryptoConfig interface { AccessTokenExpiresDate() time.Time AccessTokenResetPasswordSecret() string AccessTokenResetPasswordExpire() time.Time + AccessTokenWithdrawSecret() string + AccessTokenWithdrawExpire() time.Time } type CryptoImpl struct { @@ -186,3 +188,54 @@ func (c *CryptoImpl) ValidateResetPassword(tokenString string) (int64, error) { return claims.UserID, nil } + +func (c *CryptoImpl) GenerateJWTWithdraw(req *entity.WalletWithdrawRequest) (string, error) { + claims := &entity.JWTWithdrawClaims{ + StandardClaims: jwt.StandardClaims{ + Subject: strconv.FormatInt(req.ID, 10), + ExpiresAt: c.Config.AccessTokenWithdrawExpire().Unix(), + IssuedAt: time.Now().Unix(), + NotBefore: time.Now().Unix(), + }, + PartnerID: req.PartnerID, + Amount: req.Amount, + Fee: req.Fee, + Total: req.Total, + } + + token, err := jwt. + NewWithClaims(jwt.SigningMethodHS256, claims). + SignedString([]byte(c.Config.AccessTokenWithdrawSecret())) + + if err != nil { + return "", err + } + + return token, nil +} + +func (c *CryptoImpl) ValidateJWTWithdraw(tokenString string) (*entity.WalletWithdrawRequest, error) { + token, err := jwt.ParseWithClaims(tokenString, &entity.JWTWithdrawClaims{}, func(token *jwt.Token) (interface{}, error) { + if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"]) + } + return []byte(c.Config.AccessTokenWithdrawSecret()), nil + }) + + if err != nil { + return nil, err + } + + claims, ok := token.Claims.(*entity.JWTWithdrawClaims) + if !ok || !token.Valid { + return nil, fmt.Errorf("invalid token %v", token.Header["alg"]) + } + + return &entity.WalletWithdrawRequest{ + ID: claims.ID, + PartnerID: claims.PartnerID, + Total: claims.Total, + Amount: claims.Amount, + Fee: claims.Fee, + }, nil +} diff --git a/internal/repository/repository.go b/internal/repository/repository.go index ac91cf8..e608524 100644 --- a/internal/repository/repository.go +++ b/internal/repository/repository.go @@ -97,6 +97,8 @@ type Crypto interface { ValidateJWTOrder(tokenString string) (int64, int64, error) ValidateResetPassword(tokenString string) (int64, error) ParseAndValidateJWT(token string) (*entity.JWTAuthClaims, error) + GenerateJWTWithdraw(req *entity.WalletWithdrawRequest) (string, error) + ValidateJWTWithdraw(tokenString string) (*entity.WalletWithdrawRequest, error) } type User interface { @@ -179,6 +181,7 @@ type WalletRepository interface { Create(ctx context.Context, tx *gorm.DB, wallet *entity.Wallet) (*entity.Wallet, error) Update(ctx context.Context, db *gorm.DB, wallet *entity.Wallet) (*entity.Wallet, error) GetByPartnerID(ctx context.Context, db *gorm.DB, partnerID int64) (*entity.Wallet, error) + GetForUpdate(ctx context.Context, tx *gorm.DB, partnerID int64) (*entity.Wallet, error) } type Midtrans interface { @@ -205,6 +208,6 @@ type License interface { } type TransactionRepository interface { - Create(ctx context.Context, transaction *entity.Transaction) (*entity.Transaction, error) + Create(ctx context.Context, trx *gorm.DB, transaction *entity.Transaction) (*entity.Transaction, error) GetTransactionList(ctx mycontext.Context, req entity.TransactionSearch) ([]*entity.TransactionList, int, error) } diff --git a/internal/repository/transaction/transaction.go b/internal/repository/transaction/transaction.go index 898f3b6..5615b4c 100644 --- a/internal/repository/transaction/transaction.go +++ b/internal/repository/transaction/transaction.go @@ -20,12 +20,20 @@ func NewTransactionRepository(db *gorm.DB) *TransactionRepository { } // Create creates a new transaction in the database. -func (r *TransactionRepository) Create(ctx context.Context, transaction *entity.Transaction) (*entity.Transaction, error) { - if err := r.db.WithContext(ctx).Create(transaction).Error; err != nil { +func (r *TransactionRepository) Create(ctx context.Context, trx *gorm.DB, transaction *entity.Transaction) (*entity.Transaction, error) { + // Create the transaction record + if err := trx.WithContext(ctx).Create(transaction).Error; err != nil { zap.L().Error("error when creating transaction", zap.Error(err)) return nil, err } - return r.FindByID(ctx, transaction.ID) + + // Retrieve the created transaction using the same transaction context + var createdTransaction entity.Transaction + if err := trx.WithContext(ctx).First(&createdTransaction, "id = ?", transaction.ID).Error; err != nil { + zap.L().Error("error when fetching newly created transaction", zap.Error(err)) + return nil, err + } + return &createdTransaction, nil } // Update updates an existing transaction in the database. @@ -126,7 +134,7 @@ func (r *TransactionRepository) GetTransactionList(ctx mycontext.Context, req en var transactions []*entity.TransactionList var total int64 - query := r.db.Table("transaction t"). + query := r.db.Table("transactions t"). Select("t.id, t.transaction_type, t.status, t.created_at, s.name as site_name, p.name as partner_name, t.amount"). Joins("left join sites s on t.site_id = s.id"). Joins("left join partners p on t.partner_id = p.id"). diff --git a/internal/repository/wallet/wallet.go b/internal/repository/wallet/wallet.go index c6315bd..64cbb8f 100644 --- a/internal/repository/wallet/wallet.go +++ b/internal/repository/wallet/wallet.go @@ -4,9 +4,9 @@ import ( "context" "furtuna-be/internal/common/logger" "furtuna-be/internal/entity" - "go.uber.org/zap" "gorm.io/gorm" + "gorm.io/gorm/clause" ) type WalletRepository struct { @@ -58,6 +58,22 @@ func (r *WalletRepository) GetByID(ctx context.Context, id int64) (*entity.Walle return wallet, nil } +func (r *WalletRepository) GetForUpdate(ctx context.Context, tx *gorm.DB, partnerID int64) (*entity.Wallet, error) { + if tx == nil { + tx = r.db + } + + query := tx.WithContext(ctx).Where("partner_id = ?", partnerID). + Clauses(clause.Locking{Strength: "UPDATE"}) + + wallet := new(entity.Wallet) + if err := query.First(wallet).Error; err != nil { + logger.ContextLogger(ctx).Error("error when finding balance by partner ID", zap.Error(err)) + return nil, err + } + return wallet, nil +} + func (r *WalletRepository) GetAll(ctx context.Context) ([]*entity.Wallet, error) { var wallets []*entity.Wallet if err := r.db.Find(&wallets).Error; err != nil { diff --git a/internal/services/balance/balance.go b/internal/services/balance/balance.go index c11c58a..97f3ba9 100644 --- a/internal/services/balance/balance.go +++ b/internal/services/balance/balance.go @@ -2,20 +2,36 @@ package balance import ( "context" + "errors" "furtuna-be/internal/common/logger" + "furtuna-be/internal/common/mycontext" "furtuna-be/internal/entity" "furtuna-be/internal/repository" - "go.uber.org/zap" ) -type BalanceService struct { - repo repository.WalletRepository +type Config interface { + GetPlatformFee() int64 } -func NewBalanceService(repo repository.WalletRepository) *BalanceService { +type BalanceService struct { + repo repository.WalletRepository + trx repository.TransactionManager + crypt repository.Crypto + transaction repository.TransactionRepository + cfg Config +} + +func NewBalanceService(repo repository.WalletRepository, + trx repository.TransactionManager, + crypt repository.Crypto, cfg Config, + transaction repository.TransactionRepository) *BalanceService { return &BalanceService{ - repo: repo, + repo: repo, + trx: trx, + crypt: crypt, + cfg: cfg, + transaction: transaction, } } @@ -32,3 +48,92 @@ func (s *BalanceService) GetByID(ctx context.Context, id int64) (*entity.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", + ReferenceID: "", + 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 +} diff --git a/internal/services/service.go b/internal/services/service.go index 30924ab..6b8a002 100644 --- a/internal/services/service.go +++ b/internal/services/service.go @@ -55,7 +55,7 @@ func NewServiceManagerImpl(cfg *config.Config, repo *repository.RepoManagerImpl) SiteSvc: site.NewSiteService(repo.Site), LicenseSvc: service.NewLicenseService(repo.License), Transaction: transaction.New(repo.Transaction), - Balance: balance.NewBalanceService(repo.Wallet), + Balance: balance.NewBalanceService(repo.Wallet, repo.Trx, repo.Crypto, &cfg.Withdraw, repo.Transaction), } } @@ -150,4 +150,6 @@ type Transaction interface { type Balance interface { GetByID(ctx context.Context, id int64) (*entity.Balance, error) + WithdrawInquiry(ctx context.Context, req *entity.BalanceWithdrawInquiry) (*entity.BalanceWithdrawInquiryResponse, error) + WithdrawExecute(ctx mycontext.Context, req *entity.WalletWithdrawRequest) (*entity.WalletWithdrawResponse, error) }