package settlement import ( "context" "crypto/rand" "encoding/hex" "fmt" "time" "gl/domain/ledger" ) type EnqueueRepository interface { Enqueue(context.Context, ledger.Settlement) (ledger.Settlement, bool, error) Get(context.Context, string, string) (ledger.Settlement, error) RetryByOperator(context.Context, string, string, string, time.Time) (ledger.Settlement, error) } func (s *Service) RetryByOperator(ctx context.Context, settlementID, actorID, reason string) (ledger.Settlement, error) { if settlementID == "" || actorID == "" || reason == "" { return ledger.Settlement{}, fmt.Errorf("settlement id, actor id, and reason are required") } now := time.Now().UTC() if s.now != nil { now = s.now().UTC() } return s.repository.RetryByOperator(ctx, settlementID, actorID, reason, now) } type Service struct { repository EnqueueRepository now func() time.Time } func NewService(repository EnqueueRepository) *Service { return &Service{repository: repository} } func (s *Service) Enqueue(ctx context.Context, sourceService, sourceTxID, idempotencyKey, network, xdr string) (ledger.Settlement, bool, error) { if sourceService == "" || sourceTxID == "" || idempotencyKey == "" || network == "" || xdr == "" { return ledger.Settlement{}, false, fmt.Errorf("settlement identity, network, and signed transaction are required") } id := make([]byte, 16) if _, err := rand.Read(id); err != nil { return ledger.Settlement{}, false, fmt.Errorf("generate settlement id: %w", err) } now := time.Now().UTC() if s.now != nil { now = s.now().UTC() } encoded := hex.EncodeToString(id) record := ledger.Settlement{ID: encoded[0:8] + "-" + encoded[8:12] + "-" + encoded[12:16] + "-" + encoded[16:20] + "-" + encoded[20:], SourceService: sourceService, SourceTxID: sourceTxID, IdempotencyKey: idempotencyKey, Status: ledger.SettlementPending, Network: network, SignedTransactionXDR: xdr, AvailableAt: now} if err := record.Validate(); err != nil { return ledger.Settlement{}, false, err } return s.repository.Enqueue(ctx, record) } func (s *Service) Get(ctx context.Context, settlementID, idempotencyKey string) (ledger.Settlement, error) { if settlementID == "" && idempotencyKey == "" { return ledger.Settlement{}, fmt.Errorf("settlement id or idempotency key is required") } return s.repository.Get(ctx, settlementID, idempotencyKey) }