feat: add durable Kuknos settlement coordination
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user