diff --git a/app/wallet/api/internal/logic/wallet/topupLogic.go b/app/wallet/api/internal/logic/wallet/topupLogic.go index c46c4ce..ecc62ce 100644 --- a/app/wallet/api/internal/logic/wallet/topupLogic.go +++ b/app/wallet/api/internal/logic/wallet/topupLogic.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "strings" "time" "juwan-backend/app/wallet/rpc/pb" @@ -53,9 +54,6 @@ func (l *TopupLogic) Topup(req *types.TopupReq) (resp *types.EmptyResp, err erro return nil, err } - currentBalance := decimal.Zero - frozenBalance := decimal.Zero - expectedVersion := int64(0) if walletResp.GetWallets() == nil { zeroAmount := "0" _, err = l.svcCtx.WalletRpc.AddWallets(l.ctx, &pb.AddWalletsReq{ @@ -74,54 +72,54 @@ func (l *TopupLogic) Topup(req *types.TopupReq) (resp *types.EmptyResp, err erro if walletResp.GetWallets() == nil { return nil, errors.New("wallet not found") } - currentBalance, err = decimal.NewFromString(walletResp.Wallets.Balance) - if err != nil { + } + + const maxRetries = 3 + for i := 0; i < maxRetries; i++ { + currentBalance, perr := decimal.NewFromString(walletResp.Wallets.Balance) + if perr != nil { return nil, errors.New("invalid wallet balance") } - frozenBalance, err = decimal.NewFromString(walletResp.Wallets.FrozenBalance) - if err != nil { + frozenBalance, perr := decimal.NewFromString(walletResp.Wallets.FrozenBalance) + if perr != nil { return nil, errors.New("invalid wallet frozen balance") } - expectedVersion = walletResp.Wallets.Version - } else { - currentBalance, err = decimal.NewFromString(walletResp.Wallets.Balance) - if err != nil { - return nil, errors.New("invalid wallet balance") + expectedVersion := walletResp.Wallets.Version + + newBalance := currentBalance.Add(amount) + newBalanceStr := newBalance.String() + frozenBalanceStr := frozenBalance.String() + updatedAt := time.Now().Unix() + _, uerr := l.svcCtx.WalletRpc.UpdateWallets(l.ctx, &pb.UpdateWalletsReq{ + UserId: userID, + Balance: &newBalanceStr, + FrozenBalance: &frozenBalanceStr, + UpdatedAt: &updatedAt, + Version: &expectedVersion, + }) + if uerr == nil { + desc := fmt.Sprintf("topup via %s", req.Method) + _, err = l.svcCtx.WalletRpc.AddWalletTransactions(l.ctx, &pb.AddWalletTransactionsReq{ + UserId: userID, + Type: "topup", + Amount: amount.String(), + BalanceAfter: newBalanceStr, + Description: desc, + CreatedAt: updatedAt, + }) + if err != nil { + return nil, err + } + return &types.EmptyResp{}, nil } - frozenBalance, err = decimal.NewFromString(walletResp.Wallets.FrozenBalance) - if err != nil { - return nil, errors.New("invalid wallet frozen balance") + if !strings.Contains(uerr.Error(), "conflict") { + return nil, uerr + } + walletResp, err = l.svcCtx.WalletRpc.GetWalletsById(l.ctx, &pb.GetWalletsByIdReq{Id: userID}) + if err != nil { + return nil, err } - expectedVersion = walletResp.Wallets.Version } - newBalance := currentBalance.Add(amount) - newBalanceStr := newBalance.String() - frozenBalanceStr := frozenBalance.String() - updatedAt := time.Now().Unix() - _, err = l.svcCtx.WalletRpc.UpdateWallets(l.ctx, &pb.UpdateWalletsReq{ - UserId: userID, - Balance: &newBalanceStr, - FrozenBalance: &frozenBalanceStr, - UpdatedAt: &updatedAt, - Version: &expectedVersion, - }) - if err != nil { - return nil, err - } - - desc := fmt.Sprintf("topup via %s", req.Method) - _, err = l.svcCtx.WalletRpc.AddWalletTransactions(l.ctx, &pb.AddWalletTransactionsReq{ - UserId: userID, - Type: "topup", - Amount: amount.String(), - BalanceAfter: newBalanceStr, - Description: desc, - CreatedAt: updatedAt, - }) - if err != nil { - return nil, err - } - - return &types.EmptyResp{}, nil + return nil, errors.New("wallet update conflict, please retry") } diff --git a/app/wallet/api/internal/logic/wallet/withdrawLogic.go b/app/wallet/api/internal/logic/wallet/withdrawLogic.go index b47a0e7..2b8e7c4 100644 --- a/app/wallet/api/internal/logic/wallet/withdrawLogic.go +++ b/app/wallet/api/internal/logic/wallet/withdrawLogic.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "strings" "time" "juwan-backend/app/wallet/rpc/pb" @@ -56,46 +57,55 @@ func (l *WithdrawLogic) Withdraw(req *types.TopupReq) (resp *types.EmptyResp, er return nil, errors.New("wallet not found") } - currentBalance, err := decimal.NewFromString(walletResp.Wallets.Balance) - if err != nil { - return nil, errors.New("invalid wallet balance") - } - frozenBalance, err := decimal.NewFromString(walletResp.Wallets.FrozenBalance) - if err != nil { - return nil, errors.New("invalid wallet frozen balance") - } - expectedVersion := walletResp.Wallets.Version - if currentBalance.LessThan(amount) { - return nil, errors.New("insufficient balance") + const maxRetries = 3 + for i := 0; i < maxRetries; i++ { + currentBalance, perr := decimal.NewFromString(walletResp.Wallets.Balance) + if perr != nil { + return nil, errors.New("invalid wallet balance") + } + frozenBalance, perr := decimal.NewFromString(walletResp.Wallets.FrozenBalance) + if perr != nil { + return nil, errors.New("invalid wallet frozen balance") + } + expectedVersion := walletResp.Wallets.Version + if currentBalance.LessThan(amount) { + return nil, errors.New("insufficient balance") + } + + newBalance := currentBalance.Sub(amount) + newBalanceStr := newBalance.String() + frozenBalanceStr := frozenBalance.String() + updatedAt := time.Now().Unix() + _, uerr := l.svcCtx.WalletRpc.UpdateWallets(l.ctx, &pb.UpdateWalletsReq{ + UserId: userID, + Balance: &newBalanceStr, + FrozenBalance: &frozenBalanceStr, + UpdatedAt: &updatedAt, + Version: &expectedVersion, + }) + if uerr == nil { + desc := fmt.Sprintf("withdraw via %s", req.Method) + _, err = l.svcCtx.WalletRpc.AddWalletTransactions(l.ctx, &pb.AddWalletTransactionsReq{ + UserId: userID, + Type: "withdrawal", + Amount: amount.String(), + BalanceAfter: newBalanceStr, + Description: desc, + CreatedAt: updatedAt, + }) + if err != nil { + return nil, err + } + return &types.EmptyResp{}, nil + } + if !strings.Contains(uerr.Error(), "conflict") { + return nil, uerr + } + walletResp, err = l.svcCtx.WalletRpc.GetWalletsById(l.ctx, &pb.GetWalletsByIdReq{Id: userID}) + if err != nil { + return nil, err + } } - newBalance := currentBalance.Sub(amount) - newBalanceStr := newBalance.String() - frozenBalanceStr := frozenBalance.String() - updatedAt := time.Now().Unix() - _, err = l.svcCtx.WalletRpc.UpdateWallets(l.ctx, &pb.UpdateWalletsReq{ - UserId: userID, - Balance: &newBalanceStr, - FrozenBalance: &frozenBalanceStr, - UpdatedAt: &updatedAt, - Version: &expectedVersion, - }) - if err != nil { - return nil, err - } - - desc := fmt.Sprintf("withdraw via %s", req.Method) - _, err = l.svcCtx.WalletRpc.AddWalletTransactions(l.ctx, &pb.AddWalletTransactionsReq{ - UserId: userID, - Type: "withdrawal", - Amount: amount.String(), - BalanceAfter: newBalanceStr, - Description: desc, - CreatedAt: updatedAt, - }) - if err != nil { - return nil, err - } - - return &types.EmptyResp{}, nil + return nil, errors.New("wallet update conflict, please retry") }