diff --git a/api/gateway/chain/client/rail_gateway.go b/api/gateway/chain/client/rail_gateway.go index ce4b84d4..5100dd36 100644 --- a/api/gateway/chain/client/rail_gateway.go +++ b/api/gateway/chain/client/rail_gateway.go @@ -4,6 +4,7 @@ import ( "context" "strings" + "github.com/tech/sendico/pkg/discovery" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/model/account_role" "github.com/tech/sendico/pkg/payments/rail" @@ -29,9 +30,9 @@ type chainRailGateway struct { // NewRailGateway wraps a chain gateway client into a rail gateway adapter. func NewRailGateway(client Client, cfg RailGatewayConfig) rail.RailGateway { - railName := strings.ToUpper(strings.TrimSpace(cfg.Rail)) + railName := discovery.NormalizeRail(cfg.Rail) if railName == "" { - railName = "CRYPTO" + railName = discovery.RailCrypto } return &chainRailGateway{ client: client, diff --git a/api/gateway/chain/client/rail_gateway_test.go b/api/gateway/chain/client/rail_gateway_test.go new file mode 100644 index 00000000..81defdfa --- /dev/null +++ b/api/gateway/chain/client/rail_gateway_test.go @@ -0,0 +1,21 @@ +package client + +import ( + "testing" + + "github.com/tech/sendico/pkg/discovery" +) + +func TestNewRailGateway_NormalizesRail(t *testing.T) { + gw := NewRailGateway(nil, RailGatewayConfig{Rail: "card_payout", Network: "tron"}) + if got, want := gw.Rail(), discovery.RailCardPayout; got != want { + t.Fatalf("unexpected rail: got=%q want=%q", got, want) + } +} + +func TestNewRailGateway_DefaultsToCryptoRail(t *testing.T) { + gw := NewRailGateway(nil, RailGatewayConfig{}) + if got, want := gw.Rail(), discovery.RailCrypto; got != want { + t.Fatalf("unexpected rail: got=%q want=%q", got, want) + } +} diff --git a/api/gateway/mntx/config.yml b/api/gateway/mntx/config.yml index 31bf0022..cbbc5238 100644 --- a/api/gateway/mntx/config.yml +++ b/api/gateway/mntx/config.yml @@ -52,6 +52,7 @@ gateway: currencies: ["RUB"] limits: per_tx_min_amount: "100.00" + per_tx_max_amount: "150000.00" http: callback: diff --git a/api/gateway/tgsettle/internal/service/gateway/connector.go b/api/gateway/tgsettle/internal/service/gateway/connector.go index e5a1ac8f..91fd98dc 100644 --- a/api/gateway/tgsettle/internal/service/gateway/connector.go +++ b/api/gateway/tgsettle/internal/service/gateway/connector.go @@ -98,7 +98,7 @@ func (s *Service) SubmitOperation(ctx context.Context, req *connectorv1.SubmitOp if targetChatID != "" { metadata[metadataTargetChatID] = targetChatID } - outgoingLeg := strings.TrimSpace(reader.String("outgoing_leg")) + outgoingLeg := normalizeRail(reader.String("outgoing_leg")) if outgoingLeg != "" { metadata[metadataOutgoingLeg] = outgoingLeg } diff --git a/api/gateway/tgsettle/internal/service/gateway/service.go b/api/gateway/tgsettle/internal/service/gateway/service.go index 3d5392f1..8fffcb04 100644 --- a/api/gateway/tgsettle/internal/service/gateway/service.go +++ b/api/gateway/tgsettle/internal/service/gateway/service.go @@ -428,9 +428,9 @@ func (s *Service) buildConfirmationRequest(intent *model.PaymentGatewayIntent) ( if targetChatID == "" { return nil, merrors.InvalidArgument("target_chat_id is required", "target_chat_id") } - rail := strings.TrimSpace(intent.OutgoingLeg) + rail := normalizeRail(intent.OutgoingLeg) if rail == "" { - rail = s.rail + rail = normalizeRail(s.rail) } timeout := s.cfg.TimeoutSeconds if timeout <= 0 { @@ -549,7 +549,7 @@ func normalizeIntent(intent *model.PaymentGatewayIntent) *model.PaymentGatewayIn cp := *intent cp.PaymentIntentID = strings.TrimSpace(cp.PaymentIntentID) cp.IdempotencyKey = strings.TrimSpace(cp.IdempotencyKey) - cp.OutgoingLeg = strings.TrimSpace(cp.OutgoingLeg) + cp.OutgoingLeg = normalizeRail(cp.OutgoingLeg) cp.QuoteRef = strings.TrimSpace(cp.QuoteRef) if cp.RequestedMoney != nil { cp.RequestedMoney.Amount = strings.TrimSpace(cp.RequestedMoney.Amount) @@ -568,7 +568,7 @@ func paymentRecordFromIntent(intent *model.PaymentGatewayIntent, confirmReq *mod record.IdempotencyKey = strings.TrimSpace(intent.IdempotencyKey) record.PaymentIntentID = strings.TrimSpace(intent.PaymentIntentID) record.QuoteRef = strings.TrimSpace(intent.QuoteRef) - record.OutgoingLeg = strings.TrimSpace(intent.OutgoingLeg) + record.OutgoingLeg = normalizeRail(intent.OutgoingLeg) record.RequestedMoney = intent.RequestedMoney record.IntentRef = intent.IntentRef record.OperationRef = intent.OperationRef @@ -578,7 +578,7 @@ func paymentRecordFromIntent(intent *model.PaymentGatewayIntent, confirmReq *mod record.IdempotencyKey = strings.TrimSpace(confirmReq.RequestID) record.PaymentIntentID = strings.TrimSpace(confirmReq.PaymentIntentID) record.QuoteRef = strings.TrimSpace(confirmReq.QuoteRef) - record.OutgoingLeg = strings.TrimSpace(confirmReq.Rail) + record.OutgoingLeg = normalizeRail(confirmReq.Rail) record.RequestedMoney = confirmReq.RequestedMoney record.IntentRef = strings.TrimSpace(confirmReq.IntentRef) record.OperationRef = strings.TrimSpace(confirmReq.OperationRef) @@ -640,9 +640,9 @@ func intentFromSubmitTransfer(req *chainv1.SubmitTransferRequest, defaultRail, d } quoteRef := strings.TrimSpace(metadata[metadataQuoteRef]) targetChatID := strings.TrimSpace(metadata[metadataTargetChatID]) - outgoingLeg := strings.TrimSpace(metadata[metadataOutgoingLeg]) + outgoingLeg := normalizeRail(metadata[metadataOutgoingLeg]) if outgoingLeg == "" { - outgoingLeg = strings.TrimSpace(defaultRail) + outgoingLeg = normalizeRail(defaultRail) } if targetChatID == "" { targetChatID = strings.TrimSpace(defaultChatID) @@ -659,6 +659,10 @@ func intentFromSubmitTransfer(req *chainv1.SubmitTransferRequest, defaultRail, d }, nil } +func normalizeRail(value string) string { + return discovery.NormalizeRail(value) +} + func transferFromRequest(req *chainv1.SubmitTransferRequest) *chainv1.Transfer { if req == nil { return nil diff --git a/api/gateway/tgsettle/internal/service/gateway/service_test.go b/api/gateway/tgsettle/internal/service/gateway/service_test.go index 9940068d..9dda27ea 100644 --- a/api/gateway/tgsettle/internal/service/gateway/service_test.go +++ b/api/gateway/tgsettle/internal/service/gateway/service_test.go @@ -8,12 +8,15 @@ import ( "github.com/tech/sendico/gateway/tgsettle/storage" storagemodel "github.com/tech/sendico/gateway/tgsettle/storage/model" + "github.com/tech/sendico/pkg/discovery" envelope "github.com/tech/sendico/pkg/messaging/envelope" tnotifications "github.com/tech/sendico/pkg/messaging/notifications/telegram" mloggerfactory "github.com/tech/sendico/pkg/mlogger/factory" "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/mservice" paymenttypes "github.com/tech/sendico/pkg/payments/types" + moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" + chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1" ) // @@ -370,3 +373,22 @@ func TestTimeout(t *testing.T) { t.Fatalf("timeout must not publish reaction") } } + +func TestIntentFromSubmitTransfer_NormalizesOutgoingLeg(t *testing.T) { + intent, err := intentFromSubmitTransfer(&chainv1.SubmitTransferRequest{ + IdempotencyKey: "idem-5", + IntentRef: "pi-5", + OperationRef: "op-5", + PaymentRef: "pay-5", + Amount: &moneyv1.Money{Amount: "10", Currency: "USD"}, + Metadata: map[string]string{ + metadataOutgoingLeg: "card_payout", + }, + }, "provider_settlement", "") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if got, want := intent.OutgoingLeg, discovery.RailCardPayout; got != want { + t.Fatalf("unexpected outgoing leg: got=%q want=%q", got, want) + } +} diff --git a/api/gateway/tron/client/rail_gateway.go b/api/gateway/tron/client/rail_gateway.go index c66eff9f..2eac869e 100644 --- a/api/gateway/tron/client/rail_gateway.go +++ b/api/gateway/tron/client/rail_gateway.go @@ -4,6 +4,7 @@ import ( "context" "strings" + "github.com/tech/sendico/pkg/discovery" "github.com/tech/sendico/pkg/merrors" "github.com/tech/sendico/pkg/model/account_role" "github.com/tech/sendico/pkg/payments/rail" @@ -29,9 +30,9 @@ type chainRailGateway struct { // NewRailGateway wraps a chain gateway client into a rail gateway adapter. func NewRailGateway(client Client, cfg RailGatewayConfig) rail.RailGateway { - railName := strings.ToUpper(strings.TrimSpace(cfg.Rail)) + railName := discovery.NormalizeRail(cfg.Rail) if railName == "" { - railName = "CRYPTO" + railName = discovery.RailCrypto } return &chainRailGateway{ client: client, diff --git a/api/gateway/tron/client/rail_gateway_test.go b/api/gateway/tron/client/rail_gateway_test.go new file mode 100644 index 00000000..81defdfa --- /dev/null +++ b/api/gateway/tron/client/rail_gateway_test.go @@ -0,0 +1,21 @@ +package client + +import ( + "testing" + + "github.com/tech/sendico/pkg/discovery" +) + +func TestNewRailGateway_NormalizesRail(t *testing.T) { + gw := NewRailGateway(nil, RailGatewayConfig{Rail: "card_payout", Network: "tron"}) + if got, want := gw.Rail(), discovery.RailCardPayout; got != want { + t.Fatalf("unexpected rail: got=%q want=%q", got, want) + } +} + +func TestNewRailGateway_DefaultsToCryptoRail(t *testing.T) { + gw := NewRailGateway(nil, RailGatewayConfig{}) + if got, want := gw.Rail(), discovery.RailCrypto; got != want { + t.Fatalf("unexpected rail: got=%q want=%q", got, want) + } +} diff --git a/api/ledger/client/client.go b/api/ledger/client/client.go index b1f26419..9bfe1713 100644 --- a/api/ledger/client/client.go +++ b/api/ledger/client/client.go @@ -26,7 +26,7 @@ import ( const ( ledgerConnectorID = "ledger" - ledgerRailName = "LEDGER" + ledgerRailName = discovery.RailLedger opParamOperation = "operation" opParamToMoney = "to_money" @@ -175,6 +175,7 @@ func (c *ledgerClient) CreateTransaction(ctx context.Context, tx rail.LedgerTx) if money.GetCurrency() == "" || money.GetAmount() == "" { return "", merrors.InvalidArgument("ledger: amount is required") } + tx = normalizeLedgerTxRails(tx) description := strings.TrimSpace(tx.Description) metadata := ledgerTxMetadata(tx.Metadata, tx) @@ -849,7 +850,17 @@ func (c *ledgerClient) callContext(ctx context.Context) (context.Context, contex } func isLedgerRail(value string) bool { - return strings.EqualFold(strings.TrimSpace(value), ledgerRailName) + return normalizeRail(value) == ledgerRailName +} + +func normalizeLedgerTxRails(tx rail.LedgerTx) rail.LedgerTx { + tx.FromRail = normalizeRail(tx.FromRail) + tx.ToRail = normalizeRail(tx.ToRail) + return tx +} + +func normalizeRail(value string) string { + return discovery.NormalizeRail(value) } func cloneMoney(input *moneyv1.Money) *moneyv1.Money { @@ -881,10 +892,10 @@ func ledgerTxMetadata(base map[string]string, tx rail.LedgerTx) map[string]strin if val := strings.TrimSpace(tx.PaymentPlanID); val != "" { meta[txMetaPaymentPlanID] = val } - if val := strings.TrimSpace(tx.FromRail); val != "" { + if val := normalizeRail(tx.FromRail); val != "" { meta[txMetaFromRail] = val } - if val := strings.TrimSpace(tx.ToRail); val != "" { + if val := normalizeRail(tx.ToRail); val != "" { meta[txMetaToRail] = val } if val := strings.TrimSpace(tx.ExternalReferenceID); val != "" { diff --git a/api/ledger/client/client_test.go b/api/ledger/client/client_test.go index 12f1d127..faba3c7a 100644 --- a/api/ledger/client/client_test.go +++ b/api/ledger/client/client_test.go @@ -7,6 +7,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tech/sendico/pkg/discovery" + "github.com/tech/sendico/pkg/payments/rail" accountrolev1 "github.com/tech/sendico/pkg/proto/common/account_role/v1" moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1" @@ -156,3 +157,36 @@ func TestPostExternalDebitWithCharges_SubmitsExternalOperation(t *testing.T) { assert.Equal(t, "op-ext-debit", resp.GetJournalEntryRef()) assert.Equal(t, ledgerv1.EntryType_ENTRY_DEBIT, resp.GetEntryType()) } + +func TestCreateTransaction_NormalizesRailsForDirectionAndMetadata(t *testing.T) { + ctx := context.Background() + + var captured *connectorv1.Operation + stub := &stubConnector{ + submitFn: func(ctx context.Context, req *connectorv1.SubmitOperationRequest) (*connectorv1.SubmitOperationResponse, error) { + captured = req.GetOperation() + return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{OperationId: "op-tx"}}, nil + }, + } + + client := NewWithClient(Config{}, stub) + ref, err := client.CreateTransaction(ctx, rail.LedgerTx{ + IdempotencyKey: "idem-tx", + OrganizationRef: "org-1", + LedgerAccountRef: "acct-1", + Currency: "USD", + Amount: "10", + FromRail: "rail_ledger", + ToRail: "crypto", + }) + + require.NoError(t, err) + require.Equal(t, "op-tx", ref) + require.NotNil(t, captured) + assert.Equal(t, connectorv1.OperationType_DEBIT, captured.GetType()) + + metadata, ok := captured.GetParams().AsMap()["metadata"].(map[string]interface{}) + require.True(t, ok) + assert.Equal(t, discovery.RailLedger, metadata["from_rail"]) + assert.Equal(t, discovery.RailCrypto, metadata["to_rail"]) +} diff --git a/api/ledger/internal/service/ledger/service.go b/api/ledger/internal/service/ledger/service.go index c30df397..29b6ca3c 100644 --- a/api/ledger/internal/service/ledger/service.go +++ b/api/ledger/internal/service/ledger/service.go @@ -433,6 +433,7 @@ func (s *Service) startDiscoveryAnnouncer() { } announce := discovery.Announcement{ Service: "LEDGER", + Rail: discovery.RailLedger, Operations: discovery.LedgerServiceOperations(), InvokeURI: s.invokeURI, Version: appversion.Create().Short(), diff --git a/api/payments/orchestrator/internal/service/execution/payment_plan_helpers.go b/api/payments/orchestrator/internal/service/execution/payment_plan_helpers.go index 1306b992..4a0b1471 100644 --- a/api/payments/orchestrator/internal/service/execution/payment_plan_helpers.go +++ b/api/payments/orchestrator/internal/service/execution/payment_plan_helpers.go @@ -2,6 +2,7 @@ package execution import ( "fmt" + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/google/uuid" @@ -83,7 +84,7 @@ func blockStepConfirmed(plan *model.PaymentPlan, execPlan *model.ExecutionPlan) } execSteps := executionStepsByCode(execPlan) for idx, step := range plan.Steps { - if step == nil || step.Action != model.RailOperationBlock { + if step == nil || step.Action != discovery.RailOperationBlock { continue } execStep := execSteps[planStepID(step, idx)] @@ -106,7 +107,7 @@ func roleHintsForStep(plan *model.PaymentPlan, idx int) (*account_role.AccountRo if step == nil { continue } - if step.Rail != model.RailLedger || step.Action != model.RailOperationMove { + if step.Rail != discovery.RailLedger || step.Action != discovery.RailOperationMove { continue } if step.ToRole != nil && strings.TrimSpace(string(*step.ToRole)) != "" { @@ -135,7 +136,7 @@ func linkRailObservation(payment *model.Payment, rail model.Rail, referenceID, d if planStep == nil { continue } - if planStep.Rail != rail || planStep.Action != model.RailOperationObserveConfirm { + if planStep.Rail != rail || planStep.Action != discovery.RailOperationObserveConfirm { continue } if dep != "" { @@ -206,12 +207,12 @@ func failureCodeForStep(step *model.PaymentStep) model.PaymentFailureCode { return model.PaymentFailureCodePolicy } switch step.Rail { - case model.RailLedger: - if step.Action == model.RailOperationFXConvert { + case discovery.RailLedger: + if step.Action == discovery.RailOperationFXConvert { return model.PaymentFailureCodeFX } return model.PaymentFailureCodeLedger - case model.RailCrypto: + case discovery.RailCrypto: return model.PaymentFailureCodeChain default: return model.PaymentFailureCodePolicy diff --git a/api/payments/orchestrator/internal/service/execution/payment_plan_order.go b/api/payments/orchestrator/internal/service/execution/payment_plan_order.go index 68d869fb..9a0c6d59 100644 --- a/api/payments/orchestrator/internal/service/execution/payment_plan_order.go +++ b/api/payments/orchestrator/internal/service/execution/payment_plan_order.go @@ -1,6 +1,7 @@ package execution import ( + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/tech/sendico/payments/storage/model" @@ -199,8 +200,8 @@ func cardPayoutDependenciesConfirmed( continue } - if step.Rail != model.RailCardPayout || - step.Action != model.RailOperationSend { + if step.Rail != discovery.RailCardPayout || + step.Action != discovery.RailOperationSend { continue } diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/psvc/execute_batch.go b/api/payments/orchestrator/internal/service/orchestrationv2/psvc/execute_batch.go index e480dbef..3beed487 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/psvc/execute_batch.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/psvc/execute_batch.go @@ -5,6 +5,7 @@ import ( "crypto/sha256" "encoding/hex" "errors" + "github.com/tech/sendico/pkg/discovery" "sort" "strconv" "strings" @@ -375,11 +376,11 @@ func routeContainsCardPayout(snapshot *model.PaymentQuoteSnapshot) bool { if hop == nil { continue } - if model.ParseRail(hop.Rail) == model.RailCardPayout { + if model.ParseRail(hop.Rail) == discovery.RailCardPayout { return true } } - if model.ParseRail(snapshot.Route.Rail) == model.RailCardPayout { + if model.ParseRail(snapshot.Route.Rail) == discovery.RailCardPayout { return true } return false diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/sexec/routes.go b/api/payments/orchestrator/internal/service/orchestrationv2/sexec/routes.go index 2d6cad0a..938fdfe5 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/sexec/routes.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/sexec/routes.go @@ -3,6 +3,7 @@ package sexec import ( "github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/xplan" "github.com/tech/sendico/payments/storage/model" + "github.com/tech/sendico/pkg/discovery" ) type route int @@ -26,28 +27,28 @@ func classifyRoute(step xplan.Step) route { rail := normalizeRail(step.Rail) switch action { - case model.RailOperationObserveConfirm: + case discovery.RailOperationObserveConfirm: return routeObserveConfirm - case model.RailOperationSend: + case discovery.RailOperationSend: switch rail { - case model.RailCrypto: + case discovery.RailCrypto: return routeCrypto - case model.RailCardPayout: + case discovery.RailCardPayout: return routeCardPayout default: return routeUnknown } - case model.RailOperationFXConvert: + case discovery.RailOperationFXConvert: switch rail { - case model.RailProviderSettlement: + case discovery.RailProviderSettlement: return routeProviderSettlement - case model.RailLedger: + case discovery.RailLedger: return routeLedger default: return routeUnknown } - case model.RailOperationFee: - if rail == model.RailCrypto { + case discovery.RailOperationFee: + if rail == discovery.RailCrypto { return routeCrypto } return routeUnknown @@ -65,13 +66,13 @@ func isGuardStep(step xplan.Step) bool { func isLedgerAction(action model.RailOperation) bool { switch action { - case model.RailOperationDebit, - model.RailOperationCredit, - model.RailOperationExternalDebit, - model.RailOperationExternalCredit, - model.RailOperationMove, - model.RailOperationBlock, - model.RailOperationRelease: + case discovery.RailOperationDebit, + discovery.RailOperationCredit, + discovery.RailOperationExternalDebit, + discovery.RailOperationExternalCredit, + discovery.RailOperationMove, + discovery.RailOperationBlock, + discovery.RailOperationRelease: return true default: return false diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/sexec/service_test.go b/api/payments/orchestrator/internal/service/orchestrationv2/sexec/service_test.go index caac0a67..50134cc3 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/sexec/service_test.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/sexec/service_test.go @@ -3,12 +3,12 @@ package sexec import ( "context" "errors" + "github.com/tech/sendico/pkg/discovery" "strings" "testing" "github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg" "github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/xplan" - "github.com/tech/sendico/payments/storage/model" "github.com/tech/sendico/pkg/merrors" ) @@ -18,7 +18,7 @@ func TestExecute_DispatchLedger(t *testing.T) { out, err := registry.Execute(context.Background(), ExecuteInput{ Payment: &agg.Payment{PaymentRef: "p1"}, - Step: xplan.Step{StepRef: "s1", StepCode: "ledger.debit", Action: model.RailOperationDebit, Rail: model.RailLedger}, + Step: xplan.Step{StepRef: "s1", StepCode: "ledger.debit", Action: discovery.RailOperationDebit, Rail: discovery.RailLedger}, StepExecution: agg.StepExecution{}, }) if err != nil { @@ -58,7 +58,7 @@ func TestExecute_DispatchRailsAndObserve(t *testing.T) { { name: "send crypto", step: xplan.Step{ - StepRef: "s1", StepCode: "crypto.send", Action: model.RailOperationSend, Rail: model.RailCrypto, + StepRef: "s1", StepCode: "crypto.send", Action: discovery.RailOperationSend, Rail: discovery.RailCrypto, }, wantCalls: func(t *testing.T) { t.Helper() @@ -70,7 +70,7 @@ func TestExecute_DispatchRailsAndObserve(t *testing.T) { { name: "fx convert provider settlement", step: xplan.Step{ - StepRef: "s2", StepCode: "provider.fx_convert", Action: model.RailOperationFXConvert, Rail: model.RailProviderSettlement, + StepRef: "s2", StepCode: "provider.fx_convert", Action: discovery.RailOperationFXConvert, Rail: discovery.RailProviderSettlement, }, wantCalls: func(t *testing.T) { t.Helper() @@ -82,7 +82,7 @@ func TestExecute_DispatchRailsAndObserve(t *testing.T) { { name: "send card payout", step: xplan.Step{ - StepRef: "s3", StepCode: "card.send", Action: model.RailOperationSend, Rail: model.RailCardPayout, + StepRef: "s3", StepCode: "card.send", Action: discovery.RailOperationSend, Rail: discovery.RailCardPayout, }, wantCalls: func(t *testing.T) { t.Helper() @@ -94,7 +94,7 @@ func TestExecute_DispatchRailsAndObserve(t *testing.T) { { name: "observe confirm", step: xplan.Step{ - StepRef: "s4", StepCode: "observe", Action: model.RailOperationObserveConfirm, Rail: model.RailCardPayout, + StepRef: "s4", StepCode: "observe", Action: discovery.RailOperationObserveConfirm, Rail: discovery.RailCardPayout, }, wantCalls: func(t *testing.T) { t.Helper() @@ -106,7 +106,7 @@ func TestExecute_DispatchRailsAndObserve(t *testing.T) { { name: "crypto fee", step: xplan.Step{ - StepRef: "s5", StepCode: "crypto.fee", Action: model.RailOperationFee, Rail: model.RailCrypto, + StepRef: "s5", StepCode: "crypto.fee", Action: discovery.RailOperationFee, Rail: discovery.RailCrypto, }, wantCalls: func(t *testing.T) { t.Helper() @@ -142,8 +142,8 @@ func TestExecute_DispatchGuard(t *testing.T) { StepRef: xplan.QuoteReadinessGuardStepRef, StepCode: string(xplan.GuardOperationQuoteReadinessGuard), Kind: xplan.StepKindLiquidityCheck, - Action: model.RailOperationUnspecified, - Rail: model.RailUnspecified, + Action: discovery.RailOperationUnspecified, + Rail: discovery.RailUnspecified, DependsOn: nil, }, StepExecution: agg.StepExecution{ @@ -171,7 +171,7 @@ func TestExecute_UnsupportedStep(t *testing.T) { _, err := registry.Execute(context.Background(), ExecuteInput{ Payment: &agg.Payment{PaymentRef: "p1"}, - Step: xplan.Step{StepRef: "s1", StepCode: "bad.send", Action: model.RailOperationSend, Rail: model.RailLedger}, + Step: xplan.Step{StepRef: "s1", StepCode: "bad.send", Action: discovery.RailOperationSend, Rail: discovery.RailLedger}, StepExecution: agg.StepExecution{StepRef: "s1", StepCode: "bad.send", Attempt: 1}, }) if !errors.Is(err, ErrUnsupportedStep) { @@ -199,7 +199,7 @@ func TestExecute_UnsupportedProviderSettlementSend(t *testing.T) { _, err := registry.Execute(context.Background(), ExecuteInput{ Payment: &agg.Payment{PaymentRef: "p1"}, - Step: xplan.Step{StepRef: "s1", StepCode: "provider.send", Action: model.RailOperationSend, Rail: model.RailProviderSettlement}, + Step: xplan.Step{StepRef: "s1", StepCode: "provider.send", Action: discovery.RailOperationSend, Rail: discovery.RailProviderSettlement}, StepExecution: agg.StepExecution{StepRef: "s1", StepCode: "provider.send", Attempt: 1}, }) if !errors.Is(err, ErrUnsupportedStep) { @@ -212,7 +212,7 @@ func TestExecute_MissingExecutor(t *testing.T) { _, err := registry.Execute(context.Background(), ExecuteInput{ Payment: &agg.Payment{PaymentRef: "p1"}, - Step: xplan.Step{StepRef: "s1", StepCode: "crypto.send", Action: model.RailOperationSend, Rail: model.RailCrypto}, + Step: xplan.Step{StepRef: "s1", StepCode: "crypto.send", Action: discovery.RailOperationSend, Rail: discovery.RailCrypto}, StepExecution: agg.StepExecution{StepRef: "s1", StepCode: "crypto.send", Attempt: 1}, }) if !errors.Is(err, ErrMissingExecutor) { @@ -231,21 +231,21 @@ func TestExecute_ValidationErrors(t *testing.T) { { name: "missing payment", in: ExecuteInput{ - Step: xplan.Step{StepRef: "s1", Action: model.RailOperationDebit}, + Step: xplan.Step{StepRef: "s1", Action: discovery.RailOperationDebit}, }, }, { name: "missing step ref", in: ExecuteInput{ Payment: &agg.Payment{}, - Step: xplan.Step{StepRef: " ", Action: model.RailOperationDebit}, + Step: xplan.Step{StepRef: " ", Action: discovery.RailOperationDebit}, }, }, { name: "mismatched step execution ref", in: ExecuteInput{ Payment: &agg.Payment{}, - Step: xplan.Step{StepRef: "s1", StepCode: "s1", Action: model.RailOperationDebit}, + Step: xplan.Step{StepRef: "s1", StepCode: "s1", Action: discovery.RailOperationDebit}, StepExecution: agg.StepExecution{StepRef: "s2"}, }, }, diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/xplan/compile_flow_test.go b/api/payments/orchestrator/internal/service/orchestrationv2/xplan/compile_flow_test.go index 8dc2b5a1..ce07b53c 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/xplan/compile_flow_test.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/xplan/compile_flow_test.go @@ -1,6 +1,7 @@ package xplan import ( + "github.com/tech/sendico/pkg/discovery" "testing" "github.com/tech/sendico/payments/storage/model" @@ -47,15 +48,15 @@ func TestCompile_ExternalToExternal_BridgeExpansion(t *testing.T) { t.Fatalf("expected 9 steps, got %d", len(graph.Steps)) } - assertStep(t, graph.Steps[0], "hop.10.crypto.send", model.RailOperationSend, model.RailCrypto, model.ReportVisibilityBackoffice) - assertStep(t, graph.Steps[1], "hop.10.crypto.observe", model.RailOperationObserveConfirm, model.RailCrypto, model.ReportVisibilityBackoffice) - assertStep(t, graph.Steps[2], "edge.10_20.ledger.credit", model.RailOperationExternalCredit, model.RailLedger, model.ReportVisibilityHidden) - assertStep(t, graph.Steps[3], "edge.10_20.ledger.block", model.RailOperationBlock, model.RailLedger, model.ReportVisibilityHidden) - assertStep(t, graph.Steps[4], "hop.20.card_payout.send", model.RailOperationSend, model.RailCardPayout, model.ReportVisibilityUser) - assertStep(t, graph.Steps[5], "hop.20.card_payout.observe", model.RailOperationObserveConfirm, model.RailCardPayout, model.ReportVisibilityUser) - assertStep(t, graph.Steps[6], "edge.10_20.ledger.debit", model.RailOperationExternalDebit, model.RailLedger, model.ReportVisibilityHidden) - assertStep(t, graph.Steps[7], "edge.10_20.ledger.release", model.RailOperationRelease, model.RailLedger, model.ReportVisibilityHidden) - assertStep(t, graph.Steps[8], "edge.10_20.ledger.release", model.RailOperationRelease, model.RailLedger, model.ReportVisibilityHidden) + assertStep(t, graph.Steps[0], "hop.10.crypto.send", discovery.RailOperationSend, discovery.RailCrypto, model.ReportVisibilityBackoffice) + assertStep(t, graph.Steps[1], "hop.10.crypto.observe", discovery.RailOperationObserveConfirm, discovery.RailCrypto, model.ReportVisibilityBackoffice) + assertStep(t, graph.Steps[2], "edge.10_20.ledger.credit", discovery.RailOperationExternalCredit, discovery.RailLedger, model.ReportVisibilityHidden) + assertStep(t, graph.Steps[3], "edge.10_20.ledger.block", discovery.RailOperationBlock, discovery.RailLedger, model.ReportVisibilityHidden) + assertStep(t, graph.Steps[4], "hop.20.card_payout.send", discovery.RailOperationSend, discovery.RailCardPayout, model.ReportVisibilityUser) + assertStep(t, graph.Steps[5], "hop.20.card_payout.observe", discovery.RailOperationObserveConfirm, discovery.RailCardPayout, model.ReportVisibilityUser) + assertStep(t, graph.Steps[6], "edge.10_20.ledger.debit", discovery.RailOperationExternalDebit, discovery.RailLedger, model.ReportVisibilityHidden) + assertStep(t, graph.Steps[7], "edge.10_20.ledger.release", discovery.RailOperationRelease, discovery.RailLedger, model.ReportVisibilityHidden) + assertStep(t, graph.Steps[8], "edge.10_20.ledger.release", discovery.RailOperationRelease, discovery.RailLedger, model.ReportVisibilityHidden) if got, want := graph.Steps[1].DependsOn, []string{graph.Steps[0].StepRef}; !equalStringSlice(got, want) { t.Fatalf("step[1] deps mismatch: got=%v want=%v", got, want) @@ -122,12 +123,12 @@ func TestCompile_InternalToExternal_UsesHoldAndSettlementBranches(t *testing.T) if len(graph.Steps) != 6 { t.Fatalf("expected 6 steps, got %d", len(graph.Steps)) } - assertStep(t, graph.Steps[0], "edge.10_20.ledger.block", model.RailOperationBlock, model.RailLedger, model.ReportVisibilityHidden) - assertStep(t, graph.Steps[1], "hop.20.card_payout.send", model.RailOperationSend, model.RailCardPayout, model.ReportVisibilityUser) - assertStep(t, graph.Steps[2], "hop.20.card_payout.observe", model.RailOperationObserveConfirm, model.RailCardPayout, model.ReportVisibilityUser) - assertStep(t, graph.Steps[3], "edge.10_20.ledger.debit", model.RailOperationExternalDebit, model.RailLedger, model.ReportVisibilityHidden) - assertStep(t, graph.Steps[4], "edge.10_20.ledger.release", model.RailOperationRelease, model.RailLedger, model.ReportVisibilityHidden) - assertStep(t, graph.Steps[5], "edge.10_20.ledger.release", model.RailOperationRelease, model.RailLedger, model.ReportVisibilityHidden) + assertStep(t, graph.Steps[0], "edge.10_20.ledger.block", discovery.RailOperationBlock, discovery.RailLedger, model.ReportVisibilityHidden) + assertStep(t, graph.Steps[1], "hop.20.card_payout.send", discovery.RailOperationSend, discovery.RailCardPayout, model.ReportVisibilityUser) + assertStep(t, graph.Steps[2], "hop.20.card_payout.observe", discovery.RailOperationObserveConfirm, discovery.RailCardPayout, model.ReportVisibilityUser) + assertStep(t, graph.Steps[3], "edge.10_20.ledger.debit", discovery.RailOperationExternalDebit, discovery.RailLedger, model.ReportVisibilityHidden) + assertStep(t, graph.Steps[4], "edge.10_20.ledger.release", discovery.RailOperationRelease, discovery.RailLedger, model.ReportVisibilityHidden) + assertStep(t, graph.Steps[5], "edge.10_20.ledger.release", discovery.RailOperationRelease, discovery.RailLedger, model.ReportVisibilityHidden) if got, want := graph.Steps[4].CommitAfter, []string{graph.Steps[1].StepRef}; !equalStringSlice(got, want) { t.Fatalf("send-failure release commit_after mismatch: got=%v want=%v", got, want) } @@ -156,9 +157,9 @@ func TestCompile_ExternalToInternal_UsesCreditAfterObserve(t *testing.T) { if len(graph.Steps) != 3 { t.Fatalf("expected 3 steps, got %d", len(graph.Steps)) } - assertStep(t, graph.Steps[0], "hop.10.crypto.send", model.RailOperationSend, model.RailCrypto, model.ReportVisibilityBackoffice) - assertStep(t, graph.Steps[1], "hop.10.crypto.observe", model.RailOperationObserveConfirm, model.RailCrypto, model.ReportVisibilityBackoffice) - assertStep(t, graph.Steps[2], "edge.10_20.ledger.credit", model.RailOperationExternalCredit, model.RailLedger, model.ReportVisibilityHidden) + assertStep(t, graph.Steps[0], "hop.10.crypto.send", discovery.RailOperationSend, discovery.RailCrypto, model.ReportVisibilityBackoffice) + assertStep(t, graph.Steps[1], "hop.10.crypto.observe", discovery.RailOperationObserveConfirm, discovery.RailCrypto, model.ReportVisibilityBackoffice) + assertStep(t, graph.Steps[2], "edge.10_20.ledger.credit", discovery.RailOperationExternalCredit, discovery.RailLedger, model.ReportVisibilityHidden) } func TestCompile_ExternalViaSettlement_UsesFXConvertOnSettlementHop(t *testing.T) { @@ -201,13 +202,13 @@ func TestCompile_ExternalViaSettlement_UsesFXConvertOnSettlementHop(t *testing.T convertCount := 0 sendCount := 0 for _, step := range graph.Steps { - if step.Rail != model.RailProviderSettlement { + if step.Rail != discovery.RailProviderSettlement { continue } - if step.Action == model.RailOperationFXConvert { + if step.Action == discovery.RailOperationFXConvert { convertCount++ } - if step.Action == model.RailOperationSend { + if step.Action == discovery.RailOperationSend { sendCount++ } } @@ -239,7 +240,7 @@ func TestCompile_InternalToInternal_UsesMove(t *testing.T) { if len(graph.Steps) != 1 { t.Fatalf("expected 1 step, got %d", len(graph.Steps)) } - assertStep(t, graph.Steps[0], "edge.10_20.ledger.move", model.RailOperationMove, model.RailLedger, model.ReportVisibilityHidden) + assertStep(t, graph.Steps[0], "edge.10_20.ledger.move", discovery.RailOperationMove, discovery.RailLedger, model.ReportVisibilityHidden) } func TestCompile_GuardsArePrepended(t *testing.T) { @@ -300,8 +301,8 @@ func TestCompile_SingleExternalFallback(t *testing.T) { if len(graph.Steps) != 2 { t.Fatalf("expected 2 steps, got %d", len(graph.Steps)) } - assertStep(t, graph.Steps[0], "hop.0.card_payout.send", model.RailOperationSend, model.RailCardPayout, model.ReportVisibilityUser) - assertStep(t, graph.Steps[1], "hop.0.card_payout.observe", model.RailOperationObserveConfirm, model.RailCardPayout, model.ReportVisibilityUser) + assertStep(t, graph.Steps[0], "hop.0.card_payout.send", discovery.RailOperationSend, discovery.RailCardPayout, model.ReportVisibilityUser) + assertStep(t, graph.Steps[1], "hop.0.card_payout.observe", discovery.RailOperationObserveConfirm, discovery.RailCardPayout, model.ReportVisibilityUser) if got, want := graph.Steps[1].DependsOn, []string{graph.Steps[0].StepRef}; !equalStringSlice(got, want) { t.Fatalf("observe dependency mismatch: got=%v want=%v", got, want) } diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/xplan/compile_policy_test.go b/api/payments/orchestrator/internal/service/orchestrationv2/xplan/compile_policy_test.go index 8b3a54d2..4d5d0c2b 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/xplan/compile_policy_test.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/xplan/compile_policy_test.go @@ -2,6 +2,7 @@ package xplan import ( "errors" + "github.com/tech/sendico/pkg/discovery" "testing" "github.com/tech/sendico/payments/storage/model" @@ -12,8 +13,8 @@ import ( func TestCompile_PolicyOverrideByRailPair(t *testing.T) { compiler := New() - cardRail := model.RailCardPayout - ledgerRail := model.RailLedger + var cardRail model.Rail = discovery.RailCardPayout + var ledgerRail model.Rail = discovery.RailLedger graph, err := compiler.Compile(Input{ IntentSnapshot: testIntent(model.PaymentKindPayout), @@ -29,18 +30,18 @@ func TestCompile_PolicyOverrideByRailPair(t *testing.T) { { ID: "crypto-to-card-override", Match: EdgeMatch{ - Source: EndpointMatch{Rail: railPtr(model.RailCrypto)}, - Target: EndpointMatch{Rail: railPtr(model.RailCardPayout)}, + Source: EndpointMatch{Rail: railPtr(discovery.RailCrypto)}, + Target: EndpointMatch{Rail: railPtr(discovery.RailCardPayout)}, }, Steps: []PolicyStep{ - {Code: "custom.review", Action: model.RailOperationMove, Rail: &ledgerRail}, - {Code: "custom.submit", Action: model.RailOperationSend, Rail: &cardRail, Visibility: model.ReportVisibilityUser}, + {Code: "custom.review", Action: discovery.RailOperationMove, Rail: &ledgerRail}, + {Code: "custom.submit", Action: discovery.RailOperationSend, Rail: &cardRail, Visibility: model.ReportVisibilityUser}, }, Success: []PolicyStep{ - {Code: "custom.finalize", Action: model.RailOperationDebit, Rail: &ledgerRail}, + {Code: "custom.finalize", Action: discovery.RailOperationDebit, Rail: &ledgerRail}, }, Failure: []PolicyStep{ - {Code: "custom.release", Action: model.RailOperationRelease, Rail: &ledgerRail}, + {Code: "custom.release", Action: discovery.RailOperationRelease, Rail: &ledgerRail}, }, }, }, @@ -52,10 +53,10 @@ func TestCompile_PolicyOverrideByRailPair(t *testing.T) { if len(graph.Steps) != 4 { t.Fatalf("expected 4 steps, got %d", len(graph.Steps)) } - assertStep(t, graph.Steps[0], "custom.review", model.RailOperationMove, model.RailLedger, model.ReportVisibilityHidden) - assertStep(t, graph.Steps[1], "custom.submit", model.RailOperationSend, model.RailCardPayout, model.ReportVisibilityUser) - assertStep(t, graph.Steps[2], "custom.finalize", model.RailOperationDebit, model.RailLedger, model.ReportVisibilityHidden) - assertStep(t, graph.Steps[3], "custom.release", model.RailOperationRelease, model.RailLedger, model.ReportVisibilityHidden) + assertStep(t, graph.Steps[0], "custom.review", discovery.RailOperationMove, discovery.RailLedger, model.ReportVisibilityHidden) + assertStep(t, graph.Steps[1], "custom.submit", discovery.RailOperationSend, discovery.RailCardPayout, model.ReportVisibilityUser) + assertStep(t, graph.Steps[2], "custom.finalize", discovery.RailOperationDebit, discovery.RailLedger, model.ReportVisibilityHidden) + assertStep(t, graph.Steps[3], "custom.release", discovery.RailOperationRelease, discovery.RailLedger, model.ReportVisibilityHidden) if graph.Steps[2].CommitPolicy != model.CommitPolicyAfterSuccess { t.Fatalf("expected custom.finalize AFTER_SUCCESS, got %q", graph.Steps[2].CommitPolicy) @@ -67,7 +68,7 @@ func TestCompile_PolicyOverrideByRailPair(t *testing.T) { func TestCompile_PolicyPriorityAndCustodyMatching(t *testing.T) { compiler := New() - cardRail := model.RailCardPayout + var cardRail model.Rail = discovery.RailCardPayout on := true external := CustodyExternal @@ -91,17 +92,17 @@ func TestCompile_PolicyPriorityAndCustodyMatching(t *testing.T) { Source: EndpointMatch{Custody: &external}, Target: EndpointMatch{Custody: &external}, }, - Steps: []PolicyStep{{Code: "generic.submit", Action: model.RailOperationSend, Rail: &cardRail}}, + Steps: []PolicyStep{{Code: "generic.submit", Action: discovery.RailOperationSend, Rail: &cardRail}}, }, { ID: "specific-crypto-card", Enabled: &on, Priority: 10, Match: EdgeMatch{ - Source: EndpointMatch{Rail: railPtr(model.RailCrypto), Custody: &external}, - Target: EndpointMatch{Rail: railPtr(model.RailCardPayout), Custody: &external}, + Source: EndpointMatch{Rail: railPtr(discovery.RailCrypto), Custody: &external}, + Target: EndpointMatch{Rail: railPtr(discovery.RailCardPayout), Custody: &external}, }, - Steps: []PolicyStep{{Code: "specific.submit", Action: model.RailOperationSend, Rail: &cardRail}}, + Steps: []PolicyStep{{Code: "specific.submit", Action: discovery.RailOperationSend, Rail: &cardRail}}, }, }, }) @@ -195,11 +196,11 @@ func TestCompile_ValidationErrors(t *testing.T) { Enabled: &enabled, Priority: 1, Match: EdgeMatch{ - Source: EndpointMatch{Rail: railPtr(model.RailLedger)}, - Target: EndpointMatch{Rail: railPtr(model.RailCardPayout)}, + Source: EndpointMatch{Rail: railPtr(discovery.RailLedger)}, + Target: EndpointMatch{Rail: railPtr(discovery.RailCardPayout)}, }, Steps: []PolicyStep{ - {Code: "bad.step", Action: model.RailOperationUnspecified}, + {Code: "bad.step", Action: discovery.RailOperationUnspecified}, }, }, }, diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/xplan/expansion.go b/api/payments/orchestrator/internal/service/orchestrationv2/xplan/expansion.go index 67fdd603..84bb36ef 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/xplan/expansion.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/xplan/expansion.go @@ -1,6 +1,7 @@ package xplan import ( + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/tech/sendico/payments/storage/model" @@ -81,7 +82,7 @@ func normalizeCommitPolicy(policy model.CommitPolicy) model.CommitPolicy { func defaultVisibilityForAction(action model.RailOperation, role paymenttypes.QuoteRouteHopRole) model.ReportVisibility { switch action { - case model.RailOperationSend, model.RailOperationFXConvert, model.RailOperationObserveConfirm: + case discovery.RailOperationSend, discovery.RailOperationFXConvert, discovery.RailOperationObserveConfirm: if role == paymenttypes.QuoteRouteHopRoleDestination { return model.ReportVisibilityUser } @@ -101,15 +102,15 @@ func defaultUserLabel( return "" } switch action { - case model.RailOperationSend: - if kind == model.PaymentKindPayout && rail == model.RailCardPayout { + case discovery.RailOperationSend: + if kind == model.PaymentKindPayout && rail == discovery.RailCardPayout { return "Card payout submitted" } return "Transfer submitted" - case model.RailOperationFXConvert: + case discovery.RailOperationFXConvert: return "FX conversion submitted" - case model.RailOperationObserveConfirm: - if kind == model.PaymentKindPayout && rail == model.RailCardPayout { + case discovery.RailOperationObserveConfirm: + if kind == model.PaymentKindPayout && rail == discovery.RailCardPayout { return "Card payout confirmed" } return "Transfer confirmed" diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/xplan/route.go b/api/payments/orchestrator/internal/service/orchestrationv2/xplan/route.go index c12b29ac..cc619f78 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/xplan/route.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/xplan/route.go @@ -2,6 +2,7 @@ package xplan import ( "fmt" + "github.com/tech/sendico/pkg/discovery" "slices" "strings" @@ -17,16 +18,16 @@ func internalRailForBoundary(from normalizedHop, to normalizedHop) model.Rail { if isInternalRail(to.rail) { return to.rail } - return model.RailLedger + return discovery.RailLedger } func isInternalRail(rail model.Rail) bool { - return rail == model.RailLedger + return rail == discovery.RailLedger } func isExternalRail(rail model.Rail) bool { switch rail { - case model.RailCrypto, model.RailProviderSettlement, model.RailCardPayout, model.RailFiatOnRamp: + case discovery.RailCrypto, discovery.RailProviderSettlement, discovery.RailCardPayout, discovery.RailFiatOnRamp: return true default: return false @@ -58,7 +59,7 @@ func edgeCode(from normalizedHop, to normalizedHop, rail model.Rail, op string) } func railToken(rail model.Rail) string { - if rail == model.RailCardPayout { + if rail == discovery.RailCardPayout { return "card_payout" } return strings.ToLower(strings.TrimSpace(string(rail))) @@ -75,7 +76,7 @@ func normalizeRouteHops(route *paymenttypes.QuoteRouteSpecification, intent mode if len(route.Hops) == 0 { rail := normalizeRail(route.Rail) - if rail == model.RailUnspecified { + if rail == discovery.RailUnspecified { return nil, merrors.InvalidArgument("quote_snapshot.route.rail is required") } return []normalizedHop{ @@ -98,7 +99,7 @@ func normalizeRouteHops(route *paymenttypes.QuoteRouteSpecification, intent mode } rail := normalizeRail(firstNonEmpty(hop.Rail, route.Rail)) - if rail == model.RailUnspecified { + if rail == discovery.RailUnspecified { return nil, merrors.InvalidArgument("quote_snapshot.route.hops[" + itoa(i) + "].rail is required") } diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/xplan/service_boundaries.go b/api/payments/orchestrator/internal/service/orchestrationv2/xplan/service_boundaries.go index 27543f36..741a0b2f 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/xplan/service_boundaries.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/xplan/service_boundaries.go @@ -1,6 +1,7 @@ package xplan import ( + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/batchmeta" @@ -20,7 +21,7 @@ func (s *svc) expandSingleHop(ex *expansion, hop normalizedHop, intent model.Pay ex.appendMain(Step{ StepCode: singleHopCode(hop, "debit"), Kind: StepKindFundsDebit, - Action: model.RailOperationDebit, + Action: discovery.RailOperationDebit, Rail: hop.rail, HopIndex: hop.index, HopRole: hop.role, @@ -30,7 +31,7 @@ func (s *svc) expandSingleHop(ex *expansion, hop normalizedHop, intent model.Pay ex.appendMain(Step{ StepCode: singleHopCode(hop, "credit"), Kind: StepKindFundsCredit, - Action: model.RailOperationCredit, + Action: discovery.RailOperationCredit, Rail: hop.rail, HopIndex: hop.index, HopRole: hop.role, @@ -40,7 +41,7 @@ func (s *svc) expandSingleHop(ex *expansion, hop normalizedHop, intent model.Pay ex.appendMain(Step{ StepCode: singleHopCode(hop, "move"), Kind: StepKindFundsMove, - Action: model.RailOperationMove, + Action: discovery.RailOperationMove, Rail: hop.rail, HopIndex: hop.index, HopRole: hop.role, @@ -70,7 +71,7 @@ func (s *svc) applyDefaultBoundary( if err != nil { return err } - if to.rail == model.RailCardPayout && len(targets) > 0 { + if to.rail == discovery.RailCardPayout && len(targets) > 0 { return s.applyBatchCardPayoutBoundary(ex, from, to, internalRail, intent, targets) } ex.appendMain(makeFundsBlockStep(from, to, internalRail)) @@ -167,8 +168,8 @@ func appendGuards(ex *expansion, conditions *paymenttypes.QuoteExecutionConditio StepRef: QuoteReadinessGuardStepRef, StepCode: string(GuardOperationQuoteReadinessGuard), Kind: StepKindLiquidityCheck, - Action: model.RailOperationUnspecified, - Rail: model.RailUnspecified, + Action: discovery.RailOperationUnspecified, + Rail: discovery.RailUnspecified, Visibility: model.ReportVisibilityHidden, }) } @@ -178,8 +179,8 @@ func appendGuards(ex *expansion, conditions *paymenttypes.QuoteExecutionConditio StepRef: PrefundingGuardStepRef, StepCode: string(GuardOperationPrefundingEnsure), Kind: StepKindPrefunding, - Action: model.RailOperationUnspecified, - Rail: model.RailUnspecified, + Action: discovery.RailOperationUnspecified, + Rail: discovery.RailUnspecified, Visibility: model.ReportVisibilityHidden, }) } @@ -207,22 +208,22 @@ func makeRailSendStep(hop normalizedHop, intent model.PaymentIntent) Step { } func railActionForHop(hop normalizedHop) (model.RailOperation, string) { - if hop.rail == model.RailProviderSettlement { - return model.RailOperationFXConvert, "fx_convert" + if hop.rail == discovery.RailProviderSettlement { + return discovery.RailOperationFXConvert, "fx_convert" } - return model.RailOperationSend, "send" + return discovery.RailOperationSend, "send" } func makeRailObserveStep(hop normalizedHop, intent model.PaymentIntent) Step { - visibility := defaultVisibilityForAction(model.RailOperationObserveConfirm, hop.role) + visibility := defaultVisibilityForAction(discovery.RailOperationObserveConfirm, hop.role) userLabel := "" if visibility == model.ReportVisibilityUser { - userLabel = defaultUserLabel(model.RailOperationObserveConfirm, hop.rail, hop.role, intent.Kind) + userLabel = defaultUserLabel(discovery.RailOperationObserveConfirm, hop.rail, hop.role, intent.Kind) } return Step{ StepCode: singleHopCode(hop, "observe"), Kind: StepKindRailObserve, - Action: model.RailOperationObserveConfirm, + Action: discovery.RailOperationObserveConfirm, Rail: hop.rail, Gateway: hop.gateway, InstanceID: hop.instanceID, @@ -237,7 +238,7 @@ func makeFundsCreditStep(from normalizedHop, to normalizedHop, rail model.Rail) return Step{ StepCode: edgeCode(from, to, rail, "credit"), Kind: StepKindFundsCredit, - Action: model.RailOperationExternalCredit, + Action: discovery.RailOperationExternalCredit, Rail: rail, HopIndex: to.index, HopRole: paymenttypes.QuoteRouteHopRoleTransit, @@ -249,7 +250,7 @@ func makeFundsBlockStep(from normalizedHop, to normalizedHop, rail model.Rail) S return Step{ StepCode: edgeCode(from, to, rail, "block"), Kind: StepKindFundsBlock, - Action: model.RailOperationBlock, + Action: discovery.RailOperationBlock, Rail: rail, HopIndex: to.index, HopRole: paymenttypes.QuoteRouteHopRoleTransit, @@ -261,7 +262,7 @@ func makeFundsMoveStep(from normalizedHop, to normalizedHop, rail model.Rail) St return Step{ StepCode: edgeCode(from, to, rail, "move"), Kind: StepKindFundsMove, - Action: model.RailOperationMove, + Action: discovery.RailOperationMove, Rail: rail, HopIndex: to.index, HopRole: paymenttypes.QuoteRouteHopRoleTransit, @@ -312,7 +313,7 @@ func appendSettlementBranchesWithMetadata( successStep := Step{ StepCode: edgeCode(from, to, rail, "debit"), Kind: StepKindFundsDebit, - Action: model.RailOperationExternalDebit, + Action: discovery.RailOperationExternalDebit, DependsOn: []string{anchorObserveRef}, Rail: rail, HopIndex: to.index, @@ -328,7 +329,7 @@ func appendSettlementBranchesWithMetadata( sendFailureStep := Step{ StepCode: edgeCode(from, to, rail, "release"), Kind: StepKindFundsRelease, - Action: model.RailOperationRelease, + Action: discovery.RailOperationRelease, DependsOn: []string{anchorSendRef}, Rail: rail, HopIndex: to.index, @@ -344,7 +345,7 @@ func appendSettlementBranchesWithMetadata( failureStep := Step{ StepCode: edgeCode(from, to, rail, "release"), Kind: StepKindFundsRelease, - Action: model.RailOperationRelease, + Action: discovery.RailOperationRelease, DependsOn: []string{anchorObserveRef}, Rail: rail, HopIndex: to.index, diff --git a/api/payments/orchestrator/internal/service/orchestrationv2/xplan/service_policy.go b/api/payments/orchestrator/internal/service/orchestrationv2/xplan/service_policy.go index a9da65f4..d7533fc1 100644 --- a/api/payments/orchestrator/internal/service/orchestrationv2/xplan/service_policy.go +++ b/api/payments/orchestrator/internal/service/orchestrationv2/xplan/service_policy.go @@ -1,6 +1,7 @@ package xplan import ( + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/tech/sendico/payments/storage/model" @@ -127,12 +128,12 @@ func policyStepToStep(spec PolicyStep, from normalizedHop, to normalizedHop, int } action := normalizeAction(spec.Action) - if action == model.RailOperationUnspecified { + if action == discovery.RailOperationUnspecified { return Step{}, merrors.InvalidArgument("action is required") } rail := inferPolicyRail(spec, action, from, to) - if rail == model.RailUnspecified { + if rail == discovery.RailUnspecified { return Step{}, merrors.InvalidArgument("rail could not be inferred") } @@ -166,30 +167,30 @@ func policyStepToStep(spec PolicyStep, from normalizedHop, to normalizedHop, int func normalizeAction(action model.RailOperation) model.RailOperation { switch strings.ToUpper(strings.TrimSpace(string(action))) { - case string(model.RailOperationDebit): - return model.RailOperationDebit - case string(model.RailOperationCredit): - return model.RailOperationCredit - case string(model.RailOperationExternalDebit): - return model.RailOperationExternalDebit - case string(model.RailOperationExternalCredit): - return model.RailOperationExternalCredit - case string(model.RailOperationMove): - return model.RailOperationMove - case string(model.RailOperationSend): - return model.RailOperationSend - case string(model.RailOperationFee): - return model.RailOperationFee - case string(model.RailOperationObserveConfirm): - return model.RailOperationObserveConfirm - case string(model.RailOperationFXConvert): - return model.RailOperationFXConvert - case string(model.RailOperationBlock): - return model.RailOperationBlock - case string(model.RailOperationRelease): - return model.RailOperationRelease + case string(discovery.RailOperationDebit): + return discovery.RailOperationDebit + case string(discovery.RailOperationCredit): + return discovery.RailOperationCredit + case string(discovery.RailOperationExternalDebit): + return discovery.RailOperationExternalDebit + case string(discovery.RailOperationExternalCredit): + return discovery.RailOperationExternalCredit + case string(discovery.RailOperationMove): + return discovery.RailOperationMove + case string(discovery.RailOperationSend): + return discovery.RailOperationSend + case string(discovery.RailOperationFee): + return discovery.RailOperationFee + case string(discovery.RailOperationObserveConfirm): + return discovery.RailOperationObserveConfirm + case string(discovery.RailOperationFXConvert): + return discovery.RailOperationFXConvert + case string(discovery.RailOperationBlock): + return discovery.RailOperationBlock + case string(discovery.RailOperationRelease): + return discovery.RailOperationRelease default: - return model.RailOperationUnspecified + return discovery.RailOperationUnspecified } } @@ -199,18 +200,18 @@ func inferPolicyRail(spec PolicyStep, action model.RailOperation, from normalize } switch action { - case model.RailOperationSend, model.RailOperationFXConvert, model.RailOperationObserveConfirm, model.RailOperationFee: + case discovery.RailOperationSend, discovery.RailOperationFXConvert, discovery.RailOperationObserveConfirm, discovery.RailOperationFee: return to.rail - case model.RailOperationBlock, - model.RailOperationRelease, - model.RailOperationDebit, - model.RailOperationCredit, - model.RailOperationExternalDebit, - model.RailOperationExternalCredit, - model.RailOperationMove: + case discovery.RailOperationBlock, + discovery.RailOperationRelease, + discovery.RailOperationDebit, + discovery.RailOperationCredit, + discovery.RailOperationExternalDebit, + discovery.RailOperationExternalCredit, + discovery.RailOperationMove: return internalRailForBoundary(from, to) default: - return model.RailUnspecified + return discovery.RailUnspecified } } @@ -220,7 +221,7 @@ func resolveStepContext( from normalizedHop, to normalizedHop, ) (uint32, paymenttypes.QuoteRouteHopRole, string, string) { - if rail == to.rail && (action == model.RailOperationSend || action == model.RailOperationFXConvert || action == model.RailOperationObserveConfirm || action == model.RailOperationFee) { + if rail == to.rail && (action == discovery.RailOperationSend || action == discovery.RailOperationFXConvert || action == discovery.RailOperationObserveConfirm || action == discovery.RailOperationFee) { return to.index, to.role, to.gateway, to.instanceID } if rail == from.rail { @@ -234,19 +235,19 @@ func resolveStepContext( func kindForAction(action model.RailOperation) StepKind { switch action { - case model.RailOperationSend, model.RailOperationFXConvert: + case discovery.RailOperationSend, discovery.RailOperationFXConvert: return StepKindRailSend - case model.RailOperationObserveConfirm: + case discovery.RailOperationObserveConfirm: return StepKindRailObserve - case model.RailOperationCredit, model.RailOperationExternalCredit: + case discovery.RailOperationCredit, discovery.RailOperationExternalCredit: return StepKindFundsCredit - case model.RailOperationDebit, model.RailOperationExternalDebit: + case discovery.RailOperationDebit, discovery.RailOperationExternalDebit: return StepKindFundsDebit - case model.RailOperationMove: + case discovery.RailOperationMove: return StepKindFundsMove - case model.RailOperationBlock: + case discovery.RailOperationBlock: return StepKindFundsBlock - case model.RailOperationRelease: + case discovery.RailOperationRelease: return StepKindFundsRelease default: return StepKindUnspecified diff --git a/api/payments/orchestrator/internal/service/orchestrator/card_payout_executor.go b/api/payments/orchestrator/internal/service/orchestrator/card_payout_executor.go index 67c2948f..fccbcfe8 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/card_payout_executor.go +++ b/api/payments/orchestrator/internal/service/orchestrator/card_payout_executor.go @@ -2,6 +2,7 @@ package orchestrator import ( "context" + "github.com/tech/sendico/pkg/discovery" "strconv" "strings" @@ -42,7 +43,7 @@ func (e *gatewayCardPayoutExecutor) ExecuteCardPayout(ctx context.Context, req s if e == nil || e.mntxClient == nil { return nil, merrors.InvalidArgument("card payout send: mntx client is required") } - if model.ParseRailOperation(string(req.Step.Action)) != model.RailOperationSend { + if model.ParseRailOperation(string(req.Step.Action)) != discovery.RailOperationSend { return nil, merrors.InvalidArgument("card payout send: unsupported action") } diff --git a/api/payments/orchestrator/internal/service/orchestrator/card_payout_executor_test.go b/api/payments/orchestrator/internal/service/orchestrator/card_payout_executor_test.go index d7bc7899..a75b7306 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/card_payout_executor_test.go +++ b/api/payments/orchestrator/internal/service/orchestrator/card_payout_executor_test.go @@ -2,6 +2,7 @@ package orchestrator import ( "context" + "github.com/tech/sendico/pkg/discovery" "strings" "testing" @@ -78,8 +79,8 @@ func TestGatewayCardPayoutExecutor_ExecuteCardPayout_SubmitsCardPayout(t *testin Step: xplan.Step{ StepRef: "hop_4_card_payout_send", StepCode: "hop.4.card_payout.send", - Action: model.RailOperationSend, - Rail: model.RailCardPayout, + Action: discovery.RailOperationSend, + Rail: discovery.RailCardPayout, Gateway: paymenttypes.DefaultCardsGatewayID, InstanceID: paymenttypes.DefaultCardsGatewayID, }, @@ -121,7 +122,7 @@ func TestGatewayCardPayoutExecutor_ExecuteCardPayout_SubmitsCardPayout(t *testin if got, want := payoutReq.GetMetadata()[settlementMetadataQuoteRef], "quote-1"; got != want { t.Fatalf("quote_ref metadata mismatch: got=%q want=%q", got, want) } - if got, want := payoutReq.GetMetadata()[settlementMetadataOutgoingLeg], string(model.RailCardPayout); got != want { + if got, want := payoutReq.GetMetadata()[settlementMetadataOutgoingLeg], string(discovery.RailCardPayout); got != want { t.Fatalf("outgoing_leg metadata mismatch: got=%q want=%q", got, want) } if len(out.StepExecution.ExternalRefs) != 3 { @@ -190,8 +191,8 @@ func TestGatewayCardPayoutExecutor_ExecuteCardPayout_UsesStepMetadataOverrides(t Step: xplan.Step{ StepRef: "hop_4_card_payout_send", StepCode: "hop.4.card_payout.send", - Action: model.RailOperationSend, - Rail: model.RailCardPayout, + Action: discovery.RailOperationSend, + Rail: discovery.RailCardPayout, Gateway: paymenttypes.DefaultCardsGatewayID, InstanceID: paymenttypes.DefaultCardsGatewayID, Metadata: map[string]string{ @@ -257,8 +258,8 @@ func TestGatewayCardPayoutExecutor_ExecuteCardPayout_RequiresMntxClient(t *testi Step: xplan.Step{ StepRef: "hop_4_card_payout_send", StepCode: "hop.4.card_payout.send", - Action: model.RailOperationSend, - Rail: model.RailCardPayout, + Action: discovery.RailOperationSend, + Rail: discovery.RailCardPayout, }, StepExecution: agg.StepExecution{ StepRef: "hop_4_card_payout_send", diff --git a/api/payments/orchestrator/internal/service/orchestrator/crypto_executor.go b/api/payments/orchestrator/internal/service/orchestrator/crypto_executor.go index c8275bbc..353f9a00 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/crypto_executor.go +++ b/api/payments/orchestrator/internal/service/orchestrator/crypto_executor.go @@ -2,6 +2,7 @@ package orchestrator import ( "context" + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg" @@ -27,7 +28,7 @@ func (e *gatewayCryptoExecutor) ExecuteCrypto(ctx context.Context, req sexec.Ste } action := model.ParseRailOperation(string(req.Step.Action)) switch action { - case model.RailOperationSend, model.RailOperationFee: + case discovery.RailOperationSend, discovery.RailOperationFee: default: return nil, merrors.InvalidArgument("crypto send: unsupported action") } @@ -112,7 +113,7 @@ func (e *gatewayCryptoExecutor) resolveGateway(ctx context.Context, step xplan.S cryptoCount := 0 for i := range items { item := items[i] - if item == nil || model.ParseRail(string(item.Rail)) != model.RailCrypto || !item.IsEnabled { + if item == nil || model.ParseRail(string(item.Rail)) != discovery.RailCrypto || !item.IsEnabled { continue } cryptoCount++ @@ -242,7 +243,7 @@ func (e *gatewayCryptoExecutor) resolveCardFundingAddress(payment *agg.Payment, return "", merrors.InvalidArgument("crypto send: card gateway route is not configured") } switch action { - case model.RailOperationFee: + case discovery.RailOperationFee: if feeAddress := strings.TrimSpace(route.FeeAddress); feeAddress != "" { return feeAddress, nil } @@ -262,7 +263,7 @@ func destinationCardGatewayKey(payment *agg.Payment) string { fallback := "" for i := range hops { hop := hops[i] - if hop == nil || model.ParseRail(hop.Rail) != model.RailCardPayout { + if hop == nil || model.ParseRail(hop.Rail) != discovery.RailCardPayout { continue } key := firstNonEmpty(strings.TrimSpace(hop.Gateway), strings.TrimSpace(hop.InstanceID)) diff --git a/api/payments/orchestrator/internal/service/orchestrator/crypto_executor_test.go b/api/payments/orchestrator/internal/service/orchestrator/crypto_executor_test.go index 49c18bb2..35942f3c 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/crypto_executor_test.go +++ b/api/payments/orchestrator/internal/service/orchestrator/crypto_executor_test.go @@ -2,6 +2,7 @@ package orchestrator import ( "context" + "github.com/tech/sendico/pkg/discovery" "testing" chainclient "github.com/tech/sendico/gateway/chain/client" @@ -37,7 +38,7 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_SubmitsTransfer(t *testing.T) { { ID: "crypto_rail_gateway_arbitrum_sepolia", InstanceID: "crypto_rail_gateway_arbitrum_sepolia", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, InvokeURI: "grpc://crypto-gateway", IsEnabled: true, }, @@ -83,8 +84,8 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_SubmitsTransfer(t *testing.T) { Step: xplan.Step{ StepRef: "hop_1_crypto_send", StepCode: "hop.1.crypto.send", - Action: model.RailOperationSend, - Rail: model.RailCrypto, + Action: discovery.RailOperationSend, + Rail: discovery.RailCrypto, Gateway: "crypto_rail_gateway_arbitrum_sepolia", InstanceID: "crypto_rail_gateway_arbitrum_sepolia", }, @@ -139,7 +140,7 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_MissingCardRoute(t *testing.T) { { ID: "crypto_1", InstanceID: "crypto_1", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, InvokeURI: "grpc://crypto-gateway", IsEnabled: true, }, @@ -178,8 +179,8 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_MissingCardRoute(t *testing.T) { Step: xplan.Step{ StepRef: "hop_1_crypto_send", StepCode: "hop.1.crypto.send", - Action: model.RailOperationSend, - Rail: model.RailCrypto, + Action: discovery.RailOperationSend, + Rail: discovery.RailCrypto, Gateway: "crypto_1", InstanceID: "crypto_1", }, diff --git a/api/payments/orchestrator/internal/service/orchestrator/external_runtime.go b/api/payments/orchestrator/internal/service/orchestrator/external_runtime.go index bc120c0a..11516612 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/external_runtime.go +++ b/api/payments/orchestrator/internal/service/orchestrator/external_runtime.go @@ -3,6 +3,7 @@ package orchestrator import ( "context" "errors" + "github.com/tech/sendico/pkg/discovery" "strings" "time" @@ -503,7 +504,7 @@ func (s *Service) resolveObserveGateway(ctx context.Context, payment *agg.Paymen gatewayRegistry: s.gatewayRegistry, } step := xplan.Step{ - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, } if gatewayID := strings.TrimSpace(candidate.gatewayInstanceID); gatewayID != "" { step.InstanceID = gatewayID diff --git a/api/payments/orchestrator/internal/service/orchestrator/external_runtime_test.go b/api/payments/orchestrator/internal/service/orchestrator/external_runtime_test.go index f3902af0..ecaf7ded 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/external_runtime_test.go +++ b/api/payments/orchestrator/internal/service/orchestrator/external_runtime_test.go @@ -3,6 +3,7 @@ package orchestrator import ( "context" "errors" + "github.com/tech/sendico/pkg/discovery" "testing" "github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg" @@ -351,14 +352,14 @@ func TestResolveObserveGateway_UsesExternalRefGatewayInstanceAcrossRails(t *test { ID: "payment_gateway_settlement", InstanceID: "payment_gateway_settlement", - Rail: model.RailProviderSettlement, + Rail: discovery.RailProviderSettlement, InvokeURI: "grpc://tgsettle-gateway", IsEnabled: true, }, { ID: "crypto_rail_gateway_tron_nile", InstanceID: "crypto_rail_gateway_tron_nile", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, InvokeURI: "grpc://tron-gateway", IsEnabled: true, }, diff --git a/api/payments/orchestrator/internal/service/orchestrator/guard_executor.go b/api/payments/orchestrator/internal/service/orchestrator/guard_executor.go index e8dbda4e..be73d9c5 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/guard_executor.go +++ b/api/payments/orchestrator/internal/service/orchestrator/guard_executor.go @@ -3,6 +3,7 @@ package orchestrator import ( "context" "fmt" + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/shopspring/decimal" @@ -92,7 +93,7 @@ func (e *gatewayGuardExecutor) probeLiquidity(ctx context.Context, req sexec.Ste gateway, err := resolver.resolveGateway(ctx, xplan.Step{ Gateway: hopGateway, InstanceID: hopInstanceID, - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, }) if err != nil { return "guard.liquidity_probe_error", err.Error() @@ -167,7 +168,7 @@ func sourceCryptoHop(payment *agg.Payment) (gateway string, instanceID string, o fallbackInstance := "" for i := range hops { hop := hops[i] - if hop == nil || model.ParseRail(hop.Rail) != model.RailCrypto { + if hop == nil || model.ParseRail(hop.Rail) != discovery.RailCrypto { continue } gw := strings.TrimSpace(hop.Gateway) diff --git a/api/payments/orchestrator/internal/service/orchestrator/guard_executor_test.go b/api/payments/orchestrator/internal/service/orchestrator/guard_executor_test.go index 555c36c0..67d7f9b3 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/guard_executor_test.go +++ b/api/payments/orchestrator/internal/service/orchestrator/guard_executor_test.go @@ -2,6 +2,7 @@ package orchestrator import ( "context" + "github.com/tech/sendico/pkg/discovery" "strings" "testing" @@ -38,7 +39,7 @@ func TestGatewayGuardExecutor_ExecuteGuard_LiquidityProbePasses(t *testing.T) { { ID: "crypto_1", InstanceID: "crypto_1", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, InvokeURI: "grpc://crypto-gateway", IsEnabled: true, }, @@ -95,7 +96,7 @@ func TestGatewayGuardExecutor_ExecuteGuard_InsufficientLiquidity(t *testing.T) { { ID: "crypto_1", InstanceID: "crypto_1", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, InvokeURI: "grpc://crypto-gateway", IsEnabled: true, }, @@ -150,7 +151,7 @@ func TestGatewayGuardExecutor_ExecuteGuard_ReadinessStopsBeforeProbe(t *testing. { ID: "crypto_1", InstanceID: "crypto_1", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, InvokeURI: "grpc://crypto-gateway", IsEnabled: true, }, diff --git a/api/payments/orchestrator/internal/service/orchestrator/ledger_executor.go b/api/payments/orchestrator/internal/service/orchestrator/ledger_executor.go index 1fd8ce43..5b1288d7 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/ledger_executor.go +++ b/api/payments/orchestrator/internal/service/orchestrator/ledger_executor.go @@ -3,6 +3,7 @@ package orchestrator import ( "context" "fmt" + "github.com/tech/sendico/pkg/discovery" "strings" ledgerclient "github.com/tech/sendico/ledger/client" @@ -43,13 +44,13 @@ func (e *gatewayLedgerExecutor) ExecuteLedger(ctx context.Context, req sexec.Ste action := model.ParseRailOperation(string(req.Step.Action)) switch action { - case model.RailOperationDebit, - model.RailOperationCredit, - model.RailOperationExternalDebit, - model.RailOperationExternalCredit, - model.RailOperationMove, - model.RailOperationBlock, - model.RailOperationRelease: + case discovery.RailOperationDebit, + discovery.RailOperationCredit, + discovery.RailOperationExternalDebit, + discovery.RailOperationExternalCredit, + discovery.RailOperationMove, + discovery.RailOperationBlock, + discovery.RailOperationRelease: default: return nil, merrors.InvalidArgument("ledger step: unsupported action") } @@ -70,7 +71,7 @@ func (e *gatewayLedgerExecutor) ExecuteLedger(ctx context.Context, req sexec.Ste var resp *ledgerv1.PostResponse switch action { - case model.RailOperationExternalCredit: + case discovery.RailOperationExternalCredit: resp, err = e.ledgerClient.PostExternalCreditWithCharges(ctx, &ledgerv1.PostCreditRequest{ IdempotencyKey: idempotencyKey, OrganizationRef: organizationRef, @@ -79,7 +80,7 @@ func (e *gatewayLedgerExecutor) ExecuteLedger(ctx context.Context, req sexec.Ste Metadata: metadata, Role: ledgerRoleToProto(roles.to), }) - case model.RailOperationExternalDebit: + case discovery.RailOperationExternalDebit: resp, err = e.ledgerClient.PostExternalDebitWithCharges(ctx, &ledgerv1.PostDebitRequest{ IdempotencyKey: idempotencyKey, OrganizationRef: organizationRef, @@ -138,7 +139,7 @@ func ledgerAmountForStep( case isLedgerExternalRail(fromRail) && isLedgerInternalRail(toRail): return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required") case isLedgerInternalRail(fromRail) && isLedgerExternalRail(toRail): - if toRail == model.RailCardPayout { + if toRail == discovery.RailCardPayout { return protoMoneyRequired(payoutMoney, "ledger step: payout amount is required") } return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required") @@ -148,14 +149,14 @@ func ledgerAmountForStep( } switch action { - case model.RailOperationCredit, model.RailOperationExternalCredit: + case discovery.RailOperationCredit, discovery.RailOperationExternalCredit: return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required") - case model.RailOperationDebit, model.RailOperationExternalDebit: + case discovery.RailOperationDebit, discovery.RailOperationExternalDebit: if sourceMoney != nil { return protoMoneyRequired(sourceMoney, "ledger step: source amount is required") } return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required") - case model.RailOperationMove, model.RailOperationBlock, model.RailOperationRelease: + case discovery.RailOperationMove, discovery.RailOperationBlock, discovery.RailOperationRelease: if settlementMoney != nil { return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required") } @@ -209,18 +210,18 @@ func ledgerRolesForStep(step xplan.Step, action model.RailOperation) (ledgerRole mode := strings.ToLower(strings.TrimSpace(step.Metadata[ledgerMetadataMode])) switch action { - case model.RailOperationBlock: + case discovery.RailOperationBlock: return ledgerRoles{from: account_role.AccountRoleOperating, to: account_role.AccountRoleHold}, nil - case model.RailOperationRelease: + case discovery.RailOperationRelease: return ledgerRoles{from: account_role.AccountRoleHold, to: account_role.AccountRoleOperating}, nil - case model.RailOperationCredit, model.RailOperationExternalCredit: + case discovery.RailOperationCredit, discovery.RailOperationExternalCredit: return ledgerRoles{from: account_role.AccountRolePending, to: account_role.AccountRoleOperating}, nil - case model.RailOperationDebit, model.RailOperationExternalDebit: + case discovery.RailOperationDebit, discovery.RailOperationExternalDebit: if mode == "finalize_debit" { return ledgerRoles{from: account_role.AccountRoleHold, to: account_role.AccountRoleTransit}, nil } return ledgerRoles{from: account_role.AccountRoleOperating, to: account_role.AccountRoleTransit}, nil - case model.RailOperationMove: + case discovery.RailOperationMove: return ledgerRoles{from: account_role.AccountRoleOperating, to: account_role.AccountRoleTransit}, nil default: return ledgerRoles{}, merrors.InvalidArgument("ledger step: unsupported action") @@ -342,11 +343,11 @@ func appendLedgerExternalRef(existing []agg.ExternalRef, ref agg.ExternalRef) [] func ledgerBoundaryRails(payment *agg.Payment, step xplan.Step) (model.Rail, model.Rail, bool) { fromIndex, toIndex, ok := parseLedgerEdgeStepCode(step.StepCode) if !ok || payment == nil || payment.QuoteSnapshot == nil || payment.QuoteSnapshot.Route == nil { - return model.RailUnspecified, model.RailUnspecified, false + return discovery.RailUnspecified, discovery.RailUnspecified, false } - fromRail := model.RailUnspecified - toRail := model.RailUnspecified + var fromRail model.Rail = discovery.RailUnspecified + var toRail model.Rail = discovery.RailUnspecified for i := range payment.QuoteSnapshot.Route.Hops { hop := payment.QuoteSnapshot.Route.Hops[i] if hop == nil { @@ -359,8 +360,8 @@ func ledgerBoundaryRails(payment *agg.Payment, step xplan.Step) (model.Rail, mod toRail = model.ParseRail(hop.Rail) } } - if fromRail == model.RailUnspecified || toRail == model.RailUnspecified { - return model.RailUnspecified, model.RailUnspecified, false + if fromRail == discovery.RailUnspecified || toRail == discovery.RailUnspecified { + return discovery.RailUnspecified, discovery.RailUnspecified, false } return fromRail, toRail, true } @@ -385,12 +386,12 @@ func parseLedgerEdgeStepCode(stepCode string) (uint32, uint32, bool) { } func isLedgerInternalRail(rail model.Rail) bool { - return rail == model.RailLedger + return rail == discovery.RailLedger } func isLedgerExternalRail(rail model.Rail) bool { switch rail { - case model.RailCrypto, model.RailProviderSettlement, model.RailCardPayout, model.RailFiatOnRamp: + case discovery.RailCrypto, discovery.RailProviderSettlement, discovery.RailCardPayout, discovery.RailFiatOnRamp: return true default: return false diff --git a/api/payments/orchestrator/internal/service/orchestrator/ledger_executor_test.go b/api/payments/orchestrator/internal/service/orchestrator/ledger_executor_test.go index 8df29768..f53abe2e 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/ledger_executor_test.go +++ b/api/payments/orchestrator/internal/service/orchestrator/ledger_executor_test.go @@ -2,6 +2,7 @@ package orchestrator import ( "context" + "github.com/tech/sendico/pkg/discovery" "strings" "testing" @@ -37,8 +38,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_CreditUsesSourceAmountAndDefaultRol Step: xplan.Step{ StepRef: "edge_1_2_ledger_credit", StepCode: "edge.1_2.ledger.credit", - Action: model.RailOperationCredit, - Rail: model.RailLedger, + Action: discovery.RailOperationCredit, + Rail: discovery.RailLedger, }, StepExecution: agg.StepExecution{ StepRef: "edge_1_2_ledger_credit", @@ -107,8 +108,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_ExternalCreditUsesPostCreditWithCha Step: xplan.Step{ StepRef: "edge_1_2_ledger_credit", StepCode: "edge.1_2.ledger.credit", - Action: model.RailOperationExternalCredit, - Rail: model.RailLedger, + Action: discovery.RailOperationExternalCredit, + Rail: discovery.RailLedger, }, StepExecution: agg.StepExecution{ StepRef: "edge_1_2_ledger_credit", @@ -174,8 +175,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_ExternalDebitUsesPostDebitWithCharg Step: xplan.Step{ StepRef: "edge_3_4_ledger_debit", StepCode: "edge.3_4.ledger.debit", - Action: model.RailOperationExternalDebit, - Rail: model.RailLedger, + Action: discovery.RailOperationExternalDebit, + Rail: discovery.RailLedger, Metadata: map[string]string{ "mode": "finalize_debit", }, @@ -240,8 +241,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_FinalizeDebitUsesHoldToTransitAndSe Step: xplan.Step{ StepRef: "edge_3_4_ledger_debit", StepCode: "edge.3_4.ledger.debit", - Action: model.RailOperationDebit, - Rail: model.RailLedger, + Action: discovery.RailOperationDebit, + Rail: discovery.RailLedger, Metadata: map[string]string{"mode": "finalize_debit"}, HopIndex: 4, HopRole: paymenttypes.QuoteRouteHopRoleTransit, @@ -298,8 +299,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_BlockUsesOperatingToHoldAndPayoutAm Step: xplan.Step{ StepRef: "edge_3_4_ledger_block", StepCode: "edge.3_4.ledger.block", - Action: model.RailOperationBlock, - Rail: model.RailLedger, + Action: discovery.RailOperationBlock, + Rail: discovery.RailLedger, }, StepExecution: agg.StepExecution{ StepRef: "edge_3_4_ledger_block", @@ -349,8 +350,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_ReleaseUsesHoldToOperatingAndPayout Step: xplan.Step{ StepRef: "edge_3_4_ledger_release", StepCode: "edge.3_4.ledger.release", - Action: model.RailOperationRelease, - Rail: model.RailLedger, + Action: discovery.RailOperationRelease, + Rail: discovery.RailLedger, }, StepExecution: agg.StepExecution{ StepRef: "edge_3_4_ledger_release", @@ -400,8 +401,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_UsesStepAmountOverride(t *testing.T Step: xplan.Step{ StepRef: "edge_3_4_ledger_block", StepCode: "edge.3_4.ledger.block", - Action: model.RailOperationBlock, - Rail: model.RailLedger, + Action: discovery.RailOperationBlock, + Rail: discovery.RailLedger, Metadata: map[string]string{ batchmeta.MetaAmount: "40", batchmeta.MetaCurrency: "RUB", @@ -446,8 +447,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_UsesMetadataRoleOverrides(t *testin Step: xplan.Step{ StepRef: "edge_2_3_ledger_credit", StepCode: "edge.2_3.ledger.credit", - Action: model.RailOperationCredit, - Rail: model.RailLedger, + Action: discovery.RailOperationCredit, + Rail: discovery.RailLedger, Metadata: map[string]string{ "from_role": "reserve", "to_role": "liquidity", @@ -486,8 +487,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_ValidatesMetadataRoles(t *testing.T Step: xplan.Step{ StepRef: "edge_2_3_ledger_credit", StepCode: "edge.2_3.ledger.credit", - Action: model.RailOperationCredit, - Rail: model.RailLedger, + Action: discovery.RailOperationCredit, + Rail: discovery.RailLedger, Metadata: map[string]string{ "from_role": "bad_role", "to_role": "operating", @@ -514,8 +515,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_RequiresLedgerClient(t *testing.T) Step: xplan.Step{ StepRef: "edge_1_2_ledger_credit", StepCode: "edge.1_2.ledger.credit", - Action: model.RailOperationCredit, - Rail: model.RailLedger, + Action: discovery.RailOperationCredit, + Rail: discovery.RailLedger, }, StepExecution: agg.StepExecution{StepRef: "edge_1_2_ledger_credit", StepCode: "edge.1_2.ledger.credit", Attempt: 1}, }) diff --git a/api/payments/orchestrator/internal/service/orchestrator/options.go b/api/payments/orchestrator/internal/service/orchestrator/options.go index f3ec7237..c44ba466 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/options.go +++ b/api/payments/orchestrator/internal/service/orchestrator/options.go @@ -140,7 +140,7 @@ func (r *discoveryGatewayRegistry) List(_ context.Context) ([]*model.GatewayInst items := make([]*model.GatewayInstanceDescriptor, 0, len(entries)) for _, entry := range entries { rail := railFromDiscovery(entry.Rail) - if rail == model.RailUnspecified { + if rail == discovery.RailUnspecified { continue } operations := operationsFromDiscovery(entry.Operations) @@ -163,17 +163,17 @@ func (r *discoveryGatewayRegistry) List(_ context.Context) ([]*model.GatewayInst func railFromDiscovery(value string) model.Rail { switch discovery.NormalizeRail(value) { case discovery.RailCrypto: - return model.RailCrypto + return discovery.RailCrypto case discovery.RailProviderSettlement: - return model.RailProviderSettlement + return discovery.RailProviderSettlement case discovery.RailLedger: - return model.RailLedger + return discovery.RailLedger case discovery.RailCardPayout: - return model.RailCardPayout + return discovery.RailCardPayout case discovery.RailFiatOnRamp: - return model.RailFiatOnRamp + return discovery.RailFiatOnRamp default: - return model.RailUnspecified + return discovery.RailUnspecified } } diff --git a/api/payments/orchestrator/internal/service/orchestrator/settlement_executor.go b/api/payments/orchestrator/internal/service/orchestrator/settlement_executor.go index 0490787f..6d1835b8 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/settlement_executor.go +++ b/api/payments/orchestrator/internal/service/orchestrator/settlement_executor.go @@ -2,6 +2,7 @@ package orchestrator import ( "context" + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg" @@ -27,7 +28,7 @@ func (e *gatewayProviderSettlementExecutor) ExecuteProviderSettlement(ctx contex if req.Payment == nil { return nil, merrors.InvalidArgument("settlement fx_convert: payment is required") } - if model.ParseRailOperation(string(req.Step.Action)) != model.RailOperationFXConvert { + if model.ParseRailOperation(string(req.Step.Action)) != discovery.RailOperationFXConvert { return nil, merrors.InvalidArgument("settlement fx_convert: unsupported action") } @@ -116,7 +117,7 @@ func (e *gatewayProviderSettlementExecutor) resolveGateway(ctx context.Context, settlementCount := 0 for i := range items { item := items[i] - if item == nil || model.ParseRail(string(item.Rail)) != model.RailProviderSettlement || !item.IsEnabled { + if item == nil || model.ParseRail(string(item.Rail)) != discovery.RailProviderSettlement || !item.IsEnabled { continue } settlementCount++ diff --git a/api/payments/orchestrator/internal/service/orchestrator/settlement_executor_test.go b/api/payments/orchestrator/internal/service/orchestrator/settlement_executor_test.go index 1d6cf0c6..e3e2a7d4 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/settlement_executor_test.go +++ b/api/payments/orchestrator/internal/service/orchestrator/settlement_executor_test.go @@ -2,6 +2,7 @@ package orchestrator import ( "context" + "github.com/tech/sendico/pkg/discovery" "strings" "testing" @@ -38,7 +39,7 @@ func TestGatewayProviderSettlementExecutor_ExecuteProviderSettlement_SubmitsTran { ID: "payment_gateway_settlement", InstanceID: "payment_gateway_settlement", - Rail: model.RailProviderSettlement, + Rail: discovery.RailProviderSettlement, InvokeURI: "grpc://tgsettle-gateway", IsEnabled: true, }, @@ -78,8 +79,8 @@ func TestGatewayProviderSettlementExecutor_ExecuteProviderSettlement_SubmitsTran Step: xplan.Step{ StepRef: "hop_2_settlement_fx_convert", StepCode: "hop.2.settlement.fx_convert", - Action: model.RailOperationFXConvert, - Rail: model.RailProviderSettlement, + Action: discovery.RailOperationFXConvert, + Rail: discovery.RailProviderSettlement, Gateway: "payment_gateway_settlement", InstanceID: "payment_gateway_settlement", }, @@ -121,7 +122,7 @@ func TestGatewayProviderSettlementExecutor_ExecuteProviderSettlement_SubmitsTran if got, want := submitReq.GetMetadata()[settlementMetadataQuoteRef], "quote-1"; got != want { t.Fatalf("quote_ref metadata mismatch: got=%q want=%q", got, want) } - if got, want := submitReq.GetMetadata()[settlementMetadataOutgoingLeg], string(model.RailProviderSettlement); got != want { + if got, want := submitReq.GetMetadata()[settlementMetadataOutgoingLeg], string(discovery.RailProviderSettlement); got != want { t.Fatalf("outgoing_leg metadata mismatch: got=%q want=%q", got, want) } if len(out.StepExecution.ExternalRefs) != 2 { @@ -144,7 +145,7 @@ func TestGatewayProviderSettlementExecutor_ExecuteProviderSettlement_MissingSett { ID: "payment_gateway_settlement", InstanceID: "payment_gateway_settlement", - Rail: model.RailProviderSettlement, + Rail: discovery.RailProviderSettlement, InvokeURI: "grpc://tgsettle-gateway", IsEnabled: true, }, @@ -171,8 +172,8 @@ func TestGatewayProviderSettlementExecutor_ExecuteProviderSettlement_MissingSett Step: xplan.Step{ StepRef: "hop_2_settlement_fx_convert", StepCode: "hop.2.settlement.fx_convert", - Action: model.RailOperationFXConvert, - Rail: model.RailProviderSettlement, + Action: discovery.RailOperationFXConvert, + Rail: discovery.RailProviderSettlement, Gateway: "payment_gateway_settlement", InstanceID: "payment_gateway_settlement", }, diff --git a/api/payments/quotation/internal/service/plan/plan_builder_endpoints.go b/api/payments/quotation/internal/service/plan/plan_builder_endpoints.go index 1e136126..63823f9b 100644 --- a/api/payments/quotation/internal/service/plan/plan_builder_endpoints.go +++ b/api/payments/quotation/internal/service/plan/plan_builder_endpoints.go @@ -1,6 +1,7 @@ package plan import ( + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/tech/sendico/payments/storage/model" @@ -9,24 +10,24 @@ import ( func railFromEndpoint(endpoint model.PaymentEndpoint, attrs map[string]string, isSource bool) (model.Rail, string, error) { override := railOverrideFromAttributes(attrs, isSource) - if override != model.RailUnspecified { + if override != discovery.RailUnspecified { return override, networkFromEndpoint(endpoint), nil } switch endpoint.Type { case model.EndpointTypeLedger: - return model.RailLedger, "", nil + return discovery.RailLedger, "", nil case model.EndpointTypeManagedWallet, model.EndpointTypeExternalChain: - return model.RailCrypto, networkFromEndpoint(endpoint), nil + return discovery.RailCrypto, networkFromEndpoint(endpoint), nil case model.EndpointTypeCard: - return model.RailCardPayout, "", nil + return discovery.RailCardPayout, "", nil default: - return model.RailUnspecified, "", merrors.InvalidArgument("plan builder: unsupported payment endpoint") + return discovery.RailUnspecified, "", merrors.InvalidArgument("plan builder: unsupported payment endpoint") } } func railOverrideFromAttributes(attrs map[string]string, isSource bool) model.Rail { if len(attrs) == 0 { - return model.RailUnspecified + return discovery.RailUnspecified } keys := []string{"source_rail", "sourceRail"} if !isSource { @@ -41,45 +42,45 @@ func railOverrideFromAttributes(attrs map[string]string, isSource bool) model.Ra continue } rail := parseRailValue(value) - if rail != model.RailUnspecified { + if rail != discovery.RailUnspecified { return rail } } - return model.RailUnspecified + return discovery.RailUnspecified } func parseRailValue(value string) model.Rail { val := strings.ToUpper(strings.TrimSpace(value)) switch val { - case string(model.RailCrypto): - return model.RailCrypto - case string(model.RailProviderSettlement): - return model.RailProviderSettlement - case string(model.RailLedger): - return model.RailLedger - case string(model.RailCardPayout): - return model.RailCardPayout - case string(model.RailFiatOnRamp): - return model.RailFiatOnRamp + case string(discovery.RailCrypto): + return discovery.RailCrypto + case string(discovery.RailProviderSettlement): + return discovery.RailProviderSettlement + case string(discovery.RailLedger): + return discovery.RailLedger + case string(discovery.RailCardPayout): + return discovery.RailCardPayout + case string(discovery.RailFiatOnRamp): + return discovery.RailFiatOnRamp default: - return model.RailUnspecified + return discovery.RailUnspecified } } func gatewayNetworkForRail(rail model.Rail, sourceRail, destRail model.Rail, sourceNetwork, destNetwork string) string { switch rail { - case model.RailCrypto: - if sourceRail == model.RailCrypto { + case discovery.RailCrypto: + if sourceRail == discovery.RailCrypto { return strings.ToUpper(strings.TrimSpace(sourceNetwork)) } - if destRail == model.RailCrypto { + if destRail == discovery.RailCrypto { return strings.ToUpper(strings.TrimSpace(destNetwork)) } - case model.RailFiatOnRamp: - if sourceRail == model.RailFiatOnRamp { + case discovery.RailFiatOnRamp: + if sourceRail == discovery.RailFiatOnRamp { return strings.ToUpper(strings.TrimSpace(sourceNetwork)) } - if destRail == model.RailFiatOnRamp { + if destRail == discovery.RailFiatOnRamp { return strings.ToUpper(strings.TrimSpace(destNetwork)) } } diff --git a/api/payments/quotation/internal/service/plan/plan_builder_gateways.go b/api/payments/quotation/internal/service/plan/plan_builder_gateways.go index e982dfba..d01ae3b1 100644 --- a/api/payments/quotation/internal/service/plan/plan_builder_gateways.go +++ b/api/payments/quotation/internal/service/plan/plan_builder_gateways.go @@ -2,6 +2,7 @@ package plan import ( "context" + "github.com/tech/sendico/pkg/discovery" "sort" "strings" @@ -73,7 +74,7 @@ const ( func sendDirectionForRail(rail model.Rail) sendDirection { switch rail { - case model.RailFiatOnRamp: + case discovery.RailFiatOnRamp: return sendDirectionIn default: return sendDirectionOut diff --git a/api/payments/quotation/internal/service/plan/plan_builder_plans.go b/api/payments/quotation/internal/service/plan/plan_builder_plans.go index df2e6d2e..603b9d85 100644 --- a/api/payments/quotation/internal/service/plan/plan_builder_plans.go +++ b/api/payments/quotation/internal/service/plan/plan_builder_plans.go @@ -1,6 +1,7 @@ package plan import ( + "github.com/tech/sendico/pkg/discovery" "time" "github.com/tech/sendico/payments/storage/model" @@ -15,8 +16,8 @@ func buildFXConversionPlan(payment *model.Payment) (*model.PaymentPlan, error) { } step := &model.PaymentStep{ StepID: "fx_convert", - Rail: model.RailLedger, - Action: model.RailOperationFXConvert, + Rail: discovery.RailLedger, + Action: discovery.RailOperationFXConvert, ReportVisibility: model.ReportVisibilityUser, CommitPolicy: model.CommitPolicyImmediate, Amount: cloneMoney(payment.Intent.Amount), diff --git a/api/payments/quotation/internal/service/quotation/gateway_funding_profile/funding_profile_resolver_static.go b/api/payments/quotation/internal/service/quotation/gateway_funding_profile/funding_profile_resolver_static.go index 07155172..62cdfc43 100644 --- a/api/payments/quotation/internal/service/quotation/gateway_funding_profile/funding_profile_resolver_static.go +++ b/api/payments/quotation/internal/service/quotation/gateway_funding_profile/funding_profile_resolver_static.go @@ -2,6 +2,7 @@ package gateway_funding_profile import ( "context" + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/tech/sendico/payments/quotation/internal/shared" @@ -120,7 +121,7 @@ func (r *StaticFundingProfileResolver) ResolveGatewayFundingProfile( strings.TrimSpace(profile.InstanceID), strings.TrimSpace(req.InstanceID), ) - if profile.Rail == model.RailUnspecified { + if profile.Rail == discovery.RailUnspecified { profile.Rail = req.Rail } if strings.TrimSpace(profile.Network) == "" { diff --git a/api/payments/quotation/internal/service/quotation/gateway_resolution.go b/api/payments/quotation/internal/service/quotation/gateway_resolution.go index 7c82c703..d7a08726 100644 --- a/api/payments/quotation/internal/service/quotation/gateway_resolution.go +++ b/api/payments/quotation/internal/service/quotation/gateway_resolution.go @@ -2,6 +2,7 @@ package quotation import ( "context" + "github.com/tech/sendico/pkg/discovery" "sort" "strings" @@ -16,7 +17,7 @@ import ( func (s *Service) resolveChainGatewayClient(ctx context.Context, network string, amount *paymenttypes.Money, actions []model.RailOperation, instanceID string, paymentRef string) (chainclient.Client, *model.GatewayInstanceDescriptor, error) { if s.deps.gatewayRegistry != nil && s.deps.gatewayInvokeResolver != nil { - entry, err := selectGatewayForActions(ctx, s.deps.gatewayRegistry, model.RailCrypto, network, amount, actions, instanceID, sendDirectionForRail(model.RailCrypto)) + entry, err := selectGatewayForActions(ctx, s.deps.gatewayRegistry, discovery.RailCrypto, network, amount, actions, instanceID, sendDirectionForRail(discovery.RailCrypto)) if err != nil { return nil, nil, err } @@ -68,7 +69,7 @@ func selectGatewayForActions(ctx context.Context, registry GatewayRegistry, rail return nil, merrors.NoData("no gateway instances available") } if len(actions) == 0 { - actions = []model.RailOperation{model.RailOperationSend} + actions = []model.RailOperation{discovery.RailOperationSend} } currency := "" @@ -104,7 +105,7 @@ func selectGatewayForActions(ctx context.Context, registry GatewayRegistry, rail } if len(eligible) == 0 { - action := model.RailOperationUnspecified + var action model.RailOperation = discovery.RailOperationUnspecified if len(actions) > 0 { action = actions[0] } diff --git a/api/payments/quotation/internal/service/quotation/graph_path_finder/adjacency.go b/api/payments/quotation/internal/service/quotation/graph_path_finder/adjacency.go index 278be422..9b522123 100644 --- a/api/payments/quotation/internal/service/quotation/graph_path_finder/adjacency.go +++ b/api/payments/quotation/internal/service/quotation/graph_path_finder/adjacency.go @@ -1,6 +1,7 @@ package graph_path_finder import ( + "github.com/tech/sendico/pkg/discovery" "sort" "strings" @@ -12,7 +13,7 @@ func buildAdjacency(edges []Edge, network string) map[model.Rail][]normalizedEdg for _, edge := range edges { from := normalizeRail(edge.FromRail) to := normalizeRail(edge.ToRail) - if from == model.RailUnspecified || to == model.RailUnspecified { + if from == discovery.RailUnspecified || to == discovery.RailUnspecified { continue } en := normalizeNetwork(edge.Network) @@ -65,8 +66,8 @@ func networkPriority(edgeNetwork, requested string) int { func normalizeRail(value model.Rail) model.Rail { normalized := model.ParseRail(string(value)) - if normalized == model.RailUnspecified { - return model.RailUnspecified + if normalized == discovery.RailUnspecified { + return discovery.RailUnspecified } return normalized } diff --git a/api/payments/quotation/internal/service/quotation/graph_path_finder/search.go b/api/payments/quotation/internal/service/quotation/graph_path_finder/search.go index 56bded34..987180c4 100644 --- a/api/payments/quotation/internal/service/quotation/graph_path_finder/search.go +++ b/api/payments/quotation/internal/service/quotation/graph_path_finder/search.go @@ -1,6 +1,9 @@ package graph_path_finder -import "github.com/tech/sendico/payments/storage/model" +import ( + "github.com/tech/sendico/payments/storage/model" + "github.com/tech/sendico/pkg/discovery" +) func shortestPath( source model.Rail, @@ -75,7 +78,7 @@ func shortestPath( } func nextRail(best map[model.Rail]score, visited map[model.Rail]bool) (model.Rail, bool) { - selected := model.RailUnspecified + var selected model.Rail = discovery.RailUnspecified selectedScore := score{} found := false for rail, railScore := range best { diff --git a/api/payments/quotation/internal/service/quotation/graph_path_finder/service.go b/api/payments/quotation/internal/service/quotation/graph_path_finder/service.go index c93397ce..bb35981e 100644 --- a/api/payments/quotation/internal/service/quotation/graph_path_finder/service.go +++ b/api/payments/quotation/internal/service/quotation/graph_path_finder/service.go @@ -2,6 +2,7 @@ package graph_path_finder import ( "github.com/tech/sendico/payments/storage/model" + "github.com/tech/sendico/pkg/discovery" "github.com/tech/sendico/pkg/merrors" ) @@ -26,10 +27,10 @@ type score struct { func (f *GraphPathFinder) Find(in FindInput) (*Path, error) { source := normalizeRail(in.SourceRail) destination := normalizeRail(in.DestinationRail) - if source == model.RailUnspecified { + if source == discovery.RailUnspecified { return nil, merrors.InvalidArgument("source_rail is required") } - if destination == model.RailUnspecified { + if destination == discovery.RailUnspecified { return nil, merrors.InvalidArgument("destination_rail is required") } if source == destination { diff --git a/api/payments/quotation/internal/service/quotation/graph_path_finder/service_network_test.go b/api/payments/quotation/internal/service/quotation/graph_path_finder/service_network_test.go index fa71e088..61f5e171 100644 --- a/api/payments/quotation/internal/service/quotation/graph_path_finder/service_network_test.go +++ b/api/payments/quotation/internal/service/quotation/graph_path_finder/service_network_test.go @@ -1,22 +1,21 @@ package graph_path_finder import ( + "github.com/tech/sendico/pkg/discovery" "testing" - - "github.com/tech/sendico/payments/storage/model" ) func TestFind_NetworkFiltersEdges(t *testing.T) { finder := New() path, err := finder.Find(FindInput{ - SourceRail: model.RailCrypto, - DestinationRail: model.RailCardPayout, + SourceRail: discovery.RailCrypto, + DestinationRail: discovery.RailCardPayout, Network: "TRON", Edges: []Edge{ - {FromRail: model.RailCrypto, ToRail: model.RailLedger, Network: "ETH"}, - {FromRail: model.RailCrypto, ToRail: model.RailLedger, Network: "TRON"}, - {FromRail: model.RailLedger, ToRail: model.RailCardPayout, Network: "TRON"}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger, Network: "ETH"}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger, Network: "TRON"}, + {FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout, Network: "TRON"}, }, }) if err != nil { @@ -33,14 +32,14 @@ func TestFind_PrefersExactNetworkOverWildcard(t *testing.T) { finder := New() path, err := finder.Find(FindInput{ - SourceRail: model.RailCrypto, - DestinationRail: model.RailCardPayout, + SourceRail: discovery.RailCrypto, + DestinationRail: discovery.RailCardPayout, Network: "TRON", Edges: []Edge{ - {FromRail: model.RailCrypto, ToRail: model.RailLedger, Network: ""}, - {FromRail: model.RailLedger, ToRail: model.RailCardPayout, Network: ""}, - {FromRail: model.RailCrypto, ToRail: model.RailProviderSettlement, Network: "TRON"}, - {FromRail: model.RailProviderSettlement, ToRail: model.RailCardPayout, Network: "TRON"}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger, Network: ""}, + {FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout, Network: ""}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailProviderSettlement, Network: "TRON"}, + {FromRail: discovery.RailProviderSettlement, ToRail: discovery.RailCardPayout, Network: "TRON"}, }, }) if err != nil { @@ -54,13 +53,13 @@ func TestFind_DeterministicTieBreak(t *testing.T) { finder := New() path, err := finder.Find(FindInput{ - SourceRail: model.RailCrypto, - DestinationRail: model.RailCardPayout, + SourceRail: discovery.RailCrypto, + DestinationRail: discovery.RailCardPayout, Edges: []Edge{ - {FromRail: model.RailCrypto, ToRail: model.RailLedger}, - {FromRail: model.RailLedger, ToRail: model.RailCardPayout}, - {FromRail: model.RailCrypto, ToRail: model.RailProviderSettlement}, - {FromRail: model.RailProviderSettlement, ToRail: model.RailCardPayout}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger}, + {FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailProviderSettlement}, + {FromRail: discovery.RailProviderSettlement, ToRail: discovery.RailCardPayout}, }, }) if err != nil { @@ -75,13 +74,13 @@ func TestFind_IgnoresInvalidEdges(t *testing.T) { finder := New() path, err := finder.Find(FindInput{ - SourceRail: model.RailCrypto, - DestinationRail: model.RailCardPayout, + SourceRail: discovery.RailCrypto, + DestinationRail: discovery.RailCardPayout, Edges: []Edge{ - {FromRail: model.RailUnspecified, ToRail: model.RailLedger}, - {FromRail: model.RailCrypto, ToRail: model.RailUnspecified}, - {FromRail: model.RailCrypto, ToRail: model.RailLedger}, - {FromRail: model.RailLedger, ToRail: model.RailCardPayout}, + {FromRail: discovery.RailUnspecified, ToRail: discovery.RailLedger}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailUnspecified}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger}, + {FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout}, }, }) if err != nil { diff --git a/api/payments/quotation/internal/service/quotation/graph_path_finder/service_test.go b/api/payments/quotation/internal/service/quotation/graph_path_finder/service_test.go index 001fd148..c259fb7d 100644 --- a/api/payments/quotation/internal/service/quotation/graph_path_finder/service_test.go +++ b/api/payments/quotation/internal/service/quotation/graph_path_finder/service_test.go @@ -2,6 +2,7 @@ package graph_path_finder import ( "errors" + "github.com/tech/sendico/pkg/discovery" "testing" "github.com/tech/sendico/payments/storage/model" @@ -12,14 +13,14 @@ func TestFind_ValidatesInput(t *testing.T) { finder := New() _, err := finder.Find(FindInput{ - DestinationRail: model.RailCardPayout, + DestinationRail: discovery.RailCardPayout, }) if !errors.Is(err, merrors.ErrInvalidArg) { t.Fatalf("expected invalid argument for missing source rail, got %v", err) } _, err = finder.Find(FindInput{ - SourceRail: model.RailCrypto, + SourceRail: discovery.RailCrypto, }) if !errors.Is(err, merrors.ErrInvalidArg) { t.Fatalf("expected invalid argument for missing destination rail, got %v", err) @@ -30,8 +31,8 @@ func TestFind_SourceEqualsDestination(t *testing.T) { finder := New() path, err := finder.Find(FindInput{ - SourceRail: model.RailCrypto, - DestinationRail: model.RailCrypto, + SourceRail: discovery.RailCrypto, + DestinationRail: discovery.RailCrypto, }) if err != nil { t.Fatalf("unexpected error: %v", err) @@ -51,11 +52,11 @@ func TestFind_FindsIndirectPath(t *testing.T) { finder := New() path, err := finder.Find(FindInput{ - SourceRail: model.RailCrypto, - DestinationRail: model.RailCardPayout, + SourceRail: discovery.RailCrypto, + DestinationRail: discovery.RailCardPayout, Edges: []Edge{ - {FromRail: model.RailCrypto, ToRail: model.RailLedger}, - {FromRail: model.RailLedger, ToRail: model.RailCardPayout}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger}, + {FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout}, }, }) if err != nil { @@ -72,12 +73,12 @@ func TestFind_PrefersShortestPath(t *testing.T) { finder := New() path, err := finder.Find(FindInput{ - SourceRail: model.RailCrypto, - DestinationRail: model.RailCardPayout, + SourceRail: discovery.RailCrypto, + DestinationRail: discovery.RailCardPayout, Edges: []Edge{ - {FromRail: model.RailCrypto, ToRail: model.RailCardPayout}, - {FromRail: model.RailCrypto, ToRail: model.RailLedger}, - {FromRail: model.RailLedger, ToRail: model.RailCardPayout}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailCardPayout}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger}, + {FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout}, }, }) if err != nil { @@ -91,12 +92,12 @@ func TestFind_HandlesCycles(t *testing.T) { finder := New() path, err := finder.Find(FindInput{ - SourceRail: model.RailCrypto, - DestinationRail: model.RailCardPayout, + SourceRail: discovery.RailCrypto, + DestinationRail: discovery.RailCardPayout, Edges: []Edge{ - {FromRail: model.RailCrypto, ToRail: model.RailLedger}, - {FromRail: model.RailLedger, ToRail: model.RailCrypto}, - {FromRail: model.RailLedger, ToRail: model.RailCardPayout}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger}, + {FromRail: discovery.RailLedger, ToRail: discovery.RailCrypto}, + {FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout}, }, }) if err != nil { @@ -110,10 +111,10 @@ func TestFind_ReturnsErrorWhenPathUnavailable(t *testing.T) { finder := New() _, err := finder.Find(FindInput{ - SourceRail: model.RailCrypto, - DestinationRail: model.RailCardPayout, + SourceRail: discovery.RailCrypto, + DestinationRail: discovery.RailCardPayout, Edges: []Edge{ - {FromRail: model.RailCrypto, ToRail: model.RailLedger}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger}, }, }) if !errors.Is(err, merrors.ErrInvalidArg) { diff --git a/api/payments/quotation/internal/service/quotation/managed_wallet_network_resolver.go b/api/payments/quotation/internal/service/quotation/managed_wallet_network_resolver.go index 3f992603..32ebcad6 100644 --- a/api/payments/quotation/internal/service/quotation/managed_wallet_network_resolver.go +++ b/api/payments/quotation/internal/service/quotation/managed_wallet_network_resolver.go @@ -3,10 +3,10 @@ package quotation import ( "context" "errors" + "github.com/tech/sendico/pkg/discovery" "sort" "strings" - "github.com/tech/sendico/payments/storage/model" chainpkg "github.com/tech/sendico/pkg/chain" "github.com/tech/sendico/pkg/merrors" paymenttypes "github.com/tech/sendico/pkg/payments/types" @@ -165,7 +165,7 @@ func (r *managedWalletNetworkResolver) listDiscoveredGatewayCandidates(ctx conte candidates := make([]discoveredGatewayCandidate, 0, len(entries)) seenInvokeURI := map[string]struct{}{} for _, entry := range entries { - if entry == nil || !entry.IsEnabled || entry.Rail != model.RailCrypto { + if entry == nil || !entry.IsEnabled || entry.Rail != discovery.RailCrypto { continue } invokeURI := strings.TrimSpace(entry.InvokeURI) diff --git a/api/payments/quotation/internal/service/quotation/managed_wallet_network_resolver_test.go b/api/payments/quotation/internal/service/quotation/managed_wallet_network_resolver_test.go index c80699fa..6e1b9de0 100644 --- a/api/payments/quotation/internal/service/quotation/managed_wallet_network_resolver_test.go +++ b/api/payments/quotation/internal/service/quotation/managed_wallet_network_resolver_test.go @@ -3,6 +3,7 @@ package quotation import ( "context" "errors" + "github.com/tech/sendico/pkg/discovery" "reflect" "testing" @@ -37,8 +38,8 @@ func TestManagedWalletNetworkResolver_ResolvesAcrossDiscoveredGateways(t *testin resolver := &managedWalletNetworkResolver{ gatewayRegistry: fakeGatewayRegistry{ items: []*model.GatewayInstanceDescriptor{ - {ID: "gw-a", Rail: model.RailCrypto, IsEnabled: true, InvokeURI: "gw-a:50053"}, - {ID: "gw-b", Rail: model.RailCrypto, IsEnabled: true, InvokeURI: "gw-b:50053"}, + {ID: "gw-a", Rail: discovery.RailCrypto, IsEnabled: true, InvokeURI: "gw-a:50053"}, + {ID: "gw-b", Rail: discovery.RailCrypto, IsEnabled: true, InvokeURI: "gw-b:50053"}, }, }, gatewayInvokeResolver: invokeResolver, diff --git a/api/payments/quotation/internal/service/quotation/options.go b/api/payments/quotation/internal/service/quotation/options.go index dc9d8147..5c94253f 100644 --- a/api/payments/quotation/internal/service/quotation/options.go +++ b/api/payments/quotation/internal/service/quotation/options.go @@ -200,7 +200,7 @@ func (r *discoveryGatewayRegistry) List(_ context.Context) ([]*model.GatewayInst items := make([]*model.GatewayInstanceDescriptor, 0, len(entries)) for _, entry := range entries { railID := railFromDiscovery(entry.Rail) - if railID == model.RailUnspecified { + if railID == discovery.RailUnspecified { continue } operations := operationsFromDiscovery(entry.Operations) @@ -223,17 +223,17 @@ func (r *discoveryGatewayRegistry) List(_ context.Context) ([]*model.GatewayInst func railFromDiscovery(value string) model.Rail { switch discovery.NormalizeRail(value) { case discovery.RailCrypto: - return model.RailCrypto + return discovery.RailCrypto case discovery.RailProviderSettlement: - return model.RailProviderSettlement + return discovery.RailProviderSettlement case discovery.RailLedger: - return model.RailLedger + return discovery.RailLedger case discovery.RailCardPayout: - return model.RailCardPayout + return discovery.RailCardPayout case discovery.RailFiatOnRamp: - return model.RailFiatOnRamp + return discovery.RailFiatOnRamp default: - return model.RailUnspecified + return discovery.RailUnspecified } } diff --git a/api/payments/quotation/internal/service/quotation/quotation_service_v2/service_e2e_test.go b/api/payments/quotation/internal/service/quotation/quotation_service_v2/service_e2e_test.go index 3158bf26..43e21534 100644 --- a/api/payments/quotation/internal/service/quotation/quotation_service_v2/service_e2e_test.go +++ b/api/payments/quotation/internal/service/quotation/quotation_service_v2/service_e2e_test.go @@ -3,6 +3,7 @@ package quotation_service_v2 import ( "context" "fmt" + "github.com/tech/sendico/pkg/discovery" "strings" "testing" "time" @@ -522,7 +523,7 @@ func TestQuotePayment_SelectsEligibleGatewaysAndIgnoresIrrelevant(t *testing.T) { ID: "crypto-disabled", InstanceID: "crypto-disabled", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -533,7 +534,7 @@ func TestQuotePayment_SelectsEligibleGatewaysAndIgnoresIrrelevant(t *testing.T) { ID: "crypto-network-mismatch", InstanceID: "crypto-network-mismatch", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "ETH", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -544,7 +545,7 @@ func TestQuotePayment_SelectsEligibleGatewaysAndIgnoresIrrelevant(t *testing.T) { ID: "crypto-currency-mismatch", InstanceID: "crypto-currency-mismatch", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON", Currencies: []string{"EUR"}, Capabilities: model.RailCapabilities{ @@ -555,7 +556,7 @@ func TestQuotePayment_SelectsEligibleGatewaysAndIgnoresIrrelevant(t *testing.T) { ID: "crypto-gw-1", InstanceID: "crypto-gw-1", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -566,7 +567,7 @@ func TestQuotePayment_SelectsEligibleGatewaysAndIgnoresIrrelevant(t *testing.T) { ID: "payout-disabled", InstanceID: "payout-disabled", - Rail: model.RailCardPayout, + Rail: discovery.RailCardPayout, Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ CanPayOut: true, @@ -576,7 +577,7 @@ func TestQuotePayment_SelectsEligibleGatewaysAndIgnoresIrrelevant(t *testing.T) { ID: "payout-currency-mismatch", InstanceID: "payout-currency-mismatch", - Rail: model.RailCardPayout, + Rail: discovery.RailCardPayout, Currencies: []string{"EUR"}, Capabilities: model.RailCapabilities{ CanPayOut: true, @@ -586,7 +587,7 @@ func TestQuotePayment_SelectsEligibleGatewaysAndIgnoresIrrelevant(t *testing.T) { ID: "payout-gw-1", InstanceID: "payout-gw-1", - Rail: model.RailCardPayout, + Rail: discovery.RailCardPayout, Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ CanPayOut: true, diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/compute_test.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/compute_test.go index 33a65313..a7b1be6d 100644 --- a/api/payments/quotation/internal/service/quotation/quote_computation_service/compute_test.go +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/compute_test.go @@ -3,6 +3,7 @@ package quote_computation_service import ( "context" "errors" + "github.com/tech/sendico/pkg/discovery" "strings" "testing" "time" @@ -58,10 +59,10 @@ func TestBuildPlan_BuildsStepsAndFundingGate(t *testing.T) { if len(item.Steps) != 2 { t.Fatalf("expected 2 steps, got %d", len(item.Steps)) } - if item.Steps[0].Operation != model.RailOperationMove { + if item.Steps[0].Operation != discovery.RailOperationMove { t.Fatalf("expected source operation MOVE, got %q", item.Steps[0].Operation) } - if item.Steps[1].Operation != model.RailOperationSend { + if item.Steps[1].Operation != discovery.RailOperationSend { t.Fatalf("expected destination operation SEND, got %q", item.Steps[1].Operation) } if item.Funding == nil { @@ -118,7 +119,7 @@ func TestBuildPlan_RequiresFXAddsMiddleStep(t *testing.T) { if len(planModel.Items) != 1 || len(planModel.Items[0].Steps) != 3 { t.Fatalf("expected 3 steps for FX intent") } - if got := planModel.Items[0].Steps[1].Operation; got != model.RailOperationFXConvert { + if got := planModel.Items[0].Steps[1].Operation; got != discovery.RailOperationFXConvert { t.Fatalf("expected middle step FX_CONVERT, got %q", got) } if planModel.Items[0].Route == nil { @@ -163,8 +164,8 @@ func TestBuildPlan_RequiresFXUsesSettlementCurrencyForDestinationStep(t *testing if got, want := strings.TrimSpace(last.Amount.GetCurrency()), "RUB"; got != want { t.Fatalf("unexpected destination step currency: got=%q want=%q", got, want) } - if got, want := last.Operation, model.RailOperationSend; got != want { - t.Fatalf("unexpected destination operation: got=%q want=%q", got, want) + if got := last.Operation; got != discovery.RailOperationSend { + t.Fatalf("unexpected destination operation: got=%q want=%q", got, discovery.RailOperationSend) } } @@ -226,8 +227,8 @@ func TestBuildPlan_UsesSourceAssetCurrencyForSourceStep(t *testing.T) { if got, want := strings.TrimSpace(last.Amount.GetCurrency()), "RUB"; got != want { t.Fatalf("unexpected destination step currency: got=%q want=%q", got, want) } - if got, want := steps[1].Operation, model.RailOperationFXConvert; got != want { - t.Fatalf("unexpected middle operation: got=%q want=%q", got, want) + if got := steps[1].Operation; got != discovery.RailOperationFXConvert { + t.Fatalf("unexpected middle operation: got=%q want=%q", got, discovery.RailOperationFXConvert) } } @@ -299,7 +300,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) { { ID: "crypto-disabled", InstanceID: "crypto-disabled", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -310,7 +311,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) { { ID: "crypto-network-mismatch", InstanceID: "crypto-network-mismatch", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "ETH", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -321,7 +322,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) { { ID: "crypto-currency-mismatch", InstanceID: "crypto-currency-mismatch", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON", Currencies: []string{"EUR"}, Capabilities: model.RailCapabilities{ @@ -332,7 +333,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) { { ID: "crypto-gw-1", InstanceID: "crypto-gw-1", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -343,7 +344,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) { { ID: "payout-disabled", InstanceID: "payout-disabled", - Rail: model.RailCardPayout, + Rail: discovery.RailCardPayout, Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ CanPayOut: true, @@ -353,7 +354,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) { { ID: "payout-currency-mismatch", InstanceID: "payout-currency-mismatch", - Rail: model.RailCardPayout, + Rail: discovery.RailCardPayout, Currencies: []string{"EUR"}, Capabilities: model.RailCapabilities{ CanPayOut: true, @@ -363,7 +364,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) { { ID: "payout-gw-1", InstanceID: "payout-gw-1", - Rail: model.RailCardPayout, + Rail: discovery.RailCardPayout, Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ CanPayOut: true, @@ -373,7 +374,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) { { ID: "provider-ignored", InstanceID: "provider-ignored", - Rail: model.RailProviderSettlement, + Rail: discovery.RailProviderSettlement, Network: "TRON", Currencies: []string{"USDT"}, IsEnabled: true, diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/gateway_selector.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/gateway_selector.go index a36c505e..89f3fbaa 100644 --- a/api/payments/quotation/internal/service/quotation/quote_computation_service/gateway_selector.go +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/gateway_selector.go @@ -2,6 +2,7 @@ package quote_computation_service import ( "context" + "github.com/tech/sendico/pkg/discovery" "sort" "strings" @@ -61,7 +62,7 @@ func (s *QuoteComputationService) resolveStepGateways( continue } - if step.Rail == model.RailLedger { + if step.Rail == discovery.RailLedger { step.GatewayID = "internal" step.GatewayInvokeURI = "" @@ -201,7 +202,7 @@ func parseDecimalAmount(m *moneyv1.Money) (decimal.Decimal, error) { func networkForGatewaySelection(rail model.Rail, routeNetwork string) string { switch rail { - case model.RailCrypto, model.RailProviderSettlement, model.RailFiatOnRamp: + case discovery.RailCrypto, discovery.RailProviderSettlement, discovery.RailFiatOnRamp: return strings.ToUpper(strings.TrimSpace(routeNetwork)) default: return "" diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/gateway_selector_test.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/gateway_selector_test.go index 5658713e..587001c0 100644 --- a/api/payments/quotation/internal/service/quotation/quote_computation_service/gateway_selector_test.go +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/gateway_selector_test.go @@ -2,6 +2,7 @@ package quote_computation_service import ( "context" + "github.com/tech/sendico/pkg/discovery" "testing" "github.com/tech/sendico/payments/storage/model" @@ -23,7 +24,7 @@ func TestResolveStepGateways_FallsBackToInvokeURI(t *testing.T) { ID: "aaa", InstanceID: "inst-a", InvokeURI: "grpc://gw-a:50051", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -35,7 +36,7 @@ func TestResolveStepGateways_FallsBackToInvokeURI(t *testing.T) { ID: "bbb", InstanceID: "inst-b", InvokeURI: "grpc://gw-b:50051", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -48,8 +49,8 @@ func TestResolveStepGateways_FallsBackToInvokeURI(t *testing.T) { steps := []*QuoteComputationStep{ { StepID: "i0.destination", - Rail: model.RailCrypto, - Operation: model.RailOperationExternalCredit, + Rail: discovery.RailCrypto, + Operation: discovery.RailOperationExternalCredit, GatewayID: "legacy-id", InstanceID: "legacy-instance", GatewayInvokeURI: "grpc://gw-b:50051", @@ -78,7 +79,7 @@ func TestResolveStepGateways_FallsBackToGatewayIDWhenInstanceChanges(t *testing. ID: "aaa", InstanceID: "inst-a", InvokeURI: "grpc://gw-a:50051", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -90,7 +91,7 @@ func TestResolveStepGateways_FallsBackToGatewayIDWhenInstanceChanges(t *testing. ID: "crypto_rail_gateway_tron", InstanceID: "inst-new", InvokeURI: "grpc://gw-tron:50051", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -103,8 +104,8 @@ func TestResolveStepGateways_FallsBackToGatewayIDWhenInstanceChanges(t *testing. steps := []*QuoteComputationStep{ { StepID: "i0.destination", - Rail: model.RailCrypto, - Operation: model.RailOperationExternalCredit, + Rail: discovery.RailCrypto, + Operation: discovery.RailOperationExternalCredit, GatewayID: "crypto_rail_gateway_tron", InstanceID: "inst-old", Amount: &moneyv1.Money{Currency: "USDT", Amount: "10"}, diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/managed_wallet_network_test.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/managed_wallet_network_test.go index 4fad726d..8e0ee851 100644 --- a/api/payments/quotation/internal/service/quotation/quote_computation_service/managed_wallet_network_test.go +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/managed_wallet_network_test.go @@ -3,6 +3,7 @@ package quote_computation_service import ( "context" "errors" + "github.com/tech/sendico/pkg/discovery" "testing" "github.com/tech/sendico/payments/quotation/internal/service/quotation/transfer_intent_hydrator" @@ -25,7 +26,7 @@ func TestBuildPlan_ResolvesManagedWalletNetworkFromResolver(t *testing.T) { { ID: "crypto-arbitrum", InstanceID: "crypto-arbitrum", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "ARBITRUM_SEPOLIA", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -36,7 +37,7 @@ func TestBuildPlan_ResolvesManagedWalletNetworkFromResolver(t *testing.T) { { ID: "crypto-tron", InstanceID: "crypto-tron", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON_NILE", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -47,7 +48,7 @@ func TestBuildPlan_ResolvesManagedWalletNetworkFromResolver(t *testing.T) { { ID: "card-gw", InstanceID: "card-gw", - Rail: model.RailCardPayout, + Rail: discovery.RailCardPayout, Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ CanPayOut: true, @@ -101,7 +102,7 @@ func TestBuildPlan_ManagedWalletNetworkResolverCachesByWalletRef(t *testing.T) { { ID: "crypto-tron", InstanceID: "crypto-tron", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON_NILE", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -112,7 +113,7 @@ func TestBuildPlan_ManagedWalletNetworkResolverCachesByWalletRef(t *testing.T) { { ID: "card-gw", InstanceID: "card-gw", - Rail: model.RailCardPayout, + Rail: discovery.RailCardPayout, Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ CanPayOut: true, @@ -167,7 +168,7 @@ func TestBuildPlan_ResolvesManagedWalletAssetTokenForSourceCurrency(t *testing.T { ID: "crypto-tron", InstanceID: "crypto-tron", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON_NILE", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -178,16 +179,16 @@ func TestBuildPlan_ResolvesManagedWalletAssetTokenForSourceCurrency(t *testing.T { ID: "fx-tron", InstanceID: "fx-tron", - Rail: model.RailProviderSettlement, + Rail: discovery.RailProviderSettlement, Network: "TRON_NILE", Currencies: []string{"USDT", "RUB"}, - Operations: []model.RailOperation{model.RailOperationFXConvert}, + Operations: []model.RailOperation{discovery.RailOperationFXConvert}, IsEnabled: true, }, { ID: "card-gw", InstanceID: "card-gw", - Rail: model.RailCardPayout, + Rail: discovery.RailCardPayout, Currencies: []string{"RUB"}, Capabilities: model.RailCapabilities{ CanPayOut: true, diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/planner.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/planner.go index 21043734..beb55655 100644 --- a/api/payments/quotation/internal/service/quotation/quote_computation_service/planner.go +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/planner.go @@ -3,6 +3,7 @@ package quote_computation_service import ( "context" "fmt" + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/tech/sendico/payments/quotation/internal/service/plan" @@ -217,7 +218,7 @@ func (s *QuoteComputationService) buildPlanItem( destinationGatewayFromSteps(steps), gatewayKeyForFunding(modelIntent.Attributes, destination), ) - if provider == "" && destRail == model.RailLedger { + if provider == "" && destRail == discovery.RailLedger { provider = "internal" } diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/planner_path_finding.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/planner_path_finding.go index 5d93f9d8..d7637079 100644 --- a/api/payments/quotation/internal/service/quotation/quote_computation_service/planner_path_finding.go +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/planner_path_finding.go @@ -2,6 +2,7 @@ package quote_computation_service import ( "context" + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/tech/sendico/payments/quotation/internal/service/quotation/graph_path_finder" @@ -25,13 +26,13 @@ func (s *QuoteComputationService) resolveRouteRails( zap.String("network", network), ) - if sourceRail == model.RailUnspecified { + if sourceRail == discovery.RailUnspecified { s.logger.Warn("Route rails resolution failed: source rail is unspecified") return nil, merrors.InvalidArgument("source rail is required") } - if destinationRail == model.RailUnspecified { + if destinationRail == discovery.RailUnspecified { s.logger.Warn("Route rails resolution failed: destination rail is unspecified") return nil, merrors.InvalidArgument("destination rail is required") @@ -185,7 +186,7 @@ func (s *QuoteComputationService) routeGraphEdges(ctx context.Context) ([]graph_ from := model.ParseRail(string(route.FromRail)) to := model.ParseRail(string(route.ToRail)) - if from == model.RailUnspecified || to == model.RailUnspecified { + if from == discovery.RailUnspecified || to == discovery.RailUnspecified { continue } @@ -210,7 +211,7 @@ func fallbackRouteRails(sourceRail, destinationRail model.Rail) []model.Rail { } if requiresTransitBridgeStep(sourceRail, destinationRail) { - return []model.Rail{sourceRail, model.RailLedger, destinationRail} + return []model.Rail{sourceRail, discovery.RailLedger, destinationRail} } return []model.Rail{sourceRail, destinationRail} diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/planner_path_finding_test.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/planner_path_finding_test.go index 0acc2868..bd075413 100644 --- a/api/payments/quotation/internal/service/quotation/quote_computation_service/planner_path_finding_test.go +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/planner_path_finding_test.go @@ -3,6 +3,7 @@ package quote_computation_service import ( "context" "errors" + "github.com/tech/sendico/pkg/discovery" "strings" "testing" @@ -15,15 +16,15 @@ import ( func TestBuildPlan_UsesRouteGraphPath(t *testing.T) { svc := New(nil, WithRouteStore(staticRouteStore{items: []*model.PaymentRoute{ - {FromRail: model.RailCrypto, ToRail: model.RailProviderSettlement, Network: "TRON", IsEnabled: true}, - {FromRail: model.RailProviderSettlement, ToRail: model.RailCardPayout, Network: "TRON", IsEnabled: true}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailProviderSettlement, Network: "TRON", IsEnabled: true}, + {FromRail: discovery.RailProviderSettlement, ToRail: discovery.RailCardPayout, Network: "TRON", IsEnabled: true}, }}), WithGatewayRegistry(staticGatewayRegistry{ items: []*model.GatewayInstanceDescriptor{ { ID: "crypto-gw", InstanceID: "crypto-gw", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -34,7 +35,7 @@ func TestBuildPlan_UsesRouteGraphPath(t *testing.T) { { ID: "provider-gw", InstanceID: "provider-gw", - Rail: model.RailProviderSettlement, + Rail: discovery.RailProviderSettlement, Network: "TRON", Currencies: []string{"USDT"}, IsEnabled: true, @@ -42,7 +43,7 @@ func TestBuildPlan_UsesRouteGraphPath(t *testing.T) { { ID: "card-gw", InstanceID: "card-gw", - Rail: model.RailCardPayout, + Rail: discovery.RailCardPayout, Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ CanPayOut: true, @@ -71,7 +72,7 @@ func TestBuildPlan_UsesRouteGraphPath(t *testing.T) { if got, want := len(item.Steps), 3; got != want { t.Fatalf("unexpected step count: got=%d want=%d", got, want) } - if got, want := string(item.Steps[1].Rail), string(model.RailProviderSettlement); got != want { + if got, want := string(item.Steps[1].Rail), string(discovery.RailProviderSettlement); got != want { t.Fatalf("unexpected transit rail: got=%q want=%q", got, want) } if got := strings.ToUpper(strings.TrimSpace(item.Route.GetHops()[1].GetRail())); got != "SETTLEMENT" { @@ -81,7 +82,7 @@ func TestBuildPlan_UsesRouteGraphPath(t *testing.T) { func TestBuildPlan_RouteGraphNoPathReturnsError(t *testing.T) { svc := New(nil, WithRouteStore(staticRouteStore{items: []*model.PaymentRoute{ - {FromRail: model.RailCrypto, ToRail: model.RailLedger, Network: "TRON", IsEnabled: true}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger, Network: "TRON", IsEnabled: true}, }})) orgID := bson.NewObjectID() @@ -99,16 +100,16 @@ func TestBuildPlan_RouteGraphNoPathReturnsError(t *testing.T) { func TestBuildPlan_RouteGraphPrefersDirectPath(t *testing.T) { svc := New(nil, WithRouteStore(staticRouteStore{items: []*model.PaymentRoute{ - {FromRail: model.RailCrypto, ToRail: model.RailCardPayout, Network: "TRON", IsEnabled: true}, - {FromRail: model.RailCrypto, ToRail: model.RailLedger, Network: "TRON", IsEnabled: true}, - {FromRail: model.RailLedger, ToRail: model.RailCardPayout, Network: "TRON", IsEnabled: true}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailCardPayout, Network: "TRON", IsEnabled: true}, + {FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger, Network: "TRON", IsEnabled: true}, + {FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout, Network: "TRON", IsEnabled: true}, }}), WithGatewayRegistry(staticGatewayRegistry{ items: []*model.GatewayInstanceDescriptor{ { ID: "crypto-gw", InstanceID: "crypto-gw", - Rail: model.RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON", Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ @@ -119,7 +120,7 @@ func TestBuildPlan_RouteGraphPrefersDirectPath(t *testing.T) { { ID: "card-gw", InstanceID: "card-gw", - Rail: model.RailCardPayout, + Rail: discovery.RailCardPayout, Currencies: []string{"USDT"}, Capabilities: model.RailCapabilities{ CanPayOut: true, diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/planner_steps.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/planner_steps.go index d7c3803a..a6636f7f 100644 --- a/api/payments/quotation/internal/service/quotation/quote_computation_service/planner_steps.go +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/planner_steps.go @@ -2,6 +2,7 @@ package quote_computation_service import ( "fmt" + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/tech/sendico/payments/storage/model" @@ -56,14 +57,14 @@ func buildComputationSteps( lastStepID := sourceStepID fxAssigned := false if intent.RequiresFX { - if len(rails) > 1 && rails[1] == model.RailProviderSettlement { + if len(rails) > 1 && rails[1] == discovery.RailProviderSettlement { fxAssigned = true } else { fxStepID := fmt.Sprintf("i%d.fx", index) steps = append(steps, &QuoteComputationStep{ StepID: fxStepID, - Rail: model.RailProviderSettlement, - Operation: model.RailOperationFXConvert, + Rail: discovery.RailProviderSettlement, + Operation: discovery.RailOperationFXConvert, DependsOn: []string{sourceStepID}, Amount: cloneProtoMoney(sourceAmount), Optional: false, @@ -78,14 +79,14 @@ func buildComputationSteps( for i := 1; i < len(rails)-1; i++ { rail := rails[i] stepID := fmt.Sprintf("i%d.transit%d", index, transitIndex) - operation := model.RailOperationMove - if intent.RequiresFX && !fxAssigned && rail == model.RailProviderSettlement { + var operation model.RailOperation = discovery.RailOperationMove + if intent.RequiresFX && !fxAssigned && rail == discovery.RailProviderSettlement { stepID = fmt.Sprintf("i%d.fx", index) - operation = model.RailOperationFXConvert + operation = discovery.RailOperationFXConvert fxAssigned = true } stepAmount := amount - if operation == model.RailOperationFXConvert { + if operation == discovery.RailOperationFXConvert { stepAmount = sourceAmount } steps = append(steps, &QuoteComputationStep{ @@ -120,14 +121,14 @@ func buildComputationSteps( func normalizeRouteRails(sourceRail, destinationRail model.Rail, routeRails []model.Rail) []model.Rail { if len(routeRails) == 0 { if requiresTransitBridgeStep(sourceRail, destinationRail) { - return []model.Rail{sourceRail, model.RailLedger, destinationRail} + return []model.Rail{sourceRail, discovery.RailLedger, destinationRail} } return []model.Rail{sourceRail, destinationRail} } result := make([]model.Rail, 0, len(routeRails)) for _, rail := range routeRails { - if rail == model.RailUnspecified { + if rail == discovery.RailUnspecified { continue } if len(result) > 0 && result[len(result)-1] == rail { @@ -152,13 +153,13 @@ func normalizeRouteRails(sourceRail, destinationRail model.Rail, routeRails []mo } func requiresTransitBridgeStep(sourceRail, destinationRail model.Rail) bool { - if sourceRail == model.RailUnspecified || destinationRail == model.RailUnspecified { + if sourceRail == discovery.RailUnspecified || destinationRail == discovery.RailUnspecified { return false } if sourceRail == destinationRail { return false } - if sourceRail == model.RailLedger || destinationRail == model.RailLedger { + if sourceRail == discovery.RailLedger || destinationRail == discovery.RailLedger { return false } return true @@ -166,42 +167,42 @@ func requiresTransitBridgeStep(sourceRail, destinationRail model.Rail) bool { func sourceRailForIntent(intent model.PaymentIntent) model.Rail { if intent.Source.Type == model.EndpointTypeLedger { - return model.RailLedger + return discovery.RailLedger } if intent.Source.Type == model.EndpointTypeManagedWallet || intent.Source.Type == model.EndpointTypeExternalChain { - return model.RailCrypto + return discovery.RailCrypto } - return model.RailLedger + return discovery.RailLedger } func destinationRailForIntent(intent model.PaymentIntent) model.Rail { switch intent.Destination.Type { case model.EndpointTypeCard: - return model.RailCardPayout + return discovery.RailCardPayout case model.EndpointTypeManagedWallet, model.EndpointTypeExternalChain: - return model.RailCrypto + return discovery.RailCrypto case model.EndpointTypeLedger: - return model.RailLedger + return discovery.RailLedger default: - return model.RailProviderSettlement + return discovery.RailProviderSettlement } } func sourceOperationForRail(rail model.Rail) model.RailOperation { - if rail == model.RailLedger { - return model.RailOperationMove + if rail == discovery.RailLedger { + return discovery.RailOperationMove } - return model.RailOperationExternalDebit + return discovery.RailOperationExternalDebit } func destinationOperationForRail(rail model.Rail) model.RailOperation { switch rail { - case model.RailLedger: - return model.RailOperationMove - case model.RailCardPayout: - return model.RailOperationSend + case discovery.RailLedger: + return discovery.RailOperationMove + case discovery.RailCardPayout: + return discovery.RailOperationSend default: - return model.RailOperationExternalCredit + return discovery.RailOperationExternalCredit } } diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/quote_binding_validation.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/quote_binding_validation.go index e0bfa87d..5501a9e5 100644 --- a/api/payments/quotation/internal/service/quotation/quote_computation_service/quote_binding_validation.go +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/quote_binding_validation.go @@ -1,6 +1,7 @@ package quote_computation_service import ( + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/tech/sendico/payments/storage/model" @@ -60,7 +61,7 @@ func normalizeSettlementParts(src *quotationv2.RouteSettlement) (chain, token, c } func normalizeRail(value string) string { - if rail := model.ParseRail(value); rail != model.RailUnspecified { + if rail := model.ParseRail(value); rail != discovery.RailUnspecified { return string(rail) } return strings.ToUpper(strings.TrimSpace(value)) diff --git a/api/payments/quotation/internal/service/quotation/quote_computation_service/route_spec_builder.go b/api/payments/quotation/internal/service/quotation/quote_computation_service/route_spec_builder.go index 9344263c..f566735c 100644 --- a/api/payments/quotation/internal/service/quotation/quote_computation_service/route_spec_builder.go +++ b/api/payments/quotation/internal/service/quotation/quote_computation_service/route_spec_builder.go @@ -4,6 +4,7 @@ import ( "crypto/sha256" "encoding/hex" "fmt" + "github.com/tech/sendico/pkg/discovery" "sort" "strings" @@ -116,7 +117,7 @@ func buildRouteHops(steps []*QuoteComputationStep, fallbackNetwork string) []*qu Network: normalizeNetwork(firstNonEmpty(fallbackNetwork)), Role: roleForHopIndex(i, lastIndex), } - if hop.Gateway == "" && hop.Rail == normalizeRail(string(model.RailLedger)) { + if hop.Gateway == "" && hop.Rail == normalizeRail(string(discovery.RailLedger)) { hop.Gateway = "internal" } result = append(result, hop) diff --git a/api/payments/quotation/internal/service/quotation/quote_engine.go b/api/payments/quotation/internal/service/quotation/quote_engine.go index 0327c942..ed01361f 100644 --- a/api/payments/quotation/internal/service/quotation/quote_engine.go +++ b/api/payments/quotation/internal/service/quotation/quote_engine.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "github.com/tech/sendico/pkg/discovery" "strings" "time" @@ -427,7 +428,7 @@ func (s *Service) estimateNetworkFee(ctx context.Context, intent *sharedv1.Payme if instanceID == "" { instanceID = strings.TrimSpace(intent.GetDestination().GetInstanceId()) } - client, _, err := s.resolveChainGatewayClient(ctx, network, moneyFromProto(req.Amount), []model.RailOperation{model.RailOperationSend}, instanceID, "") + client, _, err := s.resolveChainGatewayClient(ctx, network, moneyFromProto(req.Amount), []model.RailOperation{discovery.RailOperationSend}, instanceID, "") if err != nil { if errors.Is(err, merrors.ErrNoData) { s.logger.Debug("Network fee estimation skipped: gateway unavailable", zap.Error(err)) @@ -519,7 +520,7 @@ func (s *Service) requestFXQuote(ctx context.Context, orgRef string, req *quoteR } func feesRequiredForRails(sourceRail, destRail model.Rail) bool { - if sourceRail == model.RailLedger && destRail == model.RailLedger { + if sourceRail == discovery.RailLedger && destRail == discovery.RailLedger { return false } return true diff --git a/api/payments/storage/go.mod b/api/payments/storage/go.mod index 4b4b10bc..b495c1b2 100644 --- a/api/payments/storage/go.mod +++ b/api/payments/storage/go.mod @@ -12,20 +12,32 @@ require ( ) require ( + github.com/beorn7/perks v1.0.1 // indirect github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect github.com/casbin/casbin/v2 v2.135.0 // indirect github.com/casbin/govaluate v1.10.0 // indirect github.com/casbin/mongodb-adapter/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/google/uuid v1.6.0 // indirect github.com/klauspost/compress v1.18.4 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/nats-io/nats.go v1.49.0 // indirect + github.com/nats-io/nkeys v0.4.15 // indirect + github.com/nats-io/nuid v1.0.1 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.5 // indirect + github.com/prometheus/procfs v0.20.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.2.0 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect go.uber.org/multierr v1.11.0 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect golang.org/x/crypto v0.48.0 // indirect golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.41.0 // indirect golang.org/x/text v0.34.0 // indirect google.golang.org/protobuf v1.36.11 // indirect ) diff --git a/api/payments/storage/go.sum b/api/payments/storage/go.sum index 198d8f8b..d49735fa 100644 --- a/api/payments/storage/go.sum +++ b/api/payments/storage/go.sum @@ -4,6 +4,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= github.com/bmatcuk/doublestar/v4 v4.10.0 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs= github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc= @@ -53,6 +55,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c= github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54 h1:mFWunSatvkQQDhpdyuFAYwyAan3hzCuma+Pz8sqvOfg= github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= @@ -77,6 +83,14 @@ github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/nats-io/nats.go v1.49.0 h1:yh/WvY59gXqYpgl33ZI+XoVPKyut/IcEaqtsiuTJpoE= +github.com/nats-io/nats.go v1.49.0/go.mod h1:fDCn3mN5cY8HooHwE2ukiLb4p4G4ImmzvXyJt+tGwdw= +github.com/nats-io/nkeys v0.4.15 h1:JACV5jRVO9V856KOapQ7x+EY8Jo3qw1vJt/9Jpwzkk4= +github.com/nats-io/nkeys v0.4.15/go.mod h1:CpMchTXC9fxA5zrMo4KpySxNjiDVvr8ANOSZdiNfUrs= +github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= +github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -87,6 +101,16 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/procfs v0.20.0 h1:AA7aCvjxwAquZAlonN7888f2u4IN8WVeFgBi4k82M4Q= +github.com/prometheus/procfs v0.20.0/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI= github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= @@ -136,6 +160,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= @@ -171,5 +197,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/api/payments/storage/model/gateway_eligibility.go b/api/payments/storage/model/gateway_eligibility.go index 4d8d3dba..15edc362 100644 --- a/api/payments/storage/model/gateway_eligibility.go +++ b/api/payments/storage/model/gateway_eligibility.go @@ -2,6 +2,7 @@ package model import ( "fmt" + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/shopspring/decimal" @@ -110,7 +111,7 @@ func gatewayAllowsAction(operations []RailOperation, cap RailCapabilities, actio func capabilityAllowsAction(cap RailCapabilities, action RailOperation, dir GatewayDirection) bool { switch action { - case RailOperationSend: + case discovery.RailOperationSend: switch dir { case GatewayDirectionOut: return cap.CanPayOut @@ -119,7 +120,7 @@ func capabilityAllowsAction(cap RailCapabilities, action RailOperation, dir Gate default: return cap.CanPayIn || cap.CanPayOut } - case RailOperationExternalDebit, RailOperationExternalCredit: + case discovery.RailOperationExternalDebit, discovery.RailOperationExternalCredit: switch dir { case GatewayDirectionOut: return cap.CanPayOut @@ -128,13 +129,13 @@ func capabilityAllowsAction(cap RailCapabilities, action RailOperation, dir Gate default: return cap.CanPayIn || cap.CanPayOut } - case RailOperationFee: + case discovery.RailOperationFee: return cap.CanSendFee - case RailOperationObserveConfirm: + case discovery.RailOperationObserveConfirm: return cap.RequiresObserveConfirm - case RailOperationBlock: + case discovery.RailOperationBlock: return cap.CanBlock - case RailOperationRelease: + case discovery.RailOperationRelease: return cap.CanRelease default: return true @@ -143,7 +144,7 @@ func capabilityAllowsAction(cap RailCapabilities, action RailOperation, dir Gate func operationsAllowAction(operations []RailOperation, action RailOperation, dir GatewayDirection) bool { action = ParseRailOperation(string(action)) - if action == RailOperationUnspecified { + if action == discovery.RailOperationUnspecified { return false } @@ -152,20 +153,20 @@ func operationsAllowAction(operations []RailOperation, action RailOperation, dir } switch action { - case RailOperationSend: + case discovery.RailOperationSend: switch dir { case GatewayDirectionIn: - return HasRailOperation(operations, RailOperationExternalDebit) + return HasRailOperation(operations, discovery.RailOperationExternalDebit) case GatewayDirectionOut: - return HasRailOperation(operations, RailOperationExternalCredit) + return HasRailOperation(operations, discovery.RailOperationExternalCredit) default: - return HasRailOperation(operations, RailOperationExternalDebit) || - HasRailOperation(operations, RailOperationExternalCredit) + return HasRailOperation(operations, discovery.RailOperationExternalDebit) || + HasRailOperation(operations, discovery.RailOperationExternalCredit) } - case RailOperationExternalDebit: - return HasRailOperation(operations, RailOperationSend) - case RailOperationExternalCredit: - return HasRailOperation(operations, RailOperationSend) + case discovery.RailOperationExternalDebit: + return HasRailOperation(operations, discovery.RailOperationSend) + case discovery.RailOperationExternalCredit: + return HasRailOperation(operations, discovery.RailOperationSend) default: return false } @@ -181,7 +182,7 @@ func amountWithinLimits(gw *GatewayInstanceDescriptor, limits Limits, currency s if override, ok := limits.CurrencyLimits[currency]; ok { min = firstLimitValue(override.MinAmount, min) max = firstLimitValue(override.MaxAmount, max) - if action == RailOperationFee { + if action == discovery.RailOperationFee { maxFee = firstLimitValue(override.MaxFee, maxFee) } } @@ -206,7 +207,7 @@ func amountWithinLimits(gw *GatewayInstanceDescriptor, limits Limits, currency s return gatewayIneligible(gw, fmt.Sprintf("amount %s %s exceeds per-tx max limit %s", amount.String(), currency, val.String())) } } - if action == RailOperationFee && maxFee != "" { + if action == discovery.RailOperationFee && maxFee != "" { if val, err := decimal.NewFromString(maxFee); err == nil && amount.GreaterThan(val) { return gatewayIneligible(gw, fmt.Sprintf("fee amount %s %s exceeds max fee limit %s", amount.String(), currency, val.String())) } diff --git a/api/payments/storage/model/gateway_eligibility_test.go b/api/payments/storage/model/gateway_eligibility_test.go index 1cfd432d..b9bb0dac 100644 --- a/api/payments/storage/model/gateway_eligibility_test.go +++ b/api/payments/storage/model/gateway_eligibility_test.go @@ -1,6 +1,7 @@ package model import ( + "github.com/tech/sendico/pkg/discovery" "testing" "github.com/shopspring/decimal" @@ -10,14 +11,14 @@ func TestIsGatewayEligible_AllowsMatchingGateway(t *testing.T) { gw := &GatewayInstanceDescriptor{ ID: "gw-1", InstanceID: "inst-1", - Rail: RailCrypto, + Rail: discovery.RailCrypto, Network: "TRON", Currencies: []string{"USDT"}, - Operations: []RailOperation{RailOperationSend, RailOperationExternalCredit}, + Operations: []RailOperation{discovery.RailOperationSend, discovery.RailOperationExternalCredit}, IsEnabled: true, } - err := IsGatewayEligible(gw, RailCrypto, "TRON", "USDT", RailOperationSend, GatewayDirectionOut, decimal.RequireFromString("10")) + err := IsGatewayEligible(gw, discovery.RailCrypto, "TRON", "USDT", discovery.RailOperationSend, GatewayDirectionOut, decimal.RequireFromString("10")) if err != nil { t.Fatalf("expected gateway to be eligible, got err=%v", err) } @@ -27,21 +28,21 @@ func TestIsGatewayEligible_RejectsNetworkMismatch(t *testing.T) { gw := &GatewayInstanceDescriptor{ ID: "gw-1", InstanceID: "inst-1", - Rail: RailCrypto, + Rail: discovery.RailCrypto, Network: "ETH", Currencies: []string{"USDT"}, - Operations: []RailOperation{RailOperationSend}, + Operations: []RailOperation{discovery.RailOperationSend}, IsEnabled: true, } - err := IsGatewayEligible(gw, RailCrypto, "TRON", "USDT", RailOperationSend, GatewayDirectionOut, decimal.RequireFromString("10")) + err := IsGatewayEligible(gw, discovery.RailCrypto, "TRON", "USDT", discovery.RailOperationSend, GatewayDirectionOut, decimal.RequireFromString("10")) if err == nil { t.Fatalf("expected network mismatch error") } } func TestNoEligibleGatewayMessage(t *testing.T) { - got := NoEligibleGatewayMessage("tron", "usdt", RailOperationSend, GatewayDirectionOut) + got := NoEligibleGatewayMessage("tron", "usdt", discovery.RailOperationSend, GatewayDirectionOut) want := "plan builder: no eligible gateway found for TRON USDT SEND for direction out" if got != want { t.Fatalf("unexpected message: got=%q want=%q", got, want) diff --git a/api/payments/storage/model/payment.go b/api/payments/storage/model/payment.go index ea917c1f..eb7568bf 100644 --- a/api/payments/storage/model/payment.go +++ b/api/payments/storage/model/payment.go @@ -5,6 +5,7 @@ import ( "time" "github.com/tech/sendico/pkg/db/storable" + "github.com/tech/sendico/pkg/discovery" "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/model/account_role" "github.com/tech/sendico/pkg/mservice" @@ -79,35 +80,10 @@ const ( ) // Rail identifies a payment rail for orchestration. -type Rail string - -const ( - RailUnspecified Rail = "UNSPECIFIED" - RailCrypto Rail = "CRYPTO" - RailProviderSettlement Rail = "SETTLEMENT" - RailLedger Rail = "LEDGER" - RailCardPayout Rail = "CARD" - RailFiatOnRamp Rail = "ONRAMP" - RailFiatOffRamp Rail = "OFFRAMP" -) +type Rail = discovery.Rail // RailOperation identifies an explicit action within a payment plan. -type RailOperation string - -const ( - RailOperationUnspecified RailOperation = "UNSPECIFIED" - RailOperationDebit RailOperation = "DEBIT" - RailOperationCredit RailOperation = "CREDIT" - RailOperationExternalDebit RailOperation = "EXTERNAL_DEBIT" - RailOperationExternalCredit RailOperation = "EXTERNAL_CREDIT" - RailOperationMove RailOperation = "MOVE" - RailOperationSend RailOperation = "SEND" - RailOperationFee RailOperation = "FEE" - RailOperationObserveConfirm RailOperation = "OBSERVE_CONFIRM" - RailOperationFXConvert RailOperation = "FX_CONVERT" - RailOperationBlock RailOperation = "BLOCK" - RailOperationRelease RailOperation = "RELEASE" -) +type RailOperation = discovery.RailOperation // RailCapabilities are declared per gateway instance. type RailCapabilities struct { diff --git a/api/payments/storage/model/rail_operations.go b/api/payments/storage/model/rail_operations.go index 8a156559..c427018d 100644 --- a/api/payments/storage/model/rail_operations.go +++ b/api/payments/storage/model/rail_operations.go @@ -1,26 +1,29 @@ package model -import "strings" +import ( + "github.com/tech/sendico/pkg/discovery" + "strings" +) var supportedRailOperations = map[RailOperation]struct{}{ - RailOperationDebit: {}, - RailOperationCredit: {}, - RailOperationExternalDebit: {}, - RailOperationExternalCredit: {}, - RailOperationMove: {}, - RailOperationSend: {}, - RailOperationFee: {}, - RailOperationObserveConfirm: {}, - RailOperationFXConvert: {}, - RailOperationBlock: {}, - RailOperationRelease: {}, + discovery.RailOperationDebit: {}, + discovery.RailOperationCredit: {}, + discovery.RailOperationExternalDebit: {}, + discovery.RailOperationExternalCredit: {}, + discovery.RailOperationMove: {}, + discovery.RailOperationSend: {}, + discovery.RailOperationFee: {}, + discovery.RailOperationObserveConfirm: {}, + discovery.RailOperationFXConvert: {}, + discovery.RailOperationBlock: {}, + discovery.RailOperationRelease: {}, } // ParseRailOperation canonicalizes string values into a RailOperation token. func ParseRailOperation(value string) RailOperation { clean := strings.ToUpper(strings.TrimSpace(value)) if clean == "" { - return RailOperationUnspecified + return discovery.RailOperationUnspecified } return RailOperation(clean) } @@ -40,7 +43,7 @@ func NormalizeRailOperations(values []RailOperation) []RailOperation { seen := map[RailOperation]bool{} for _, value := range values { op := ParseRailOperation(string(value)) - if op == RailOperationUnspecified || !IsSupportedRailOperation(op) || seen[op] { + if op == discovery.RailOperationUnspecified || !IsSupportedRailOperation(op) || seen[op] { continue } seen[op] = true @@ -67,7 +70,7 @@ func NormalizeRailOperationStrings(values []string) []RailOperation { // HasRailOperation checks whether ops includes action. func HasRailOperation(ops []RailOperation, action RailOperation) bool { want := ParseRailOperation(string(action)) - if want == RailOperationUnspecified { + if want == discovery.RailOperationUnspecified { return false } for _, op := range ops { @@ -82,12 +85,12 @@ func HasRailOperation(ops []RailOperation, action RailOperation) bool { func RailCapabilitiesFromOperations(ops []RailOperation) RailCapabilities { normalized := NormalizeRailOperations(ops) return RailCapabilities{ - CanPayIn: HasRailOperation(normalized, RailOperationExternalDebit), - CanPayOut: HasRailOperation(normalized, RailOperationSend) || HasRailOperation(normalized, RailOperationExternalCredit), + CanPayIn: HasRailOperation(normalized, discovery.RailOperationExternalDebit), + CanPayOut: HasRailOperation(normalized, discovery.RailOperationSend) || HasRailOperation(normalized, discovery.RailOperationExternalCredit), CanReadBalance: false, - CanSendFee: HasRailOperation(normalized, RailOperationFee), - RequiresObserveConfirm: HasRailOperation(normalized, RailOperationObserveConfirm), - CanBlock: HasRailOperation(normalized, RailOperationBlock), - CanRelease: HasRailOperation(normalized, RailOperationRelease), + CanSendFee: HasRailOperation(normalized, discovery.RailOperationFee), + RequiresObserveConfirm: HasRailOperation(normalized, discovery.RailOperationObserveConfirm), + CanBlock: HasRailOperation(normalized, discovery.RailOperationBlock), + CanRelease: HasRailOperation(normalized, discovery.RailOperationRelease), } } diff --git a/api/payments/storage/model/rail_operations_test.go b/api/payments/storage/model/rail_operations_test.go index aca5f732..9b06edd8 100644 --- a/api/payments/storage/model/rail_operations_test.go +++ b/api/payments/storage/model/rail_operations_test.go @@ -1,6 +1,9 @@ package model -import "testing" +import ( + "github.com/tech/sendico/pkg/discovery" + "testing" +) func TestNormalizeRailOperations(t *testing.T) { ops := NormalizeRailOperations([]RailOperation{ @@ -13,35 +16,35 @@ func TestNormalizeRailOperations(t *testing.T) { if len(ops) != 2 { t.Fatalf("unexpected operations count: got=%d want=2", len(ops)) } - if ops[0] != RailOperationSend { - t.Fatalf("unexpected first operation: got=%q want=%q", ops[0], RailOperationSend) + if ops[0] != discovery.RailOperationSend { + t.Fatalf("unexpected first operation: got=%q want=%q", ops[0], discovery.RailOperationSend) } - if ops[1] != RailOperationExternalCredit { - t.Fatalf("unexpected second operation: got=%q want=%q", ops[1], RailOperationExternalCredit) + if ops[1] != discovery.RailOperationExternalCredit { + t.Fatalf("unexpected second operation: got=%q want=%q", ops[1], discovery.RailOperationExternalCredit) } } func TestHasRailOperation(t *testing.T) { - ops := []RailOperation{RailOperationSend, RailOperationExternalCredit} - if !HasRailOperation(ops, RailOperationSend) { + ops := []RailOperation{discovery.RailOperationSend, discovery.RailOperationExternalCredit} + if !HasRailOperation(ops, discovery.RailOperationSend) { t.Fatalf("expected send operation to be present") } if !HasRailOperation(ops, " external_credit ") { t.Fatalf("expected external credit operation to be present") } - if HasRailOperation(ops, RailOperationObserveConfirm) { + if HasRailOperation(ops, discovery.RailOperationObserveConfirm) { t.Fatalf("did not expect observe confirm operation to be present") } } func TestRailCapabilitiesFromOperations(t *testing.T) { cap := RailCapabilitiesFromOperations([]RailOperation{ - RailOperationExternalDebit, - RailOperationExternalCredit, - RailOperationFee, - RailOperationObserveConfirm, - RailOperationBlock, - RailOperationRelease, + discovery.RailOperationExternalDebit, + discovery.RailOperationExternalCredit, + discovery.RailOperationFee, + discovery.RailOperationObserveConfirm, + discovery.RailOperationBlock, + discovery.RailOperationRelease, }) if !cap.CanPayIn { diff --git a/api/payments/storage/model/rails.go b/api/payments/storage/model/rails.go index 5619a482..c79fba26 100644 --- a/api/payments/storage/model/rails.go +++ b/api/payments/storage/model/rails.go @@ -1,21 +1,24 @@ package model -import "strings" +import ( + "github.com/tech/sendico/pkg/discovery" + "strings" +) var supportedRails = map[Rail]struct{}{ - RailCrypto: {}, - RailProviderSettlement: {}, - RailLedger: {}, - RailCardPayout: {}, - RailFiatOnRamp: {}, - RailFiatOffRamp: {}, + discovery.RailCrypto: {}, + discovery.RailProviderSettlement: {}, + discovery.RailLedger: {}, + discovery.RailCardPayout: {}, + discovery.RailFiatOnRamp: {}, + discovery.RailFiatOffRamp: {}, } // ParseRail canonicalizes string values into a Rail token. func ParseRail(value string) Rail { clean := strings.ToUpper(strings.TrimSpace(value)) if clean == "" { - return RailUnspecified + return discovery.RailUnspecified } clean = strings.ReplaceAll(clean, "-", "_") clean = strings.ReplaceAll(clean, " ", "_") @@ -24,20 +27,20 @@ func ParseRail(value string) Rail { } switch clean { - case string(RailCrypto), "RAIL_CRYPTO": - return RailCrypto - case string(RailProviderSettlement), "PROVIDER_SETTLEMENT", "RAIL_SETTLEMENT", "RAIL_PROVIDER_SETTLEMENT": - return RailProviderSettlement - case string(RailLedger), "RAIL_LEDGER": - return RailLedger - case string(RailCardPayout), "CARD_PAYOUT", "RAIL_CARD", "RAIL_CARD_PAYOUT": - return RailCardPayout - case string(RailFiatOnRamp), "FIAT_ONRAMP", "RAIL_ONRAMP", "RAIL_FIAT_ONRAMP": - return RailFiatOnRamp - case string(RailFiatOffRamp), "FIAT_OFFRAMP", "RAIL_OFFRAMP", "RAIL_FIAT_OFFRAMP": - return RailFiatOffRamp + case string(discovery.RailCrypto), "RAIL_CRYPTO": + return discovery.RailCrypto + case string(discovery.RailProviderSettlement), "PROVIDER_SETTLEMENT", "RAIL_SETTLEMENT", "RAIL_PROVIDER_SETTLEMENT": + return discovery.RailProviderSettlement + case string(discovery.RailLedger), "RAIL_LEDGER": + return discovery.RailLedger + case string(discovery.RailCardPayout), "CARD_PAYOUT", "RAIL_CARD", "RAIL_CARD_PAYOUT": + return discovery.RailCardPayout + case string(discovery.RailFiatOnRamp), "FIAT_ONRAMP", "RAIL_ONRAMP", "RAIL_FIAT_ONRAMP": + return discovery.RailFiatOnRamp + case string(discovery.RailFiatOffRamp), "FIAT_OFFRAMP", "RAIL_OFFRAMP", "RAIL_FIAT_OFFRAMP": + return discovery.RailFiatOffRamp default: - return RailUnspecified + return discovery.RailUnspecified } } @@ -49,13 +52,13 @@ func IsSupportedRail(rail Rail) bool { func normalizeRail(value Rail) Rail { parsed := ParseRail(string(value)) - if parsed != RailUnspecified { + if parsed != discovery.RailUnspecified { return parsed } clean := strings.ToUpper(strings.TrimSpace(string(value))) if clean == "" { - return RailUnspecified + return discovery.RailUnspecified } return Rail(clean) diff --git a/api/payments/storage/model/rails_test.go b/api/payments/storage/model/rails_test.go index 5412451e..60a471e5 100644 --- a/api/payments/storage/model/rails_test.go +++ b/api/payments/storage/model/rails_test.go @@ -1,6 +1,9 @@ package model -import "testing" +import ( + "github.com/tech/sendico/pkg/discovery" + "testing" +) func TestParseRail(t *testing.T) { cases := []struct { @@ -8,14 +11,14 @@ func TestParseRail(t *testing.T) { input string want Rail }{ - {name: "crypto", input: "crypto", want: RailCrypto}, - {name: "settlement canonical", input: "SETTLEMENT", want: RailProviderSettlement}, - {name: "settlement legacy", input: "provider_settlement", want: RailProviderSettlement}, - {name: "card canonical", input: "card", want: RailCardPayout}, - {name: "card legacy", input: "card_payout", want: RailCardPayout}, - {name: "onramp", input: "fiat_onramp", want: RailFiatOnRamp}, - {name: "offramp", input: "fiat_offramp", want: RailFiatOffRamp}, - {name: "unknown", input: "telegram", want: RailUnspecified}, + {name: "crypto", input: "crypto", want: discovery.RailCrypto}, + {name: "settlement canonical", input: "SETTLEMENT", want: discovery.RailProviderSettlement}, + {name: "settlement legacy", input: "provider_settlement", want: discovery.RailProviderSettlement}, + {name: "card canonical", input: "card", want: discovery.RailCardPayout}, + {name: "card legacy", input: "card_payout", want: discovery.RailCardPayout}, + {name: "onramp", input: "fiat_onramp", want: discovery.RailFiatOnRamp}, + {name: "offramp", input: "fiat_offramp", want: discovery.RailFiatOffRamp}, + {name: "unknown", input: "telegram", want: discovery.RailUnspecified}, } for _, tc := range cases { diff --git a/api/payments/storage/mongo/store/routes.go b/api/payments/storage/mongo/store/routes.go index fe2a8060..336852b3 100644 --- a/api/payments/storage/mongo/store/routes.go +++ b/api/payments/storage/mongo/store/routes.go @@ -3,6 +3,7 @@ package store import ( "context" "errors" + "github.com/tech/sendico/pkg/discovery" "strings" "github.com/tech/sendico/payments/storage" @@ -65,10 +66,10 @@ func (r *Routes) Create(ctx context.Context, route *model.PaymentRoute) error { return merrors.InvalidArgument("routesStore: nil route") } route.Normalize() - if route.FromRail == "" || route.FromRail == model.RailUnspecified { + if route.FromRail == "" || route.FromRail == discovery.RailUnspecified { return merrors.InvalidArgument("routesStore: from_rail is required") } - if route.ToRail == "" || route.ToRail == model.RailUnspecified { + if route.ToRail == "" || route.ToRail == discovery.RailUnspecified { return merrors.InvalidArgument("routesStore: to_rail is required") } if route.ID.IsZero() { @@ -176,20 +177,20 @@ func normalizedRailFilterValues(rail model.Rail) []string { return nil } - if parsed := model.ParseRail(string(rail)); parsed != model.RailUnspecified { + if parsed := model.ParseRail(string(rail)); parsed != discovery.RailUnspecified { switch parsed { - case model.RailCrypto: - return []string{string(model.RailCrypto), "RAIL_CRYPTO"} - case model.RailProviderSettlement: - return []string{string(model.RailProviderSettlement), "PROVIDER_SETTLEMENT", "RAIL_SETTLEMENT", "RAIL_PROVIDER_SETTLEMENT"} - case model.RailLedger: - return []string{string(model.RailLedger), "RAIL_LEDGER"} - case model.RailCardPayout: - return []string{string(model.RailCardPayout), "CARD_PAYOUT", "RAIL_CARD", "RAIL_CARD_PAYOUT"} - case model.RailFiatOnRamp: - return []string{string(model.RailFiatOnRamp), "FIAT_ONRAMP", "RAIL_ONRAMP", "RAIL_FIAT_ONRAMP"} - case model.RailFiatOffRamp: - return []string{string(model.RailFiatOffRamp), "FIAT_OFFRAMP", "RAIL_OFFRAMP", "RAIL_FIAT_OFFRAMP"} + case discovery.RailCrypto: + return []string{string(discovery.RailCrypto), "RAIL_CRYPTO"} + case discovery.RailProviderSettlement: + return []string{string(discovery.RailProviderSettlement), "PROVIDER_SETTLEMENT", "RAIL_SETTLEMENT", "RAIL_PROVIDER_SETTLEMENT"} + case discovery.RailLedger: + return []string{string(discovery.RailLedger), "RAIL_LEDGER"} + case discovery.RailCardPayout: + return []string{string(discovery.RailCardPayout), "CARD_PAYOUT", "RAIL_CARD", "RAIL_CARD_PAYOUT"} + case discovery.RailFiatOnRamp: + return []string{string(discovery.RailFiatOnRamp), "FIAT_ONRAMP", "RAIL_ONRAMP", "RAIL_FIAT_ONRAMP"} + case discovery.RailFiatOffRamp: + return []string{string(discovery.RailFiatOffRamp), "FIAT_OFFRAMP", "RAIL_OFFRAMP", "RAIL_FIAT_OFFRAMP"} default: return []string{string(parsed)} } diff --git a/api/pkg/discovery/operations.go b/api/pkg/discovery/operations.go index 44b323b7..43562fd5 100644 --- a/api/pkg/discovery/operations.go +++ b/api/pkg/discovery/operations.go @@ -2,6 +2,12 @@ package discovery import "strings" +// Rail identifies a canonical payment rail token. +type Rail string + +// RailOperation identifies a canonical payment-plan rail action token. +type RailOperation string + const ( OperationDiscoveryLookup = "discovery.lookup" @@ -33,6 +39,33 @@ const ( OperationFXConvert = "fx.convert" ) +// Canonical rail identifiers. +const ( + RailUnspecified = "UNSPECIFIED" + RailCrypto = "CRYPTO" + RailProviderSettlement = "SETTLEMENT" + RailLedger = "LEDGER" + RailCardPayout = "CARD" + RailFiatOnRamp = "ONRAMP" + RailFiatOffRamp = "OFFRAMP" +) + +// Canonical payment-plan rail operation identifiers. +const ( + RailOperationUnspecified = "UNSPECIFIED" + RailOperationDebit = "DEBIT" + RailOperationCredit = "CREDIT" + RailOperationExternalDebit = "EXTERNAL_DEBIT" + RailOperationExternalCredit = "EXTERNAL_CREDIT" + RailOperationMove = "MOVE" + RailOperationSend = "SEND" + RailOperationFee = "FEE" + RailOperationObserveConfirm = "OBSERVE_CONFIRM" + RailOperationFXConvert = "FX_CONVERT" + RailOperationBlock = "BLOCK" + RailOperationRelease = "RELEASE" +) + // NormalizeOperation canonicalizes an operation string for comparisons. func NormalizeOperation(value string) string { return strings.ToLower(strings.TrimSpace(value)) diff --git a/api/pkg/discovery/rail_vocab.go b/api/pkg/discovery/rail_vocab.go index 22eaf59c..dcbb7771 100644 --- a/api/pkg/discovery/rail_vocab.go +++ b/api/pkg/discovery/rail_vocab.go @@ -2,28 +2,6 @@ package discovery import "strings" -const ( - RailCrypto = "CRYPTO" - RailProviderSettlement = "SETTLEMENT" - RailLedger = "LEDGER" - RailCardPayout = "CARD" - RailFiatOnRamp = "ONRAMP" -) - -const ( - RailOperationDebit = "DEBIT" - RailOperationCredit = "CREDIT" - RailOperationExternalDebit = "EXTERNAL_DEBIT" - RailOperationExternalCredit = "EXTERNAL_CREDIT" - RailOperationMove = "MOVE" - RailOperationSend = "SEND" - RailOperationFee = "FEE" - RailOperationObserveConfirm = "OBSERVE_CONFIRM" - RailOperationFXConvert = "FX_CONVERT" - RailOperationBlock = "BLOCK" - RailOperationRelease = "RELEASE" -) - var knownRails = map[string]struct{}{ RailCrypto: {}, RailProviderSettlement: {}, @@ -57,21 +35,7 @@ func NormalizeRail(value string) string { for strings.Contains(clean, "__") { clean = strings.ReplaceAll(clean, "__", "_") } - - switch clean { - case RailCrypto, "RAIL_CRYPTO": - return RailCrypto - case RailProviderSettlement, "PROVIDER_SETTLEMENT", "RAIL_SETTLEMENT", "RAIL_PROVIDER_SETTLEMENT": - return RailProviderSettlement - case RailLedger, "RAIL_LEDGER": - return RailLedger - case RailCardPayout, "CARD_PAYOUT", "RAIL_CARD", "RAIL_CARD_PAYOUT": - return RailCardPayout - case RailFiatOnRamp, "FIAT_ONRAMP", "RAIL_ONRAMP", "RAIL_FIAT_ONRAMP": - return RailFiatOnRamp - default: - return clean - } + return clean } // IsKnownRail reports whether the value is a recognized payment rail. @@ -82,11 +46,7 @@ func IsKnownRail(value string) bool { // NormalizeRailOperation canonicalizes a rail operation token. func NormalizeRailOperation(value string) string { - clean := strings.ToUpper(strings.TrimSpace(value)) - if after, ok := strings.CutPrefix(clean, "RAIL_OPERATION_"); ok { - clean = after - } - return clean + return strings.ToUpper(strings.TrimSpace(value)) } // IsKnownRailOperation reports whether the value is a recognized rail operation. @@ -104,24 +64,16 @@ func ExpandRailOperation(value string) []string { } switch strings.ToLower(strings.TrimSpace(value)) { - case OperationExternalDebit, "external_debit": + case OperationExternalDebit: return []string{RailOperationExternalDebit} - case OperationExternalCredit, "external_credit": + case OperationExternalCredit: return []string{RailOperationExternalCredit} - case "payin", "payin.crypto", "payin.fiat", "payin.card": - return []string{RailOperationExternalDebit} - case "payout", "payout.crypto", "payout.fiat", "payout.card": - return []string{RailOperationExternalCredit, RailOperationSend} - case "fee.send", "fees.send", OperationFee: + case OperationFee: return []string{RailOperationFee} - case OperationObserveConfirm, "observe_confirm": + case OperationObserveConfirm: return []string{RailOperationObserveConfirm} - case OperationFXConvert, "fx_convert": + case OperationFXConvert: return []string{RailOperationFXConvert} - case "funds.block", "hold.balance", "block": - return []string{RailOperationBlock} - case "funds.release", "release", "unblock": - return []string{RailOperationRelease} default: return nil } @@ -159,12 +111,6 @@ func CryptoRailGatewayOperations() []string { OperationExternalCredit, OperationFee, OperationObserveConfirm, - // Legacy rail tokens retained for backward compatibility. - RailOperationSend, - RailOperationExternalDebit, - RailOperationExternalCredit, - RailOperationFee, - RailOperationObserveConfirm, } } @@ -174,10 +120,6 @@ func CardPayoutRailGatewayOperations() []string { OperationSend, OperationExternalCredit, OperationObserveConfirm, - // Legacy rail tokens retained for backward compatibility. - RailOperationSend, - RailOperationExternalCredit, - RailOperationObserveConfirm, } } @@ -186,8 +128,5 @@ func ProviderSettlementRailGatewayOperations() []string { return []string{ OperationFXConvert, OperationObserveConfirm, - // Legacy rail tokens retained for backward compatibility. - RailOperationFXConvert, - RailOperationObserveConfirm, } } diff --git a/api/pkg/discovery/rail_vocab_test.go b/api/pkg/discovery/rail_vocab_test.go index 96eb1ce9..d79c5562 100644 --- a/api/pkg/discovery/rail_vocab_test.go +++ b/api/pkg/discovery/rail_vocab_test.go @@ -5,17 +5,17 @@ import "testing" func TestNormalizeRailOperations(t *testing.T) { got := NormalizeRailOperations([]string{ "send", - "payout.crypto", "observe.confirm", "external.credit", "fx.convert", + "payout.crypto", "unknown", }) want := []string{ RailOperationSend, - RailOperationExternalCredit, RailOperationObserveConfirm, + RailOperationExternalCredit, RailOperationFXConvert, } if len(got) != len(want) { @@ -28,16 +28,9 @@ func TestNormalizeRailOperations(t *testing.T) { } } -func TestExpandRailOperationLegacyAliases(t *testing.T) { - got := ExpandRailOperation("payout.fiat") - if len(got) != 2 { - t.Fatalf("unexpected operations count: got=%d want=2", len(got)) - } - if got[0] != RailOperationExternalCredit { - t.Fatalf("unexpected first operation: got=%q want=%q", got[0], RailOperationExternalCredit) - } - if got[1] != RailOperationSend { - t.Fatalf("unexpected second operation: got=%q want=%q", got[1], RailOperationSend) +func TestExpandRailOperationRejectsLegacyAliases(t *testing.T) { + if got := ExpandRailOperation("payout.fiat"); len(got) != 0 { + t.Fatalf("expected no expansion for legacy alias, got=%v", got) } } @@ -50,14 +43,65 @@ func TestIsKnownRail(t *testing.T) { } } -func TestNormalizeRailAliases(t *testing.T) { - if got := NormalizeRail("provider_settlement"); got != RailProviderSettlement { - t.Fatalf("provider_settlement alias mismatch: got=%q want=%q", got, RailProviderSettlement) +func TestNormalizeRailRejectsLegacyAliases(t *testing.T) { + if IsKnownRail("provider_settlement") { + t.Fatalf("did not expect provider_settlement alias to be known") } - if got := NormalizeRail("card_payout"); got != RailCardPayout { - t.Fatalf("card_payout alias mismatch: got=%q want=%q", got, RailCardPayout) + if IsKnownRail("card_payout") { + t.Fatalf("did not expect card_payout alias to be known") } - if got := NormalizeRail("RAIL_SETTLEMENT"); got != RailProviderSettlement { - t.Fatalf("RAIL_SETTLEMENT alias mismatch: got=%q want=%q", got, RailProviderSettlement) + if IsKnownRail("RAIL_SETTLEMENT") { + t.Fatalf("did not expect RAIL_SETTLEMENT alias to be known") + } +} + +func TestGatewayOperationSetsAreCanonicalOnly(t *testing.T) { + tests := []struct { + name string + got []string + want []string + }{ + { + name: "crypto rail gateway operations", + got: CryptoRailGatewayOperations(), + want: []string{ + OperationBalanceRead, + OperationSend, + OperationExternalDebit, + OperationExternalCredit, + OperationFee, + OperationObserveConfirm, + }, + }, + { + name: "card payout rail gateway operations", + got: CardPayoutRailGatewayOperations(), + want: []string{ + OperationSend, + OperationExternalCredit, + OperationObserveConfirm, + }, + }, + { + name: "provider settlement rail gateway operations", + got: ProviderSettlementRailGatewayOperations(), + want: []string{ + OperationFXConvert, + OperationObserveConfirm, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if len(tt.got) != len(tt.want) { + t.Fatalf("unexpected operations count: got=%d want=%d", len(tt.got), len(tt.want)) + } + for i := range tt.want { + if tt.got[i] != tt.want[i] { + t.Fatalf("unexpected operation[%d]: got=%q want=%q", i, tt.got[i], tt.want[i]) + } + } + }) } }