68 lines
1.8 KiB
Go
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
|
|
}
|