fixed rail & operation names
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
"github.com/tech/sendico/pkg/model/account_role"
|
"github.com/tech/sendico/pkg/model/account_role"
|
||||||
"github.com/tech/sendico/pkg/payments/rail"
|
"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.
|
// NewRailGateway wraps a chain gateway client into a rail gateway adapter.
|
||||||
func NewRailGateway(client Client, cfg RailGatewayConfig) rail.RailGateway {
|
func NewRailGateway(client Client, cfg RailGatewayConfig) rail.RailGateway {
|
||||||
railName := strings.ToUpper(strings.TrimSpace(cfg.Rail))
|
railName := discovery.NormalizeRail(cfg.Rail)
|
||||||
if railName == "" {
|
if railName == "" {
|
||||||
railName = "CRYPTO"
|
railName = discovery.RailCrypto
|
||||||
}
|
}
|
||||||
return &chainRailGateway{
|
return &chainRailGateway{
|
||||||
client: client,
|
client: client,
|
||||||
|
|||||||
21
api/gateway/chain/client/rail_gateway_test.go
Normal file
21
api/gateway/chain/client/rail_gateway_test.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,6 +52,7 @@ gateway:
|
|||||||
currencies: ["RUB"]
|
currencies: ["RUB"]
|
||||||
limits:
|
limits:
|
||||||
per_tx_min_amount: "100.00"
|
per_tx_min_amount: "100.00"
|
||||||
|
per_tx_max_amount: "150000.00"
|
||||||
|
|
||||||
http:
|
http:
|
||||||
callback:
|
callback:
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ func (s *Service) SubmitOperation(ctx context.Context, req *connectorv1.SubmitOp
|
|||||||
if targetChatID != "" {
|
if targetChatID != "" {
|
||||||
metadata[metadataTargetChatID] = targetChatID
|
metadata[metadataTargetChatID] = targetChatID
|
||||||
}
|
}
|
||||||
outgoingLeg := strings.TrimSpace(reader.String("outgoing_leg"))
|
outgoingLeg := normalizeRail(reader.String("outgoing_leg"))
|
||||||
if outgoingLeg != "" {
|
if outgoingLeg != "" {
|
||||||
metadata[metadataOutgoingLeg] = outgoingLeg
|
metadata[metadataOutgoingLeg] = outgoingLeg
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -428,9 +428,9 @@ func (s *Service) buildConfirmationRequest(intent *model.PaymentGatewayIntent) (
|
|||||||
if targetChatID == "" {
|
if targetChatID == "" {
|
||||||
return nil, merrors.InvalidArgument("target_chat_id is required", "target_chat_id")
|
return nil, merrors.InvalidArgument("target_chat_id is required", "target_chat_id")
|
||||||
}
|
}
|
||||||
rail := strings.TrimSpace(intent.OutgoingLeg)
|
rail := normalizeRail(intent.OutgoingLeg)
|
||||||
if rail == "" {
|
if rail == "" {
|
||||||
rail = s.rail
|
rail = normalizeRail(s.rail)
|
||||||
}
|
}
|
||||||
timeout := s.cfg.TimeoutSeconds
|
timeout := s.cfg.TimeoutSeconds
|
||||||
if timeout <= 0 {
|
if timeout <= 0 {
|
||||||
@@ -549,7 +549,7 @@ func normalizeIntent(intent *model.PaymentGatewayIntent) *model.PaymentGatewayIn
|
|||||||
cp := *intent
|
cp := *intent
|
||||||
cp.PaymentIntentID = strings.TrimSpace(cp.PaymentIntentID)
|
cp.PaymentIntentID = strings.TrimSpace(cp.PaymentIntentID)
|
||||||
cp.IdempotencyKey = strings.TrimSpace(cp.IdempotencyKey)
|
cp.IdempotencyKey = strings.TrimSpace(cp.IdempotencyKey)
|
||||||
cp.OutgoingLeg = strings.TrimSpace(cp.OutgoingLeg)
|
cp.OutgoingLeg = normalizeRail(cp.OutgoingLeg)
|
||||||
cp.QuoteRef = strings.TrimSpace(cp.QuoteRef)
|
cp.QuoteRef = strings.TrimSpace(cp.QuoteRef)
|
||||||
if cp.RequestedMoney != nil {
|
if cp.RequestedMoney != nil {
|
||||||
cp.RequestedMoney.Amount = strings.TrimSpace(cp.RequestedMoney.Amount)
|
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.IdempotencyKey = strings.TrimSpace(intent.IdempotencyKey)
|
||||||
record.PaymentIntentID = strings.TrimSpace(intent.PaymentIntentID)
|
record.PaymentIntentID = strings.TrimSpace(intent.PaymentIntentID)
|
||||||
record.QuoteRef = strings.TrimSpace(intent.QuoteRef)
|
record.QuoteRef = strings.TrimSpace(intent.QuoteRef)
|
||||||
record.OutgoingLeg = strings.TrimSpace(intent.OutgoingLeg)
|
record.OutgoingLeg = normalizeRail(intent.OutgoingLeg)
|
||||||
record.RequestedMoney = intent.RequestedMoney
|
record.RequestedMoney = intent.RequestedMoney
|
||||||
record.IntentRef = intent.IntentRef
|
record.IntentRef = intent.IntentRef
|
||||||
record.OperationRef = intent.OperationRef
|
record.OperationRef = intent.OperationRef
|
||||||
@@ -578,7 +578,7 @@ func paymentRecordFromIntent(intent *model.PaymentGatewayIntent, confirmReq *mod
|
|||||||
record.IdempotencyKey = strings.TrimSpace(confirmReq.RequestID)
|
record.IdempotencyKey = strings.TrimSpace(confirmReq.RequestID)
|
||||||
record.PaymentIntentID = strings.TrimSpace(confirmReq.PaymentIntentID)
|
record.PaymentIntentID = strings.TrimSpace(confirmReq.PaymentIntentID)
|
||||||
record.QuoteRef = strings.TrimSpace(confirmReq.QuoteRef)
|
record.QuoteRef = strings.TrimSpace(confirmReq.QuoteRef)
|
||||||
record.OutgoingLeg = strings.TrimSpace(confirmReq.Rail)
|
record.OutgoingLeg = normalizeRail(confirmReq.Rail)
|
||||||
record.RequestedMoney = confirmReq.RequestedMoney
|
record.RequestedMoney = confirmReq.RequestedMoney
|
||||||
record.IntentRef = strings.TrimSpace(confirmReq.IntentRef)
|
record.IntentRef = strings.TrimSpace(confirmReq.IntentRef)
|
||||||
record.OperationRef = strings.TrimSpace(confirmReq.OperationRef)
|
record.OperationRef = strings.TrimSpace(confirmReq.OperationRef)
|
||||||
@@ -640,9 +640,9 @@ func intentFromSubmitTransfer(req *chainv1.SubmitTransferRequest, defaultRail, d
|
|||||||
}
|
}
|
||||||
quoteRef := strings.TrimSpace(metadata[metadataQuoteRef])
|
quoteRef := strings.TrimSpace(metadata[metadataQuoteRef])
|
||||||
targetChatID := strings.TrimSpace(metadata[metadataTargetChatID])
|
targetChatID := strings.TrimSpace(metadata[metadataTargetChatID])
|
||||||
outgoingLeg := strings.TrimSpace(metadata[metadataOutgoingLeg])
|
outgoingLeg := normalizeRail(metadata[metadataOutgoingLeg])
|
||||||
if outgoingLeg == "" {
|
if outgoingLeg == "" {
|
||||||
outgoingLeg = strings.TrimSpace(defaultRail)
|
outgoingLeg = normalizeRail(defaultRail)
|
||||||
}
|
}
|
||||||
if targetChatID == "" {
|
if targetChatID == "" {
|
||||||
targetChatID = strings.TrimSpace(defaultChatID)
|
targetChatID = strings.TrimSpace(defaultChatID)
|
||||||
@@ -659,6 +659,10 @@ func intentFromSubmitTransfer(req *chainv1.SubmitTransferRequest, defaultRail, d
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func normalizeRail(value string) string {
|
||||||
|
return discovery.NormalizeRail(value)
|
||||||
|
}
|
||||||
|
|
||||||
func transferFromRequest(req *chainv1.SubmitTransferRequest) *chainv1.Transfer {
|
func transferFromRequest(req *chainv1.SubmitTransferRequest) *chainv1.Transfer {
|
||||||
if req == nil {
|
if req == nil {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -8,12 +8,15 @@ import (
|
|||||||
|
|
||||||
"github.com/tech/sendico/gateway/tgsettle/storage"
|
"github.com/tech/sendico/gateway/tgsettle/storage"
|
||||||
storagemodel "github.com/tech/sendico/gateway/tgsettle/storage/model"
|
storagemodel "github.com/tech/sendico/gateway/tgsettle/storage/model"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
envelope "github.com/tech/sendico/pkg/messaging/envelope"
|
envelope "github.com/tech/sendico/pkg/messaging/envelope"
|
||||||
tnotifications "github.com/tech/sendico/pkg/messaging/notifications/telegram"
|
tnotifications "github.com/tech/sendico/pkg/messaging/notifications/telegram"
|
||||||
mloggerfactory "github.com/tech/sendico/pkg/mlogger/factory"
|
mloggerfactory "github.com/tech/sendico/pkg/mlogger/factory"
|
||||||
"github.com/tech/sendico/pkg/model"
|
"github.com/tech/sendico/pkg/model"
|
||||||
"github.com/tech/sendico/pkg/mservice"
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
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")
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
"github.com/tech/sendico/pkg/model/account_role"
|
"github.com/tech/sendico/pkg/model/account_role"
|
||||||
"github.com/tech/sendico/pkg/payments/rail"
|
"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.
|
// NewRailGateway wraps a chain gateway client into a rail gateway adapter.
|
||||||
func NewRailGateway(client Client, cfg RailGatewayConfig) rail.RailGateway {
|
func NewRailGateway(client Client, cfg RailGatewayConfig) rail.RailGateway {
|
||||||
railName := strings.ToUpper(strings.TrimSpace(cfg.Rail))
|
railName := discovery.NormalizeRail(cfg.Rail)
|
||||||
if railName == "" {
|
if railName == "" {
|
||||||
railName = "CRYPTO"
|
railName = discovery.RailCrypto
|
||||||
}
|
}
|
||||||
return &chainRailGateway{
|
return &chainRailGateway{
|
||||||
client: client,
|
client: client,
|
||||||
|
|||||||
21
api/gateway/tron/client/rail_gateway_test.go
Normal file
21
api/gateway/tron/client/rail_gateway_test.go
Normal file
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -26,7 +26,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
ledgerConnectorID = "ledger"
|
ledgerConnectorID = "ledger"
|
||||||
ledgerRailName = "LEDGER"
|
ledgerRailName = discovery.RailLedger
|
||||||
|
|
||||||
opParamOperation = "operation"
|
opParamOperation = "operation"
|
||||||
opParamToMoney = "to_money"
|
opParamToMoney = "to_money"
|
||||||
@@ -175,6 +175,7 @@ func (c *ledgerClient) CreateTransaction(ctx context.Context, tx rail.LedgerTx)
|
|||||||
if money.GetCurrency() == "" || money.GetAmount() == "" {
|
if money.GetCurrency() == "" || money.GetAmount() == "" {
|
||||||
return "", merrors.InvalidArgument("ledger: amount is required")
|
return "", merrors.InvalidArgument("ledger: amount is required")
|
||||||
}
|
}
|
||||||
|
tx = normalizeLedgerTxRails(tx)
|
||||||
|
|
||||||
description := strings.TrimSpace(tx.Description)
|
description := strings.TrimSpace(tx.Description)
|
||||||
metadata := ledgerTxMetadata(tx.Metadata, tx)
|
metadata := ledgerTxMetadata(tx.Metadata, tx)
|
||||||
@@ -849,7 +850,17 @@ func (c *ledgerClient) callContext(ctx context.Context) (context.Context, contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isLedgerRail(value string) bool {
|
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 {
|
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 != "" {
|
if val := strings.TrimSpace(tx.PaymentPlanID); val != "" {
|
||||||
meta[txMetaPaymentPlanID] = val
|
meta[txMetaPaymentPlanID] = val
|
||||||
}
|
}
|
||||||
if val := strings.TrimSpace(tx.FromRail); val != "" {
|
if val := normalizeRail(tx.FromRail); val != "" {
|
||||||
meta[txMetaFromRail] = val
|
meta[txMetaFromRail] = val
|
||||||
}
|
}
|
||||||
if val := strings.TrimSpace(tx.ToRail); val != "" {
|
if val := normalizeRail(tx.ToRail); val != "" {
|
||||||
meta[txMetaToRail] = val
|
meta[txMetaToRail] = val
|
||||||
}
|
}
|
||||||
if val := strings.TrimSpace(tx.ExternalReferenceID); val != "" {
|
if val := strings.TrimSpace(tx.ExternalReferenceID); val != "" {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"github.com/tech/sendico/pkg/discovery"
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
|
"github.com/tech/sendico/pkg/payments/rail"
|
||||||
accountrolev1 "github.com/tech/sendico/pkg/proto/common/account_role/v1"
|
accountrolev1 "github.com/tech/sendico/pkg/proto/common/account_role/v1"
|
||||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||||
connectorv1 "github.com/tech/sendico/pkg/proto/connector/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, "op-ext-debit", resp.GetJournalEntryRef())
|
||||||
assert.Equal(t, ledgerv1.EntryType_ENTRY_DEBIT, resp.GetEntryType())
|
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"])
|
||||||
|
}
|
||||||
|
|||||||
@@ -433,6 +433,7 @@ func (s *Service) startDiscoveryAnnouncer() {
|
|||||||
}
|
}
|
||||||
announce := discovery.Announcement{
|
announce := discovery.Announcement{
|
||||||
Service: "LEDGER",
|
Service: "LEDGER",
|
||||||
|
Rail: discovery.RailLedger,
|
||||||
Operations: discovery.LedgerServiceOperations(),
|
Operations: discovery.LedgerServiceOperations(),
|
||||||
InvokeURI: s.invokeURI,
|
InvokeURI: s.invokeURI,
|
||||||
Version: appversion.Create().Short(),
|
Version: appversion.Create().Short(),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package execution
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
@@ -83,7 +84,7 @@ func blockStepConfirmed(plan *model.PaymentPlan, execPlan *model.ExecutionPlan)
|
|||||||
}
|
}
|
||||||
execSteps := executionStepsByCode(execPlan)
|
execSteps := executionStepsByCode(execPlan)
|
||||||
for idx, step := range plan.Steps {
|
for idx, step := range plan.Steps {
|
||||||
if step == nil || step.Action != model.RailOperationBlock {
|
if step == nil || step.Action != discovery.RailOperationBlock {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
execStep := execSteps[planStepID(step, idx)]
|
execStep := execSteps[planStepID(step, idx)]
|
||||||
@@ -106,7 +107,7 @@ func roleHintsForStep(plan *model.PaymentPlan, idx int) (*account_role.AccountRo
|
|||||||
if step == nil {
|
if step == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if step.Rail != model.RailLedger || step.Action != model.RailOperationMove {
|
if step.Rail != discovery.RailLedger || step.Action != discovery.RailOperationMove {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if step.ToRole != nil && strings.TrimSpace(string(*step.ToRole)) != "" {
|
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 {
|
if planStep == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if planStep.Rail != rail || planStep.Action != model.RailOperationObserveConfirm {
|
if planStep.Rail != rail || planStep.Action != discovery.RailOperationObserveConfirm {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if dep != "" {
|
if dep != "" {
|
||||||
@@ -206,12 +207,12 @@ func failureCodeForStep(step *model.PaymentStep) model.PaymentFailureCode {
|
|||||||
return model.PaymentFailureCodePolicy
|
return model.PaymentFailureCodePolicy
|
||||||
}
|
}
|
||||||
switch step.Rail {
|
switch step.Rail {
|
||||||
case model.RailLedger:
|
case discovery.RailLedger:
|
||||||
if step.Action == model.RailOperationFXConvert {
|
if step.Action == discovery.RailOperationFXConvert {
|
||||||
return model.PaymentFailureCodeFX
|
return model.PaymentFailureCodeFX
|
||||||
}
|
}
|
||||||
return model.PaymentFailureCodeLedger
|
return model.PaymentFailureCodeLedger
|
||||||
case model.RailCrypto:
|
case discovery.RailCrypto:
|
||||||
return model.PaymentFailureCodeChain
|
return model.PaymentFailureCodeChain
|
||||||
default:
|
default:
|
||||||
return model.PaymentFailureCodePolicy
|
return model.PaymentFailureCodePolicy
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package execution
|
package execution
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
@@ -199,8 +200,8 @@ func cardPayoutDependenciesConfirmed(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if step.Rail != model.RailCardPayout ||
|
if step.Rail != discovery.RailCardPayout ||
|
||||||
step.Action != model.RailOperationSend {
|
step.Action != discovery.RailOperationSend {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -375,11 +376,11 @@ func routeContainsCardPayout(snapshot *model.PaymentQuoteSnapshot) bool {
|
|||||||
if hop == nil {
|
if hop == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if model.ParseRail(hop.Rail) == model.RailCardPayout {
|
if model.ParseRail(hop.Rail) == discovery.RailCardPayout {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if model.ParseRail(snapshot.Route.Rail) == model.RailCardPayout {
|
if model.ParseRail(snapshot.Route.Rail) == discovery.RailCardPayout {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package sexec
|
|||||||
import (
|
import (
|
||||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/xplan"
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/xplan"
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
)
|
)
|
||||||
|
|
||||||
type route int
|
type route int
|
||||||
@@ -26,28 +27,28 @@ func classifyRoute(step xplan.Step) route {
|
|||||||
rail := normalizeRail(step.Rail)
|
rail := normalizeRail(step.Rail)
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case model.RailOperationObserveConfirm:
|
case discovery.RailOperationObserveConfirm:
|
||||||
return routeObserveConfirm
|
return routeObserveConfirm
|
||||||
case model.RailOperationSend:
|
case discovery.RailOperationSend:
|
||||||
switch rail {
|
switch rail {
|
||||||
case model.RailCrypto:
|
case discovery.RailCrypto:
|
||||||
return routeCrypto
|
return routeCrypto
|
||||||
case model.RailCardPayout:
|
case discovery.RailCardPayout:
|
||||||
return routeCardPayout
|
return routeCardPayout
|
||||||
default:
|
default:
|
||||||
return routeUnknown
|
return routeUnknown
|
||||||
}
|
}
|
||||||
case model.RailOperationFXConvert:
|
case discovery.RailOperationFXConvert:
|
||||||
switch rail {
|
switch rail {
|
||||||
case model.RailProviderSettlement:
|
case discovery.RailProviderSettlement:
|
||||||
return routeProviderSettlement
|
return routeProviderSettlement
|
||||||
case model.RailLedger:
|
case discovery.RailLedger:
|
||||||
return routeLedger
|
return routeLedger
|
||||||
default:
|
default:
|
||||||
return routeUnknown
|
return routeUnknown
|
||||||
}
|
}
|
||||||
case model.RailOperationFee:
|
case discovery.RailOperationFee:
|
||||||
if rail == model.RailCrypto {
|
if rail == discovery.RailCrypto {
|
||||||
return routeCrypto
|
return routeCrypto
|
||||||
}
|
}
|
||||||
return routeUnknown
|
return routeUnknown
|
||||||
@@ -65,13 +66,13 @@ func isGuardStep(step xplan.Step) bool {
|
|||||||
|
|
||||||
func isLedgerAction(action model.RailOperation) bool {
|
func isLedgerAction(action model.RailOperation) bool {
|
||||||
switch action {
|
switch action {
|
||||||
case model.RailOperationDebit,
|
case discovery.RailOperationDebit,
|
||||||
model.RailOperationCredit,
|
discovery.RailOperationCredit,
|
||||||
model.RailOperationExternalDebit,
|
discovery.RailOperationExternalDebit,
|
||||||
model.RailOperationExternalCredit,
|
discovery.RailOperationExternalCredit,
|
||||||
model.RailOperationMove,
|
discovery.RailOperationMove,
|
||||||
model.RailOperationBlock,
|
discovery.RailOperationBlock,
|
||||||
model.RailOperationRelease:
|
discovery.RailOperationRelease:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ package sexec
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
|
"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/orchestrator/internal/service/orchestrationv2/xplan"
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ func TestExecute_DispatchLedger(t *testing.T) {
|
|||||||
|
|
||||||
out, err := registry.Execute(context.Background(), ExecuteInput{
|
out, err := registry.Execute(context.Background(), ExecuteInput{
|
||||||
Payment: &agg.Payment{PaymentRef: "p1"},
|
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{},
|
StepExecution: agg.StepExecution{},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -58,7 +58,7 @@ func TestExecute_DispatchRailsAndObserve(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "send crypto",
|
name: "send crypto",
|
||||||
step: xplan.Step{
|
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) {
|
wantCalls: func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
@@ -70,7 +70,7 @@ func TestExecute_DispatchRailsAndObserve(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "fx convert provider settlement",
|
name: "fx convert provider settlement",
|
||||||
step: xplan.Step{
|
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) {
|
wantCalls: func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
@@ -82,7 +82,7 @@ func TestExecute_DispatchRailsAndObserve(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "send card payout",
|
name: "send card payout",
|
||||||
step: xplan.Step{
|
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) {
|
wantCalls: func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
@@ -94,7 +94,7 @@ func TestExecute_DispatchRailsAndObserve(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "observe confirm",
|
name: "observe confirm",
|
||||||
step: xplan.Step{
|
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) {
|
wantCalls: func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
@@ -106,7 +106,7 @@ func TestExecute_DispatchRailsAndObserve(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "crypto fee",
|
name: "crypto fee",
|
||||||
step: xplan.Step{
|
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) {
|
wantCalls: func(t *testing.T) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
@@ -142,8 +142,8 @@ func TestExecute_DispatchGuard(t *testing.T) {
|
|||||||
StepRef: xplan.QuoteReadinessGuardStepRef,
|
StepRef: xplan.QuoteReadinessGuardStepRef,
|
||||||
StepCode: string(xplan.GuardOperationQuoteReadinessGuard),
|
StepCode: string(xplan.GuardOperationQuoteReadinessGuard),
|
||||||
Kind: xplan.StepKindLiquidityCheck,
|
Kind: xplan.StepKindLiquidityCheck,
|
||||||
Action: model.RailOperationUnspecified,
|
Action: discovery.RailOperationUnspecified,
|
||||||
Rail: model.RailUnspecified,
|
Rail: discovery.RailUnspecified,
|
||||||
DependsOn: nil,
|
DependsOn: nil,
|
||||||
},
|
},
|
||||||
StepExecution: agg.StepExecution{
|
StepExecution: agg.StepExecution{
|
||||||
@@ -171,7 +171,7 @@ func TestExecute_UnsupportedStep(t *testing.T) {
|
|||||||
|
|
||||||
_, err := registry.Execute(context.Background(), ExecuteInput{
|
_, err := registry.Execute(context.Background(), ExecuteInput{
|
||||||
Payment: &agg.Payment{PaymentRef: "p1"},
|
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},
|
StepExecution: agg.StepExecution{StepRef: "s1", StepCode: "bad.send", Attempt: 1},
|
||||||
})
|
})
|
||||||
if !errors.Is(err, ErrUnsupportedStep) {
|
if !errors.Is(err, ErrUnsupportedStep) {
|
||||||
@@ -199,7 +199,7 @@ func TestExecute_UnsupportedProviderSettlementSend(t *testing.T) {
|
|||||||
|
|
||||||
_, err := registry.Execute(context.Background(), ExecuteInput{
|
_, err := registry.Execute(context.Background(), ExecuteInput{
|
||||||
Payment: &agg.Payment{PaymentRef: "p1"},
|
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},
|
StepExecution: agg.StepExecution{StepRef: "s1", StepCode: "provider.send", Attempt: 1},
|
||||||
})
|
})
|
||||||
if !errors.Is(err, ErrUnsupportedStep) {
|
if !errors.Is(err, ErrUnsupportedStep) {
|
||||||
@@ -212,7 +212,7 @@ func TestExecute_MissingExecutor(t *testing.T) {
|
|||||||
|
|
||||||
_, err := registry.Execute(context.Background(), ExecuteInput{
|
_, err := registry.Execute(context.Background(), ExecuteInput{
|
||||||
Payment: &agg.Payment{PaymentRef: "p1"},
|
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},
|
StepExecution: agg.StepExecution{StepRef: "s1", StepCode: "crypto.send", Attempt: 1},
|
||||||
})
|
})
|
||||||
if !errors.Is(err, ErrMissingExecutor) {
|
if !errors.Is(err, ErrMissingExecutor) {
|
||||||
@@ -231,21 +231,21 @@ func TestExecute_ValidationErrors(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "missing payment",
|
name: "missing payment",
|
||||||
in: ExecuteInput{
|
in: ExecuteInput{
|
||||||
Step: xplan.Step{StepRef: "s1", Action: model.RailOperationDebit},
|
Step: xplan.Step{StepRef: "s1", Action: discovery.RailOperationDebit},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing step ref",
|
name: "missing step ref",
|
||||||
in: ExecuteInput{
|
in: ExecuteInput{
|
||||||
Payment: &agg.Payment{},
|
Payment: &agg.Payment{},
|
||||||
Step: xplan.Step{StepRef: " ", Action: model.RailOperationDebit},
|
Step: xplan.Step{StepRef: " ", Action: discovery.RailOperationDebit},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "mismatched step execution ref",
|
name: "mismatched step execution ref",
|
||||||
in: ExecuteInput{
|
in: ExecuteInput{
|
||||||
Payment: &agg.Payment{},
|
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"},
|
StepExecution: agg.StepExecution{StepRef: "s2"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package xplan
|
package xplan
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"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))
|
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[0], "hop.10.crypto.send", discovery.RailOperationSend, discovery.RailCrypto, model.ReportVisibilityBackoffice)
|
||||||
assertStep(t, graph.Steps[1], "hop.10.crypto.observe", model.RailOperationObserveConfirm, model.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", model.RailOperationExternalCredit, model.RailLedger, model.ReportVisibilityHidden)
|
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", model.RailOperationBlock, model.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", model.RailOperationSend, model.RailCardPayout, model.ReportVisibilityUser)
|
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", model.RailOperationObserveConfirm, model.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", model.RailOperationExternalDebit, model.RailLedger, model.ReportVisibilityHidden)
|
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", model.RailOperationRelease, model.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", model.RailOperationRelease, model.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) {
|
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)
|
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 {
|
if len(graph.Steps) != 6 {
|
||||||
t.Fatalf("expected 6 steps, got %d", len(graph.Steps))
|
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[0], "edge.10_20.ledger.block", discovery.RailOperationBlock, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||||
assertStep(t, graph.Steps[1], "hop.20.card_payout.send", model.RailOperationSend, model.RailCardPayout, model.ReportVisibilityUser)
|
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", model.RailOperationObserveConfirm, model.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", model.RailOperationExternalDebit, model.RailLedger, model.ReportVisibilityHidden)
|
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", model.RailOperationRelease, model.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", model.RailOperationRelease, model.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) {
|
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)
|
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 {
|
if len(graph.Steps) != 3 {
|
||||||
t.Fatalf("expected 3 steps, got %d", len(graph.Steps))
|
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[0], "hop.10.crypto.send", discovery.RailOperationSend, discovery.RailCrypto, model.ReportVisibilityBackoffice)
|
||||||
assertStep(t, graph.Steps[1], "hop.10.crypto.observe", model.RailOperationObserveConfirm, model.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", model.RailOperationExternalCredit, model.RailLedger, model.ReportVisibilityHidden)
|
assertStep(t, graph.Steps[2], "edge.10_20.ledger.credit", discovery.RailOperationExternalCredit, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCompile_ExternalViaSettlement_UsesFXConvertOnSettlementHop(t *testing.T) {
|
func TestCompile_ExternalViaSettlement_UsesFXConvertOnSettlementHop(t *testing.T) {
|
||||||
@@ -201,13 +202,13 @@ func TestCompile_ExternalViaSettlement_UsesFXConvertOnSettlementHop(t *testing.T
|
|||||||
convertCount := 0
|
convertCount := 0
|
||||||
sendCount := 0
|
sendCount := 0
|
||||||
for _, step := range graph.Steps {
|
for _, step := range graph.Steps {
|
||||||
if step.Rail != model.RailProviderSettlement {
|
if step.Rail != discovery.RailProviderSettlement {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if step.Action == model.RailOperationFXConvert {
|
if step.Action == discovery.RailOperationFXConvert {
|
||||||
convertCount++
|
convertCount++
|
||||||
}
|
}
|
||||||
if step.Action == model.RailOperationSend {
|
if step.Action == discovery.RailOperationSend {
|
||||||
sendCount++
|
sendCount++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -239,7 +240,7 @@ func TestCompile_InternalToInternal_UsesMove(t *testing.T) {
|
|||||||
if len(graph.Steps) != 1 {
|
if len(graph.Steps) != 1 {
|
||||||
t.Fatalf("expected 1 step, got %d", len(graph.Steps))
|
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) {
|
func TestCompile_GuardsArePrepended(t *testing.T) {
|
||||||
@@ -300,8 +301,8 @@ func TestCompile_SingleExternalFallback(t *testing.T) {
|
|||||||
if len(graph.Steps) != 2 {
|
if len(graph.Steps) != 2 {
|
||||||
t.Fatalf("expected 2 steps, got %d", len(graph.Steps))
|
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[0], "hop.0.card_payout.send", discovery.RailOperationSend, discovery.RailCardPayout, model.ReportVisibilityUser)
|
||||||
assertStep(t, graph.Steps[1], "hop.0.card_payout.observe", model.RailOperationObserveConfirm, model.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) {
|
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)
|
t.Fatalf("observe dependency mismatch: got=%v want=%v", got, want)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package xplan
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
@@ -12,8 +13,8 @@ import (
|
|||||||
func TestCompile_PolicyOverrideByRailPair(t *testing.T) {
|
func TestCompile_PolicyOverrideByRailPair(t *testing.T) {
|
||||||
compiler := New()
|
compiler := New()
|
||||||
|
|
||||||
cardRail := model.RailCardPayout
|
var cardRail model.Rail = discovery.RailCardPayout
|
||||||
ledgerRail := model.RailLedger
|
var ledgerRail model.Rail = discovery.RailLedger
|
||||||
|
|
||||||
graph, err := compiler.Compile(Input{
|
graph, err := compiler.Compile(Input{
|
||||||
IntentSnapshot: testIntent(model.PaymentKindPayout),
|
IntentSnapshot: testIntent(model.PaymentKindPayout),
|
||||||
@@ -29,18 +30,18 @@ func TestCompile_PolicyOverrideByRailPair(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "crypto-to-card-override",
|
ID: "crypto-to-card-override",
|
||||||
Match: EdgeMatch{
|
Match: EdgeMatch{
|
||||||
Source: EndpointMatch{Rail: railPtr(model.RailCrypto)},
|
Source: EndpointMatch{Rail: railPtr(discovery.RailCrypto)},
|
||||||
Target: EndpointMatch{Rail: railPtr(model.RailCardPayout)},
|
Target: EndpointMatch{Rail: railPtr(discovery.RailCardPayout)},
|
||||||
},
|
},
|
||||||
Steps: []PolicyStep{
|
Steps: []PolicyStep{
|
||||||
{Code: "custom.review", Action: model.RailOperationMove, Rail: &ledgerRail},
|
{Code: "custom.review", Action: discovery.RailOperationMove, Rail: &ledgerRail},
|
||||||
{Code: "custom.submit", Action: model.RailOperationSend, Rail: &cardRail, Visibility: model.ReportVisibilityUser},
|
{Code: "custom.submit", Action: discovery.RailOperationSend, Rail: &cardRail, Visibility: model.ReportVisibilityUser},
|
||||||
},
|
},
|
||||||
Success: []PolicyStep{
|
Success: []PolicyStep{
|
||||||
{Code: "custom.finalize", Action: model.RailOperationDebit, Rail: &ledgerRail},
|
{Code: "custom.finalize", Action: discovery.RailOperationDebit, Rail: &ledgerRail},
|
||||||
},
|
},
|
||||||
Failure: []PolicyStep{
|
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 {
|
if len(graph.Steps) != 4 {
|
||||||
t.Fatalf("expected 4 steps, got %d", len(graph.Steps))
|
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[0], "custom.review", discovery.RailOperationMove, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||||
assertStep(t, graph.Steps[1], "custom.submit", model.RailOperationSend, model.RailCardPayout, model.ReportVisibilityUser)
|
assertStep(t, graph.Steps[1], "custom.submit", discovery.RailOperationSend, discovery.RailCardPayout, model.ReportVisibilityUser)
|
||||||
assertStep(t, graph.Steps[2], "custom.finalize", model.RailOperationDebit, model.RailLedger, model.ReportVisibilityHidden)
|
assertStep(t, graph.Steps[2], "custom.finalize", discovery.RailOperationDebit, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||||
assertStep(t, graph.Steps[3], "custom.release", model.RailOperationRelease, model.RailLedger, model.ReportVisibilityHidden)
|
assertStep(t, graph.Steps[3], "custom.release", discovery.RailOperationRelease, discovery.RailLedger, model.ReportVisibilityHidden)
|
||||||
|
|
||||||
if graph.Steps[2].CommitPolicy != model.CommitPolicyAfterSuccess {
|
if graph.Steps[2].CommitPolicy != model.CommitPolicyAfterSuccess {
|
||||||
t.Fatalf("expected custom.finalize AFTER_SUCCESS, got %q", graph.Steps[2].CommitPolicy)
|
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) {
|
func TestCompile_PolicyPriorityAndCustodyMatching(t *testing.T) {
|
||||||
compiler := New()
|
compiler := New()
|
||||||
cardRail := model.RailCardPayout
|
var cardRail model.Rail = discovery.RailCardPayout
|
||||||
|
|
||||||
on := true
|
on := true
|
||||||
external := CustodyExternal
|
external := CustodyExternal
|
||||||
@@ -91,17 +92,17 @@ func TestCompile_PolicyPriorityAndCustodyMatching(t *testing.T) {
|
|||||||
Source: EndpointMatch{Custody: &external},
|
Source: EndpointMatch{Custody: &external},
|
||||||
Target: 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",
|
ID: "specific-crypto-card",
|
||||||
Enabled: &on,
|
Enabled: &on,
|
||||||
Priority: 10,
|
Priority: 10,
|
||||||
Match: EdgeMatch{
|
Match: EdgeMatch{
|
||||||
Source: EndpointMatch{Rail: railPtr(model.RailCrypto), Custody: &external},
|
Source: EndpointMatch{Rail: railPtr(discovery.RailCrypto), Custody: &external},
|
||||||
Target: EndpointMatch{Rail: railPtr(model.RailCardPayout), 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,
|
Enabled: &enabled,
|
||||||
Priority: 1,
|
Priority: 1,
|
||||||
Match: EdgeMatch{
|
Match: EdgeMatch{
|
||||||
Source: EndpointMatch{Rail: railPtr(model.RailLedger)},
|
Source: EndpointMatch{Rail: railPtr(discovery.RailLedger)},
|
||||||
Target: EndpointMatch{Rail: railPtr(model.RailCardPayout)},
|
Target: EndpointMatch{Rail: railPtr(discovery.RailCardPayout)},
|
||||||
},
|
},
|
||||||
Steps: []PolicyStep{
|
Steps: []PolicyStep{
|
||||||
{Code: "bad.step", Action: model.RailOperationUnspecified},
|
{Code: "bad.step", Action: discovery.RailOperationUnspecified},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package xplan
|
package xplan
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"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 {
|
func defaultVisibilityForAction(action model.RailOperation, role paymenttypes.QuoteRouteHopRole) model.ReportVisibility {
|
||||||
switch action {
|
switch action {
|
||||||
case model.RailOperationSend, model.RailOperationFXConvert, model.RailOperationObserveConfirm:
|
case discovery.RailOperationSend, discovery.RailOperationFXConvert, discovery.RailOperationObserveConfirm:
|
||||||
if role == paymenttypes.QuoteRouteHopRoleDestination {
|
if role == paymenttypes.QuoteRouteHopRoleDestination {
|
||||||
return model.ReportVisibilityUser
|
return model.ReportVisibilityUser
|
||||||
}
|
}
|
||||||
@@ -101,15 +102,15 @@ func defaultUserLabel(
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
switch action {
|
switch action {
|
||||||
case model.RailOperationSend:
|
case discovery.RailOperationSend:
|
||||||
if kind == model.PaymentKindPayout && rail == model.RailCardPayout {
|
if kind == model.PaymentKindPayout && rail == discovery.RailCardPayout {
|
||||||
return "Card payout submitted"
|
return "Card payout submitted"
|
||||||
}
|
}
|
||||||
return "Transfer submitted"
|
return "Transfer submitted"
|
||||||
case model.RailOperationFXConvert:
|
case discovery.RailOperationFXConvert:
|
||||||
return "FX conversion submitted"
|
return "FX conversion submitted"
|
||||||
case model.RailOperationObserveConfirm:
|
case discovery.RailOperationObserveConfirm:
|
||||||
if kind == model.PaymentKindPayout && rail == model.RailCardPayout {
|
if kind == model.PaymentKindPayout && rail == discovery.RailCardPayout {
|
||||||
return "Card payout confirmed"
|
return "Card payout confirmed"
|
||||||
}
|
}
|
||||||
return "Transfer confirmed"
|
return "Transfer confirmed"
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package xplan
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"slices"
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -17,16 +18,16 @@ func internalRailForBoundary(from normalizedHop, to normalizedHop) model.Rail {
|
|||||||
if isInternalRail(to.rail) {
|
if isInternalRail(to.rail) {
|
||||||
return to.rail
|
return to.rail
|
||||||
}
|
}
|
||||||
return model.RailLedger
|
return discovery.RailLedger
|
||||||
}
|
}
|
||||||
|
|
||||||
func isInternalRail(rail model.Rail) bool {
|
func isInternalRail(rail model.Rail) bool {
|
||||||
return rail == model.RailLedger
|
return rail == discovery.RailLedger
|
||||||
}
|
}
|
||||||
|
|
||||||
func isExternalRail(rail model.Rail) bool {
|
func isExternalRail(rail model.Rail) bool {
|
||||||
switch rail {
|
switch rail {
|
||||||
case model.RailCrypto, model.RailProviderSettlement, model.RailCardPayout, model.RailFiatOnRamp:
|
case discovery.RailCrypto, discovery.RailProviderSettlement, discovery.RailCardPayout, discovery.RailFiatOnRamp:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
@@ -58,7 +59,7 @@ func edgeCode(from normalizedHop, to normalizedHop, rail model.Rail, op string)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func railToken(rail model.Rail) string {
|
func railToken(rail model.Rail) string {
|
||||||
if rail == model.RailCardPayout {
|
if rail == discovery.RailCardPayout {
|
||||||
return "card_payout"
|
return "card_payout"
|
||||||
}
|
}
|
||||||
return strings.ToLower(strings.TrimSpace(string(rail)))
|
return strings.ToLower(strings.TrimSpace(string(rail)))
|
||||||
@@ -75,7 +76,7 @@ func normalizeRouteHops(route *paymenttypes.QuoteRouteSpecification, intent mode
|
|||||||
|
|
||||||
if len(route.Hops) == 0 {
|
if len(route.Hops) == 0 {
|
||||||
rail := normalizeRail(route.Rail)
|
rail := normalizeRail(route.Rail)
|
||||||
if rail == model.RailUnspecified {
|
if rail == discovery.RailUnspecified {
|
||||||
return nil, merrors.InvalidArgument("quote_snapshot.route.rail is required")
|
return nil, merrors.InvalidArgument("quote_snapshot.route.rail is required")
|
||||||
}
|
}
|
||||||
return []normalizedHop{
|
return []normalizedHop{
|
||||||
@@ -98,7 +99,7 @@ func normalizeRouteHops(route *paymenttypes.QuoteRouteSpecification, intent mode
|
|||||||
}
|
}
|
||||||
|
|
||||||
rail := normalizeRail(firstNonEmpty(hop.Rail, route.Rail))
|
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")
|
return nil, merrors.InvalidArgument("quote_snapshot.route.hops[" + itoa(i) + "].rail is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package xplan
|
package xplan
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/batchmeta"
|
"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{
|
ex.appendMain(Step{
|
||||||
StepCode: singleHopCode(hop, "debit"),
|
StepCode: singleHopCode(hop, "debit"),
|
||||||
Kind: StepKindFundsDebit,
|
Kind: StepKindFundsDebit,
|
||||||
Action: model.RailOperationDebit,
|
Action: discovery.RailOperationDebit,
|
||||||
Rail: hop.rail,
|
Rail: hop.rail,
|
||||||
HopIndex: hop.index,
|
HopIndex: hop.index,
|
||||||
HopRole: hop.role,
|
HopRole: hop.role,
|
||||||
@@ -30,7 +31,7 @@ func (s *svc) expandSingleHop(ex *expansion, hop normalizedHop, intent model.Pay
|
|||||||
ex.appendMain(Step{
|
ex.appendMain(Step{
|
||||||
StepCode: singleHopCode(hop, "credit"),
|
StepCode: singleHopCode(hop, "credit"),
|
||||||
Kind: StepKindFundsCredit,
|
Kind: StepKindFundsCredit,
|
||||||
Action: model.RailOperationCredit,
|
Action: discovery.RailOperationCredit,
|
||||||
Rail: hop.rail,
|
Rail: hop.rail,
|
||||||
HopIndex: hop.index,
|
HopIndex: hop.index,
|
||||||
HopRole: hop.role,
|
HopRole: hop.role,
|
||||||
@@ -40,7 +41,7 @@ func (s *svc) expandSingleHop(ex *expansion, hop normalizedHop, intent model.Pay
|
|||||||
ex.appendMain(Step{
|
ex.appendMain(Step{
|
||||||
StepCode: singleHopCode(hop, "move"),
|
StepCode: singleHopCode(hop, "move"),
|
||||||
Kind: StepKindFundsMove,
|
Kind: StepKindFundsMove,
|
||||||
Action: model.RailOperationMove,
|
Action: discovery.RailOperationMove,
|
||||||
Rail: hop.rail,
|
Rail: hop.rail,
|
||||||
HopIndex: hop.index,
|
HopIndex: hop.index,
|
||||||
HopRole: hop.role,
|
HopRole: hop.role,
|
||||||
@@ -70,7 +71,7 @@ func (s *svc) applyDefaultBoundary(
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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)
|
return s.applyBatchCardPayoutBoundary(ex, from, to, internalRail, intent, targets)
|
||||||
}
|
}
|
||||||
ex.appendMain(makeFundsBlockStep(from, to, internalRail))
|
ex.appendMain(makeFundsBlockStep(from, to, internalRail))
|
||||||
@@ -167,8 +168,8 @@ func appendGuards(ex *expansion, conditions *paymenttypes.QuoteExecutionConditio
|
|||||||
StepRef: QuoteReadinessGuardStepRef,
|
StepRef: QuoteReadinessGuardStepRef,
|
||||||
StepCode: string(GuardOperationQuoteReadinessGuard),
|
StepCode: string(GuardOperationQuoteReadinessGuard),
|
||||||
Kind: StepKindLiquidityCheck,
|
Kind: StepKindLiquidityCheck,
|
||||||
Action: model.RailOperationUnspecified,
|
Action: discovery.RailOperationUnspecified,
|
||||||
Rail: model.RailUnspecified,
|
Rail: discovery.RailUnspecified,
|
||||||
Visibility: model.ReportVisibilityHidden,
|
Visibility: model.ReportVisibilityHidden,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -178,8 +179,8 @@ func appendGuards(ex *expansion, conditions *paymenttypes.QuoteExecutionConditio
|
|||||||
StepRef: PrefundingGuardStepRef,
|
StepRef: PrefundingGuardStepRef,
|
||||||
StepCode: string(GuardOperationPrefundingEnsure),
|
StepCode: string(GuardOperationPrefundingEnsure),
|
||||||
Kind: StepKindPrefunding,
|
Kind: StepKindPrefunding,
|
||||||
Action: model.RailOperationUnspecified,
|
Action: discovery.RailOperationUnspecified,
|
||||||
Rail: model.RailUnspecified,
|
Rail: discovery.RailUnspecified,
|
||||||
Visibility: model.ReportVisibilityHidden,
|
Visibility: model.ReportVisibilityHidden,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -207,22 +208,22 @@ func makeRailSendStep(hop normalizedHop, intent model.PaymentIntent) Step {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func railActionForHop(hop normalizedHop) (model.RailOperation, string) {
|
func railActionForHop(hop normalizedHop) (model.RailOperation, string) {
|
||||||
if hop.rail == model.RailProviderSettlement {
|
if hop.rail == discovery.RailProviderSettlement {
|
||||||
return model.RailOperationFXConvert, "fx_convert"
|
return discovery.RailOperationFXConvert, "fx_convert"
|
||||||
}
|
}
|
||||||
return model.RailOperationSend, "send"
|
return discovery.RailOperationSend, "send"
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeRailObserveStep(hop normalizedHop, intent model.PaymentIntent) Step {
|
func makeRailObserveStep(hop normalizedHop, intent model.PaymentIntent) Step {
|
||||||
visibility := defaultVisibilityForAction(model.RailOperationObserveConfirm, hop.role)
|
visibility := defaultVisibilityForAction(discovery.RailOperationObserveConfirm, hop.role)
|
||||||
userLabel := ""
|
userLabel := ""
|
||||||
if visibility == model.ReportVisibilityUser {
|
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{
|
return Step{
|
||||||
StepCode: singleHopCode(hop, "observe"),
|
StepCode: singleHopCode(hop, "observe"),
|
||||||
Kind: StepKindRailObserve,
|
Kind: StepKindRailObserve,
|
||||||
Action: model.RailOperationObserveConfirm,
|
Action: discovery.RailOperationObserveConfirm,
|
||||||
Rail: hop.rail,
|
Rail: hop.rail,
|
||||||
Gateway: hop.gateway,
|
Gateway: hop.gateway,
|
||||||
InstanceID: hop.instanceID,
|
InstanceID: hop.instanceID,
|
||||||
@@ -237,7 +238,7 @@ func makeFundsCreditStep(from normalizedHop, to normalizedHop, rail model.Rail)
|
|||||||
return Step{
|
return Step{
|
||||||
StepCode: edgeCode(from, to, rail, "credit"),
|
StepCode: edgeCode(from, to, rail, "credit"),
|
||||||
Kind: StepKindFundsCredit,
|
Kind: StepKindFundsCredit,
|
||||||
Action: model.RailOperationExternalCredit,
|
Action: discovery.RailOperationExternalCredit,
|
||||||
Rail: rail,
|
Rail: rail,
|
||||||
HopIndex: to.index,
|
HopIndex: to.index,
|
||||||
HopRole: paymenttypes.QuoteRouteHopRoleTransit,
|
HopRole: paymenttypes.QuoteRouteHopRoleTransit,
|
||||||
@@ -249,7 +250,7 @@ func makeFundsBlockStep(from normalizedHop, to normalizedHop, rail model.Rail) S
|
|||||||
return Step{
|
return Step{
|
||||||
StepCode: edgeCode(from, to, rail, "block"),
|
StepCode: edgeCode(from, to, rail, "block"),
|
||||||
Kind: StepKindFundsBlock,
|
Kind: StepKindFundsBlock,
|
||||||
Action: model.RailOperationBlock,
|
Action: discovery.RailOperationBlock,
|
||||||
Rail: rail,
|
Rail: rail,
|
||||||
HopIndex: to.index,
|
HopIndex: to.index,
|
||||||
HopRole: paymenttypes.QuoteRouteHopRoleTransit,
|
HopRole: paymenttypes.QuoteRouteHopRoleTransit,
|
||||||
@@ -261,7 +262,7 @@ func makeFundsMoveStep(from normalizedHop, to normalizedHop, rail model.Rail) St
|
|||||||
return Step{
|
return Step{
|
||||||
StepCode: edgeCode(from, to, rail, "move"),
|
StepCode: edgeCode(from, to, rail, "move"),
|
||||||
Kind: StepKindFundsMove,
|
Kind: StepKindFundsMove,
|
||||||
Action: model.RailOperationMove,
|
Action: discovery.RailOperationMove,
|
||||||
Rail: rail,
|
Rail: rail,
|
||||||
HopIndex: to.index,
|
HopIndex: to.index,
|
||||||
HopRole: paymenttypes.QuoteRouteHopRoleTransit,
|
HopRole: paymenttypes.QuoteRouteHopRoleTransit,
|
||||||
@@ -312,7 +313,7 @@ func appendSettlementBranchesWithMetadata(
|
|||||||
successStep := Step{
|
successStep := Step{
|
||||||
StepCode: edgeCode(from, to, rail, "debit"),
|
StepCode: edgeCode(from, to, rail, "debit"),
|
||||||
Kind: StepKindFundsDebit,
|
Kind: StepKindFundsDebit,
|
||||||
Action: model.RailOperationExternalDebit,
|
Action: discovery.RailOperationExternalDebit,
|
||||||
DependsOn: []string{anchorObserveRef},
|
DependsOn: []string{anchorObserveRef},
|
||||||
Rail: rail,
|
Rail: rail,
|
||||||
HopIndex: to.index,
|
HopIndex: to.index,
|
||||||
@@ -328,7 +329,7 @@ func appendSettlementBranchesWithMetadata(
|
|||||||
sendFailureStep := Step{
|
sendFailureStep := Step{
|
||||||
StepCode: edgeCode(from, to, rail, "release"),
|
StepCode: edgeCode(from, to, rail, "release"),
|
||||||
Kind: StepKindFundsRelease,
|
Kind: StepKindFundsRelease,
|
||||||
Action: model.RailOperationRelease,
|
Action: discovery.RailOperationRelease,
|
||||||
DependsOn: []string{anchorSendRef},
|
DependsOn: []string{anchorSendRef},
|
||||||
Rail: rail,
|
Rail: rail,
|
||||||
HopIndex: to.index,
|
HopIndex: to.index,
|
||||||
@@ -344,7 +345,7 @@ func appendSettlementBranchesWithMetadata(
|
|||||||
failureStep := Step{
|
failureStep := Step{
|
||||||
StepCode: edgeCode(from, to, rail, "release"),
|
StepCode: edgeCode(from, to, rail, "release"),
|
||||||
Kind: StepKindFundsRelease,
|
Kind: StepKindFundsRelease,
|
||||||
Action: model.RailOperationRelease,
|
Action: discovery.RailOperationRelease,
|
||||||
DependsOn: []string{anchorObserveRef},
|
DependsOn: []string{anchorObserveRef},
|
||||||
Rail: rail,
|
Rail: rail,
|
||||||
HopIndex: to.index,
|
HopIndex: to.index,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package xplan
|
package xplan
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
@@ -127,12 +128,12 @@ func policyStepToStep(spec PolicyStep, from normalizedHop, to normalizedHop, int
|
|||||||
}
|
}
|
||||||
|
|
||||||
action := normalizeAction(spec.Action)
|
action := normalizeAction(spec.Action)
|
||||||
if action == model.RailOperationUnspecified {
|
if action == discovery.RailOperationUnspecified {
|
||||||
return Step{}, merrors.InvalidArgument("action is required")
|
return Step{}, merrors.InvalidArgument("action is required")
|
||||||
}
|
}
|
||||||
|
|
||||||
rail := inferPolicyRail(spec, action, from, to)
|
rail := inferPolicyRail(spec, action, from, to)
|
||||||
if rail == model.RailUnspecified {
|
if rail == discovery.RailUnspecified {
|
||||||
return Step{}, merrors.InvalidArgument("rail could not be inferred")
|
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 {
|
func normalizeAction(action model.RailOperation) model.RailOperation {
|
||||||
switch strings.ToUpper(strings.TrimSpace(string(action))) {
|
switch strings.ToUpper(strings.TrimSpace(string(action))) {
|
||||||
case string(model.RailOperationDebit):
|
case string(discovery.RailOperationDebit):
|
||||||
return model.RailOperationDebit
|
return discovery.RailOperationDebit
|
||||||
case string(model.RailOperationCredit):
|
case string(discovery.RailOperationCredit):
|
||||||
return model.RailOperationCredit
|
return discovery.RailOperationCredit
|
||||||
case string(model.RailOperationExternalDebit):
|
case string(discovery.RailOperationExternalDebit):
|
||||||
return model.RailOperationExternalDebit
|
return discovery.RailOperationExternalDebit
|
||||||
case string(model.RailOperationExternalCredit):
|
case string(discovery.RailOperationExternalCredit):
|
||||||
return model.RailOperationExternalCredit
|
return discovery.RailOperationExternalCredit
|
||||||
case string(model.RailOperationMove):
|
case string(discovery.RailOperationMove):
|
||||||
return model.RailOperationMove
|
return discovery.RailOperationMove
|
||||||
case string(model.RailOperationSend):
|
case string(discovery.RailOperationSend):
|
||||||
return model.RailOperationSend
|
return discovery.RailOperationSend
|
||||||
case string(model.RailOperationFee):
|
case string(discovery.RailOperationFee):
|
||||||
return model.RailOperationFee
|
return discovery.RailOperationFee
|
||||||
case string(model.RailOperationObserveConfirm):
|
case string(discovery.RailOperationObserveConfirm):
|
||||||
return model.RailOperationObserveConfirm
|
return discovery.RailOperationObserveConfirm
|
||||||
case string(model.RailOperationFXConvert):
|
case string(discovery.RailOperationFXConvert):
|
||||||
return model.RailOperationFXConvert
|
return discovery.RailOperationFXConvert
|
||||||
case string(model.RailOperationBlock):
|
case string(discovery.RailOperationBlock):
|
||||||
return model.RailOperationBlock
|
return discovery.RailOperationBlock
|
||||||
case string(model.RailOperationRelease):
|
case string(discovery.RailOperationRelease):
|
||||||
return model.RailOperationRelease
|
return discovery.RailOperationRelease
|
||||||
default:
|
default:
|
||||||
return model.RailOperationUnspecified
|
return discovery.RailOperationUnspecified
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,18 +200,18 @@ func inferPolicyRail(spec PolicyStep, action model.RailOperation, from normalize
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case model.RailOperationSend, model.RailOperationFXConvert, model.RailOperationObserveConfirm, model.RailOperationFee:
|
case discovery.RailOperationSend, discovery.RailOperationFXConvert, discovery.RailOperationObserveConfirm, discovery.RailOperationFee:
|
||||||
return to.rail
|
return to.rail
|
||||||
case model.RailOperationBlock,
|
case discovery.RailOperationBlock,
|
||||||
model.RailOperationRelease,
|
discovery.RailOperationRelease,
|
||||||
model.RailOperationDebit,
|
discovery.RailOperationDebit,
|
||||||
model.RailOperationCredit,
|
discovery.RailOperationCredit,
|
||||||
model.RailOperationExternalDebit,
|
discovery.RailOperationExternalDebit,
|
||||||
model.RailOperationExternalCredit,
|
discovery.RailOperationExternalCredit,
|
||||||
model.RailOperationMove:
|
discovery.RailOperationMove:
|
||||||
return internalRailForBoundary(from, to)
|
return internalRailForBoundary(from, to)
|
||||||
default:
|
default:
|
||||||
return model.RailUnspecified
|
return discovery.RailUnspecified
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +221,7 @@ func resolveStepContext(
|
|||||||
from normalizedHop,
|
from normalizedHop,
|
||||||
to normalizedHop,
|
to normalizedHop,
|
||||||
) (uint32, paymenttypes.QuoteRouteHopRole, string, string) {
|
) (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
|
return to.index, to.role, to.gateway, to.instanceID
|
||||||
}
|
}
|
||||||
if rail == from.rail {
|
if rail == from.rail {
|
||||||
@@ -234,19 +235,19 @@ func resolveStepContext(
|
|||||||
|
|
||||||
func kindForAction(action model.RailOperation) StepKind {
|
func kindForAction(action model.RailOperation) StepKind {
|
||||||
switch action {
|
switch action {
|
||||||
case model.RailOperationSend, model.RailOperationFXConvert:
|
case discovery.RailOperationSend, discovery.RailOperationFXConvert:
|
||||||
return StepKindRailSend
|
return StepKindRailSend
|
||||||
case model.RailOperationObserveConfirm:
|
case discovery.RailOperationObserveConfirm:
|
||||||
return StepKindRailObserve
|
return StepKindRailObserve
|
||||||
case model.RailOperationCredit, model.RailOperationExternalCredit:
|
case discovery.RailOperationCredit, discovery.RailOperationExternalCredit:
|
||||||
return StepKindFundsCredit
|
return StepKindFundsCredit
|
||||||
case model.RailOperationDebit, model.RailOperationExternalDebit:
|
case discovery.RailOperationDebit, discovery.RailOperationExternalDebit:
|
||||||
return StepKindFundsDebit
|
return StepKindFundsDebit
|
||||||
case model.RailOperationMove:
|
case discovery.RailOperationMove:
|
||||||
return StepKindFundsMove
|
return StepKindFundsMove
|
||||||
case model.RailOperationBlock:
|
case discovery.RailOperationBlock:
|
||||||
return StepKindFundsBlock
|
return StepKindFundsBlock
|
||||||
case model.RailOperationRelease:
|
case discovery.RailOperationRelease:
|
||||||
return StepKindFundsRelease
|
return StepKindFundsRelease
|
||||||
default:
|
default:
|
||||||
return StepKindUnspecified
|
return StepKindUnspecified
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package orchestrator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -42,7 +43,7 @@ func (e *gatewayCardPayoutExecutor) ExecuteCardPayout(ctx context.Context, req s
|
|||||||
if e == nil || e.mntxClient == nil {
|
if e == nil || e.mntxClient == nil {
|
||||||
return nil, merrors.InvalidArgument("card payout send: mntx client is required")
|
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")
|
return nil, merrors.InvalidArgument("card payout send: unsupported action")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package orchestrator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -78,8 +79,8 @@ func TestGatewayCardPayoutExecutor_ExecuteCardPayout_SubmitsCardPayout(t *testin
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "hop_4_card_payout_send",
|
StepRef: "hop_4_card_payout_send",
|
||||||
StepCode: "hop.4.card_payout.send",
|
StepCode: "hop.4.card_payout.send",
|
||||||
Action: model.RailOperationSend,
|
Action: discovery.RailOperationSend,
|
||||||
Rail: model.RailCardPayout,
|
Rail: discovery.RailCardPayout,
|
||||||
Gateway: paymenttypes.DefaultCardsGatewayID,
|
Gateway: paymenttypes.DefaultCardsGatewayID,
|
||||||
InstanceID: 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 {
|
if got, want := payoutReq.GetMetadata()[settlementMetadataQuoteRef], "quote-1"; got != want {
|
||||||
t.Fatalf("quote_ref metadata mismatch: got=%q want=%q", 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)
|
t.Fatalf("outgoing_leg metadata mismatch: got=%q want=%q", got, want)
|
||||||
}
|
}
|
||||||
if len(out.StepExecution.ExternalRefs) != 3 {
|
if len(out.StepExecution.ExternalRefs) != 3 {
|
||||||
@@ -190,8 +191,8 @@ func TestGatewayCardPayoutExecutor_ExecuteCardPayout_UsesStepMetadataOverrides(t
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "hop_4_card_payout_send",
|
StepRef: "hop_4_card_payout_send",
|
||||||
StepCode: "hop.4.card_payout.send",
|
StepCode: "hop.4.card_payout.send",
|
||||||
Action: model.RailOperationSend,
|
Action: discovery.RailOperationSend,
|
||||||
Rail: model.RailCardPayout,
|
Rail: discovery.RailCardPayout,
|
||||||
Gateway: paymenttypes.DefaultCardsGatewayID,
|
Gateway: paymenttypes.DefaultCardsGatewayID,
|
||||||
InstanceID: paymenttypes.DefaultCardsGatewayID,
|
InstanceID: paymenttypes.DefaultCardsGatewayID,
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
@@ -257,8 +258,8 @@ func TestGatewayCardPayoutExecutor_ExecuteCardPayout_RequiresMntxClient(t *testi
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "hop_4_card_payout_send",
|
StepRef: "hop_4_card_payout_send",
|
||||||
StepCode: "hop.4.card_payout.send",
|
StepCode: "hop.4.card_payout.send",
|
||||||
Action: model.RailOperationSend,
|
Action: discovery.RailOperationSend,
|
||||||
Rail: model.RailCardPayout,
|
Rail: discovery.RailCardPayout,
|
||||||
},
|
},
|
||||||
StepExecution: agg.StepExecution{
|
StepExecution: agg.StepExecution{
|
||||||
StepRef: "hop_4_card_payout_send",
|
StepRef: "hop_4_card_payout_send",
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package orchestrator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
|
"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))
|
action := model.ParseRailOperation(string(req.Step.Action))
|
||||||
switch action {
|
switch action {
|
||||||
case model.RailOperationSend, model.RailOperationFee:
|
case discovery.RailOperationSend, discovery.RailOperationFee:
|
||||||
default:
|
default:
|
||||||
return nil, merrors.InvalidArgument("crypto send: unsupported action")
|
return nil, merrors.InvalidArgument("crypto send: unsupported action")
|
||||||
}
|
}
|
||||||
@@ -112,7 +113,7 @@ func (e *gatewayCryptoExecutor) resolveGateway(ctx context.Context, step xplan.S
|
|||||||
cryptoCount := 0
|
cryptoCount := 0
|
||||||
for i := range items {
|
for i := range items {
|
||||||
item := items[i]
|
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
|
continue
|
||||||
}
|
}
|
||||||
cryptoCount++
|
cryptoCount++
|
||||||
@@ -242,7 +243,7 @@ func (e *gatewayCryptoExecutor) resolveCardFundingAddress(payment *agg.Payment,
|
|||||||
return "", merrors.InvalidArgument("crypto send: card gateway route is not configured")
|
return "", merrors.InvalidArgument("crypto send: card gateway route is not configured")
|
||||||
}
|
}
|
||||||
switch action {
|
switch action {
|
||||||
case model.RailOperationFee:
|
case discovery.RailOperationFee:
|
||||||
if feeAddress := strings.TrimSpace(route.FeeAddress); feeAddress != "" {
|
if feeAddress := strings.TrimSpace(route.FeeAddress); feeAddress != "" {
|
||||||
return feeAddress, nil
|
return feeAddress, nil
|
||||||
}
|
}
|
||||||
@@ -262,7 +263,7 @@ func destinationCardGatewayKey(payment *agg.Payment) string {
|
|||||||
fallback := ""
|
fallback := ""
|
||||||
for i := range hops {
|
for i := range hops {
|
||||||
hop := hops[i]
|
hop := hops[i]
|
||||||
if hop == nil || model.ParseRail(hop.Rail) != model.RailCardPayout {
|
if hop == nil || model.ParseRail(hop.Rail) != discovery.RailCardPayout {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
key := firstNonEmpty(strings.TrimSpace(hop.Gateway), strings.TrimSpace(hop.InstanceID))
|
key := firstNonEmpty(strings.TrimSpace(hop.Gateway), strings.TrimSpace(hop.InstanceID))
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package orchestrator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
chainclient "github.com/tech/sendico/gateway/chain/client"
|
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",
|
ID: "crypto_rail_gateway_arbitrum_sepolia",
|
||||||
InstanceID: "crypto_rail_gateway_arbitrum_sepolia",
|
InstanceID: "crypto_rail_gateway_arbitrum_sepolia",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
InvokeURI: "grpc://crypto-gateway",
|
InvokeURI: "grpc://crypto-gateway",
|
||||||
IsEnabled: true,
|
IsEnabled: true,
|
||||||
},
|
},
|
||||||
@@ -83,8 +84,8 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_SubmitsTransfer(t *testing.T) {
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "hop_1_crypto_send",
|
StepRef: "hop_1_crypto_send",
|
||||||
StepCode: "hop.1.crypto.send",
|
StepCode: "hop.1.crypto.send",
|
||||||
Action: model.RailOperationSend,
|
Action: discovery.RailOperationSend,
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Gateway: "crypto_rail_gateway_arbitrum_sepolia",
|
Gateway: "crypto_rail_gateway_arbitrum_sepolia",
|
||||||
InstanceID: "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",
|
ID: "crypto_1",
|
||||||
InstanceID: "crypto_1",
|
InstanceID: "crypto_1",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
InvokeURI: "grpc://crypto-gateway",
|
InvokeURI: "grpc://crypto-gateway",
|
||||||
IsEnabled: true,
|
IsEnabled: true,
|
||||||
},
|
},
|
||||||
@@ -178,8 +179,8 @@ func TestGatewayCryptoExecutor_ExecuteCrypto_MissingCardRoute(t *testing.T) {
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "hop_1_crypto_send",
|
StepRef: "hop_1_crypto_send",
|
||||||
StepCode: "hop.1.crypto.send",
|
StepCode: "hop.1.crypto.send",
|
||||||
Action: model.RailOperationSend,
|
Action: discovery.RailOperationSend,
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Gateway: "crypto_1",
|
Gateway: "crypto_1",
|
||||||
InstanceID: "crypto_1",
|
InstanceID: "crypto_1",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package orchestrator
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -503,7 +504,7 @@ func (s *Service) resolveObserveGateway(ctx context.Context, payment *agg.Paymen
|
|||||||
gatewayRegistry: s.gatewayRegistry,
|
gatewayRegistry: s.gatewayRegistry,
|
||||||
}
|
}
|
||||||
step := xplan.Step{
|
step := xplan.Step{
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
}
|
}
|
||||||
if gatewayID := strings.TrimSpace(candidate.gatewayInstanceID); gatewayID != "" {
|
if gatewayID := strings.TrimSpace(candidate.gatewayInstanceID); gatewayID != "" {
|
||||||
step.InstanceID = gatewayID
|
step.InstanceID = gatewayID
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package orchestrator
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
|
||||||
@@ -351,14 +352,14 @@ func TestResolveObserveGateway_UsesExternalRefGatewayInstanceAcrossRails(t *test
|
|||||||
{
|
{
|
||||||
ID: "payment_gateway_settlement",
|
ID: "payment_gateway_settlement",
|
||||||
InstanceID: "payment_gateway_settlement",
|
InstanceID: "payment_gateway_settlement",
|
||||||
Rail: model.RailProviderSettlement,
|
Rail: discovery.RailProviderSettlement,
|
||||||
InvokeURI: "grpc://tgsettle-gateway",
|
InvokeURI: "grpc://tgsettle-gateway",
|
||||||
IsEnabled: true,
|
IsEnabled: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "crypto_rail_gateway_tron_nile",
|
ID: "crypto_rail_gateway_tron_nile",
|
||||||
InstanceID: "crypto_rail_gateway_tron_nile",
|
InstanceID: "crypto_rail_gateway_tron_nile",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
InvokeURI: "grpc://tron-gateway",
|
InvokeURI: "grpc://tron-gateway",
|
||||||
IsEnabled: true,
|
IsEnabled: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package orchestrator
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
"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, err := resolver.resolveGateway(ctx, xplan.Step{
|
||||||
Gateway: hopGateway,
|
Gateway: hopGateway,
|
||||||
InstanceID: hopInstanceID,
|
InstanceID: hopInstanceID,
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "guard.liquidity_probe_error", err.Error()
|
return "guard.liquidity_probe_error", err.Error()
|
||||||
@@ -167,7 +168,7 @@ func sourceCryptoHop(payment *agg.Payment) (gateway string, instanceID string, o
|
|||||||
fallbackInstance := ""
|
fallbackInstance := ""
|
||||||
for i := range hops {
|
for i := range hops {
|
||||||
hop := hops[i]
|
hop := hops[i]
|
||||||
if hop == nil || model.ParseRail(hop.Rail) != model.RailCrypto {
|
if hop == nil || model.ParseRail(hop.Rail) != discovery.RailCrypto {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
gw := strings.TrimSpace(hop.Gateway)
|
gw := strings.TrimSpace(hop.Gateway)
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package orchestrator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ func TestGatewayGuardExecutor_ExecuteGuard_LiquidityProbePasses(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "crypto_1",
|
ID: "crypto_1",
|
||||||
InstanceID: "crypto_1",
|
InstanceID: "crypto_1",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
InvokeURI: "grpc://crypto-gateway",
|
InvokeURI: "grpc://crypto-gateway",
|
||||||
IsEnabled: true,
|
IsEnabled: true,
|
||||||
},
|
},
|
||||||
@@ -95,7 +96,7 @@ func TestGatewayGuardExecutor_ExecuteGuard_InsufficientLiquidity(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "crypto_1",
|
ID: "crypto_1",
|
||||||
InstanceID: "crypto_1",
|
InstanceID: "crypto_1",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
InvokeURI: "grpc://crypto-gateway",
|
InvokeURI: "grpc://crypto-gateway",
|
||||||
IsEnabled: true,
|
IsEnabled: true,
|
||||||
},
|
},
|
||||||
@@ -150,7 +151,7 @@ func TestGatewayGuardExecutor_ExecuteGuard_ReadinessStopsBeforeProbe(t *testing.
|
|||||||
{
|
{
|
||||||
ID: "crypto_1",
|
ID: "crypto_1",
|
||||||
InstanceID: "crypto_1",
|
InstanceID: "crypto_1",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
InvokeURI: "grpc://crypto-gateway",
|
InvokeURI: "grpc://crypto-gateway",
|
||||||
IsEnabled: true,
|
IsEnabled: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package orchestrator
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
ledgerclient "github.com/tech/sendico/ledger/client"
|
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))
|
action := model.ParseRailOperation(string(req.Step.Action))
|
||||||
switch action {
|
switch action {
|
||||||
case model.RailOperationDebit,
|
case discovery.RailOperationDebit,
|
||||||
model.RailOperationCredit,
|
discovery.RailOperationCredit,
|
||||||
model.RailOperationExternalDebit,
|
discovery.RailOperationExternalDebit,
|
||||||
model.RailOperationExternalCredit,
|
discovery.RailOperationExternalCredit,
|
||||||
model.RailOperationMove,
|
discovery.RailOperationMove,
|
||||||
model.RailOperationBlock,
|
discovery.RailOperationBlock,
|
||||||
model.RailOperationRelease:
|
discovery.RailOperationRelease:
|
||||||
default:
|
default:
|
||||||
return nil, merrors.InvalidArgument("ledger step: unsupported action")
|
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
|
var resp *ledgerv1.PostResponse
|
||||||
switch action {
|
switch action {
|
||||||
case model.RailOperationExternalCredit:
|
case discovery.RailOperationExternalCredit:
|
||||||
resp, err = e.ledgerClient.PostExternalCreditWithCharges(ctx, &ledgerv1.PostCreditRequest{
|
resp, err = e.ledgerClient.PostExternalCreditWithCharges(ctx, &ledgerv1.PostCreditRequest{
|
||||||
IdempotencyKey: idempotencyKey,
|
IdempotencyKey: idempotencyKey,
|
||||||
OrganizationRef: organizationRef,
|
OrganizationRef: organizationRef,
|
||||||
@@ -79,7 +80,7 @@ func (e *gatewayLedgerExecutor) ExecuteLedger(ctx context.Context, req sexec.Ste
|
|||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
Role: ledgerRoleToProto(roles.to),
|
Role: ledgerRoleToProto(roles.to),
|
||||||
})
|
})
|
||||||
case model.RailOperationExternalDebit:
|
case discovery.RailOperationExternalDebit:
|
||||||
resp, err = e.ledgerClient.PostExternalDebitWithCharges(ctx, &ledgerv1.PostDebitRequest{
|
resp, err = e.ledgerClient.PostExternalDebitWithCharges(ctx, &ledgerv1.PostDebitRequest{
|
||||||
IdempotencyKey: idempotencyKey,
|
IdempotencyKey: idempotencyKey,
|
||||||
OrganizationRef: organizationRef,
|
OrganizationRef: organizationRef,
|
||||||
@@ -138,7 +139,7 @@ func ledgerAmountForStep(
|
|||||||
case isLedgerExternalRail(fromRail) && isLedgerInternalRail(toRail):
|
case isLedgerExternalRail(fromRail) && isLedgerInternalRail(toRail):
|
||||||
return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required")
|
return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required")
|
||||||
case isLedgerInternalRail(fromRail) && isLedgerExternalRail(toRail):
|
case isLedgerInternalRail(fromRail) && isLedgerExternalRail(toRail):
|
||||||
if toRail == model.RailCardPayout {
|
if toRail == discovery.RailCardPayout {
|
||||||
return protoMoneyRequired(payoutMoney, "ledger step: payout amount is required")
|
return protoMoneyRequired(payoutMoney, "ledger step: payout amount is required")
|
||||||
}
|
}
|
||||||
return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required")
|
return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required")
|
||||||
@@ -148,14 +149,14 @@ func ledgerAmountForStep(
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case model.RailOperationCredit, model.RailOperationExternalCredit:
|
case discovery.RailOperationCredit, discovery.RailOperationExternalCredit:
|
||||||
return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required")
|
return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required")
|
||||||
case model.RailOperationDebit, model.RailOperationExternalDebit:
|
case discovery.RailOperationDebit, discovery.RailOperationExternalDebit:
|
||||||
if sourceMoney != nil {
|
if sourceMoney != nil {
|
||||||
return protoMoneyRequired(sourceMoney, "ledger step: source amount is required")
|
return protoMoneyRequired(sourceMoney, "ledger step: source amount is required")
|
||||||
}
|
}
|
||||||
return protoMoneyRequired(settlementMoney, "ledger step: settlement 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 {
|
if settlementMoney != nil {
|
||||||
return protoMoneyRequired(settlementMoney, "ledger step: settlement amount is required")
|
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]))
|
mode := strings.ToLower(strings.TrimSpace(step.Metadata[ledgerMetadataMode]))
|
||||||
switch action {
|
switch action {
|
||||||
case model.RailOperationBlock:
|
case discovery.RailOperationBlock:
|
||||||
return ledgerRoles{from: account_role.AccountRoleOperating, to: account_role.AccountRoleHold}, nil
|
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
|
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
|
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" {
|
if mode == "finalize_debit" {
|
||||||
return ledgerRoles{from: account_role.AccountRoleHold, to: account_role.AccountRoleTransit}, nil
|
return ledgerRoles{from: account_role.AccountRoleHold, to: account_role.AccountRoleTransit}, nil
|
||||||
}
|
}
|
||||||
return ledgerRoles{from: account_role.AccountRoleOperating, 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
|
return ledgerRoles{from: account_role.AccountRoleOperating, to: account_role.AccountRoleTransit}, nil
|
||||||
default:
|
default:
|
||||||
return ledgerRoles{}, merrors.InvalidArgument("ledger step: unsupported action")
|
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) {
|
func ledgerBoundaryRails(payment *agg.Payment, step xplan.Step) (model.Rail, model.Rail, bool) {
|
||||||
fromIndex, toIndex, ok := parseLedgerEdgeStepCode(step.StepCode)
|
fromIndex, toIndex, ok := parseLedgerEdgeStepCode(step.StepCode)
|
||||||
if !ok || payment == nil || payment.QuoteSnapshot == nil || payment.QuoteSnapshot.Route == nil {
|
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
|
var fromRail model.Rail = discovery.RailUnspecified
|
||||||
toRail := model.RailUnspecified
|
var toRail model.Rail = discovery.RailUnspecified
|
||||||
for i := range payment.QuoteSnapshot.Route.Hops {
|
for i := range payment.QuoteSnapshot.Route.Hops {
|
||||||
hop := payment.QuoteSnapshot.Route.Hops[i]
|
hop := payment.QuoteSnapshot.Route.Hops[i]
|
||||||
if hop == nil {
|
if hop == nil {
|
||||||
@@ -359,8 +360,8 @@ func ledgerBoundaryRails(payment *agg.Payment, step xplan.Step) (model.Rail, mod
|
|||||||
toRail = model.ParseRail(hop.Rail)
|
toRail = model.ParseRail(hop.Rail)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if fromRail == model.RailUnspecified || toRail == model.RailUnspecified {
|
if fromRail == discovery.RailUnspecified || toRail == discovery.RailUnspecified {
|
||||||
return model.RailUnspecified, model.RailUnspecified, false
|
return discovery.RailUnspecified, discovery.RailUnspecified, false
|
||||||
}
|
}
|
||||||
return fromRail, toRail, true
|
return fromRail, toRail, true
|
||||||
}
|
}
|
||||||
@@ -385,12 +386,12 @@ func parseLedgerEdgeStepCode(stepCode string) (uint32, uint32, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isLedgerInternalRail(rail model.Rail) bool {
|
func isLedgerInternalRail(rail model.Rail) bool {
|
||||||
return rail == model.RailLedger
|
return rail == discovery.RailLedger
|
||||||
}
|
}
|
||||||
|
|
||||||
func isLedgerExternalRail(rail model.Rail) bool {
|
func isLedgerExternalRail(rail model.Rail) bool {
|
||||||
switch rail {
|
switch rail {
|
||||||
case model.RailCrypto, model.RailProviderSettlement, model.RailCardPayout, model.RailFiatOnRamp:
|
case discovery.RailCrypto, discovery.RailProviderSettlement, discovery.RailCardPayout, discovery.RailFiatOnRamp:
|
||||||
return true
|
return true
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package orchestrator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -37,8 +38,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_CreditUsesSourceAmountAndDefaultRol
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "edge_1_2_ledger_credit",
|
StepRef: "edge_1_2_ledger_credit",
|
||||||
StepCode: "edge.1_2.ledger.credit",
|
StepCode: "edge.1_2.ledger.credit",
|
||||||
Action: model.RailOperationCredit,
|
Action: discovery.RailOperationCredit,
|
||||||
Rail: model.RailLedger,
|
Rail: discovery.RailLedger,
|
||||||
},
|
},
|
||||||
StepExecution: agg.StepExecution{
|
StepExecution: agg.StepExecution{
|
||||||
StepRef: "edge_1_2_ledger_credit",
|
StepRef: "edge_1_2_ledger_credit",
|
||||||
@@ -107,8 +108,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_ExternalCreditUsesPostCreditWithCha
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "edge_1_2_ledger_credit",
|
StepRef: "edge_1_2_ledger_credit",
|
||||||
StepCode: "edge.1_2.ledger.credit",
|
StepCode: "edge.1_2.ledger.credit",
|
||||||
Action: model.RailOperationExternalCredit,
|
Action: discovery.RailOperationExternalCredit,
|
||||||
Rail: model.RailLedger,
|
Rail: discovery.RailLedger,
|
||||||
},
|
},
|
||||||
StepExecution: agg.StepExecution{
|
StepExecution: agg.StepExecution{
|
||||||
StepRef: "edge_1_2_ledger_credit",
|
StepRef: "edge_1_2_ledger_credit",
|
||||||
@@ -174,8 +175,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_ExternalDebitUsesPostDebitWithCharg
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "edge_3_4_ledger_debit",
|
StepRef: "edge_3_4_ledger_debit",
|
||||||
StepCode: "edge.3_4.ledger.debit",
|
StepCode: "edge.3_4.ledger.debit",
|
||||||
Action: model.RailOperationExternalDebit,
|
Action: discovery.RailOperationExternalDebit,
|
||||||
Rail: model.RailLedger,
|
Rail: discovery.RailLedger,
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
"mode": "finalize_debit",
|
"mode": "finalize_debit",
|
||||||
},
|
},
|
||||||
@@ -240,8 +241,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_FinalizeDebitUsesHoldToTransitAndSe
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "edge_3_4_ledger_debit",
|
StepRef: "edge_3_4_ledger_debit",
|
||||||
StepCode: "edge.3_4.ledger.debit",
|
StepCode: "edge.3_4.ledger.debit",
|
||||||
Action: model.RailOperationDebit,
|
Action: discovery.RailOperationDebit,
|
||||||
Rail: model.RailLedger,
|
Rail: discovery.RailLedger,
|
||||||
Metadata: map[string]string{"mode": "finalize_debit"},
|
Metadata: map[string]string{"mode": "finalize_debit"},
|
||||||
HopIndex: 4,
|
HopIndex: 4,
|
||||||
HopRole: paymenttypes.QuoteRouteHopRoleTransit,
|
HopRole: paymenttypes.QuoteRouteHopRoleTransit,
|
||||||
@@ -298,8 +299,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_BlockUsesOperatingToHoldAndPayoutAm
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "edge_3_4_ledger_block",
|
StepRef: "edge_3_4_ledger_block",
|
||||||
StepCode: "edge.3_4.ledger.block",
|
StepCode: "edge.3_4.ledger.block",
|
||||||
Action: model.RailOperationBlock,
|
Action: discovery.RailOperationBlock,
|
||||||
Rail: model.RailLedger,
|
Rail: discovery.RailLedger,
|
||||||
},
|
},
|
||||||
StepExecution: agg.StepExecution{
|
StepExecution: agg.StepExecution{
|
||||||
StepRef: "edge_3_4_ledger_block",
|
StepRef: "edge_3_4_ledger_block",
|
||||||
@@ -349,8 +350,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_ReleaseUsesHoldToOperatingAndPayout
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "edge_3_4_ledger_release",
|
StepRef: "edge_3_4_ledger_release",
|
||||||
StepCode: "edge.3_4.ledger.release",
|
StepCode: "edge.3_4.ledger.release",
|
||||||
Action: model.RailOperationRelease,
|
Action: discovery.RailOperationRelease,
|
||||||
Rail: model.RailLedger,
|
Rail: discovery.RailLedger,
|
||||||
},
|
},
|
||||||
StepExecution: agg.StepExecution{
|
StepExecution: agg.StepExecution{
|
||||||
StepRef: "edge_3_4_ledger_release",
|
StepRef: "edge_3_4_ledger_release",
|
||||||
@@ -400,8 +401,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_UsesStepAmountOverride(t *testing.T
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "edge_3_4_ledger_block",
|
StepRef: "edge_3_4_ledger_block",
|
||||||
StepCode: "edge.3_4.ledger.block",
|
StepCode: "edge.3_4.ledger.block",
|
||||||
Action: model.RailOperationBlock,
|
Action: discovery.RailOperationBlock,
|
||||||
Rail: model.RailLedger,
|
Rail: discovery.RailLedger,
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
batchmeta.MetaAmount: "40",
|
batchmeta.MetaAmount: "40",
|
||||||
batchmeta.MetaCurrency: "RUB",
|
batchmeta.MetaCurrency: "RUB",
|
||||||
@@ -446,8 +447,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_UsesMetadataRoleOverrides(t *testin
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "edge_2_3_ledger_credit",
|
StepRef: "edge_2_3_ledger_credit",
|
||||||
StepCode: "edge.2_3.ledger.credit",
|
StepCode: "edge.2_3.ledger.credit",
|
||||||
Action: model.RailOperationCredit,
|
Action: discovery.RailOperationCredit,
|
||||||
Rail: model.RailLedger,
|
Rail: discovery.RailLedger,
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
"from_role": "reserve",
|
"from_role": "reserve",
|
||||||
"to_role": "liquidity",
|
"to_role": "liquidity",
|
||||||
@@ -486,8 +487,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_ValidatesMetadataRoles(t *testing.T
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "edge_2_3_ledger_credit",
|
StepRef: "edge_2_3_ledger_credit",
|
||||||
StepCode: "edge.2_3.ledger.credit",
|
StepCode: "edge.2_3.ledger.credit",
|
||||||
Action: model.RailOperationCredit,
|
Action: discovery.RailOperationCredit,
|
||||||
Rail: model.RailLedger,
|
Rail: discovery.RailLedger,
|
||||||
Metadata: map[string]string{
|
Metadata: map[string]string{
|
||||||
"from_role": "bad_role",
|
"from_role": "bad_role",
|
||||||
"to_role": "operating",
|
"to_role": "operating",
|
||||||
@@ -514,8 +515,8 @@ func TestGatewayLedgerExecutor_ExecuteLedger_RequiresLedgerClient(t *testing.T)
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "edge_1_2_ledger_credit",
|
StepRef: "edge_1_2_ledger_credit",
|
||||||
StepCode: "edge.1_2.ledger.credit",
|
StepCode: "edge.1_2.ledger.credit",
|
||||||
Action: model.RailOperationCredit,
|
Action: discovery.RailOperationCredit,
|
||||||
Rail: model.RailLedger,
|
Rail: discovery.RailLedger,
|
||||||
},
|
},
|
||||||
StepExecution: agg.StepExecution{StepRef: "edge_1_2_ledger_credit", StepCode: "edge.1_2.ledger.credit", Attempt: 1},
|
StepExecution: agg.StepExecution{StepRef: "edge_1_2_ledger_credit", StepCode: "edge.1_2.ledger.credit", Attempt: 1},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -140,7 +140,7 @@ func (r *discoveryGatewayRegistry) List(_ context.Context) ([]*model.GatewayInst
|
|||||||
items := make([]*model.GatewayInstanceDescriptor, 0, len(entries))
|
items := make([]*model.GatewayInstanceDescriptor, 0, len(entries))
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
rail := railFromDiscovery(entry.Rail)
|
rail := railFromDiscovery(entry.Rail)
|
||||||
if rail == model.RailUnspecified {
|
if rail == discovery.RailUnspecified {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
operations := operationsFromDiscovery(entry.Operations)
|
operations := operationsFromDiscovery(entry.Operations)
|
||||||
@@ -163,17 +163,17 @@ func (r *discoveryGatewayRegistry) List(_ context.Context) ([]*model.GatewayInst
|
|||||||
func railFromDiscovery(value string) model.Rail {
|
func railFromDiscovery(value string) model.Rail {
|
||||||
switch discovery.NormalizeRail(value) {
|
switch discovery.NormalizeRail(value) {
|
||||||
case discovery.RailCrypto:
|
case discovery.RailCrypto:
|
||||||
return model.RailCrypto
|
return discovery.RailCrypto
|
||||||
case discovery.RailProviderSettlement:
|
case discovery.RailProviderSettlement:
|
||||||
return model.RailProviderSettlement
|
return discovery.RailProviderSettlement
|
||||||
case discovery.RailLedger:
|
case discovery.RailLedger:
|
||||||
return model.RailLedger
|
return discovery.RailLedger
|
||||||
case discovery.RailCardPayout:
|
case discovery.RailCardPayout:
|
||||||
return model.RailCardPayout
|
return discovery.RailCardPayout
|
||||||
case discovery.RailFiatOnRamp:
|
case discovery.RailFiatOnRamp:
|
||||||
return model.RailFiatOnRamp
|
return discovery.RailFiatOnRamp
|
||||||
default:
|
default:
|
||||||
return model.RailUnspecified
|
return discovery.RailUnspecified
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package orchestrator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
|
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/agg"
|
||||||
@@ -27,7 +28,7 @@ func (e *gatewayProviderSettlementExecutor) ExecuteProviderSettlement(ctx contex
|
|||||||
if req.Payment == nil {
|
if req.Payment == nil {
|
||||||
return nil, merrors.InvalidArgument("settlement fx_convert: payment is required")
|
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")
|
return nil, merrors.InvalidArgument("settlement fx_convert: unsupported action")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ func (e *gatewayProviderSettlementExecutor) resolveGateway(ctx context.Context,
|
|||||||
settlementCount := 0
|
settlementCount := 0
|
||||||
for i := range items {
|
for i := range items {
|
||||||
item := items[i]
|
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
|
continue
|
||||||
}
|
}
|
||||||
settlementCount++
|
settlementCount++
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package orchestrator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -38,7 +39,7 @@ func TestGatewayProviderSettlementExecutor_ExecuteProviderSettlement_SubmitsTran
|
|||||||
{
|
{
|
||||||
ID: "payment_gateway_settlement",
|
ID: "payment_gateway_settlement",
|
||||||
InstanceID: "payment_gateway_settlement",
|
InstanceID: "payment_gateway_settlement",
|
||||||
Rail: model.RailProviderSettlement,
|
Rail: discovery.RailProviderSettlement,
|
||||||
InvokeURI: "grpc://tgsettle-gateway",
|
InvokeURI: "grpc://tgsettle-gateway",
|
||||||
IsEnabled: true,
|
IsEnabled: true,
|
||||||
},
|
},
|
||||||
@@ -78,8 +79,8 @@ func TestGatewayProviderSettlementExecutor_ExecuteProviderSettlement_SubmitsTran
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "hop_2_settlement_fx_convert",
|
StepRef: "hop_2_settlement_fx_convert",
|
||||||
StepCode: "hop.2.settlement.fx_convert",
|
StepCode: "hop.2.settlement.fx_convert",
|
||||||
Action: model.RailOperationFXConvert,
|
Action: discovery.RailOperationFXConvert,
|
||||||
Rail: model.RailProviderSettlement,
|
Rail: discovery.RailProviderSettlement,
|
||||||
Gateway: "payment_gateway_settlement",
|
Gateway: "payment_gateway_settlement",
|
||||||
InstanceID: "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 {
|
if got, want := submitReq.GetMetadata()[settlementMetadataQuoteRef], "quote-1"; got != want {
|
||||||
t.Fatalf("quote_ref metadata mismatch: got=%q want=%q", 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)
|
t.Fatalf("outgoing_leg metadata mismatch: got=%q want=%q", got, want)
|
||||||
}
|
}
|
||||||
if len(out.StepExecution.ExternalRefs) != 2 {
|
if len(out.StepExecution.ExternalRefs) != 2 {
|
||||||
@@ -144,7 +145,7 @@ func TestGatewayProviderSettlementExecutor_ExecuteProviderSettlement_MissingSett
|
|||||||
{
|
{
|
||||||
ID: "payment_gateway_settlement",
|
ID: "payment_gateway_settlement",
|
||||||
InstanceID: "payment_gateway_settlement",
|
InstanceID: "payment_gateway_settlement",
|
||||||
Rail: model.RailProviderSettlement,
|
Rail: discovery.RailProviderSettlement,
|
||||||
InvokeURI: "grpc://tgsettle-gateway",
|
InvokeURI: "grpc://tgsettle-gateway",
|
||||||
IsEnabled: true,
|
IsEnabled: true,
|
||||||
},
|
},
|
||||||
@@ -171,8 +172,8 @@ func TestGatewayProviderSettlementExecutor_ExecuteProviderSettlement_MissingSett
|
|||||||
Step: xplan.Step{
|
Step: xplan.Step{
|
||||||
StepRef: "hop_2_settlement_fx_convert",
|
StepRef: "hop_2_settlement_fx_convert",
|
||||||
StepCode: "hop.2.settlement.fx_convert",
|
StepCode: "hop.2.settlement.fx_convert",
|
||||||
Action: model.RailOperationFXConvert,
|
Action: discovery.RailOperationFXConvert,
|
||||||
Rail: model.RailProviderSettlement,
|
Rail: discovery.RailProviderSettlement,
|
||||||
Gateway: "payment_gateway_settlement",
|
Gateway: "payment_gateway_settlement",
|
||||||
InstanceID: "payment_gateway_settlement",
|
InstanceID: "payment_gateway_settlement",
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package plan
|
package plan
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"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) {
|
func railFromEndpoint(endpoint model.PaymentEndpoint, attrs map[string]string, isSource bool) (model.Rail, string, error) {
|
||||||
override := railOverrideFromAttributes(attrs, isSource)
|
override := railOverrideFromAttributes(attrs, isSource)
|
||||||
if override != model.RailUnspecified {
|
if override != discovery.RailUnspecified {
|
||||||
return override, networkFromEndpoint(endpoint), nil
|
return override, networkFromEndpoint(endpoint), nil
|
||||||
}
|
}
|
||||||
switch endpoint.Type {
|
switch endpoint.Type {
|
||||||
case model.EndpointTypeLedger:
|
case model.EndpointTypeLedger:
|
||||||
return model.RailLedger, "", nil
|
return discovery.RailLedger, "", nil
|
||||||
case model.EndpointTypeManagedWallet, model.EndpointTypeExternalChain:
|
case model.EndpointTypeManagedWallet, model.EndpointTypeExternalChain:
|
||||||
return model.RailCrypto, networkFromEndpoint(endpoint), nil
|
return discovery.RailCrypto, networkFromEndpoint(endpoint), nil
|
||||||
case model.EndpointTypeCard:
|
case model.EndpointTypeCard:
|
||||||
return model.RailCardPayout, "", nil
|
return discovery.RailCardPayout, "", nil
|
||||||
default:
|
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 {
|
func railOverrideFromAttributes(attrs map[string]string, isSource bool) model.Rail {
|
||||||
if len(attrs) == 0 {
|
if len(attrs) == 0 {
|
||||||
return model.RailUnspecified
|
return discovery.RailUnspecified
|
||||||
}
|
}
|
||||||
keys := []string{"source_rail", "sourceRail"}
|
keys := []string{"source_rail", "sourceRail"}
|
||||||
if !isSource {
|
if !isSource {
|
||||||
@@ -41,45 +42,45 @@ func railOverrideFromAttributes(attrs map[string]string, isSource bool) model.Ra
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
rail := parseRailValue(value)
|
rail := parseRailValue(value)
|
||||||
if rail != model.RailUnspecified {
|
if rail != discovery.RailUnspecified {
|
||||||
return rail
|
return rail
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return model.RailUnspecified
|
return discovery.RailUnspecified
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseRailValue(value string) model.Rail {
|
func parseRailValue(value string) model.Rail {
|
||||||
val := strings.ToUpper(strings.TrimSpace(value))
|
val := strings.ToUpper(strings.TrimSpace(value))
|
||||||
switch val {
|
switch val {
|
||||||
case string(model.RailCrypto):
|
case string(discovery.RailCrypto):
|
||||||
return model.RailCrypto
|
return discovery.RailCrypto
|
||||||
case string(model.RailProviderSettlement):
|
case string(discovery.RailProviderSettlement):
|
||||||
return model.RailProviderSettlement
|
return discovery.RailProviderSettlement
|
||||||
case string(model.RailLedger):
|
case string(discovery.RailLedger):
|
||||||
return model.RailLedger
|
return discovery.RailLedger
|
||||||
case string(model.RailCardPayout):
|
case string(discovery.RailCardPayout):
|
||||||
return model.RailCardPayout
|
return discovery.RailCardPayout
|
||||||
case string(model.RailFiatOnRamp):
|
case string(discovery.RailFiatOnRamp):
|
||||||
return model.RailFiatOnRamp
|
return discovery.RailFiatOnRamp
|
||||||
default:
|
default:
|
||||||
return model.RailUnspecified
|
return discovery.RailUnspecified
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func gatewayNetworkForRail(rail model.Rail, sourceRail, destRail model.Rail, sourceNetwork, destNetwork string) string {
|
func gatewayNetworkForRail(rail model.Rail, sourceRail, destRail model.Rail, sourceNetwork, destNetwork string) string {
|
||||||
switch rail {
|
switch rail {
|
||||||
case model.RailCrypto:
|
case discovery.RailCrypto:
|
||||||
if sourceRail == model.RailCrypto {
|
if sourceRail == discovery.RailCrypto {
|
||||||
return strings.ToUpper(strings.TrimSpace(sourceNetwork))
|
return strings.ToUpper(strings.TrimSpace(sourceNetwork))
|
||||||
}
|
}
|
||||||
if destRail == model.RailCrypto {
|
if destRail == discovery.RailCrypto {
|
||||||
return strings.ToUpper(strings.TrimSpace(destNetwork))
|
return strings.ToUpper(strings.TrimSpace(destNetwork))
|
||||||
}
|
}
|
||||||
case model.RailFiatOnRamp:
|
case discovery.RailFiatOnRamp:
|
||||||
if sourceRail == model.RailFiatOnRamp {
|
if sourceRail == discovery.RailFiatOnRamp {
|
||||||
return strings.ToUpper(strings.TrimSpace(sourceNetwork))
|
return strings.ToUpper(strings.TrimSpace(sourceNetwork))
|
||||||
}
|
}
|
||||||
if destRail == model.RailFiatOnRamp {
|
if destRail == discovery.RailFiatOnRamp {
|
||||||
return strings.ToUpper(strings.TrimSpace(destNetwork))
|
return strings.ToUpper(strings.TrimSpace(destNetwork))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package plan
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -73,7 +74,7 @@ const (
|
|||||||
|
|
||||||
func sendDirectionForRail(rail model.Rail) sendDirection {
|
func sendDirectionForRail(rail model.Rail) sendDirection {
|
||||||
switch rail {
|
switch rail {
|
||||||
case model.RailFiatOnRamp:
|
case discovery.RailFiatOnRamp:
|
||||||
return sendDirectionIn
|
return sendDirectionIn
|
||||||
default:
|
default:
|
||||||
return sendDirectionOut
|
return sendDirectionOut
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package plan
|
package plan
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
@@ -15,8 +16,8 @@ func buildFXConversionPlan(payment *model.Payment) (*model.PaymentPlan, error) {
|
|||||||
}
|
}
|
||||||
step := &model.PaymentStep{
|
step := &model.PaymentStep{
|
||||||
StepID: "fx_convert",
|
StepID: "fx_convert",
|
||||||
Rail: model.RailLedger,
|
Rail: discovery.RailLedger,
|
||||||
Action: model.RailOperationFXConvert,
|
Action: discovery.RailOperationFXConvert,
|
||||||
ReportVisibility: model.ReportVisibilityUser,
|
ReportVisibility: model.ReportVisibilityUser,
|
||||||
CommitPolicy: model.CommitPolicyImmediate,
|
CommitPolicy: model.CommitPolicyImmediate,
|
||||||
Amount: cloneMoney(payment.Intent.Amount),
|
Amount: cloneMoney(payment.Intent.Amount),
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package gateway_funding_profile
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/quotation/internal/shared"
|
"github.com/tech/sendico/payments/quotation/internal/shared"
|
||||||
@@ -120,7 +121,7 @@ func (r *StaticFundingProfileResolver) ResolveGatewayFundingProfile(
|
|||||||
strings.TrimSpace(profile.InstanceID),
|
strings.TrimSpace(profile.InstanceID),
|
||||||
strings.TrimSpace(req.InstanceID),
|
strings.TrimSpace(req.InstanceID),
|
||||||
)
|
)
|
||||||
if profile.Rail == model.RailUnspecified {
|
if profile.Rail == discovery.RailUnspecified {
|
||||||
profile.Rail = req.Rail
|
profile.Rail = req.Rail
|
||||||
}
|
}
|
||||||
if strings.TrimSpace(profile.Network) == "" {
|
if strings.TrimSpace(profile.Network) == "" {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package quotation
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"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) {
|
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 {
|
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 {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -68,7 +69,7 @@ func selectGatewayForActions(ctx context.Context, registry GatewayRegistry, rail
|
|||||||
return nil, merrors.NoData("no gateway instances available")
|
return nil, merrors.NoData("no gateway instances available")
|
||||||
}
|
}
|
||||||
if len(actions) == 0 {
|
if len(actions) == 0 {
|
||||||
actions = []model.RailOperation{model.RailOperationSend}
|
actions = []model.RailOperation{discovery.RailOperationSend}
|
||||||
}
|
}
|
||||||
|
|
||||||
currency := ""
|
currency := ""
|
||||||
@@ -104,7 +105,7 @@ func selectGatewayForActions(ctx context.Context, registry GatewayRegistry, rail
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(eligible) == 0 {
|
if len(eligible) == 0 {
|
||||||
action := model.RailOperationUnspecified
|
var action model.RailOperation = discovery.RailOperationUnspecified
|
||||||
if len(actions) > 0 {
|
if len(actions) > 0 {
|
||||||
action = actions[0]
|
action = actions[0]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package graph_path_finder
|
package graph_path_finder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -12,7 +13,7 @@ func buildAdjacency(edges []Edge, network string) map[model.Rail][]normalizedEdg
|
|||||||
for _, edge := range edges {
|
for _, edge := range edges {
|
||||||
from := normalizeRail(edge.FromRail)
|
from := normalizeRail(edge.FromRail)
|
||||||
to := normalizeRail(edge.ToRail)
|
to := normalizeRail(edge.ToRail)
|
||||||
if from == model.RailUnspecified || to == model.RailUnspecified {
|
if from == discovery.RailUnspecified || to == discovery.RailUnspecified {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
en := normalizeNetwork(edge.Network)
|
en := normalizeNetwork(edge.Network)
|
||||||
@@ -65,8 +66,8 @@ func networkPriority(edgeNetwork, requested string) int {
|
|||||||
|
|
||||||
func normalizeRail(value model.Rail) model.Rail {
|
func normalizeRail(value model.Rail) model.Rail {
|
||||||
normalized := model.ParseRail(string(value))
|
normalized := model.ParseRail(string(value))
|
||||||
if normalized == model.RailUnspecified {
|
if normalized == discovery.RailUnspecified {
|
||||||
return model.RailUnspecified
|
return discovery.RailUnspecified
|
||||||
}
|
}
|
||||||
return normalized
|
return normalized
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package graph_path_finder
|
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(
|
func shortestPath(
|
||||||
source model.Rail,
|
source model.Rail,
|
||||||
@@ -75,7 +78,7 @@ func shortestPath(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func nextRail(best map[model.Rail]score, visited map[model.Rail]bool) (model.Rail, bool) {
|
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{}
|
selectedScore := score{}
|
||||||
found := false
|
found := false
|
||||||
for rail, railScore := range best {
|
for rail, railScore := range best {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package graph_path_finder
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -26,10 +27,10 @@ type score struct {
|
|||||||
func (f *GraphPathFinder) Find(in FindInput) (*Path, error) {
|
func (f *GraphPathFinder) Find(in FindInput) (*Path, error) {
|
||||||
source := normalizeRail(in.SourceRail)
|
source := normalizeRail(in.SourceRail)
|
||||||
destination := normalizeRail(in.DestinationRail)
|
destination := normalizeRail(in.DestinationRail)
|
||||||
if source == model.RailUnspecified {
|
if source == discovery.RailUnspecified {
|
||||||
return nil, merrors.InvalidArgument("source_rail is required")
|
return nil, merrors.InvalidArgument("source_rail is required")
|
||||||
}
|
}
|
||||||
if destination == model.RailUnspecified {
|
if destination == discovery.RailUnspecified {
|
||||||
return nil, merrors.InvalidArgument("destination_rail is required")
|
return nil, merrors.InvalidArgument("destination_rail is required")
|
||||||
}
|
}
|
||||||
if source == destination {
|
if source == destination {
|
||||||
|
|||||||
@@ -1,22 +1,21 @@
|
|||||||
package graph_path_finder
|
package graph_path_finder
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFind_NetworkFiltersEdges(t *testing.T) {
|
func TestFind_NetworkFiltersEdges(t *testing.T) {
|
||||||
finder := New()
|
finder := New()
|
||||||
|
|
||||||
path, err := finder.Find(FindInput{
|
path, err := finder.Find(FindInput{
|
||||||
SourceRail: model.RailCrypto,
|
SourceRail: discovery.RailCrypto,
|
||||||
DestinationRail: model.RailCardPayout,
|
DestinationRail: discovery.RailCardPayout,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Edges: []Edge{
|
Edges: []Edge{
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailLedger, Network: "ETH"},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger, Network: "ETH"},
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailLedger, Network: "TRON"},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger, Network: "TRON"},
|
||||||
{FromRail: model.RailLedger, ToRail: model.RailCardPayout, Network: "TRON"},
|
{FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout, Network: "TRON"},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -33,14 +32,14 @@ func TestFind_PrefersExactNetworkOverWildcard(t *testing.T) {
|
|||||||
finder := New()
|
finder := New()
|
||||||
|
|
||||||
path, err := finder.Find(FindInput{
|
path, err := finder.Find(FindInput{
|
||||||
SourceRail: model.RailCrypto,
|
SourceRail: discovery.RailCrypto,
|
||||||
DestinationRail: model.RailCardPayout,
|
DestinationRail: discovery.RailCardPayout,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Edges: []Edge{
|
Edges: []Edge{
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailLedger, Network: ""},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger, Network: ""},
|
||||||
{FromRail: model.RailLedger, ToRail: model.RailCardPayout, Network: ""},
|
{FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout, Network: ""},
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailProviderSettlement, Network: "TRON"},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailProviderSettlement, Network: "TRON"},
|
||||||
{FromRail: model.RailProviderSettlement, ToRail: model.RailCardPayout, Network: "TRON"},
|
{FromRail: discovery.RailProviderSettlement, ToRail: discovery.RailCardPayout, Network: "TRON"},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -54,13 +53,13 @@ func TestFind_DeterministicTieBreak(t *testing.T) {
|
|||||||
finder := New()
|
finder := New()
|
||||||
|
|
||||||
path, err := finder.Find(FindInput{
|
path, err := finder.Find(FindInput{
|
||||||
SourceRail: model.RailCrypto,
|
SourceRail: discovery.RailCrypto,
|
||||||
DestinationRail: model.RailCardPayout,
|
DestinationRail: discovery.RailCardPayout,
|
||||||
Edges: []Edge{
|
Edges: []Edge{
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailLedger},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger},
|
||||||
{FromRail: model.RailLedger, ToRail: model.RailCardPayout},
|
{FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout},
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailProviderSettlement},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailProviderSettlement},
|
||||||
{FromRail: model.RailProviderSettlement, ToRail: model.RailCardPayout},
|
{FromRail: discovery.RailProviderSettlement, ToRail: discovery.RailCardPayout},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -75,13 +74,13 @@ func TestFind_IgnoresInvalidEdges(t *testing.T) {
|
|||||||
finder := New()
|
finder := New()
|
||||||
|
|
||||||
path, err := finder.Find(FindInput{
|
path, err := finder.Find(FindInput{
|
||||||
SourceRail: model.RailCrypto,
|
SourceRail: discovery.RailCrypto,
|
||||||
DestinationRail: model.RailCardPayout,
|
DestinationRail: discovery.RailCardPayout,
|
||||||
Edges: []Edge{
|
Edges: []Edge{
|
||||||
{FromRail: model.RailUnspecified, ToRail: model.RailLedger},
|
{FromRail: discovery.RailUnspecified, ToRail: discovery.RailLedger},
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailUnspecified},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailUnspecified},
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailLedger},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger},
|
||||||
{FromRail: model.RailLedger, ToRail: model.RailCardPayout},
|
{FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package graph_path_finder
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
@@ -12,14 +13,14 @@ func TestFind_ValidatesInput(t *testing.T) {
|
|||||||
finder := New()
|
finder := New()
|
||||||
|
|
||||||
_, err := finder.Find(FindInput{
|
_, err := finder.Find(FindInput{
|
||||||
DestinationRail: model.RailCardPayout,
|
DestinationRail: discovery.RailCardPayout,
|
||||||
})
|
})
|
||||||
if !errors.Is(err, merrors.ErrInvalidArg) {
|
if !errors.Is(err, merrors.ErrInvalidArg) {
|
||||||
t.Fatalf("expected invalid argument for missing source rail, got %v", err)
|
t.Fatalf("expected invalid argument for missing source rail, got %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = finder.Find(FindInput{
|
_, err = finder.Find(FindInput{
|
||||||
SourceRail: model.RailCrypto,
|
SourceRail: discovery.RailCrypto,
|
||||||
})
|
})
|
||||||
if !errors.Is(err, merrors.ErrInvalidArg) {
|
if !errors.Is(err, merrors.ErrInvalidArg) {
|
||||||
t.Fatalf("expected invalid argument for missing destination rail, got %v", err)
|
t.Fatalf("expected invalid argument for missing destination rail, got %v", err)
|
||||||
@@ -30,8 +31,8 @@ func TestFind_SourceEqualsDestination(t *testing.T) {
|
|||||||
finder := New()
|
finder := New()
|
||||||
|
|
||||||
path, err := finder.Find(FindInput{
|
path, err := finder.Find(FindInput{
|
||||||
SourceRail: model.RailCrypto,
|
SourceRail: discovery.RailCrypto,
|
||||||
DestinationRail: model.RailCrypto,
|
DestinationRail: discovery.RailCrypto,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("unexpected error: %v", err)
|
t.Fatalf("unexpected error: %v", err)
|
||||||
@@ -51,11 +52,11 @@ func TestFind_FindsIndirectPath(t *testing.T) {
|
|||||||
finder := New()
|
finder := New()
|
||||||
|
|
||||||
path, err := finder.Find(FindInput{
|
path, err := finder.Find(FindInput{
|
||||||
SourceRail: model.RailCrypto,
|
SourceRail: discovery.RailCrypto,
|
||||||
DestinationRail: model.RailCardPayout,
|
DestinationRail: discovery.RailCardPayout,
|
||||||
Edges: []Edge{
|
Edges: []Edge{
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailLedger},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger},
|
||||||
{FromRail: model.RailLedger, ToRail: model.RailCardPayout},
|
{FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -72,12 +73,12 @@ func TestFind_PrefersShortestPath(t *testing.T) {
|
|||||||
finder := New()
|
finder := New()
|
||||||
|
|
||||||
path, err := finder.Find(FindInput{
|
path, err := finder.Find(FindInput{
|
||||||
SourceRail: model.RailCrypto,
|
SourceRail: discovery.RailCrypto,
|
||||||
DestinationRail: model.RailCardPayout,
|
DestinationRail: discovery.RailCardPayout,
|
||||||
Edges: []Edge{
|
Edges: []Edge{
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailCardPayout},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailCardPayout},
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailLedger},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger},
|
||||||
{FromRail: model.RailLedger, ToRail: model.RailCardPayout},
|
{FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -91,12 +92,12 @@ func TestFind_HandlesCycles(t *testing.T) {
|
|||||||
finder := New()
|
finder := New()
|
||||||
|
|
||||||
path, err := finder.Find(FindInput{
|
path, err := finder.Find(FindInput{
|
||||||
SourceRail: model.RailCrypto,
|
SourceRail: discovery.RailCrypto,
|
||||||
DestinationRail: model.RailCardPayout,
|
DestinationRail: discovery.RailCardPayout,
|
||||||
Edges: []Edge{
|
Edges: []Edge{
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailLedger},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger},
|
||||||
{FromRail: model.RailLedger, ToRail: model.RailCrypto},
|
{FromRail: discovery.RailLedger, ToRail: discovery.RailCrypto},
|
||||||
{FromRail: model.RailLedger, ToRail: model.RailCardPayout},
|
{FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -110,10 +111,10 @@ func TestFind_ReturnsErrorWhenPathUnavailable(t *testing.T) {
|
|||||||
finder := New()
|
finder := New()
|
||||||
|
|
||||||
_, err := finder.Find(FindInput{
|
_, err := finder.Find(FindInput{
|
||||||
SourceRail: model.RailCrypto,
|
SourceRail: discovery.RailCrypto,
|
||||||
DestinationRail: model.RailCardPayout,
|
DestinationRail: discovery.RailCardPayout,
|
||||||
Edges: []Edge{
|
Edges: []Edge{
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailLedger},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger},
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if !errors.Is(err, merrors.ErrInvalidArg) {
|
if !errors.Is(err, merrors.ErrInvalidArg) {
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ package quotation
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
|
||||||
chainpkg "github.com/tech/sendico/pkg/chain"
|
chainpkg "github.com/tech/sendico/pkg/chain"
|
||||||
"github.com/tech/sendico/pkg/merrors"
|
"github.com/tech/sendico/pkg/merrors"
|
||||||
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
paymenttypes "github.com/tech/sendico/pkg/payments/types"
|
||||||
@@ -165,7 +165,7 @@ func (r *managedWalletNetworkResolver) listDiscoveredGatewayCandidates(ctx conte
|
|||||||
candidates := make([]discoveredGatewayCandidate, 0, len(entries))
|
candidates := make([]discoveredGatewayCandidate, 0, len(entries))
|
||||||
seenInvokeURI := map[string]struct{}{}
|
seenInvokeURI := map[string]struct{}{}
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
if entry == nil || !entry.IsEnabled || entry.Rail != model.RailCrypto {
|
if entry == nil || !entry.IsEnabled || entry.Rail != discovery.RailCrypto {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
invokeURI := strings.TrimSpace(entry.InvokeURI)
|
invokeURI := strings.TrimSpace(entry.InvokeURI)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package quotation
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -37,8 +38,8 @@ func TestManagedWalletNetworkResolver_ResolvesAcrossDiscoveredGateways(t *testin
|
|||||||
resolver := &managedWalletNetworkResolver{
|
resolver := &managedWalletNetworkResolver{
|
||||||
gatewayRegistry: fakeGatewayRegistry{
|
gatewayRegistry: fakeGatewayRegistry{
|
||||||
items: []*model.GatewayInstanceDescriptor{
|
items: []*model.GatewayInstanceDescriptor{
|
||||||
{ID: "gw-a", Rail: model.RailCrypto, IsEnabled: true, InvokeURI: "gw-a:50053"},
|
{ID: "gw-a", Rail: discovery.RailCrypto, IsEnabled: true, InvokeURI: "gw-a:50053"},
|
||||||
{ID: "gw-b", Rail: model.RailCrypto, IsEnabled: true, InvokeURI: "gw-b:50053"},
|
{ID: "gw-b", Rail: discovery.RailCrypto, IsEnabled: true, InvokeURI: "gw-b:50053"},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
gatewayInvokeResolver: invokeResolver,
|
gatewayInvokeResolver: invokeResolver,
|
||||||
|
|||||||
@@ -200,7 +200,7 @@ func (r *discoveryGatewayRegistry) List(_ context.Context) ([]*model.GatewayInst
|
|||||||
items := make([]*model.GatewayInstanceDescriptor, 0, len(entries))
|
items := make([]*model.GatewayInstanceDescriptor, 0, len(entries))
|
||||||
for _, entry := range entries {
|
for _, entry := range entries {
|
||||||
railID := railFromDiscovery(entry.Rail)
|
railID := railFromDiscovery(entry.Rail)
|
||||||
if railID == model.RailUnspecified {
|
if railID == discovery.RailUnspecified {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
operations := operationsFromDiscovery(entry.Operations)
|
operations := operationsFromDiscovery(entry.Operations)
|
||||||
@@ -223,17 +223,17 @@ func (r *discoveryGatewayRegistry) List(_ context.Context) ([]*model.GatewayInst
|
|||||||
func railFromDiscovery(value string) model.Rail {
|
func railFromDiscovery(value string) model.Rail {
|
||||||
switch discovery.NormalizeRail(value) {
|
switch discovery.NormalizeRail(value) {
|
||||||
case discovery.RailCrypto:
|
case discovery.RailCrypto:
|
||||||
return model.RailCrypto
|
return discovery.RailCrypto
|
||||||
case discovery.RailProviderSettlement:
|
case discovery.RailProviderSettlement:
|
||||||
return model.RailProviderSettlement
|
return discovery.RailProviderSettlement
|
||||||
case discovery.RailLedger:
|
case discovery.RailLedger:
|
||||||
return model.RailLedger
|
return discovery.RailLedger
|
||||||
case discovery.RailCardPayout:
|
case discovery.RailCardPayout:
|
||||||
return model.RailCardPayout
|
return discovery.RailCardPayout
|
||||||
case discovery.RailFiatOnRamp:
|
case discovery.RailFiatOnRamp:
|
||||||
return model.RailFiatOnRamp
|
return discovery.RailFiatOnRamp
|
||||||
default:
|
default:
|
||||||
return model.RailUnspecified
|
return discovery.RailUnspecified
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package quotation_service_v2
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -522,7 +523,7 @@ func TestQuotePayment_SelectsEligibleGatewaysAndIgnoresIrrelevant(t *testing.T)
|
|||||||
{
|
{
|
||||||
ID: "crypto-disabled",
|
ID: "crypto-disabled",
|
||||||
InstanceID: "crypto-disabled",
|
InstanceID: "crypto-disabled",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -533,7 +534,7 @@ func TestQuotePayment_SelectsEligibleGatewaysAndIgnoresIrrelevant(t *testing.T)
|
|||||||
{
|
{
|
||||||
ID: "crypto-network-mismatch",
|
ID: "crypto-network-mismatch",
|
||||||
InstanceID: "crypto-network-mismatch",
|
InstanceID: "crypto-network-mismatch",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "ETH",
|
Network: "ETH",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -544,7 +545,7 @@ func TestQuotePayment_SelectsEligibleGatewaysAndIgnoresIrrelevant(t *testing.T)
|
|||||||
{
|
{
|
||||||
ID: "crypto-currency-mismatch",
|
ID: "crypto-currency-mismatch",
|
||||||
InstanceID: "crypto-currency-mismatch",
|
InstanceID: "crypto-currency-mismatch",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"EUR"},
|
Currencies: []string{"EUR"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -555,7 +556,7 @@ func TestQuotePayment_SelectsEligibleGatewaysAndIgnoresIrrelevant(t *testing.T)
|
|||||||
{
|
{
|
||||||
ID: "crypto-gw-1",
|
ID: "crypto-gw-1",
|
||||||
InstanceID: "crypto-gw-1",
|
InstanceID: "crypto-gw-1",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -566,7 +567,7 @@ func TestQuotePayment_SelectsEligibleGatewaysAndIgnoresIrrelevant(t *testing.T)
|
|||||||
{
|
{
|
||||||
ID: "payout-disabled",
|
ID: "payout-disabled",
|
||||||
InstanceID: "payout-disabled",
|
InstanceID: "payout-disabled",
|
||||||
Rail: model.RailCardPayout,
|
Rail: discovery.RailCardPayout,
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
CanPayOut: true,
|
CanPayOut: true,
|
||||||
@@ -576,7 +577,7 @@ func TestQuotePayment_SelectsEligibleGatewaysAndIgnoresIrrelevant(t *testing.T)
|
|||||||
{
|
{
|
||||||
ID: "payout-currency-mismatch",
|
ID: "payout-currency-mismatch",
|
||||||
InstanceID: "payout-currency-mismatch",
|
InstanceID: "payout-currency-mismatch",
|
||||||
Rail: model.RailCardPayout,
|
Rail: discovery.RailCardPayout,
|
||||||
Currencies: []string{"EUR"},
|
Currencies: []string{"EUR"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
CanPayOut: true,
|
CanPayOut: true,
|
||||||
@@ -586,7 +587,7 @@ func TestQuotePayment_SelectsEligibleGatewaysAndIgnoresIrrelevant(t *testing.T)
|
|||||||
{
|
{
|
||||||
ID: "payout-gw-1",
|
ID: "payout-gw-1",
|
||||||
InstanceID: "payout-gw-1",
|
InstanceID: "payout-gw-1",
|
||||||
Rail: model.RailCardPayout,
|
Rail: discovery.RailCardPayout,
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
CanPayOut: true,
|
CanPayOut: true,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package quote_computation_service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -58,10 +59,10 @@ func TestBuildPlan_BuildsStepsAndFundingGate(t *testing.T) {
|
|||||||
if len(item.Steps) != 2 {
|
if len(item.Steps) != 2 {
|
||||||
t.Fatalf("expected 2 steps, got %d", len(item.Steps))
|
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)
|
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)
|
t.Fatalf("expected destination operation SEND, got %q", item.Steps[1].Operation)
|
||||||
}
|
}
|
||||||
if item.Funding == nil {
|
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 {
|
if len(planModel.Items) != 1 || len(planModel.Items[0].Steps) != 3 {
|
||||||
t.Fatalf("expected 3 steps for FX intent")
|
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)
|
t.Fatalf("expected middle step FX_CONVERT, got %q", got)
|
||||||
}
|
}
|
||||||
if planModel.Items[0].Route == nil {
|
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 {
|
if got, want := strings.TrimSpace(last.Amount.GetCurrency()), "RUB"; got != want {
|
||||||
t.Fatalf("unexpected destination step currency: got=%q want=%q", got, want)
|
t.Fatalf("unexpected destination step currency: got=%q want=%q", got, want)
|
||||||
}
|
}
|
||||||
if got, want := last.Operation, model.RailOperationSend; got != want {
|
if got := last.Operation; got != discovery.RailOperationSend {
|
||||||
t.Fatalf("unexpected destination operation: got=%q want=%q", got, want)
|
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 {
|
if got, want := strings.TrimSpace(last.Amount.GetCurrency()), "RUB"; got != want {
|
||||||
t.Fatalf("unexpected destination step currency: got=%q want=%q", got, want)
|
t.Fatalf("unexpected destination step currency: got=%q want=%q", got, want)
|
||||||
}
|
}
|
||||||
if got, want := steps[1].Operation, model.RailOperationFXConvert; got != want {
|
if got := steps[1].Operation; got != discovery.RailOperationFXConvert {
|
||||||
t.Fatalf("unexpected middle operation: got=%q want=%q", got, want)
|
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",
|
ID: "crypto-disabled",
|
||||||
InstanceID: "crypto-disabled",
|
InstanceID: "crypto-disabled",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -310,7 +311,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "crypto-network-mismatch",
|
ID: "crypto-network-mismatch",
|
||||||
InstanceID: "crypto-network-mismatch",
|
InstanceID: "crypto-network-mismatch",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "ETH",
|
Network: "ETH",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -321,7 +322,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "crypto-currency-mismatch",
|
ID: "crypto-currency-mismatch",
|
||||||
InstanceID: "crypto-currency-mismatch",
|
InstanceID: "crypto-currency-mismatch",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"EUR"},
|
Currencies: []string{"EUR"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -332,7 +333,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "crypto-gw-1",
|
ID: "crypto-gw-1",
|
||||||
InstanceID: "crypto-gw-1",
|
InstanceID: "crypto-gw-1",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -343,7 +344,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "payout-disabled",
|
ID: "payout-disabled",
|
||||||
InstanceID: "payout-disabled",
|
InstanceID: "payout-disabled",
|
||||||
Rail: model.RailCardPayout,
|
Rail: discovery.RailCardPayout,
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
CanPayOut: true,
|
CanPayOut: true,
|
||||||
@@ -353,7 +354,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "payout-currency-mismatch",
|
ID: "payout-currency-mismatch",
|
||||||
InstanceID: "payout-currency-mismatch",
|
InstanceID: "payout-currency-mismatch",
|
||||||
Rail: model.RailCardPayout,
|
Rail: discovery.RailCardPayout,
|
||||||
Currencies: []string{"EUR"},
|
Currencies: []string{"EUR"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
CanPayOut: true,
|
CanPayOut: true,
|
||||||
@@ -363,7 +364,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "payout-gw-1",
|
ID: "payout-gw-1",
|
||||||
InstanceID: "payout-gw-1",
|
InstanceID: "payout-gw-1",
|
||||||
Rail: model.RailCardPayout,
|
Rail: discovery.RailCardPayout,
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
CanPayOut: true,
|
CanPayOut: true,
|
||||||
@@ -373,7 +374,7 @@ func TestBuildPlan_SelectsGatewaysAndIgnoresIrrelevant(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "provider-ignored",
|
ID: "provider-ignored",
|
||||||
InstanceID: "provider-ignored",
|
InstanceID: "provider-ignored",
|
||||||
Rail: model.RailProviderSettlement,
|
Rail: discovery.RailProviderSettlement,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
IsEnabled: true,
|
IsEnabled: true,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package quote_computation_service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -61,7 +62,7 @@ func (s *QuoteComputationService) resolveStepGateways(
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if step.Rail == model.RailLedger {
|
if step.Rail == discovery.RailLedger {
|
||||||
step.GatewayID = "internal"
|
step.GatewayID = "internal"
|
||||||
step.GatewayInvokeURI = ""
|
step.GatewayInvokeURI = ""
|
||||||
|
|
||||||
@@ -201,7 +202,7 @@ func parseDecimalAmount(m *moneyv1.Money) (decimal.Decimal, error) {
|
|||||||
|
|
||||||
func networkForGatewaySelection(rail model.Rail, routeNetwork string) string {
|
func networkForGatewaySelection(rail model.Rail, routeNetwork string) string {
|
||||||
switch rail {
|
switch rail {
|
||||||
case model.RailCrypto, model.RailProviderSettlement, model.RailFiatOnRamp:
|
case discovery.RailCrypto, discovery.RailProviderSettlement, discovery.RailFiatOnRamp:
|
||||||
return strings.ToUpper(strings.TrimSpace(routeNetwork))
|
return strings.ToUpper(strings.TrimSpace(routeNetwork))
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package quote_computation_service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
@@ -23,7 +24,7 @@ func TestResolveStepGateways_FallsBackToInvokeURI(t *testing.T) {
|
|||||||
ID: "aaa",
|
ID: "aaa",
|
||||||
InstanceID: "inst-a",
|
InstanceID: "inst-a",
|
||||||
InvokeURI: "grpc://gw-a:50051",
|
InvokeURI: "grpc://gw-a:50051",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -35,7 +36,7 @@ func TestResolveStepGateways_FallsBackToInvokeURI(t *testing.T) {
|
|||||||
ID: "bbb",
|
ID: "bbb",
|
||||||
InstanceID: "inst-b",
|
InstanceID: "inst-b",
|
||||||
InvokeURI: "grpc://gw-b:50051",
|
InvokeURI: "grpc://gw-b:50051",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -48,8 +49,8 @@ func TestResolveStepGateways_FallsBackToInvokeURI(t *testing.T) {
|
|||||||
steps := []*QuoteComputationStep{
|
steps := []*QuoteComputationStep{
|
||||||
{
|
{
|
||||||
StepID: "i0.destination",
|
StepID: "i0.destination",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Operation: model.RailOperationExternalCredit,
|
Operation: discovery.RailOperationExternalCredit,
|
||||||
GatewayID: "legacy-id",
|
GatewayID: "legacy-id",
|
||||||
InstanceID: "legacy-instance",
|
InstanceID: "legacy-instance",
|
||||||
GatewayInvokeURI: "grpc://gw-b:50051",
|
GatewayInvokeURI: "grpc://gw-b:50051",
|
||||||
@@ -78,7 +79,7 @@ func TestResolveStepGateways_FallsBackToGatewayIDWhenInstanceChanges(t *testing.
|
|||||||
ID: "aaa",
|
ID: "aaa",
|
||||||
InstanceID: "inst-a",
|
InstanceID: "inst-a",
|
||||||
InvokeURI: "grpc://gw-a:50051",
|
InvokeURI: "grpc://gw-a:50051",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -90,7 +91,7 @@ func TestResolveStepGateways_FallsBackToGatewayIDWhenInstanceChanges(t *testing.
|
|||||||
ID: "crypto_rail_gateway_tron",
|
ID: "crypto_rail_gateway_tron",
|
||||||
InstanceID: "inst-new",
|
InstanceID: "inst-new",
|
||||||
InvokeURI: "grpc://gw-tron:50051",
|
InvokeURI: "grpc://gw-tron:50051",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -103,8 +104,8 @@ func TestResolveStepGateways_FallsBackToGatewayIDWhenInstanceChanges(t *testing.
|
|||||||
steps := []*QuoteComputationStep{
|
steps := []*QuoteComputationStep{
|
||||||
{
|
{
|
||||||
StepID: "i0.destination",
|
StepID: "i0.destination",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Operation: model.RailOperationExternalCredit,
|
Operation: discovery.RailOperationExternalCredit,
|
||||||
GatewayID: "crypto_rail_gateway_tron",
|
GatewayID: "crypto_rail_gateway_tron",
|
||||||
InstanceID: "inst-old",
|
InstanceID: "inst-old",
|
||||||
Amount: &moneyv1.Money{Currency: "USDT", Amount: "10"},
|
Amount: &moneyv1.Money{Currency: "USDT", Amount: "10"},
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package quote_computation_service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/quotation/internal/service/quotation/transfer_intent_hydrator"
|
"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",
|
ID: "crypto-arbitrum",
|
||||||
InstanceID: "crypto-arbitrum",
|
InstanceID: "crypto-arbitrum",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "ARBITRUM_SEPOLIA",
|
Network: "ARBITRUM_SEPOLIA",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -36,7 +37,7 @@ func TestBuildPlan_ResolvesManagedWalletNetworkFromResolver(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "crypto-tron",
|
ID: "crypto-tron",
|
||||||
InstanceID: "crypto-tron",
|
InstanceID: "crypto-tron",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON_NILE",
|
Network: "TRON_NILE",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -47,7 +48,7 @@ func TestBuildPlan_ResolvesManagedWalletNetworkFromResolver(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "card-gw",
|
ID: "card-gw",
|
||||||
InstanceID: "card-gw",
|
InstanceID: "card-gw",
|
||||||
Rail: model.RailCardPayout,
|
Rail: discovery.RailCardPayout,
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
CanPayOut: true,
|
CanPayOut: true,
|
||||||
@@ -101,7 +102,7 @@ func TestBuildPlan_ManagedWalletNetworkResolverCachesByWalletRef(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "crypto-tron",
|
ID: "crypto-tron",
|
||||||
InstanceID: "crypto-tron",
|
InstanceID: "crypto-tron",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON_NILE",
|
Network: "TRON_NILE",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -112,7 +113,7 @@ func TestBuildPlan_ManagedWalletNetworkResolverCachesByWalletRef(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "card-gw",
|
ID: "card-gw",
|
||||||
InstanceID: "card-gw",
|
InstanceID: "card-gw",
|
||||||
Rail: model.RailCardPayout,
|
Rail: discovery.RailCardPayout,
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
CanPayOut: true,
|
CanPayOut: true,
|
||||||
@@ -167,7 +168,7 @@ func TestBuildPlan_ResolvesManagedWalletAssetTokenForSourceCurrency(t *testing.T
|
|||||||
{
|
{
|
||||||
ID: "crypto-tron",
|
ID: "crypto-tron",
|
||||||
InstanceID: "crypto-tron",
|
InstanceID: "crypto-tron",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON_NILE",
|
Network: "TRON_NILE",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -178,16 +179,16 @@ func TestBuildPlan_ResolvesManagedWalletAssetTokenForSourceCurrency(t *testing.T
|
|||||||
{
|
{
|
||||||
ID: "fx-tron",
|
ID: "fx-tron",
|
||||||
InstanceID: "fx-tron",
|
InstanceID: "fx-tron",
|
||||||
Rail: model.RailProviderSettlement,
|
Rail: discovery.RailProviderSettlement,
|
||||||
Network: "TRON_NILE",
|
Network: "TRON_NILE",
|
||||||
Currencies: []string{"USDT", "RUB"},
|
Currencies: []string{"USDT", "RUB"},
|
||||||
Operations: []model.RailOperation{model.RailOperationFXConvert},
|
Operations: []model.RailOperation{discovery.RailOperationFXConvert},
|
||||||
IsEnabled: true,
|
IsEnabled: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ID: "card-gw",
|
ID: "card-gw",
|
||||||
InstanceID: "card-gw",
|
InstanceID: "card-gw",
|
||||||
Rail: model.RailCardPayout,
|
Rail: discovery.RailCardPayout,
|
||||||
Currencies: []string{"RUB"},
|
Currencies: []string{"RUB"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
CanPayOut: true,
|
CanPayOut: true,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package quote_computation_service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/quotation/internal/service/plan"
|
"github.com/tech/sendico/payments/quotation/internal/service/plan"
|
||||||
@@ -217,7 +218,7 @@ func (s *QuoteComputationService) buildPlanItem(
|
|||||||
destinationGatewayFromSteps(steps),
|
destinationGatewayFromSteps(steps),
|
||||||
gatewayKeyForFunding(modelIntent.Attributes, destination),
|
gatewayKeyForFunding(modelIntent.Attributes, destination),
|
||||||
)
|
)
|
||||||
if provider == "" && destRail == model.RailLedger {
|
if provider == "" && destRail == discovery.RailLedger {
|
||||||
provider = "internal"
|
provider = "internal"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package quote_computation_service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/quotation/internal/service/quotation/graph_path_finder"
|
"github.com/tech/sendico/payments/quotation/internal/service/quotation/graph_path_finder"
|
||||||
@@ -25,13 +26,13 @@ func (s *QuoteComputationService) resolveRouteRails(
|
|||||||
zap.String("network", network),
|
zap.String("network", network),
|
||||||
)
|
)
|
||||||
|
|
||||||
if sourceRail == model.RailUnspecified {
|
if sourceRail == discovery.RailUnspecified {
|
||||||
s.logger.Warn("Route rails resolution failed: source rail is unspecified")
|
s.logger.Warn("Route rails resolution failed: source rail is unspecified")
|
||||||
|
|
||||||
return nil, merrors.InvalidArgument("source rail is required")
|
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")
|
s.logger.Warn("Route rails resolution failed: destination rail is unspecified")
|
||||||
|
|
||||||
return nil, merrors.InvalidArgument("destination rail is required")
|
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))
|
from := model.ParseRail(string(route.FromRail))
|
||||||
to := model.ParseRail(string(route.ToRail))
|
to := model.ParseRail(string(route.ToRail))
|
||||||
|
|
||||||
if from == model.RailUnspecified || to == model.RailUnspecified {
|
if from == discovery.RailUnspecified || to == discovery.RailUnspecified {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -210,7 +211,7 @@ func fallbackRouteRails(sourceRail, destinationRail model.Rail) []model.Rail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if requiresTransitBridgeStep(sourceRail, destinationRail) {
|
if requiresTransitBridgeStep(sourceRail, destinationRail) {
|
||||||
return []model.Rail{sourceRail, model.RailLedger, destinationRail}
|
return []model.Rail{sourceRail, discovery.RailLedger, destinationRail}
|
||||||
}
|
}
|
||||||
|
|
||||||
return []model.Rail{sourceRail, destinationRail}
|
return []model.Rail{sourceRail, destinationRail}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package quote_computation_service
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -15,15 +16,15 @@ import (
|
|||||||
func TestBuildPlan_UsesRouteGraphPath(t *testing.T) {
|
func TestBuildPlan_UsesRouteGraphPath(t *testing.T) {
|
||||||
svc := New(nil,
|
svc := New(nil,
|
||||||
WithRouteStore(staticRouteStore{items: []*model.PaymentRoute{
|
WithRouteStore(staticRouteStore{items: []*model.PaymentRoute{
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailProviderSettlement, Network: "TRON", IsEnabled: true},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailProviderSettlement, Network: "TRON", IsEnabled: true},
|
||||||
{FromRail: model.RailProviderSettlement, ToRail: model.RailCardPayout, Network: "TRON", IsEnabled: true},
|
{FromRail: discovery.RailProviderSettlement, ToRail: discovery.RailCardPayout, Network: "TRON", IsEnabled: true},
|
||||||
}}),
|
}}),
|
||||||
WithGatewayRegistry(staticGatewayRegistry{
|
WithGatewayRegistry(staticGatewayRegistry{
|
||||||
items: []*model.GatewayInstanceDescriptor{
|
items: []*model.GatewayInstanceDescriptor{
|
||||||
{
|
{
|
||||||
ID: "crypto-gw",
|
ID: "crypto-gw",
|
||||||
InstanceID: "crypto-gw",
|
InstanceID: "crypto-gw",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -34,7 +35,7 @@ func TestBuildPlan_UsesRouteGraphPath(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "provider-gw",
|
ID: "provider-gw",
|
||||||
InstanceID: "provider-gw",
|
InstanceID: "provider-gw",
|
||||||
Rail: model.RailProviderSettlement,
|
Rail: discovery.RailProviderSettlement,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
IsEnabled: true,
|
IsEnabled: true,
|
||||||
@@ -42,7 +43,7 @@ func TestBuildPlan_UsesRouteGraphPath(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "card-gw",
|
ID: "card-gw",
|
||||||
InstanceID: "card-gw",
|
InstanceID: "card-gw",
|
||||||
Rail: model.RailCardPayout,
|
Rail: discovery.RailCardPayout,
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
CanPayOut: true,
|
CanPayOut: true,
|
||||||
@@ -71,7 +72,7 @@ func TestBuildPlan_UsesRouteGraphPath(t *testing.T) {
|
|||||||
if got, want := len(item.Steps), 3; got != want {
|
if got, want := len(item.Steps), 3; got != want {
|
||||||
t.Fatalf("unexpected step count: got=%d want=%d", 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)
|
t.Fatalf("unexpected transit rail: got=%q want=%q", got, want)
|
||||||
}
|
}
|
||||||
if got := strings.ToUpper(strings.TrimSpace(item.Route.GetHops()[1].GetRail())); got != "SETTLEMENT" {
|
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) {
|
func TestBuildPlan_RouteGraphNoPathReturnsError(t *testing.T) {
|
||||||
svc := New(nil, WithRouteStore(staticRouteStore{items: []*model.PaymentRoute{
|
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()
|
orgID := bson.NewObjectID()
|
||||||
@@ -99,16 +100,16 @@ func TestBuildPlan_RouteGraphNoPathReturnsError(t *testing.T) {
|
|||||||
func TestBuildPlan_RouteGraphPrefersDirectPath(t *testing.T) {
|
func TestBuildPlan_RouteGraphPrefersDirectPath(t *testing.T) {
|
||||||
svc := New(nil,
|
svc := New(nil,
|
||||||
WithRouteStore(staticRouteStore{items: []*model.PaymentRoute{
|
WithRouteStore(staticRouteStore{items: []*model.PaymentRoute{
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailCardPayout, Network: "TRON", IsEnabled: true},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailCardPayout, Network: "TRON", IsEnabled: true},
|
||||||
{FromRail: model.RailCrypto, ToRail: model.RailLedger, Network: "TRON", IsEnabled: true},
|
{FromRail: discovery.RailCrypto, ToRail: discovery.RailLedger, Network: "TRON", IsEnabled: true},
|
||||||
{FromRail: model.RailLedger, ToRail: model.RailCardPayout, Network: "TRON", IsEnabled: true},
|
{FromRail: discovery.RailLedger, ToRail: discovery.RailCardPayout, Network: "TRON", IsEnabled: true},
|
||||||
}}),
|
}}),
|
||||||
WithGatewayRegistry(staticGatewayRegistry{
|
WithGatewayRegistry(staticGatewayRegistry{
|
||||||
items: []*model.GatewayInstanceDescriptor{
|
items: []*model.GatewayInstanceDescriptor{
|
||||||
{
|
{
|
||||||
ID: "crypto-gw",
|
ID: "crypto-gw",
|
||||||
InstanceID: "crypto-gw",
|
InstanceID: "crypto-gw",
|
||||||
Rail: model.RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
@@ -119,7 +120,7 @@ func TestBuildPlan_RouteGraphPrefersDirectPath(t *testing.T) {
|
|||||||
{
|
{
|
||||||
ID: "card-gw",
|
ID: "card-gw",
|
||||||
InstanceID: "card-gw",
|
InstanceID: "card-gw",
|
||||||
Rail: model.RailCardPayout,
|
Rail: discovery.RailCardPayout,
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Capabilities: model.RailCapabilities{
|
Capabilities: model.RailCapabilities{
|
||||||
CanPayOut: true,
|
CanPayOut: true,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package quote_computation_service
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
@@ -56,14 +57,14 @@ func buildComputationSteps(
|
|||||||
lastStepID := sourceStepID
|
lastStepID := sourceStepID
|
||||||
fxAssigned := false
|
fxAssigned := false
|
||||||
if intent.RequiresFX {
|
if intent.RequiresFX {
|
||||||
if len(rails) > 1 && rails[1] == model.RailProviderSettlement {
|
if len(rails) > 1 && rails[1] == discovery.RailProviderSettlement {
|
||||||
fxAssigned = true
|
fxAssigned = true
|
||||||
} else {
|
} else {
|
||||||
fxStepID := fmt.Sprintf("i%d.fx", index)
|
fxStepID := fmt.Sprintf("i%d.fx", index)
|
||||||
steps = append(steps, &QuoteComputationStep{
|
steps = append(steps, &QuoteComputationStep{
|
||||||
StepID: fxStepID,
|
StepID: fxStepID,
|
||||||
Rail: model.RailProviderSettlement,
|
Rail: discovery.RailProviderSettlement,
|
||||||
Operation: model.RailOperationFXConvert,
|
Operation: discovery.RailOperationFXConvert,
|
||||||
DependsOn: []string{sourceStepID},
|
DependsOn: []string{sourceStepID},
|
||||||
Amount: cloneProtoMoney(sourceAmount),
|
Amount: cloneProtoMoney(sourceAmount),
|
||||||
Optional: false,
|
Optional: false,
|
||||||
@@ -78,14 +79,14 @@ func buildComputationSteps(
|
|||||||
for i := 1; i < len(rails)-1; i++ {
|
for i := 1; i < len(rails)-1; i++ {
|
||||||
rail := rails[i]
|
rail := rails[i]
|
||||||
stepID := fmt.Sprintf("i%d.transit%d", index, transitIndex)
|
stepID := fmt.Sprintf("i%d.transit%d", index, transitIndex)
|
||||||
operation := model.RailOperationMove
|
var operation model.RailOperation = discovery.RailOperationMove
|
||||||
if intent.RequiresFX && !fxAssigned && rail == model.RailProviderSettlement {
|
if intent.RequiresFX && !fxAssigned && rail == discovery.RailProviderSettlement {
|
||||||
stepID = fmt.Sprintf("i%d.fx", index)
|
stepID = fmt.Sprintf("i%d.fx", index)
|
||||||
operation = model.RailOperationFXConvert
|
operation = discovery.RailOperationFXConvert
|
||||||
fxAssigned = true
|
fxAssigned = true
|
||||||
}
|
}
|
||||||
stepAmount := amount
|
stepAmount := amount
|
||||||
if operation == model.RailOperationFXConvert {
|
if operation == discovery.RailOperationFXConvert {
|
||||||
stepAmount = sourceAmount
|
stepAmount = sourceAmount
|
||||||
}
|
}
|
||||||
steps = append(steps, &QuoteComputationStep{
|
steps = append(steps, &QuoteComputationStep{
|
||||||
@@ -120,14 +121,14 @@ func buildComputationSteps(
|
|||||||
func normalizeRouteRails(sourceRail, destinationRail model.Rail, routeRails []model.Rail) []model.Rail {
|
func normalizeRouteRails(sourceRail, destinationRail model.Rail, routeRails []model.Rail) []model.Rail {
|
||||||
if len(routeRails) == 0 {
|
if len(routeRails) == 0 {
|
||||||
if requiresTransitBridgeStep(sourceRail, destinationRail) {
|
if requiresTransitBridgeStep(sourceRail, destinationRail) {
|
||||||
return []model.Rail{sourceRail, model.RailLedger, destinationRail}
|
return []model.Rail{sourceRail, discovery.RailLedger, destinationRail}
|
||||||
}
|
}
|
||||||
return []model.Rail{sourceRail, destinationRail}
|
return []model.Rail{sourceRail, destinationRail}
|
||||||
}
|
}
|
||||||
|
|
||||||
result := make([]model.Rail, 0, len(routeRails))
|
result := make([]model.Rail, 0, len(routeRails))
|
||||||
for _, rail := range routeRails {
|
for _, rail := range routeRails {
|
||||||
if rail == model.RailUnspecified {
|
if rail == discovery.RailUnspecified {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(result) > 0 && result[len(result)-1] == rail {
|
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 {
|
func requiresTransitBridgeStep(sourceRail, destinationRail model.Rail) bool {
|
||||||
if sourceRail == model.RailUnspecified || destinationRail == model.RailUnspecified {
|
if sourceRail == discovery.RailUnspecified || destinationRail == discovery.RailUnspecified {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if sourceRail == destinationRail {
|
if sourceRail == destinationRail {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if sourceRail == model.RailLedger || destinationRail == model.RailLedger {
|
if sourceRail == discovery.RailLedger || destinationRail == discovery.RailLedger {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
@@ -166,42 +167,42 @@ func requiresTransitBridgeStep(sourceRail, destinationRail model.Rail) bool {
|
|||||||
|
|
||||||
func sourceRailForIntent(intent model.PaymentIntent) model.Rail {
|
func sourceRailForIntent(intent model.PaymentIntent) model.Rail {
|
||||||
if intent.Source.Type == model.EndpointTypeLedger {
|
if intent.Source.Type == model.EndpointTypeLedger {
|
||||||
return model.RailLedger
|
return discovery.RailLedger
|
||||||
}
|
}
|
||||||
if intent.Source.Type == model.EndpointTypeManagedWallet || intent.Source.Type == model.EndpointTypeExternalChain {
|
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 {
|
func destinationRailForIntent(intent model.PaymentIntent) model.Rail {
|
||||||
switch intent.Destination.Type {
|
switch intent.Destination.Type {
|
||||||
case model.EndpointTypeCard:
|
case model.EndpointTypeCard:
|
||||||
return model.RailCardPayout
|
return discovery.RailCardPayout
|
||||||
case model.EndpointTypeManagedWallet, model.EndpointTypeExternalChain:
|
case model.EndpointTypeManagedWallet, model.EndpointTypeExternalChain:
|
||||||
return model.RailCrypto
|
return discovery.RailCrypto
|
||||||
case model.EndpointTypeLedger:
|
case model.EndpointTypeLedger:
|
||||||
return model.RailLedger
|
return discovery.RailLedger
|
||||||
default:
|
default:
|
||||||
return model.RailProviderSettlement
|
return discovery.RailProviderSettlement
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sourceOperationForRail(rail model.Rail) model.RailOperation {
|
func sourceOperationForRail(rail model.Rail) model.RailOperation {
|
||||||
if rail == model.RailLedger {
|
if rail == discovery.RailLedger {
|
||||||
return model.RailOperationMove
|
return discovery.RailOperationMove
|
||||||
}
|
}
|
||||||
return model.RailOperationExternalDebit
|
return discovery.RailOperationExternalDebit
|
||||||
}
|
}
|
||||||
|
|
||||||
func destinationOperationForRail(rail model.Rail) model.RailOperation {
|
func destinationOperationForRail(rail model.Rail) model.RailOperation {
|
||||||
switch rail {
|
switch rail {
|
||||||
case model.RailLedger:
|
case discovery.RailLedger:
|
||||||
return model.RailOperationMove
|
return discovery.RailOperationMove
|
||||||
case model.RailCardPayout:
|
case discovery.RailCardPayout:
|
||||||
return model.RailOperationSend
|
return discovery.RailOperationSend
|
||||||
default:
|
default:
|
||||||
return model.RailOperationExternalCredit
|
return discovery.RailOperationExternalCredit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package quote_computation_service
|
package quote_computation_service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage/model"
|
"github.com/tech/sendico/payments/storage/model"
|
||||||
@@ -60,7 +61,7 @@ func normalizeSettlementParts(src *quotationv2.RouteSettlement) (chain, token, c
|
|||||||
}
|
}
|
||||||
|
|
||||||
func normalizeRail(value string) string {
|
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 string(rail)
|
||||||
}
|
}
|
||||||
return strings.ToUpper(strings.TrimSpace(value))
|
return strings.ToUpper(strings.TrimSpace(value))
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -116,7 +117,7 @@ func buildRouteHops(steps []*QuoteComputationStep, fallbackNetwork string) []*qu
|
|||||||
Network: normalizeNetwork(firstNonEmpty(fallbackNetwork)),
|
Network: normalizeNetwork(firstNonEmpty(fallbackNetwork)),
|
||||||
Role: roleForHopIndex(i, lastIndex),
|
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"
|
hop.Gateway = "internal"
|
||||||
}
|
}
|
||||||
result = append(result, hop)
|
result = append(result, hop)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -427,7 +428,7 @@ func (s *Service) estimateNetworkFee(ctx context.Context, intent *sharedv1.Payme
|
|||||||
if instanceID == "" {
|
if instanceID == "" {
|
||||||
instanceID = strings.TrimSpace(intent.GetDestination().GetInstanceId())
|
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 err != nil {
|
||||||
if errors.Is(err, merrors.ErrNoData) {
|
if errors.Is(err, merrors.ErrNoData) {
|
||||||
s.logger.Debug("Network fee estimation skipped: gateway unavailable", zap.Error(err))
|
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 {
|
func feesRequiredForRails(sourceRail, destRail model.Rail) bool {
|
||||||
if sourceRail == model.RailLedger && destRail == model.RailLedger {
|
if sourceRail == discovery.RailLedger && destRail == discovery.RailLedger {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -12,20 +12,32 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
github.com/bmatcuk/doublestar/v4 v4.10.0 // indirect
|
||||||
github.com/casbin/casbin/v2 v2.135.0 // indirect
|
github.com/casbin/casbin/v2 v2.135.0 // indirect
|
||||||
github.com/casbin/govaluate v1.10.0 // indirect
|
github.com/casbin/govaluate v1.10.0 // indirect
|
||||||
github.com/casbin/mongodb-adapter/v4 v4.3.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/google/uuid v1.6.0 // indirect
|
||||||
github.com/klauspost/compress v1.18.4 // indirect
|
github.com/klauspost/compress v1.18.4 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // 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/pbkdf2 v1.0.0 // indirect
|
||||||
github.com/xdg-go/scram v1.2.0 // indirect
|
github.com/xdg-go/scram v1.2.0 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // 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/crypto v0.48.0 // indirect
|
||||||
golang.org/x/sync v0.19.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
|
golang.org/x/text v0.34.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.11 // indirect
|
google.golang.org/protobuf v1.36.11 // indirect
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -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/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 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
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.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 h1:zU9WiOla1YA122oLM6i4EXvGW62DvKZVxIe6TYWexEs=
|
||||||
github.com/bmatcuk/doublestar/v4 v4.10.0/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTSPVIjEY1Wr7jzc=
|
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/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 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=
|
||||||
github.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
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 h1:mFWunSatvkQQDhpdyuFAYwyAan3hzCuma+Pz8sqvOfg=
|
||||||
github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
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/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 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
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 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
|
||||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
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=
|
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/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 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
|
||||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
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 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
|
||||||
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
|
||||||
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
|
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/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 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=
|
||||||
go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
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-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.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
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=
|
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 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package model
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
@@ -110,7 +111,7 @@ func gatewayAllowsAction(operations []RailOperation, cap RailCapabilities, actio
|
|||||||
|
|
||||||
func capabilityAllowsAction(cap RailCapabilities, action RailOperation, dir GatewayDirection) bool {
|
func capabilityAllowsAction(cap RailCapabilities, action RailOperation, dir GatewayDirection) bool {
|
||||||
switch action {
|
switch action {
|
||||||
case RailOperationSend:
|
case discovery.RailOperationSend:
|
||||||
switch dir {
|
switch dir {
|
||||||
case GatewayDirectionOut:
|
case GatewayDirectionOut:
|
||||||
return cap.CanPayOut
|
return cap.CanPayOut
|
||||||
@@ -119,7 +120,7 @@ func capabilityAllowsAction(cap RailCapabilities, action RailOperation, dir Gate
|
|||||||
default:
|
default:
|
||||||
return cap.CanPayIn || cap.CanPayOut
|
return cap.CanPayIn || cap.CanPayOut
|
||||||
}
|
}
|
||||||
case RailOperationExternalDebit, RailOperationExternalCredit:
|
case discovery.RailOperationExternalDebit, discovery.RailOperationExternalCredit:
|
||||||
switch dir {
|
switch dir {
|
||||||
case GatewayDirectionOut:
|
case GatewayDirectionOut:
|
||||||
return cap.CanPayOut
|
return cap.CanPayOut
|
||||||
@@ -128,13 +129,13 @@ func capabilityAllowsAction(cap RailCapabilities, action RailOperation, dir Gate
|
|||||||
default:
|
default:
|
||||||
return cap.CanPayIn || cap.CanPayOut
|
return cap.CanPayIn || cap.CanPayOut
|
||||||
}
|
}
|
||||||
case RailOperationFee:
|
case discovery.RailOperationFee:
|
||||||
return cap.CanSendFee
|
return cap.CanSendFee
|
||||||
case RailOperationObserveConfirm:
|
case discovery.RailOperationObserveConfirm:
|
||||||
return cap.RequiresObserveConfirm
|
return cap.RequiresObserveConfirm
|
||||||
case RailOperationBlock:
|
case discovery.RailOperationBlock:
|
||||||
return cap.CanBlock
|
return cap.CanBlock
|
||||||
case RailOperationRelease:
|
case discovery.RailOperationRelease:
|
||||||
return cap.CanRelease
|
return cap.CanRelease
|
||||||
default:
|
default:
|
||||||
return true
|
return true
|
||||||
@@ -143,7 +144,7 @@ func capabilityAllowsAction(cap RailCapabilities, action RailOperation, dir Gate
|
|||||||
|
|
||||||
func operationsAllowAction(operations []RailOperation, action RailOperation, dir GatewayDirection) bool {
|
func operationsAllowAction(operations []RailOperation, action RailOperation, dir GatewayDirection) bool {
|
||||||
action = ParseRailOperation(string(action))
|
action = ParseRailOperation(string(action))
|
||||||
if action == RailOperationUnspecified {
|
if action == discovery.RailOperationUnspecified {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,20 +153,20 @@ func operationsAllowAction(operations []RailOperation, action RailOperation, dir
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch action {
|
switch action {
|
||||||
case RailOperationSend:
|
case discovery.RailOperationSend:
|
||||||
switch dir {
|
switch dir {
|
||||||
case GatewayDirectionIn:
|
case GatewayDirectionIn:
|
||||||
return HasRailOperation(operations, RailOperationExternalDebit)
|
return HasRailOperation(operations, discovery.RailOperationExternalDebit)
|
||||||
case GatewayDirectionOut:
|
case GatewayDirectionOut:
|
||||||
return HasRailOperation(operations, RailOperationExternalCredit)
|
return HasRailOperation(operations, discovery.RailOperationExternalCredit)
|
||||||
default:
|
default:
|
||||||
return HasRailOperation(operations, RailOperationExternalDebit) ||
|
return HasRailOperation(operations, discovery.RailOperationExternalDebit) ||
|
||||||
HasRailOperation(operations, RailOperationExternalCredit)
|
HasRailOperation(operations, discovery.RailOperationExternalCredit)
|
||||||
}
|
}
|
||||||
case RailOperationExternalDebit:
|
case discovery.RailOperationExternalDebit:
|
||||||
return HasRailOperation(operations, RailOperationSend)
|
return HasRailOperation(operations, discovery.RailOperationSend)
|
||||||
case RailOperationExternalCredit:
|
case discovery.RailOperationExternalCredit:
|
||||||
return HasRailOperation(operations, RailOperationSend)
|
return HasRailOperation(operations, discovery.RailOperationSend)
|
||||||
default:
|
default:
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@@ -181,7 +182,7 @@ func amountWithinLimits(gw *GatewayInstanceDescriptor, limits Limits, currency s
|
|||||||
if override, ok := limits.CurrencyLimits[currency]; ok {
|
if override, ok := limits.CurrencyLimits[currency]; ok {
|
||||||
min = firstLimitValue(override.MinAmount, min)
|
min = firstLimitValue(override.MinAmount, min)
|
||||||
max = firstLimitValue(override.MaxAmount, max)
|
max = firstLimitValue(override.MaxAmount, max)
|
||||||
if action == RailOperationFee {
|
if action == discovery.RailOperationFee {
|
||||||
maxFee = firstLimitValue(override.MaxFee, maxFee)
|
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()))
|
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) {
|
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()))
|
return gatewayIneligible(gw, fmt.Sprintf("fee amount %s %s exceeds max fee limit %s", amount.String(), currency, val.String()))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/shopspring/decimal"
|
"github.com/shopspring/decimal"
|
||||||
@@ -10,14 +11,14 @@ func TestIsGatewayEligible_AllowsMatchingGateway(t *testing.T) {
|
|||||||
gw := &GatewayInstanceDescriptor{
|
gw := &GatewayInstanceDescriptor{
|
||||||
ID: "gw-1",
|
ID: "gw-1",
|
||||||
InstanceID: "inst-1",
|
InstanceID: "inst-1",
|
||||||
Rail: RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "TRON",
|
Network: "TRON",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Operations: []RailOperation{RailOperationSend, RailOperationExternalCredit},
|
Operations: []RailOperation{discovery.RailOperationSend, discovery.RailOperationExternalCredit},
|
||||||
IsEnabled: true,
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("expected gateway to be eligible, got err=%v", err)
|
t.Fatalf("expected gateway to be eligible, got err=%v", err)
|
||||||
}
|
}
|
||||||
@@ -27,21 +28,21 @@ func TestIsGatewayEligible_RejectsNetworkMismatch(t *testing.T) {
|
|||||||
gw := &GatewayInstanceDescriptor{
|
gw := &GatewayInstanceDescriptor{
|
||||||
ID: "gw-1",
|
ID: "gw-1",
|
||||||
InstanceID: "inst-1",
|
InstanceID: "inst-1",
|
||||||
Rail: RailCrypto,
|
Rail: discovery.RailCrypto,
|
||||||
Network: "ETH",
|
Network: "ETH",
|
||||||
Currencies: []string{"USDT"},
|
Currencies: []string{"USDT"},
|
||||||
Operations: []RailOperation{RailOperationSend},
|
Operations: []RailOperation{discovery.RailOperationSend},
|
||||||
IsEnabled: true,
|
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 {
|
if err == nil {
|
||||||
t.Fatalf("expected network mismatch error")
|
t.Fatalf("expected network mismatch error")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNoEligibleGatewayMessage(t *testing.T) {
|
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"
|
want := "plan builder: no eligible gateway found for TRON USDT SEND for direction out"
|
||||||
if got != want {
|
if got != want {
|
||||||
t.Fatalf("unexpected message: got=%q want=%q", got, want)
|
t.Fatalf("unexpected message: got=%q want=%q", got, want)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/tech/sendico/pkg/db/storable"
|
"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"
|
||||||
"github.com/tech/sendico/pkg/model/account_role"
|
"github.com/tech/sendico/pkg/model/account_role"
|
||||||
"github.com/tech/sendico/pkg/mservice"
|
"github.com/tech/sendico/pkg/mservice"
|
||||||
@@ -79,35 +80,10 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// Rail identifies a payment rail for orchestration.
|
// Rail identifies a payment rail for orchestration.
|
||||||
type Rail string
|
type Rail = discovery.Rail
|
||||||
|
|
||||||
const (
|
|
||||||
RailUnspecified Rail = "UNSPECIFIED"
|
|
||||||
RailCrypto Rail = "CRYPTO"
|
|
||||||
RailProviderSettlement Rail = "SETTLEMENT"
|
|
||||||
RailLedger Rail = "LEDGER"
|
|
||||||
RailCardPayout Rail = "CARD"
|
|
||||||
RailFiatOnRamp Rail = "ONRAMP"
|
|
||||||
RailFiatOffRamp Rail = "OFFRAMP"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RailOperation identifies an explicit action within a payment plan.
|
// RailOperation identifies an explicit action within a payment plan.
|
||||||
type RailOperation string
|
type RailOperation = discovery.RailOperation
|
||||||
|
|
||||||
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"
|
|
||||||
)
|
|
||||||
|
|
||||||
// RailCapabilities are declared per gateway instance.
|
// RailCapabilities are declared per gateway instance.
|
||||||
type RailCapabilities struct {
|
type RailCapabilities struct {
|
||||||
|
|||||||
@@ -1,26 +1,29 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
var supportedRailOperations = map[RailOperation]struct{}{
|
var supportedRailOperations = map[RailOperation]struct{}{
|
||||||
RailOperationDebit: {},
|
discovery.RailOperationDebit: {},
|
||||||
RailOperationCredit: {},
|
discovery.RailOperationCredit: {},
|
||||||
RailOperationExternalDebit: {},
|
discovery.RailOperationExternalDebit: {},
|
||||||
RailOperationExternalCredit: {},
|
discovery.RailOperationExternalCredit: {},
|
||||||
RailOperationMove: {},
|
discovery.RailOperationMove: {},
|
||||||
RailOperationSend: {},
|
discovery.RailOperationSend: {},
|
||||||
RailOperationFee: {},
|
discovery.RailOperationFee: {},
|
||||||
RailOperationObserveConfirm: {},
|
discovery.RailOperationObserveConfirm: {},
|
||||||
RailOperationFXConvert: {},
|
discovery.RailOperationFXConvert: {},
|
||||||
RailOperationBlock: {},
|
discovery.RailOperationBlock: {},
|
||||||
RailOperationRelease: {},
|
discovery.RailOperationRelease: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseRailOperation canonicalizes string values into a RailOperation token.
|
// ParseRailOperation canonicalizes string values into a RailOperation token.
|
||||||
func ParseRailOperation(value string) RailOperation {
|
func ParseRailOperation(value string) RailOperation {
|
||||||
clean := strings.ToUpper(strings.TrimSpace(value))
|
clean := strings.ToUpper(strings.TrimSpace(value))
|
||||||
if clean == "" {
|
if clean == "" {
|
||||||
return RailOperationUnspecified
|
return discovery.RailOperationUnspecified
|
||||||
}
|
}
|
||||||
return RailOperation(clean)
|
return RailOperation(clean)
|
||||||
}
|
}
|
||||||
@@ -40,7 +43,7 @@ func NormalizeRailOperations(values []RailOperation) []RailOperation {
|
|||||||
seen := map[RailOperation]bool{}
|
seen := map[RailOperation]bool{}
|
||||||
for _, value := range values {
|
for _, value := range values {
|
||||||
op := ParseRailOperation(string(value))
|
op := ParseRailOperation(string(value))
|
||||||
if op == RailOperationUnspecified || !IsSupportedRailOperation(op) || seen[op] {
|
if op == discovery.RailOperationUnspecified || !IsSupportedRailOperation(op) || seen[op] {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
seen[op] = true
|
seen[op] = true
|
||||||
@@ -67,7 +70,7 @@ func NormalizeRailOperationStrings(values []string) []RailOperation {
|
|||||||
// HasRailOperation checks whether ops includes action.
|
// HasRailOperation checks whether ops includes action.
|
||||||
func HasRailOperation(ops []RailOperation, action RailOperation) bool {
|
func HasRailOperation(ops []RailOperation, action RailOperation) bool {
|
||||||
want := ParseRailOperation(string(action))
|
want := ParseRailOperation(string(action))
|
||||||
if want == RailOperationUnspecified {
|
if want == discovery.RailOperationUnspecified {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
for _, op := range ops {
|
for _, op := range ops {
|
||||||
@@ -82,12 +85,12 @@ func HasRailOperation(ops []RailOperation, action RailOperation) bool {
|
|||||||
func RailCapabilitiesFromOperations(ops []RailOperation) RailCapabilities {
|
func RailCapabilitiesFromOperations(ops []RailOperation) RailCapabilities {
|
||||||
normalized := NormalizeRailOperations(ops)
|
normalized := NormalizeRailOperations(ops)
|
||||||
return RailCapabilities{
|
return RailCapabilities{
|
||||||
CanPayIn: HasRailOperation(normalized, RailOperationExternalDebit),
|
CanPayIn: HasRailOperation(normalized, discovery.RailOperationExternalDebit),
|
||||||
CanPayOut: HasRailOperation(normalized, RailOperationSend) || HasRailOperation(normalized, RailOperationExternalCredit),
|
CanPayOut: HasRailOperation(normalized, discovery.RailOperationSend) || HasRailOperation(normalized, discovery.RailOperationExternalCredit),
|
||||||
CanReadBalance: false,
|
CanReadBalance: false,
|
||||||
CanSendFee: HasRailOperation(normalized, RailOperationFee),
|
CanSendFee: HasRailOperation(normalized, discovery.RailOperationFee),
|
||||||
RequiresObserveConfirm: HasRailOperation(normalized, RailOperationObserveConfirm),
|
RequiresObserveConfirm: HasRailOperation(normalized, discovery.RailOperationObserveConfirm),
|
||||||
CanBlock: HasRailOperation(normalized, RailOperationBlock),
|
CanBlock: HasRailOperation(normalized, discovery.RailOperationBlock),
|
||||||
CanRelease: HasRailOperation(normalized, RailOperationRelease),
|
CanRelease: HasRailOperation(normalized, discovery.RailOperationRelease),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestNormalizeRailOperations(t *testing.T) {
|
func TestNormalizeRailOperations(t *testing.T) {
|
||||||
ops := NormalizeRailOperations([]RailOperation{
|
ops := NormalizeRailOperations([]RailOperation{
|
||||||
@@ -13,35 +16,35 @@ func TestNormalizeRailOperations(t *testing.T) {
|
|||||||
if len(ops) != 2 {
|
if len(ops) != 2 {
|
||||||
t.Fatalf("unexpected operations count: got=%d want=2", len(ops))
|
t.Fatalf("unexpected operations count: got=%d want=2", len(ops))
|
||||||
}
|
}
|
||||||
if ops[0] != RailOperationSend {
|
if ops[0] != discovery.RailOperationSend {
|
||||||
t.Fatalf("unexpected first operation: got=%q want=%q", ops[0], RailOperationSend)
|
t.Fatalf("unexpected first operation: got=%q want=%q", ops[0], discovery.RailOperationSend)
|
||||||
}
|
}
|
||||||
if ops[1] != RailOperationExternalCredit {
|
if ops[1] != discovery.RailOperationExternalCredit {
|
||||||
t.Fatalf("unexpected second operation: got=%q want=%q", ops[1], RailOperationExternalCredit)
|
t.Fatalf("unexpected second operation: got=%q want=%q", ops[1], discovery.RailOperationExternalCredit)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHasRailOperation(t *testing.T) {
|
func TestHasRailOperation(t *testing.T) {
|
||||||
ops := []RailOperation{RailOperationSend, RailOperationExternalCredit}
|
ops := []RailOperation{discovery.RailOperationSend, discovery.RailOperationExternalCredit}
|
||||||
if !HasRailOperation(ops, RailOperationSend) {
|
if !HasRailOperation(ops, discovery.RailOperationSend) {
|
||||||
t.Fatalf("expected send operation to be present")
|
t.Fatalf("expected send operation to be present")
|
||||||
}
|
}
|
||||||
if !HasRailOperation(ops, " external_credit ") {
|
if !HasRailOperation(ops, " external_credit ") {
|
||||||
t.Fatalf("expected external credit operation to be present")
|
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")
|
t.Fatalf("did not expect observe confirm operation to be present")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRailCapabilitiesFromOperations(t *testing.T) {
|
func TestRailCapabilitiesFromOperations(t *testing.T) {
|
||||||
cap := RailCapabilitiesFromOperations([]RailOperation{
|
cap := RailCapabilitiesFromOperations([]RailOperation{
|
||||||
RailOperationExternalDebit,
|
discovery.RailOperationExternalDebit,
|
||||||
RailOperationExternalCredit,
|
discovery.RailOperationExternalCredit,
|
||||||
RailOperationFee,
|
discovery.RailOperationFee,
|
||||||
RailOperationObserveConfirm,
|
discovery.RailOperationObserveConfirm,
|
||||||
RailOperationBlock,
|
discovery.RailOperationBlock,
|
||||||
RailOperationRelease,
|
discovery.RailOperationRelease,
|
||||||
})
|
})
|
||||||
|
|
||||||
if !cap.CanPayIn {
|
if !cap.CanPayIn {
|
||||||
|
|||||||
@@ -1,21 +1,24 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
var supportedRails = map[Rail]struct{}{
|
var supportedRails = map[Rail]struct{}{
|
||||||
RailCrypto: {},
|
discovery.RailCrypto: {},
|
||||||
RailProviderSettlement: {},
|
discovery.RailProviderSettlement: {},
|
||||||
RailLedger: {},
|
discovery.RailLedger: {},
|
||||||
RailCardPayout: {},
|
discovery.RailCardPayout: {},
|
||||||
RailFiatOnRamp: {},
|
discovery.RailFiatOnRamp: {},
|
||||||
RailFiatOffRamp: {},
|
discovery.RailFiatOffRamp: {},
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseRail canonicalizes string values into a Rail token.
|
// ParseRail canonicalizes string values into a Rail token.
|
||||||
func ParseRail(value string) Rail {
|
func ParseRail(value string) Rail {
|
||||||
clean := strings.ToUpper(strings.TrimSpace(value))
|
clean := strings.ToUpper(strings.TrimSpace(value))
|
||||||
if clean == "" {
|
if clean == "" {
|
||||||
return RailUnspecified
|
return discovery.RailUnspecified
|
||||||
}
|
}
|
||||||
clean = strings.ReplaceAll(clean, "-", "_")
|
clean = strings.ReplaceAll(clean, "-", "_")
|
||||||
clean = strings.ReplaceAll(clean, " ", "_")
|
clean = strings.ReplaceAll(clean, " ", "_")
|
||||||
@@ -24,20 +27,20 @@ func ParseRail(value string) Rail {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch clean {
|
switch clean {
|
||||||
case string(RailCrypto), "RAIL_CRYPTO":
|
case string(discovery.RailCrypto), "RAIL_CRYPTO":
|
||||||
return RailCrypto
|
return discovery.RailCrypto
|
||||||
case string(RailProviderSettlement), "PROVIDER_SETTLEMENT", "RAIL_SETTLEMENT", "RAIL_PROVIDER_SETTLEMENT":
|
case string(discovery.RailProviderSettlement), "PROVIDER_SETTLEMENT", "RAIL_SETTLEMENT", "RAIL_PROVIDER_SETTLEMENT":
|
||||||
return RailProviderSettlement
|
return discovery.RailProviderSettlement
|
||||||
case string(RailLedger), "RAIL_LEDGER":
|
case string(discovery.RailLedger), "RAIL_LEDGER":
|
||||||
return RailLedger
|
return discovery.RailLedger
|
||||||
case string(RailCardPayout), "CARD_PAYOUT", "RAIL_CARD", "RAIL_CARD_PAYOUT":
|
case string(discovery.RailCardPayout), "CARD_PAYOUT", "RAIL_CARD", "RAIL_CARD_PAYOUT":
|
||||||
return RailCardPayout
|
return discovery.RailCardPayout
|
||||||
case string(RailFiatOnRamp), "FIAT_ONRAMP", "RAIL_ONRAMP", "RAIL_FIAT_ONRAMP":
|
case string(discovery.RailFiatOnRamp), "FIAT_ONRAMP", "RAIL_ONRAMP", "RAIL_FIAT_ONRAMP":
|
||||||
return RailFiatOnRamp
|
return discovery.RailFiatOnRamp
|
||||||
case string(RailFiatOffRamp), "FIAT_OFFRAMP", "RAIL_OFFRAMP", "RAIL_FIAT_OFFRAMP":
|
case string(discovery.RailFiatOffRamp), "FIAT_OFFRAMP", "RAIL_OFFRAMP", "RAIL_FIAT_OFFRAMP":
|
||||||
return RailFiatOffRamp
|
return discovery.RailFiatOffRamp
|
||||||
default:
|
default:
|
||||||
return RailUnspecified
|
return discovery.RailUnspecified
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,13 +52,13 @@ func IsSupportedRail(rail Rail) bool {
|
|||||||
|
|
||||||
func normalizeRail(value Rail) Rail {
|
func normalizeRail(value Rail) Rail {
|
||||||
parsed := ParseRail(string(value))
|
parsed := ParseRail(string(value))
|
||||||
if parsed != RailUnspecified {
|
if parsed != discovery.RailUnspecified {
|
||||||
return parsed
|
return parsed
|
||||||
}
|
}
|
||||||
|
|
||||||
clean := strings.ToUpper(strings.TrimSpace(string(value)))
|
clean := strings.ToUpper(strings.TrimSpace(string(value)))
|
||||||
if clean == "" {
|
if clean == "" {
|
||||||
return RailUnspecified
|
return discovery.RailUnspecified
|
||||||
}
|
}
|
||||||
|
|
||||||
return Rail(clean)
|
return Rail(clean)
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func TestParseRail(t *testing.T) {
|
func TestParseRail(t *testing.T) {
|
||||||
cases := []struct {
|
cases := []struct {
|
||||||
@@ -8,14 +11,14 @@ func TestParseRail(t *testing.T) {
|
|||||||
input string
|
input string
|
||||||
want Rail
|
want Rail
|
||||||
}{
|
}{
|
||||||
{name: "crypto", input: "crypto", want: RailCrypto},
|
{name: "crypto", input: "crypto", want: discovery.RailCrypto},
|
||||||
{name: "settlement canonical", input: "SETTLEMENT", want: RailProviderSettlement},
|
{name: "settlement canonical", input: "SETTLEMENT", want: discovery.RailProviderSettlement},
|
||||||
{name: "settlement legacy", input: "provider_settlement", want: RailProviderSettlement},
|
{name: "settlement legacy", input: "provider_settlement", want: discovery.RailProviderSettlement},
|
||||||
{name: "card canonical", input: "card", want: RailCardPayout},
|
{name: "card canonical", input: "card", want: discovery.RailCardPayout},
|
||||||
{name: "card legacy", input: "card_payout", want: RailCardPayout},
|
{name: "card legacy", input: "card_payout", want: discovery.RailCardPayout},
|
||||||
{name: "onramp", input: "fiat_onramp", want: RailFiatOnRamp},
|
{name: "onramp", input: "fiat_onramp", want: discovery.RailFiatOnRamp},
|
||||||
{name: "offramp", input: "fiat_offramp", want: RailFiatOffRamp},
|
{name: "offramp", input: "fiat_offramp", want: discovery.RailFiatOffRamp},
|
||||||
{name: "unknown", input: "telegram", want: RailUnspecified},
|
{name: "unknown", input: "telegram", want: discovery.RailUnspecified},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, tc := range cases {
|
for _, tc := range cases {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package store
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
|
"github.com/tech/sendico/pkg/discovery"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/tech/sendico/payments/storage"
|
"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")
|
return merrors.InvalidArgument("routesStore: nil route")
|
||||||
}
|
}
|
||||||
route.Normalize()
|
route.Normalize()
|
||||||
if route.FromRail == "" || route.FromRail == model.RailUnspecified {
|
if route.FromRail == "" || route.FromRail == discovery.RailUnspecified {
|
||||||
return merrors.InvalidArgument("routesStore: from_rail is required")
|
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")
|
return merrors.InvalidArgument("routesStore: to_rail is required")
|
||||||
}
|
}
|
||||||
if route.ID.IsZero() {
|
if route.ID.IsZero() {
|
||||||
@@ -176,20 +177,20 @@ func normalizedRailFilterValues(rail model.Rail) []string {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if parsed := model.ParseRail(string(rail)); parsed != model.RailUnspecified {
|
if parsed := model.ParseRail(string(rail)); parsed != discovery.RailUnspecified {
|
||||||
switch parsed {
|
switch parsed {
|
||||||
case model.RailCrypto:
|
case discovery.RailCrypto:
|
||||||
return []string{string(model.RailCrypto), "RAIL_CRYPTO"}
|
return []string{string(discovery.RailCrypto), "RAIL_CRYPTO"}
|
||||||
case model.RailProviderSettlement:
|
case discovery.RailProviderSettlement:
|
||||||
return []string{string(model.RailProviderSettlement), "PROVIDER_SETTLEMENT", "RAIL_SETTLEMENT", "RAIL_PROVIDER_SETTLEMENT"}
|
return []string{string(discovery.RailProviderSettlement), "PROVIDER_SETTLEMENT", "RAIL_SETTLEMENT", "RAIL_PROVIDER_SETTLEMENT"}
|
||||||
case model.RailLedger:
|
case discovery.RailLedger:
|
||||||
return []string{string(model.RailLedger), "RAIL_LEDGER"}
|
return []string{string(discovery.RailLedger), "RAIL_LEDGER"}
|
||||||
case model.RailCardPayout:
|
case discovery.RailCardPayout:
|
||||||
return []string{string(model.RailCardPayout), "CARD_PAYOUT", "RAIL_CARD", "RAIL_CARD_PAYOUT"}
|
return []string{string(discovery.RailCardPayout), "CARD_PAYOUT", "RAIL_CARD", "RAIL_CARD_PAYOUT"}
|
||||||
case model.RailFiatOnRamp:
|
case discovery.RailFiatOnRamp:
|
||||||
return []string{string(model.RailFiatOnRamp), "FIAT_ONRAMP", "RAIL_ONRAMP", "RAIL_FIAT_ONRAMP"}
|
return []string{string(discovery.RailFiatOnRamp), "FIAT_ONRAMP", "RAIL_ONRAMP", "RAIL_FIAT_ONRAMP"}
|
||||||
case model.RailFiatOffRamp:
|
case discovery.RailFiatOffRamp:
|
||||||
return []string{string(model.RailFiatOffRamp), "FIAT_OFFRAMP", "RAIL_OFFRAMP", "RAIL_FIAT_OFFRAMP"}
|
return []string{string(discovery.RailFiatOffRamp), "FIAT_OFFRAMP", "RAIL_OFFRAMP", "RAIL_FIAT_OFFRAMP"}
|
||||||
default:
|
default:
|
||||||
return []string{string(parsed)}
|
return []string{string(parsed)}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,12 @@ package discovery
|
|||||||
|
|
||||||
import "strings"
|
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 (
|
const (
|
||||||
OperationDiscoveryLookup = "discovery.lookup"
|
OperationDiscoveryLookup = "discovery.lookup"
|
||||||
|
|
||||||
@@ -33,6 +39,33 @@ const (
|
|||||||
OperationFXConvert = "fx.convert"
|
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.
|
// NormalizeOperation canonicalizes an operation string for comparisons.
|
||||||
func NormalizeOperation(value string) string {
|
func NormalizeOperation(value string) string {
|
||||||
return strings.ToLower(strings.TrimSpace(value))
|
return strings.ToLower(strings.TrimSpace(value))
|
||||||
|
|||||||
@@ -2,28 +2,6 @@ package discovery
|
|||||||
|
|
||||||
import "strings"
|
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{}{
|
var knownRails = map[string]struct{}{
|
||||||
RailCrypto: {},
|
RailCrypto: {},
|
||||||
RailProviderSettlement: {},
|
RailProviderSettlement: {},
|
||||||
@@ -57,21 +35,7 @@ func NormalizeRail(value string) string {
|
|||||||
for strings.Contains(clean, "__") {
|
for strings.Contains(clean, "__") {
|
||||||
clean = strings.ReplaceAll(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.
|
// 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.
|
// NormalizeRailOperation canonicalizes a rail operation token.
|
||||||
func NormalizeRailOperation(value string) string {
|
func NormalizeRailOperation(value string) string {
|
||||||
clean := strings.ToUpper(strings.TrimSpace(value))
|
return strings.ToUpper(strings.TrimSpace(value))
|
||||||
if after, ok := strings.CutPrefix(clean, "RAIL_OPERATION_"); ok {
|
|
||||||
clean = after
|
|
||||||
}
|
|
||||||
return clean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsKnownRailOperation reports whether the value is a recognized rail operation.
|
// 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)) {
|
switch strings.ToLower(strings.TrimSpace(value)) {
|
||||||
case OperationExternalDebit, "external_debit":
|
case OperationExternalDebit:
|
||||||
return []string{RailOperationExternalDebit}
|
return []string{RailOperationExternalDebit}
|
||||||
case OperationExternalCredit, "external_credit":
|
case OperationExternalCredit:
|
||||||
return []string{RailOperationExternalCredit}
|
return []string{RailOperationExternalCredit}
|
||||||
case "payin", "payin.crypto", "payin.fiat", "payin.card":
|
case OperationFee:
|
||||||
return []string{RailOperationExternalDebit}
|
|
||||||
case "payout", "payout.crypto", "payout.fiat", "payout.card":
|
|
||||||
return []string{RailOperationExternalCredit, RailOperationSend}
|
|
||||||
case "fee.send", "fees.send", OperationFee:
|
|
||||||
return []string{RailOperationFee}
|
return []string{RailOperationFee}
|
||||||
case OperationObserveConfirm, "observe_confirm":
|
case OperationObserveConfirm:
|
||||||
return []string{RailOperationObserveConfirm}
|
return []string{RailOperationObserveConfirm}
|
||||||
case OperationFXConvert, "fx_convert":
|
case OperationFXConvert:
|
||||||
return []string{RailOperationFXConvert}
|
return []string{RailOperationFXConvert}
|
||||||
case "funds.block", "hold.balance", "block":
|
|
||||||
return []string{RailOperationBlock}
|
|
||||||
case "funds.release", "release", "unblock":
|
|
||||||
return []string{RailOperationRelease}
|
|
||||||
default:
|
default:
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -159,12 +111,6 @@ func CryptoRailGatewayOperations() []string {
|
|||||||
OperationExternalCredit,
|
OperationExternalCredit,
|
||||||
OperationFee,
|
OperationFee,
|
||||||
OperationObserveConfirm,
|
OperationObserveConfirm,
|
||||||
// Legacy rail tokens retained for backward compatibility.
|
|
||||||
RailOperationSend,
|
|
||||||
RailOperationExternalDebit,
|
|
||||||
RailOperationExternalCredit,
|
|
||||||
RailOperationFee,
|
|
||||||
RailOperationObserveConfirm,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -174,10 +120,6 @@ func CardPayoutRailGatewayOperations() []string {
|
|||||||
OperationSend,
|
OperationSend,
|
||||||
OperationExternalCredit,
|
OperationExternalCredit,
|
||||||
OperationObserveConfirm,
|
OperationObserveConfirm,
|
||||||
// Legacy rail tokens retained for backward compatibility.
|
|
||||||
RailOperationSend,
|
|
||||||
RailOperationExternalCredit,
|
|
||||||
RailOperationObserveConfirm,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -186,8 +128,5 @@ func ProviderSettlementRailGatewayOperations() []string {
|
|||||||
return []string{
|
return []string{
|
||||||
OperationFXConvert,
|
OperationFXConvert,
|
||||||
OperationObserveConfirm,
|
OperationObserveConfirm,
|
||||||
// Legacy rail tokens retained for backward compatibility.
|
|
||||||
RailOperationFXConvert,
|
|
||||||
RailOperationObserveConfirm,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,17 +5,17 @@ import "testing"
|
|||||||
func TestNormalizeRailOperations(t *testing.T) {
|
func TestNormalizeRailOperations(t *testing.T) {
|
||||||
got := NormalizeRailOperations([]string{
|
got := NormalizeRailOperations([]string{
|
||||||
"send",
|
"send",
|
||||||
"payout.crypto",
|
|
||||||
"observe.confirm",
|
"observe.confirm",
|
||||||
"external.credit",
|
"external.credit",
|
||||||
"fx.convert",
|
"fx.convert",
|
||||||
|
"payout.crypto",
|
||||||
"unknown",
|
"unknown",
|
||||||
})
|
})
|
||||||
|
|
||||||
want := []string{
|
want := []string{
|
||||||
RailOperationSend,
|
RailOperationSend,
|
||||||
RailOperationExternalCredit,
|
|
||||||
RailOperationObserveConfirm,
|
RailOperationObserveConfirm,
|
||||||
|
RailOperationExternalCredit,
|
||||||
RailOperationFXConvert,
|
RailOperationFXConvert,
|
||||||
}
|
}
|
||||||
if len(got) != len(want) {
|
if len(got) != len(want) {
|
||||||
@@ -28,16 +28,9 @@ func TestNormalizeRailOperations(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestExpandRailOperationLegacyAliases(t *testing.T) {
|
func TestExpandRailOperationRejectsLegacyAliases(t *testing.T) {
|
||||||
got := ExpandRailOperation("payout.fiat")
|
if got := ExpandRailOperation("payout.fiat"); len(got) != 0 {
|
||||||
if len(got) != 2 {
|
t.Fatalf("expected no expansion for legacy alias, got=%v", got)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,14 +43,65 @@ func TestIsKnownRail(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNormalizeRailAliases(t *testing.T) {
|
func TestNormalizeRailRejectsLegacyAliases(t *testing.T) {
|
||||||
if got := NormalizeRail("provider_settlement"); got != RailProviderSettlement {
|
if IsKnownRail("provider_settlement") {
|
||||||
t.Fatalf("provider_settlement alias mismatch: got=%q want=%q", got, RailProviderSettlement)
|
t.Fatalf("did not expect provider_settlement alias to be known")
|
||||||
}
|
}
|
||||||
if got := NormalizeRail("card_payout"); got != RailCardPayout {
|
if IsKnownRail("card_payout") {
|
||||||
t.Fatalf("card_payout alias mismatch: got=%q want=%q", got, RailCardPayout)
|
t.Fatalf("did not expect card_payout alias to be known")
|
||||||
}
|
}
|
||||||
if got := NormalizeRail("RAIL_SETTLEMENT"); got != RailProviderSettlement {
|
if IsKnownRail("RAIL_SETTLEMENT") {
|
||||||
t.Fatalf("RAIL_SETTLEMENT alias mismatch: got=%q want=%q", got, RailProviderSettlement)
|
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])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user