Yimaru-BackEnd/internal/services/chapa/service.go

129 lines
3.2 KiB
Go

package chapa
import (
"context"
"fmt"
"strconv"
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
"github.com/SamuelTariku/FortuneBet-Backend/internal/repository"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/transaction"
"github.com/SamuelTariku/FortuneBet-Backend/internal/services/wallet"
"github.com/shopspring/decimal"
)
type Service struct {
transactionStore transaction.TransactionStore
walletStore wallet.WalletStore
store *repository.Store
}
func (s *Service) HandleChapaTransferWebhook(ctx context.Context, req domain.ChapaWebHookTransfer) error {
tx, err := s.store.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx)
// 1. Fetch transaction
referenceID, err := strconv.ParseInt(req.Reference, 10, 64)
txn, err := s.transactionStore.GetTransactionByID(ctx, referenceID)
if err != nil {
return err
}
if txn.Verified {
return nil // already processed
}
// 2. Compare amount
webhookAmount, _ := decimal.NewFromString(req.Amount)
storedAmount, _ := decimal.NewFromString(txn.Amount.String())
if !webhookAmount.Equal(storedAmount) {
return fmt.Errorf("amount mismatch")
}
// 3. Update transaction
txn.Verified = true
if err := s.transactionStore.UpdateTransactionVerified(ctx, txn.ID, txn.Verified, txn.ApprovedBy.Value, txn.ApproverName.Value); err != nil {
return err
}
return tx.Commit(ctx)
}
func (s *Service) HandleChapaPaymentWebhook(ctx context.Context, req domain.ChapaWebHookPayment) error {
tx, err := s.store.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(ctx)
if req.Status != "success" {
return fmt.Errorf("payment status not successful")
}
// 1. Parse reference ID
referenceID, err := strconv.ParseInt(req.TxRef, 10, 64)
if err != nil {
return fmt.Errorf("invalid tx_ref: %w", err)
}
// 2. Fetch transaction
txn, err := s.transactionStore.GetTransactionByID(ctx, referenceID)
if err != nil {
return err
}
if txn.Verified {
return nil // already processed
}
// 3. Amount validation
webhookAmount, _ := decimal.NewFromString(req.Amount)
storedAmount := txn.Amount // assuming it's domain.Currency (decimal.Decimal alias)
if webhookAmount.LessThan(storedAmount) {
return fmt.Errorf("webhook amount is less than expected")
}
// 4. Fetch wallet
wallet, err := s.walletStore.GetWalletByID(ctx, txn.ID)
if err != nil {
return err
}
// 5. Update wallet balance
newBalance := wallet.Balance.Add(storedAmount)
if err := s.walletStore.UpdateBalance(ctx, wallet.ID, newBalance); err != nil {
return err
}
// 6. Mark transaction as verified
if err := s.transactionStore.UpdateTransactionVerified(ctx, txn.ID, true, txn.ApprovedBy.Value, txn.ApproverName.Value); err != nil {
return err
}
// 7. Check & generate referral code
hasCode, err := s.userStore.HasReferralCode(ctx, wallet.UserID)
if err != nil {
return err
}
if !hasCode {
code := misc.GenerateReferralCode(req.FirstName)
if err := s.userStore.SetReferralCode(ctx, wallet.UserID, code); err != nil {
return err
}
if err := s.referralStore.CreateReferralCode(ctx, domain.ReferralCode{
Code: code,
Amount: config.ReferralRewardBase,
}); err != nil {
return err
}
}
return tx.Commit(ctx)
}