refactored payment orchestration

This commit is contained in:
Stephan D
2026-02-03 00:40:46 +01:00
parent 05d998e0f7
commit 5e87e2f2f9
184 changed files with 3920 additions and 2219 deletions

View File

@@ -0,0 +1,15 @@
package model
type OperationState string
const (
OperationStateCreated OperationState = "created" // record exists, not started
OperationStateProcessing OperationState = "processing" // we are working on it
OperationStatePlanned OperationState = "planned" // waiting for execution
OperationStateWaiting OperationState = "waiting" // waiting external world
OperationStateSuccess OperationState = "success" // final success
OperationStateFailed OperationState = "failed" // final failure
OperationStateCancelled OperationState = "cancelled" // final cancelled
OperationStateSkipped OperationState = "skipped" // final skipped
)

View File

@@ -6,7 +6,7 @@ import (
"github.com/tech/sendico/pkg/db/storable"
"github.com/tech/sendico/pkg/model"
pmodel "github.com/tech/sendico/pkg/model"
"github.com/tech/sendico/pkg/model/account_role"
"github.com/tech/sendico/pkg/mservice"
paymenttypes "github.com/tech/sendico/pkg/payments/types"
)
@@ -65,6 +65,7 @@ const (
PaymentFailureCodeChain PaymentFailureCode = "chain"
PaymentFailureCodeFees PaymentFailureCode = "fees"
PaymentFailureCodePolicy PaymentFailureCode = "policy"
PaymentFailureCodeSettlement PaymentFailureCode = "settlement"
)
// Rail identifies a payment rail for orchestration.
@@ -220,6 +221,7 @@ type FXIntent struct {
// PaymentIntent models the requested payment operation.
type PaymentIntent struct {
Ref string `bson:"ref" json:"ref"`
Kind PaymentKind `bson:"kind" json:"kind"`
Source PaymentEndpoint `bson:"source" json:"source"`
Destination PaymentEndpoint `bson:"destination" json:"destination"`
@@ -271,18 +273,17 @@ type ExecutionRefs struct {
// PaymentStep is an explicit action within a payment plan.
type PaymentStep struct {
StepID string `bson:"stepId,omitempty" json:"stepId,omitempty"`
Rail Rail `bson:"rail" json:"rail"`
GatewayID string `bson:"gatewayId,omitempty" json:"gatewayId,omitempty"`
InstanceID string `bson:"instanceId,omitempty" json:"instanceId,omitempty"`
Action RailOperation `bson:"action" json:"action"`
DependsOn []string `bson:"dependsOn,omitempty" json:"dependsOn,omitempty"`
CommitPolicy CommitPolicy `bson:"commitPolicy,omitempty" json:"commitPolicy,omitempty"`
CommitAfter []string `bson:"commitAfter,omitempty" json:"commitAfter,omitempty"`
Amount *paymenttypes.Money `bson:"amount,omitempty" json:"amount,omitempty"`
Ref string `bson:"ref,omitempty" json:"ref,omitempty"`
FromRole *pmodel.AccountRole `bson:"fromRole,omitempty" json:"fromRole,omitempty"`
ToRole *pmodel.AccountRole `bson:"toRole,omitempty" json:"toRole,omitempty"`
StepID string `bson:"stepId,omitempty" json:"stepId,omitempty"`
Rail Rail `bson:"rail" json:"rail"`
GatewayID string `bson:"gatewayId,omitempty" json:"gatewayId,omitempty"`
InstanceID string `bson:"instanceId,omitempty" json:"instanceId,omitempty"`
Action RailOperation `bson:"action" json:"action"`
DependsOn []string `bson:"dependsOn,omitempty" json:"dependsOn,omitempty"`
CommitPolicy CommitPolicy `bson:"commitPolicy,omitempty" json:"commitPolicy,omitempty"`
CommitAfter []string `bson:"commitAfter,omitempty" json:"commitAfter,omitempty"`
Amount *paymenttypes.Money `bson:"amount,omitempty" json:"amount,omitempty"`
FromRole *account_role.AccountRole `bson:"fromRole,omitempty" json:"fromRole,omitempty"`
ToRole *account_role.AccountRole `bson:"toRole,omitempty" json:"toRole,omitempty"`
}
// PaymentPlan captures the ordered list of steps to execute a payment.
@@ -304,9 +305,37 @@ type ExecutionStep struct {
SourceWalletRef string `bson:"sourceWalletRef,omitempty" json:"sourceWalletRef,omitempty"`
DestinationRef string `bson:"destinationRef,omitempty" json:"destinationRef,omitempty"`
TransferRef string `bson:"transferRef,omitempty" json:"transferRef,omitempty"`
OperationRef string `bson:"operationRef,omitempty" json:"operationRef,omitempty"`
Error string `bson:"error,omitempty" json:"error,omitempty"`
State OperationState `bson:"state,omitempty" json:"state,omitempty"`
Metadata map[string]string `bson:"metadata,omitempty" json:"metadata,omitempty"`
}
func (s *ExecutionStep) IsTerminal() bool {
if s.State == OperationStateSuccess ||
s.State == OperationStateFailed ||
s.State == OperationStateCancelled ||
s.State == OperationStateSkipped {
return true
}
return false
}
func (s *ExecutionStep) IsSuccess() bool {
return s.State == OperationStateSuccess
}
func (s *ExecutionStep) ReadyForNext() bool {
switch s.State {
case OperationStateSuccess,
OperationStateSkipped:
return true
default:
return false
}
}
// ExecutionPlan captures the ordered list of steps to execute a payment.
type ExecutionPlan struct {
Steps []*ExecutionStep `bson:"steps,omitempty" json:"steps,omitempty"`
@@ -420,7 +449,6 @@ func (p *Payment) Normalize() {
step.CommitPolicy = normalizeCommitPolicy(step.CommitPolicy)
step.DependsOn = normalizeStringList(step.DependsOn)
step.CommitAfter = normalizeStringList(step.CommitAfter)
step.Ref = strings.TrimSpace(step.Ref)
}
}
}

View File

@@ -4,20 +4,20 @@ import (
"strings"
"github.com/tech/sendico/pkg/db/storable"
pmodel "github.com/tech/sendico/pkg/model"
"github.com/tech/sendico/pkg/model/account_role"
"github.com/tech/sendico/pkg/mservice"
)
// OrchestrationStep defines a template step for execution planning.
type OrchestrationStep struct {
StepID string `bson:"stepId" json:"stepId"`
Rail Rail `bson:"rail" json:"rail"`
Operation string `bson:"operation" json:"operation"`
DependsOn []string `bson:"dependsOn,omitempty" json:"dependsOn,omitempty"`
CommitPolicy CommitPolicy `bson:"commitPolicy,omitempty" json:"commitPolicy,omitempty"`
CommitAfter []string `bson:"commitAfter,omitempty" json:"commitAfter,omitempty"`
FromRole *pmodel.AccountRole `bson:"fromRole,omitempty" json:"fromRole,omitempty"`
ToRole *pmodel.AccountRole `bson:"toRole,omitempty" json:"toRole,omitempty"`
StepID string `bson:"stepId" json:"stepId"`
Rail Rail `bson:"rail" json:"rail"`
Operation string `bson:"operation" json:"operation"`
DependsOn []string `bson:"dependsOn,omitempty" json:"dependsOn,omitempty"`
CommitPolicy CommitPolicy `bson:"commitPolicy,omitempty" json:"commitPolicy,omitempty"`
CommitAfter []string `bson:"commitAfter,omitempty" json:"commitAfter,omitempty"`
FromRole *account_role.AccountRole `bson:"fromRole,omitempty" json:"fromRole,omitempty"`
ToRole *account_role.AccountRole `bson:"toRole,omitempty" json:"toRole,omitempty"`
}
// PaymentPlanTemplate stores reusable orchestration templates.
@@ -60,7 +60,7 @@ func (t *PaymentPlanTemplate) Normalize() {
}
}
func normalizeAccountRole(role *pmodel.AccountRole) *pmodel.AccountRole {
func normalizeAccountRole(role *account_role.AccountRole) *account_role.AccountRole {
if role == nil {
return nil
}
@@ -68,14 +68,14 @@ func normalizeAccountRole(role *pmodel.AccountRole) *pmodel.AccountRole {
if trimmed == "" {
return nil
}
if parsed, ok := pmodel.Parse(trimmed); ok {
if parsed, ok := account_role.Parse(trimmed); ok {
if parsed == "" {
return nil
}
normalized := parsed
return &normalized
}
normalized := pmodel.AccountRole(strings.ToLower(trimmed))
normalized := account_role.AccountRole(strings.ToLower(trimmed))
return &normalized
}