fixed rail & operation names
This commit is contained in:
@@ -2,6 +2,7 @@ package model
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/tech/sendico/pkg/discovery"
|
||||
"strings"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
@@ -110,7 +111,7 @@ func gatewayAllowsAction(operations []RailOperation, cap RailCapabilities, actio
|
||||
|
||||
func capabilityAllowsAction(cap RailCapabilities, action RailOperation, dir GatewayDirection) bool {
|
||||
switch action {
|
||||
case RailOperationSend:
|
||||
case discovery.RailOperationSend:
|
||||
switch dir {
|
||||
case GatewayDirectionOut:
|
||||
return cap.CanPayOut
|
||||
@@ -119,7 +120,7 @@ func capabilityAllowsAction(cap RailCapabilities, action RailOperation, dir Gate
|
||||
default:
|
||||
return cap.CanPayIn || cap.CanPayOut
|
||||
}
|
||||
case RailOperationExternalDebit, RailOperationExternalCredit:
|
||||
case discovery.RailOperationExternalDebit, discovery.RailOperationExternalCredit:
|
||||
switch dir {
|
||||
case GatewayDirectionOut:
|
||||
return cap.CanPayOut
|
||||
@@ -128,13 +129,13 @@ func capabilityAllowsAction(cap RailCapabilities, action RailOperation, dir Gate
|
||||
default:
|
||||
return cap.CanPayIn || cap.CanPayOut
|
||||
}
|
||||
case RailOperationFee:
|
||||
case discovery.RailOperationFee:
|
||||
return cap.CanSendFee
|
||||
case RailOperationObserveConfirm:
|
||||
case discovery.RailOperationObserveConfirm:
|
||||
return cap.RequiresObserveConfirm
|
||||
case RailOperationBlock:
|
||||
case discovery.RailOperationBlock:
|
||||
return cap.CanBlock
|
||||
case RailOperationRelease:
|
||||
case discovery.RailOperationRelease:
|
||||
return cap.CanRelease
|
||||
default:
|
||||
return true
|
||||
@@ -143,7 +144,7 @@ func capabilityAllowsAction(cap RailCapabilities, action RailOperation, dir Gate
|
||||
|
||||
func operationsAllowAction(operations []RailOperation, action RailOperation, dir GatewayDirection) bool {
|
||||
action = ParseRailOperation(string(action))
|
||||
if action == RailOperationUnspecified {
|
||||
if action == discovery.RailOperationUnspecified {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -152,20 +153,20 @@ func operationsAllowAction(operations []RailOperation, action RailOperation, dir
|
||||
}
|
||||
|
||||
switch action {
|
||||
case RailOperationSend:
|
||||
case discovery.RailOperationSend:
|
||||
switch dir {
|
||||
case GatewayDirectionIn:
|
||||
return HasRailOperation(operations, RailOperationExternalDebit)
|
||||
return HasRailOperation(operations, discovery.RailOperationExternalDebit)
|
||||
case GatewayDirectionOut:
|
||||
return HasRailOperation(operations, RailOperationExternalCredit)
|
||||
return HasRailOperation(operations, discovery.RailOperationExternalCredit)
|
||||
default:
|
||||
return HasRailOperation(operations, RailOperationExternalDebit) ||
|
||||
HasRailOperation(operations, RailOperationExternalCredit)
|
||||
return HasRailOperation(operations, discovery.RailOperationExternalDebit) ||
|
||||
HasRailOperation(operations, discovery.RailOperationExternalCredit)
|
||||
}
|
||||
case RailOperationExternalDebit:
|
||||
return HasRailOperation(operations, RailOperationSend)
|
||||
case RailOperationExternalCredit:
|
||||
return HasRailOperation(operations, RailOperationSend)
|
||||
case discovery.RailOperationExternalDebit:
|
||||
return HasRailOperation(operations, discovery.RailOperationSend)
|
||||
case discovery.RailOperationExternalCredit:
|
||||
return HasRailOperation(operations, discovery.RailOperationSend)
|
||||
default:
|
||||
return false
|
||||
}
|
||||
@@ -181,7 +182,7 @@ func amountWithinLimits(gw *GatewayInstanceDescriptor, limits Limits, currency s
|
||||
if override, ok := limits.CurrencyLimits[currency]; ok {
|
||||
min = firstLimitValue(override.MinAmount, min)
|
||||
max = firstLimitValue(override.MaxAmount, max)
|
||||
if action == RailOperationFee {
|
||||
if action == discovery.RailOperationFee {
|
||||
maxFee = firstLimitValue(override.MaxFee, maxFee)
|
||||
}
|
||||
}
|
||||
@@ -206,7 +207,7 @@ func amountWithinLimits(gw *GatewayInstanceDescriptor, limits Limits, currency s
|
||||
return gatewayIneligible(gw, fmt.Sprintf("amount %s %s exceeds per-tx max limit %s", amount.String(), currency, val.String()))
|
||||
}
|
||||
}
|
||||
if action == RailOperationFee && maxFee != "" {
|
||||
if action == discovery.RailOperationFee && maxFee != "" {
|
||||
if val, err := decimal.NewFromString(maxFee); err == nil && amount.GreaterThan(val) {
|
||||
return gatewayIneligible(gw, fmt.Sprintf("fee amount %s %s exceeds max fee limit %s", amount.String(), currency, val.String()))
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/tech/sendico/pkg/discovery"
|
||||
"testing"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
@@ -10,14 +11,14 @@ func TestIsGatewayEligible_AllowsMatchingGateway(t *testing.T) {
|
||||
gw := &GatewayInstanceDescriptor{
|
||||
ID: "gw-1",
|
||||
InstanceID: "inst-1",
|
||||
Rail: RailCrypto,
|
||||
Rail: discovery.RailCrypto,
|
||||
Network: "TRON",
|
||||
Currencies: []string{"USDT"},
|
||||
Operations: []RailOperation{RailOperationSend, RailOperationExternalCredit},
|
||||
Operations: []RailOperation{discovery.RailOperationSend, discovery.RailOperationExternalCredit},
|
||||
IsEnabled: true,
|
||||
}
|
||||
|
||||
err := IsGatewayEligible(gw, RailCrypto, "TRON", "USDT", RailOperationSend, GatewayDirectionOut, decimal.RequireFromString("10"))
|
||||
err := IsGatewayEligible(gw, discovery.RailCrypto, "TRON", "USDT", discovery.RailOperationSend, GatewayDirectionOut, decimal.RequireFromString("10"))
|
||||
if err != nil {
|
||||
t.Fatalf("expected gateway to be eligible, got err=%v", err)
|
||||
}
|
||||
@@ -27,21 +28,21 @@ func TestIsGatewayEligible_RejectsNetworkMismatch(t *testing.T) {
|
||||
gw := &GatewayInstanceDescriptor{
|
||||
ID: "gw-1",
|
||||
InstanceID: "inst-1",
|
||||
Rail: RailCrypto,
|
||||
Rail: discovery.RailCrypto,
|
||||
Network: "ETH",
|
||||
Currencies: []string{"USDT"},
|
||||
Operations: []RailOperation{RailOperationSend},
|
||||
Operations: []RailOperation{discovery.RailOperationSend},
|
||||
IsEnabled: true,
|
||||
}
|
||||
|
||||
err := IsGatewayEligible(gw, RailCrypto, "TRON", "USDT", RailOperationSend, GatewayDirectionOut, decimal.RequireFromString("10"))
|
||||
err := IsGatewayEligible(gw, discovery.RailCrypto, "TRON", "USDT", discovery.RailOperationSend, GatewayDirectionOut, decimal.RequireFromString("10"))
|
||||
if err == nil {
|
||||
t.Fatalf("expected network mismatch error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoEligibleGatewayMessage(t *testing.T) {
|
||||
got := NoEligibleGatewayMessage("tron", "usdt", RailOperationSend, GatewayDirectionOut)
|
||||
got := NoEligibleGatewayMessage("tron", "usdt", discovery.RailOperationSend, GatewayDirectionOut)
|
||||
want := "plan builder: no eligible gateway found for TRON USDT SEND for direction out"
|
||||
if got != want {
|
||||
t.Fatalf("unexpected message: got=%q want=%q", got, want)
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/tech/sendico/pkg/db/storable"
|
||||
"github.com/tech/sendico/pkg/discovery"
|
||||
"github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/model/account_role"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
@@ -79,35 +80,10 @@ const (
|
||||
)
|
||||
|
||||
// Rail identifies a payment rail for orchestration.
|
||||
type Rail string
|
||||
|
||||
const (
|
||||
RailUnspecified Rail = "UNSPECIFIED"
|
||||
RailCrypto Rail = "CRYPTO"
|
||||
RailProviderSettlement Rail = "SETTLEMENT"
|
||||
RailLedger Rail = "LEDGER"
|
||||
RailCardPayout Rail = "CARD"
|
||||
RailFiatOnRamp Rail = "ONRAMP"
|
||||
RailFiatOffRamp Rail = "OFFRAMP"
|
||||
)
|
||||
type Rail = discovery.Rail
|
||||
|
||||
// RailOperation identifies an explicit action within a payment plan.
|
||||
type RailOperation string
|
||||
|
||||
const (
|
||||
RailOperationUnspecified RailOperation = "UNSPECIFIED"
|
||||
RailOperationDebit RailOperation = "DEBIT"
|
||||
RailOperationCredit RailOperation = "CREDIT"
|
||||
RailOperationExternalDebit RailOperation = "EXTERNAL_DEBIT"
|
||||
RailOperationExternalCredit RailOperation = "EXTERNAL_CREDIT"
|
||||
RailOperationMove RailOperation = "MOVE"
|
||||
RailOperationSend RailOperation = "SEND"
|
||||
RailOperationFee RailOperation = "FEE"
|
||||
RailOperationObserveConfirm RailOperation = "OBSERVE_CONFIRM"
|
||||
RailOperationFXConvert RailOperation = "FX_CONVERT"
|
||||
RailOperationBlock RailOperation = "BLOCK"
|
||||
RailOperationRelease RailOperation = "RELEASE"
|
||||
)
|
||||
type RailOperation = discovery.RailOperation
|
||||
|
||||
// RailCapabilities are declared per gateway instance.
|
||||
type RailCapabilities struct {
|
||||
|
||||
@@ -1,26 +1,29 @@
|
||||
package model
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"github.com/tech/sendico/pkg/discovery"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var supportedRailOperations = map[RailOperation]struct{}{
|
||||
RailOperationDebit: {},
|
||||
RailOperationCredit: {},
|
||||
RailOperationExternalDebit: {},
|
||||
RailOperationExternalCredit: {},
|
||||
RailOperationMove: {},
|
||||
RailOperationSend: {},
|
||||
RailOperationFee: {},
|
||||
RailOperationObserveConfirm: {},
|
||||
RailOperationFXConvert: {},
|
||||
RailOperationBlock: {},
|
||||
RailOperationRelease: {},
|
||||
discovery.RailOperationDebit: {},
|
||||
discovery.RailOperationCredit: {},
|
||||
discovery.RailOperationExternalDebit: {},
|
||||
discovery.RailOperationExternalCredit: {},
|
||||
discovery.RailOperationMove: {},
|
||||
discovery.RailOperationSend: {},
|
||||
discovery.RailOperationFee: {},
|
||||
discovery.RailOperationObserveConfirm: {},
|
||||
discovery.RailOperationFXConvert: {},
|
||||
discovery.RailOperationBlock: {},
|
||||
discovery.RailOperationRelease: {},
|
||||
}
|
||||
|
||||
// ParseRailOperation canonicalizes string values into a RailOperation token.
|
||||
func ParseRailOperation(value string) RailOperation {
|
||||
clean := strings.ToUpper(strings.TrimSpace(value))
|
||||
if clean == "" {
|
||||
return RailOperationUnspecified
|
||||
return discovery.RailOperationUnspecified
|
||||
}
|
||||
return RailOperation(clean)
|
||||
}
|
||||
@@ -40,7 +43,7 @@ func NormalizeRailOperations(values []RailOperation) []RailOperation {
|
||||
seen := map[RailOperation]bool{}
|
||||
for _, value := range values {
|
||||
op := ParseRailOperation(string(value))
|
||||
if op == RailOperationUnspecified || !IsSupportedRailOperation(op) || seen[op] {
|
||||
if op == discovery.RailOperationUnspecified || !IsSupportedRailOperation(op) || seen[op] {
|
||||
continue
|
||||
}
|
||||
seen[op] = true
|
||||
@@ -67,7 +70,7 @@ func NormalizeRailOperationStrings(values []string) []RailOperation {
|
||||
// HasRailOperation checks whether ops includes action.
|
||||
func HasRailOperation(ops []RailOperation, action RailOperation) bool {
|
||||
want := ParseRailOperation(string(action))
|
||||
if want == RailOperationUnspecified {
|
||||
if want == discovery.RailOperationUnspecified {
|
||||
return false
|
||||
}
|
||||
for _, op := range ops {
|
||||
@@ -82,12 +85,12 @@ func HasRailOperation(ops []RailOperation, action RailOperation) bool {
|
||||
func RailCapabilitiesFromOperations(ops []RailOperation) RailCapabilities {
|
||||
normalized := NormalizeRailOperations(ops)
|
||||
return RailCapabilities{
|
||||
CanPayIn: HasRailOperation(normalized, RailOperationExternalDebit),
|
||||
CanPayOut: HasRailOperation(normalized, RailOperationSend) || HasRailOperation(normalized, RailOperationExternalCredit),
|
||||
CanPayIn: HasRailOperation(normalized, discovery.RailOperationExternalDebit),
|
||||
CanPayOut: HasRailOperation(normalized, discovery.RailOperationSend) || HasRailOperation(normalized, discovery.RailOperationExternalCredit),
|
||||
CanReadBalance: false,
|
||||
CanSendFee: HasRailOperation(normalized, RailOperationFee),
|
||||
RequiresObserveConfirm: HasRailOperation(normalized, RailOperationObserveConfirm),
|
||||
CanBlock: HasRailOperation(normalized, RailOperationBlock),
|
||||
CanRelease: HasRailOperation(normalized, RailOperationRelease),
|
||||
CanSendFee: HasRailOperation(normalized, discovery.RailOperationFee),
|
||||
RequiresObserveConfirm: HasRailOperation(normalized, discovery.RailOperationObserveConfirm),
|
||||
CanBlock: HasRailOperation(normalized, discovery.RailOperationBlock),
|
||||
CanRelease: HasRailOperation(normalized, discovery.RailOperationRelease),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package model
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"github.com/tech/sendico/pkg/discovery"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNormalizeRailOperations(t *testing.T) {
|
||||
ops := NormalizeRailOperations([]RailOperation{
|
||||
@@ -13,35 +16,35 @@ func TestNormalizeRailOperations(t *testing.T) {
|
||||
if len(ops) != 2 {
|
||||
t.Fatalf("unexpected operations count: got=%d want=2", len(ops))
|
||||
}
|
||||
if ops[0] != RailOperationSend {
|
||||
t.Fatalf("unexpected first operation: got=%q want=%q", ops[0], RailOperationSend)
|
||||
if ops[0] != discovery.RailOperationSend {
|
||||
t.Fatalf("unexpected first operation: got=%q want=%q", ops[0], discovery.RailOperationSend)
|
||||
}
|
||||
if ops[1] != RailOperationExternalCredit {
|
||||
t.Fatalf("unexpected second operation: got=%q want=%q", ops[1], RailOperationExternalCredit)
|
||||
if ops[1] != discovery.RailOperationExternalCredit {
|
||||
t.Fatalf("unexpected second operation: got=%q want=%q", ops[1], discovery.RailOperationExternalCredit)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHasRailOperation(t *testing.T) {
|
||||
ops := []RailOperation{RailOperationSend, RailOperationExternalCredit}
|
||||
if !HasRailOperation(ops, RailOperationSend) {
|
||||
ops := []RailOperation{discovery.RailOperationSend, discovery.RailOperationExternalCredit}
|
||||
if !HasRailOperation(ops, discovery.RailOperationSend) {
|
||||
t.Fatalf("expected send operation to be present")
|
||||
}
|
||||
if !HasRailOperation(ops, " external_credit ") {
|
||||
t.Fatalf("expected external credit operation to be present")
|
||||
}
|
||||
if HasRailOperation(ops, RailOperationObserveConfirm) {
|
||||
if HasRailOperation(ops, discovery.RailOperationObserveConfirm) {
|
||||
t.Fatalf("did not expect observe confirm operation to be present")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRailCapabilitiesFromOperations(t *testing.T) {
|
||||
cap := RailCapabilitiesFromOperations([]RailOperation{
|
||||
RailOperationExternalDebit,
|
||||
RailOperationExternalCredit,
|
||||
RailOperationFee,
|
||||
RailOperationObserveConfirm,
|
||||
RailOperationBlock,
|
||||
RailOperationRelease,
|
||||
discovery.RailOperationExternalDebit,
|
||||
discovery.RailOperationExternalCredit,
|
||||
discovery.RailOperationFee,
|
||||
discovery.RailOperationObserveConfirm,
|
||||
discovery.RailOperationBlock,
|
||||
discovery.RailOperationRelease,
|
||||
})
|
||||
|
||||
if !cap.CanPayIn {
|
||||
|
||||
@@ -1,21 +1,24 @@
|
||||
package model
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"github.com/tech/sendico/pkg/discovery"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var supportedRails = map[Rail]struct{}{
|
||||
RailCrypto: {},
|
||||
RailProviderSettlement: {},
|
||||
RailLedger: {},
|
||||
RailCardPayout: {},
|
||||
RailFiatOnRamp: {},
|
||||
RailFiatOffRamp: {},
|
||||
discovery.RailCrypto: {},
|
||||
discovery.RailProviderSettlement: {},
|
||||
discovery.RailLedger: {},
|
||||
discovery.RailCardPayout: {},
|
||||
discovery.RailFiatOnRamp: {},
|
||||
discovery.RailFiatOffRamp: {},
|
||||
}
|
||||
|
||||
// ParseRail canonicalizes string values into a Rail token.
|
||||
func ParseRail(value string) Rail {
|
||||
clean := strings.ToUpper(strings.TrimSpace(value))
|
||||
if clean == "" {
|
||||
return RailUnspecified
|
||||
return discovery.RailUnspecified
|
||||
}
|
||||
clean = strings.ReplaceAll(clean, "-", "_")
|
||||
clean = strings.ReplaceAll(clean, " ", "_")
|
||||
@@ -24,20 +27,20 @@ func ParseRail(value string) Rail {
|
||||
}
|
||||
|
||||
switch clean {
|
||||
case string(RailCrypto), "RAIL_CRYPTO":
|
||||
return RailCrypto
|
||||
case string(RailProviderSettlement), "PROVIDER_SETTLEMENT", "RAIL_SETTLEMENT", "RAIL_PROVIDER_SETTLEMENT":
|
||||
return RailProviderSettlement
|
||||
case string(RailLedger), "RAIL_LEDGER":
|
||||
return RailLedger
|
||||
case string(RailCardPayout), "CARD_PAYOUT", "RAIL_CARD", "RAIL_CARD_PAYOUT":
|
||||
return RailCardPayout
|
||||
case string(RailFiatOnRamp), "FIAT_ONRAMP", "RAIL_ONRAMP", "RAIL_FIAT_ONRAMP":
|
||||
return RailFiatOnRamp
|
||||
case string(RailFiatOffRamp), "FIAT_OFFRAMP", "RAIL_OFFRAMP", "RAIL_FIAT_OFFRAMP":
|
||||
return RailFiatOffRamp
|
||||
case string(discovery.RailCrypto), "RAIL_CRYPTO":
|
||||
return discovery.RailCrypto
|
||||
case string(discovery.RailProviderSettlement), "PROVIDER_SETTLEMENT", "RAIL_SETTLEMENT", "RAIL_PROVIDER_SETTLEMENT":
|
||||
return discovery.RailProviderSettlement
|
||||
case string(discovery.RailLedger), "RAIL_LEDGER":
|
||||
return discovery.RailLedger
|
||||
case string(discovery.RailCardPayout), "CARD_PAYOUT", "RAIL_CARD", "RAIL_CARD_PAYOUT":
|
||||
return discovery.RailCardPayout
|
||||
case string(discovery.RailFiatOnRamp), "FIAT_ONRAMP", "RAIL_ONRAMP", "RAIL_FIAT_ONRAMP":
|
||||
return discovery.RailFiatOnRamp
|
||||
case string(discovery.RailFiatOffRamp), "FIAT_OFFRAMP", "RAIL_OFFRAMP", "RAIL_FIAT_OFFRAMP":
|
||||
return discovery.RailFiatOffRamp
|
||||
default:
|
||||
return RailUnspecified
|
||||
return discovery.RailUnspecified
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,13 +52,13 @@ func IsSupportedRail(rail Rail) bool {
|
||||
|
||||
func normalizeRail(value Rail) Rail {
|
||||
parsed := ParseRail(string(value))
|
||||
if parsed != RailUnspecified {
|
||||
if parsed != discovery.RailUnspecified {
|
||||
return parsed
|
||||
}
|
||||
|
||||
clean := strings.ToUpper(strings.TrimSpace(string(value)))
|
||||
if clean == "" {
|
||||
return RailUnspecified
|
||||
return discovery.RailUnspecified
|
||||
}
|
||||
|
||||
return Rail(clean)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package model
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"github.com/tech/sendico/pkg/discovery"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestParseRail(t *testing.T) {
|
||||
cases := []struct {
|
||||
@@ -8,14 +11,14 @@ func TestParseRail(t *testing.T) {
|
||||
input string
|
||||
want Rail
|
||||
}{
|
||||
{name: "crypto", input: "crypto", want: RailCrypto},
|
||||
{name: "settlement canonical", input: "SETTLEMENT", want: RailProviderSettlement},
|
||||
{name: "settlement legacy", input: "provider_settlement", want: RailProviderSettlement},
|
||||
{name: "card canonical", input: "card", want: RailCardPayout},
|
||||
{name: "card legacy", input: "card_payout", want: RailCardPayout},
|
||||
{name: "onramp", input: "fiat_onramp", want: RailFiatOnRamp},
|
||||
{name: "offramp", input: "fiat_offramp", want: RailFiatOffRamp},
|
||||
{name: "unknown", input: "telegram", want: RailUnspecified},
|
||||
{name: "crypto", input: "crypto", want: discovery.RailCrypto},
|
||||
{name: "settlement canonical", input: "SETTLEMENT", want: discovery.RailProviderSettlement},
|
||||
{name: "settlement legacy", input: "provider_settlement", want: discovery.RailProviderSettlement},
|
||||
{name: "card canonical", input: "card", want: discovery.RailCardPayout},
|
||||
{name: "card legacy", input: "card_payout", want: discovery.RailCardPayout},
|
||||
{name: "onramp", input: "fiat_onramp", want: discovery.RailFiatOnRamp},
|
||||
{name: "offramp", input: "fiat_offramp", want: discovery.RailFiatOffRamp},
|
||||
{name: "unknown", input: "telegram", want: discovery.RailUnspecified},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
|
||||
Reference in New Issue
Block a user