package service import ( "context" "errors" "fmt" "time" ) type PaymentService struct { repo PaymentRepository gwFactory GatewayFactory } type PaymentRepository interface { CreatePayment(ctx context.Context, p *Payment) error CreateTransaction(ctx context.Context, t *PaymentTransaction) error UpdateTransaction(ctx context.Context, t *PaymentTransaction) error GetGatewayByCode(ctx context.Context, code string) (*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(r PaymentRepository, gwf GatewayFactory) *PaymentService { return &PaymentService{ repo: r, gwFactory: gwf, } } func (s *PaymentService) InitiatePayment(ctx context.Context, req InitiatePaymentRequest) (*InitiatePaymentResponse, error) { gateway, err := s.repo.GetGatewayByCode(ctx, req.GatewayCode) if err != nil || !gateway.IsActive { return nil, errors.New("gateway is not available") } payment := &Payment{ UserID: req.UserID, PayableType: req.PayableType, PayableID: req.PayableID, GatewayID: gateway.ID, TotalAmount: req.Amount, Currency: CurrencyIRR, Status: PaymentStatusPending, CreatedAt: time.Now(), UpdatedAt: time.Now(), } if err := s.repo.CreatePayment(ctx, payment); err != nil { return nil, fmt.Errorf("failed to create payment: %w", err) } transaction := &PaymentTransaction{ PaymentID: payment.ID, Type: TransactionTypeRequest, Status: TransactionStatusPending, CreatedAt: time.Now(), UpdatedAt: time.Now(), } if err := s.repo.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 = TransactionStatusFailed transaction.ErrorMessage = gwErr.Error() _ = s.repo.UpdateTransaction(ctx, transaction) return nil, fmt.Errorf("gateway request failed: %w", gwErr) } transaction.Status = TransactionStatusSuccess transaction.GatewayToken = token if err := s.repo.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 }