155 lines
4.4 KiB
Go
155 lines
4.4 KiB
Go
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
|
|
}
|