package quotation import ( "context" "github.com/tech/sendico/pkg/discovery" "sort" "strings" "github.com/shopspring/decimal" chainclient "github.com/tech/sendico/gateway/chain/client" "github.com/tech/sendico/payments/quotation/internal/service/plan" "github.com/tech/sendico/payments/storage/model" "github.com/tech/sendico/pkg/merrors" paymenttypes "github.com/tech/sendico/pkg/payments/types" "go.uber.org/zap" ) func (s *Service) resolveChainGatewayClient(ctx context.Context, network string, amount *paymenttypes.Money, actions []model.RailOperation, instanceID string, paymentRef string) (chainclient.Client, *model.GatewayInstanceDescriptor, error) { if s.deps.gatewayRegistry != nil && s.deps.gatewayInvokeResolver != nil { entry, err := selectGatewayForActions(ctx, s.deps.gatewayRegistry, discovery.RailCrypto, network, amount, actions, instanceID, sendDirectionForRail(discovery.RailCrypto)) if err != nil { return nil, nil, err } invokeURI := strings.TrimSpace(entry.InvokeURI) if invokeURI == "" { return nil, nil, merrors.InvalidArgument("chain gateway: invoke uri is required") } client, err := s.deps.gatewayInvokeResolver.Resolve(ctx, invokeURI) if err != nil { return nil, nil, err } if s.logger != nil { fields := []zap.Field{ zap.String("gateway_id", entry.ID), zap.String("instance_id", entry.InstanceID), zap.String("rail", string(entry.Rail)), zap.String("network", entry.Network), zap.String("invoke_uri", invokeURI), } if paymentRef != "" { fields = append(fields, zap.String("payment_ref", paymentRef)) } if len(actions) > 0 { fields = append(fields, zap.Strings("actions", railActionNames(actions))) } s.logger.Info("Chain gateway selected", fields...) } return client, entry, nil } if s.deps.gateway.resolver != nil { client, err := s.deps.gateway.resolver.Resolve(ctx, network) if err != nil { return nil, nil, err } return client, nil, nil } return nil, nil, merrors.NoData("chain gateway unavailable") } func selectGatewayForActions(ctx context.Context, registry GatewayRegistry, rail model.Rail, network string, amount *paymenttypes.Money, actions []model.RailOperation, instanceID string, dir plan.SendDirection) (*model.GatewayInstanceDescriptor, error) { if registry == nil { return nil, merrors.NoData("gateway registry unavailable") } all, err := registry.List(ctx) if err != nil { return nil, err } if len(all) == 0 { return nil, merrors.NoData("no gateway instances available") } if len(actions) == 0 { actions = []model.RailOperation{discovery.RailOperationSend} } currency := "" amt := decimal.Zero if amount != nil && strings.TrimSpace(amount.GetAmount()) != "" { amt, err = decimalFromMoney(amount) if err != nil { return nil, err } currency = strings.ToUpper(strings.TrimSpace(amount.GetCurrency())) } network = strings.ToUpper(strings.TrimSpace(network)) eligible := make([]*model.GatewayInstanceDescriptor, 0) for _, entry := range all { if entry == nil || !entry.IsEnabled { continue } if entry.Rail != rail { continue } ok := true for _, action := range actions { if err := isGatewayEligible(entry, rail, network, currency, action, dir, amt); err != nil { ok = false break } } if !ok { continue } eligible = append(eligible, entry) } if len(eligible) == 0 { var action model.RailOperation = discovery.RailOperationUnspecified if len(actions) > 0 { action = actions[0] } return nil, merrors.NoData(model.NoEligibleGatewayMessage(network, currency, action, toGatewayDirection(dir))) } sort.Slice(eligible, func(i, j int) bool { return eligible[i].ID < eligible[j].ID }) if instanceID != "" { for _, entry := range eligible { if strings.EqualFold(strings.TrimSpace(entry.InstanceID), strings.TrimSpace(instanceID)) { return entry, nil } } } return eligible[0], nil } func toGatewayDirection(dir plan.SendDirection) model.GatewayDirection { switch dir { case plan.SendDirectionOut: return model.GatewayDirectionOut case plan.SendDirectionIn: return model.GatewayDirectionIn default: return model.GatewayDirectionAny } } func railActionNames(actions []model.RailOperation) []string { if len(actions) == 0 { return nil } names := make([]string, 0, len(actions)) for _, action := range actions { name := strings.TrimSpace(string(action)) if name == "" { continue } names = append(names, name) } if len(names) == 0 { return nil } return names }