niki/domain/payment/service/service.go

100 lines
2.8 KiB
Go

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
}