Files
GL/domain/ledger/settlement.go
T

68 lines
1.8 KiB
Go

package ledger
import (
"fmt"
"time"
)
type SettlementStatus string
const (
SettlementPending SettlementStatus = "PENDING"
SettlementSubmitted SettlementStatus = "SUBMITTED"
SettlementConfirmed SettlementStatus = "CONFIRMED"
SettlementRetryable SettlementStatus = "RETRYABLE"
SettlementManualReview SettlementStatus = "MANUAL_REVIEW"
)
type Settlement struct {
ID string
SourceService string
SourceTxID string
IdempotencyKey string
Status SettlementStatus
Network string
TransactionHash string
Attempts int
AvailableAt time.Time
LastError string
SignedTransactionXDR string
}
type SettlementStats struct {
Pending int64
Retryable int64
ManualReview int64
OldestPendingSeconds int64
}
func (s Settlement) Validate() error {
if s.ID == "" || s.SourceService == "" || s.SourceTxID == "" || s.IdempotencyKey == "" {
return fmt.Errorf("settlement identity fields are required")
}
if s.Status != SettlementPending && s.Status != SettlementSubmitted && s.Status != SettlementConfirmed && s.Status != SettlementRetryable && s.Status != SettlementManualReview {
return fmt.Errorf("invalid settlement status %q", s.Status)
}
if s.Attempts < 0 || s.AvailableAt.IsZero() {
return fmt.Errorf("invalid settlement retry state")
}
return nil
}
func (s Settlement) Retry(now time.Time, maxAttempts int, err error) Settlement {
s.LastError = err.Error()
s.Status = SettlementRetryable
if maxAttempts > 0 && s.Attempts >= maxAttempts {
s.Status = SettlementManualReview
}
delay := time.Second
for i := 1; i < s.Attempts && delay < 15*time.Minute; i++ {
delay *= 2
}
if delay > 15*time.Minute {
delay = 15 * time.Minute
}
s.AvailableAt = now.UTC().Add(delay)
return s
}