/cancel callback nil dereference fix
This commit is contained in:
parent
c5fe2b8297
commit
8aefd54562
|
|
@ -153,7 +153,7 @@ func main() {
|
||||||
virtualGameSvc := virtualgameservice.New(vitualGameRepo, *walletSvc, store, cfg, logger)
|
virtualGameSvc := virtualgameservice.New(vitualGameRepo, *walletSvc, store, cfg, logger)
|
||||||
aleaService := alea.NewAleaPlayService(vitualGameRepo, *walletSvc, cfg, logger)
|
aleaService := alea.NewAleaPlayService(vitualGameRepo, *walletSvc, cfg, logger)
|
||||||
veliCLient := veli.NewClient(cfg, walletSvc)
|
veliCLient := veli.NewClient(cfg, walletSvc)
|
||||||
veliVirtualGameService := veli.New(veliCLient, walletSvc, cfg)
|
veliVirtualGameService := veli.New(veliCLient, walletSvc, wallet.TransferStore(store), cfg)
|
||||||
recommendationSvc := recommendation.NewService(recommendationRepo)
|
recommendationSvc := recommendation.NewService(recommendationRepo)
|
||||||
chapaClient := chapa.NewClient(cfg.CHAPA_BASE_URL, cfg.CHAPA_SECRET_KEY)
|
chapaClient := chapa.NewClient(cfg.CHAPA_BASE_URL, cfg.CHAPA_SECRET_KEY)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,7 @@ type CancelRequest struct {
|
||||||
CorrelationID string `json:"correlationId,omitempty"`
|
CorrelationID string `json:"correlationId,omitempty"`
|
||||||
ProviderID string `json:"providerId"`
|
ProviderID string `json:"providerId"`
|
||||||
BrandID string `json:"brandId"`
|
BrandID string `json:"brandId"`
|
||||||
|
IsAdjustment bool `json:"isAdjustment,omitempty"`
|
||||||
AdjustmentRefund *struct {
|
AdjustmentRefund *struct {
|
||||||
Amount float64 `json:"amount"`
|
Amount float64 `json:"amount"`
|
||||||
Currency string `json:"currency"`
|
Currency string `json:"currency"`
|
||||||
|
|
|
||||||
|
|
@ -19,16 +19,18 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
client *Client
|
client *Client
|
||||||
walletSvc *wallet.Service
|
walletSvc *wallet.Service
|
||||||
cfg *config.Config
|
transfetStore wallet.TransferStore
|
||||||
|
cfg *config.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(client *Client, walletSvc *wallet.Service, cfg *config.Config) VeliVirtualGameService {
|
func New(client *Client, walletSvc *wallet.Service, transferStore wallet.TransferStore, cfg *config.Config) VeliVirtualGameService {
|
||||||
return &service{
|
return &service{
|
||||||
client: client,
|
client: client,
|
||||||
walletSvc: walletSvc,
|
walletSvc: walletSvc,
|
||||||
cfg: cfg,
|
transfetStore: transferStore,
|
||||||
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -338,7 +340,7 @@ func (s *service) ProcessCancel(ctx context.Context, req domain.CancelRequest) (
|
||||||
// --- 1. Validate PlayerID ---
|
// --- 1. Validate PlayerID ---
|
||||||
playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64)
|
playerIDInt64, err := strconv.ParseInt(req.PlayerID, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid PlayerID %s", req.PlayerID)
|
return nil, fmt.Errorf("invalid PlayerID %q", req.PlayerID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 2. Get player wallets ---
|
// --- 2. Get player wallets ---
|
||||||
|
|
@ -358,18 +360,23 @@ func (s *service) ProcessCancel(ctx context.Context, req domain.CancelRequest) (
|
||||||
bonusBalance = float64(playerWallets[1].Balance)
|
bonusBalance = float64(playerWallets[1].Balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 3. Refund handling ---
|
// --- 3. Determine refund amount based on IsAdjustment ---
|
||||||
var refundAmount float64
|
var refundAmount float64
|
||||||
if req.AdjustmentRefund.Amount > 0 {
|
if req.IsAdjustment {
|
||||||
|
if req.AdjustmentRefund.Amount <= 0 {
|
||||||
|
return nil, fmt.Errorf("missing adjustmentRefund for adjustment cancel")
|
||||||
|
}
|
||||||
refundAmount = req.AdjustmentRefund.Amount
|
refundAmount = req.AdjustmentRefund.Amount
|
||||||
} else {
|
} else {
|
||||||
// If cancelType = CANCEL_BET and no explicit adjustmentRefund,
|
// Regular cancel: fetch original bet amount if needed
|
||||||
// we may need to look up the original bet transaction and refund that.
|
originalTransfer, err := s.transfetStore.GetTransferByReference(ctx, req.RefTransactionID)
|
||||||
// TODO: implement transaction lookup if required by your domain.
|
if err != nil {
|
||||||
return nil, fmt.Errorf("missing adjustmentRefund for CANCEL_BET")
|
return nil, fmt.Errorf("failed to get original bet for cancellation: %w", err)
|
||||||
|
}
|
||||||
|
refundAmount = float64(originalTransfer.Amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For now, we assume refund goes back fully to real wallet
|
// --- 4. Refund to wallet ---
|
||||||
usedReal := refundAmount
|
usedReal := refundAmount
|
||||||
usedBonus := 0.0
|
usedBonus := 0.0
|
||||||
|
|
||||||
|
|
@ -392,7 +399,7 @@ func (s *service) ProcessCancel(ctx context.Context, req domain.CancelRequest) (
|
||||||
return nil, fmt.Errorf("failed to refund wallet: %w", err)
|
return nil, fmt.Errorf("failed to refund wallet: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 4. Reload balances after refund ---
|
// --- 5. Reload balances after refund ---
|
||||||
updatedWallets, err := s.walletSvc.GetWalletsByUser(ctx, playerIDInt64)
|
updatedWallets, err := s.walletSvc.GetWalletsByUser(ctx, playerIDInt64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to reload balances: %w", err)
|
return nil, fmt.Errorf("failed to reload balances: %w", err)
|
||||||
|
|
@ -405,11 +412,11 @@ func (s *service) ProcessCancel(ctx context.Context, req domain.CancelRequest) (
|
||||||
bonusBalance = float64(updatedWallets[1].Balance)
|
bonusBalance = float64(updatedWallets[1].Balance)
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- 5. Build response ---
|
// --- 6. Build response ---
|
||||||
res := &domain.CancelResponse{
|
res := &domain.CancelResponse{
|
||||||
WalletTransactionID: req.TransactionID,
|
WalletTransactionID: req.TransactionID,
|
||||||
Real: domain.BalanceDetail{
|
Real: domain.BalanceDetail{
|
||||||
Currency: req.AdjustmentRefund.Currency,
|
Currency: "ETB",
|
||||||
Amount: realBalance,
|
Amount: realBalance,
|
||||||
},
|
},
|
||||||
UsedRealAmount: usedReal,
|
UsedRealAmount: usedReal,
|
||||||
|
|
@ -418,7 +425,7 @@ func (s *service) ProcessCancel(ctx context.Context, req domain.CancelRequest) (
|
||||||
|
|
||||||
if bonusBalance > 0 {
|
if bonusBalance > 0 {
|
||||||
res.Bonus = &domain.BalanceDetail{
|
res.Bonus = &domain.BalanceDetail{
|
||||||
Currency: req.AdjustmentRefund.Currency,
|
Currency: "ETB",
|
||||||
Amount: bonusBalance,
|
Amount: bonusBalance,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -426,6 +433,12 @@ func (s *service) ProcessCancel(ctx context.Context, req domain.CancelRequest) (
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Example helper to fetch original bet
|
||||||
|
// func (s *service) getOriginalBet(ctx context.Context, transactionID string) (*domain.BetRecord, error) {
|
||||||
|
// // TODO: implement actual lookup
|
||||||
|
// return &domain.BetRecord{Amount: 50}, nil
|
||||||
|
// }
|
||||||
|
|
||||||
func (s *service) GetGamingActivity(ctx context.Context, req domain.GamingActivityRequest) (*domain.GamingActivityResponse, error) {
|
func (s *service) GetGamingActivity(ctx context.Context, req domain.GamingActivityRequest) (*domain.GamingActivityResponse, error) {
|
||||||
// --- Signature Params (flattened strings for signing) ---
|
// --- Signature Params (flattened strings for signing) ---
|
||||||
sigParams := map[string]any{
|
sigParams := map[string]any{
|
||||||
|
|
|
||||||
|
|
@ -110,7 +110,7 @@ func (h *Handler) HandlePlayerInfo(c *fiber.Ctx) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) HandleBet(c *fiber.Ctx) error {
|
func (h *Handler) HandleBet(c *fiber.Ctx) error {
|
||||||
// Read the raw body to avoid parsing issues
|
// Read the raw body
|
||||||
body := c.Body()
|
body := c.Body()
|
||||||
if len(body) == 0 {
|
if len(body) == 0 {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
|
@ -119,26 +119,11 @@ func (h *Handler) HandleBet(c *fiber.Ctx) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to identify the provider based on the request structure
|
// Try parsing as Veli bet request
|
||||||
provider, err := identifyBetProvider(body)
|
var veliReq domain.BetRequest
|
||||||
if err != nil {
|
if err := json.Unmarshal(body, &veliReq); err == nil && veliReq.SessionID != "" && veliReq.BrandID != "" {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
// Process as Veli
|
||||||
Message: "Unrecognized request format",
|
res, err := h.veliVirtualGameSvc.ProcessBet(c.Context(), veliReq)
|
||||||
Error: err.Error(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
switch provider {
|
|
||||||
case "veli":
|
|
||||||
var req domain.BetRequest
|
|
||||||
if err := json.Unmarshal(body, &req); err != nil {
|
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
|
||||||
Message: "Invalid Veli bet request",
|
|
||||||
Error: err.Error(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
res, err := h.veliVirtualGameSvc.ProcessBet(c.Context(), req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, veli.ErrDuplicateTransaction) {
|
if errors.Is(err, veli.ErrDuplicateTransaction) {
|
||||||
return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{
|
return c.Status(fiber.StatusConflict).JSON(domain.ErrorResponse{
|
||||||
|
|
@ -152,17 +137,13 @@ func (h *Handler) HandleBet(c *fiber.Ctx) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return c.JSON(res)
|
return c.JSON(res)
|
||||||
|
}
|
||||||
|
|
||||||
case "popok":
|
// Try parsing as PopOK bet request
|
||||||
var req domain.PopOKBetRequest
|
var popokReq domain.PopOKBetRequest
|
||||||
if err := json.Unmarshal(body, &req); err != nil {
|
if err := json.Unmarshal(body, &popokReq); err == nil && popokReq.ExternalToken != "" {
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
// Process as PopOK
|
||||||
Message: "Invalid PopOK bet request",
|
resp, err := h.virtualGameSvc.ProcessBet(c.Context(), &popokReq)
|
||||||
Error: err.Error(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := h.virtualGameSvc.ProcessBet(c.Context(), &req)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
code := fiber.StatusInternalServerError
|
code := fiber.StatusInternalServerError
|
||||||
switch err.Error() {
|
switch err.Error() {
|
||||||
|
|
@ -177,13 +158,13 @@ func (h *Handler) HandleBet(c *fiber.Ctx) error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return c.JSON(resp)
|
return c.JSON(resp)
|
||||||
|
|
||||||
default:
|
|
||||||
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
|
||||||
Message: "Unsupported provider",
|
|
||||||
Error: "Request format doesn't match any supported provider",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If neither works
|
||||||
|
return c.Status(fiber.StatusBadRequest).JSON(domain.ErrorResponse{
|
||||||
|
Message: "Unsupported provider",
|
||||||
|
Error: "Request format doesn't match any supported provider",
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// identifyProvider examines the request body to determine the provider
|
// identifyProvider examines the request body to determine the provider
|
||||||
|
|
@ -513,7 +494,7 @@ func (h *Handler) ListFavorites(c *fiber.Ctx) error {
|
||||||
return c.Status(fiber.StatusOK).JSON(games)
|
return c.Status(fiber.StatusOK).JSON(games)
|
||||||
}
|
}
|
||||||
|
|
||||||
func identifyBetProvider(body []byte) (string, error) {
|
func IdentifyBetProvider(body []byte) (string, error) {
|
||||||
// Check for Veli signature fields
|
// Check for Veli signature fields
|
||||||
var veliCheck struct {
|
var veliCheck struct {
|
||||||
SessionID string `json:"sessionId"`
|
SessionID string `json:"sessionId"`
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user