package ledger import ( "encoding/hex" "fmt" "strings" "time" ) type TransactionState string const ( TransactionStateCreated TransactionState = "CREATED" TransactionStatePendingTransaction TransactionState = "PENDING_TRANSACTION" TransactionStatePendingAdmin TransactionState = "PENDING_ADMIN" TransactionStateSuccessful TransactionState = "SUCCESSFUL" TransactionStateFailed TransactionState = "FAILED" TransactionStateSuspended TransactionState = "SUSPENDED" ) func (s TransactionState) Valid() bool { switch s { case TransactionStateCreated, TransactionStatePendingTransaction, TransactionStatePendingAdmin, TransactionStateSuccessful, TransactionStateFailed, TransactionStateSuspended: return true default: return false } } type TransactionEvent struct { ID string SourceService string IdempotencyKey string SourceTransactionID string TrackingCode string EventVersion uint32 State TransactionState ErrorCode string ErrorMessage string OccurredAt time.Time RecordedAt time.Time CorrelationID string ActorID string Blockchain BlockchainReference Metadata map[string]string PayloadHash string } func (e TransactionEvent) Validate() error { if e.ID == "" || e.SourceService == "" || e.IdempotencyKey == "" || e.SourceTransactionID == "" { return fmt.Errorf("transaction event identity fields are required") } if e.EventVersion == 0 { return fmt.Errorf("event version must be positive") } if !e.State.Valid() { return fmt.Errorf("invalid transaction state %q", e.State) } if e.OccurredAt.IsZero() { return fmt.Errorf("occurred time is required") } if len(e.PayloadHash) != 64 || e.PayloadHash != strings.ToLower(e.PayloadHash) { return fmt.Errorf("payload hash must be a lowercase SHA-256 hex string") } if _, err := hex.DecodeString(e.PayloadHash); err != nil { return fmt.Errorf("payload hash must be a lowercase SHA-256 hex string") } return nil }