package plan import ( "context" "sort" "strings" "github.com/tech/sendico/payments/storage/model" "github.com/tech/sendico/pkg/merrors" ) func resolveRouteNetwork(attrs map[string]string, sourceNetwork, destNetwork string) (string, error) { src := strings.ToUpper(strings.TrimSpace(sourceNetwork)) dst := strings.ToUpper(strings.TrimSpace(destNetwork)) if src != "" && dst != "" && !strings.EqualFold(src, dst) { return "", merrors.InvalidArgument("plan builder: source and destination networks mismatch") } override := strings.ToUpper(strings.TrimSpace(attributeLookup(attrs, "network", "route_network", "routeNetwork", "source_network", "sourceNetwork", "destination_network", "destinationNetwork", ))) if override != "" { if src != "" && !strings.EqualFold(src, override) { return "", merrors.InvalidArgument("plan builder: source network does not match override") } if dst != "" && !strings.EqualFold(dst, override) { return "", merrors.InvalidArgument("plan builder: destination network does not match override") } return override, nil } if src != "" { return src, nil } if dst != "" { return dst, nil } return "", nil } func selectRoute(ctx context.Context, routes RouteStore, sourceRail, destRail model.Rail, network string) (*model.PaymentRoute, error) { if routes == nil { return nil, merrors.InvalidArgument("plan builder: routes store is required") } enabled := true result, err := routes.List(ctx, &model.PaymentRouteFilter{ FromRail: sourceRail, ToRail: destRail, Network: "", IsEnabled: &enabled, }) if err != nil { return nil, err } if result == nil || len(result.Items) == 0 { return nil, merrors.InvalidArgument("plan builder: route not allowed") } candidates := make([]*model.PaymentRoute, 0, len(result.Items)) for _, route := range result.Items { if route == nil || !route.IsEnabled { continue } if route.FromRail != sourceRail || route.ToRail != destRail { continue } if !routeMatchesNetwork(route, network) { continue } candidates = append(candidates, route) } if len(candidates) == 0 { return nil, merrors.InvalidArgument("plan builder: route not allowed") } sort.Slice(candidates, func(i, j int) bool { pi := routePriority(candidates[i], network) pj := routePriority(candidates[j], network) if pi != pj { return pi < pj } if candidates[i].Network != candidates[j].Network { return candidates[i].Network < candidates[j].Network } return candidates[i].ID.Hex() < candidates[j].ID.Hex() }) return candidates[0], nil } func routeMatchesNetwork(route *model.PaymentRoute, network string) bool { if route == nil { return false } routeNetwork := strings.ToUpper(strings.TrimSpace(route.Network)) net := strings.ToUpper(strings.TrimSpace(network)) if routeNetwork == "" { return true } if net == "" { return false } return strings.EqualFold(routeNetwork, net) } func routePriority(route *model.PaymentRoute, network string) int { if route == nil { return 2 } routeNetwork := strings.ToUpper(strings.TrimSpace(route.Network)) net := strings.ToUpper(strings.TrimSpace(network)) if net != "" && strings.EqualFold(routeNetwork, net) { return 0 } if routeNetwork == "" { return 1 } return 2 }