niki/domain/payment/service/payment.go

106 lines
3.1 KiB
Go

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
}