package service import ( "context" "errors" "fmt" "time" "git.gocasts.ir/ebhomengo/niki/domain/payment/entity" ) type PaymentService struct { paymentRepo PaymentRepo paymentMethodRepo PaymentMethodRepo gwFactory GatewayFactory } type PaymentRepo interface { CreatePayment(ctx context.Context, p *entity.Payment) error CreateTransaction(ctx context.Context, t *entity.PaymentTransaction) error UpdateTransaction(ctx context.Context, t *entity.PaymentTransaction) error } type PaymentMethodRepo interface { GetGatewayByCode(ctx context.Context, code string) (entity.Gateway, error) } type GatewayFactory interface { GetGatewayAdapter(code string) (GatewayPort, error) } type GatewayPort interface { Request(amount int64, callbackURL string, description string) (token string, redirectURL string, rawReq []byte, rawRes []byte, err error) } func NewPaymentService(pr PaymentRepo, pmr PaymentMethodRepo, gwf GatewayFactory) *PaymentService { return &PaymentService{ paymentRepo: pr, paymentMethodRepo: pmr, gwFactory: gwf, } } func (s *PaymentService) InitiatePayment(ctx context.Context, req InitiatePaymentRequest) (*InitiatePaymentResponse, error) { gateway, err := s.paymentMethodRepo.GetGatewayByCode(ctx, req.GatewayCode) if err != nil || !gateway.IsActive { return nil, errors.New("gateway is not available") } payment := &entity.Payment{ UserID: req.UserID, PayableType: req.PayableType, PayableID: req.PayableID, GatewayID: gateway.ID, TotalAmount: req.Amount, Currency: entity.CurrencyIRR, Status: entity.PaymentStatusPending, CreatedAt: time.Now(), UpdatedAt: time.Now(), } if err := s.paymentRepo.CreatePayment(ctx, payment); err != nil { return nil, fmt.Errorf("failed to create payment: %w", err) } transaction := &entity.PaymentTransaction{ PaymentID: payment.ID, Type: entity.TransactionTypeRequest, Status: entity.TransactionStatusPending, CreatedAt: time.Now(), UpdatedAt: time.Now(), } if err := s.paymentRepo.CreateTransaction(ctx, transaction); err != nil { return nil, fmt.Errorf("failed to create initial transaction: %w", err) } gwAdapter, err := s.gwFactory.GetGatewayAdapter(gateway.Code) if err != nil { return nil, fmt.Errorf("failed to load gateway adapter: %w", err) } desc := fmt.Sprintf(" پرداخت برای %s شماره %d", req.PayableType, req.PayableID) token, redirectURL, rawReq, rawRes, gwErr := gwAdapter.Request(req.Amount, req.CallbackURL, desc) transaction.RequestData = rawReq transaction.ResponseData = rawRes transaction.UpdatedAt = time.Now() if gwErr != nil { transaction.Status = entity.TransactionStatusFailed transaction.ErrorMessage = gwErr.Error() _ = s.paymentRepo.UpdateTransaction(ctx, transaction) return nil, fmt.Errorf("gateway request failed: %w", gwErr) } transaction.Status = entity.TransactionStatusSuccess transaction.GatewayToken = token if err := s.paymentRepo.UpdateTransaction(ctx, transaction); err != nil { return nil, fmt.Errorf("failed to update transaction with token: %w", err) } return &InitiatePaymentResponse{ PaymentID: payment.ID, RedirectURL: redirectURL, }, nil }