hold/release + discovery based routing

This commit is contained in:
Stephan D
2026-01-16 14:33:05 +01:00
parent fe0caaa611
commit a4333a5385
44 changed files with 2086 additions and 348 deletions

View File

@@ -48,10 +48,19 @@ func buildFeeLedgerAccounts(src map[string]string) map[string]string {
}
func buildGatewayRegistry(logger mlogger.Logger, src []gatewayInstanceConfig, registry *discovery.Registry) orchestrator.GatewayRegistry {
static := buildGatewayInstances(logger, src)
staticRegistry := orchestrator.NewGatewayRegistry(logger, static)
discoveryRegistry := orchestrator.NewDiscoveryGatewayRegistry(logger, registry)
return orchestrator.NewCompositeGatewayRegistry(logger, staticRegistry, discoveryRegistry)
if logger != nil {
logger = logger.Named("gateway_registry")
}
if len(src) > 0 && logger != nil {
logger.Warn("Static gateway configuration ignored; using discovery registry only")
}
if registry == nil {
if logger != nil {
logger.Warn("Discovery registry unavailable; gateway routing disabled")
}
return nil
}
return orchestrator.NewDiscoveryGatewayRegistry(logger, registry)
}
func buildRailGateways(chainClient chainclient.Client, paymentGatewayClient chainclient.Client, src []gatewayInstanceConfig) map[string]rail.RailGateway {
@@ -76,6 +85,8 @@ func buildRailGateways(chainClient chainclient.Client, paymentGatewayClient chai
CanReadBalance: inst.Capabilities.CanReadBalance,
CanSendFee: inst.Capabilities.CanSendFee,
RequiresObserveConfirm: inst.Capabilities.RequiresObserveConfirm,
CanBlock: inst.Capabilities.CanBlock,
CanRelease: inst.Capabilities.CanRelease,
},
}
switch inst.Rail {
@@ -135,6 +146,8 @@ func buildGatewayInstances(logger mlogger.Logger, src []gatewayInstanceConfig) [
CanReadBalance: cfg.Capabilities.CanReadBalance,
CanSendFee: cfg.Capabilities.CanSendFee,
RequiresObserveConfirm: cfg.Capabilities.RequiresObserveConfirm,
CanBlock: cfg.Capabilities.CanBlock,
CanRelease: cfg.Capabilities.CanRelease,
},
Limits: buildGatewayLimits(cfg.Limits),
Version: strings.TrimSpace(cfg.Version),

View File

@@ -155,22 +155,8 @@ func (i *Imp) initOracleClient(cfg clientConfig) oracleclient.Client {
}
func (i *Imp) closeClients() {
if i.ledgerClient != nil {
_ = i.ledgerClient.Close()
}
if i.gatewayClient != nil {
_ = i.gatewayClient.Close()
}
if i.paymentGatewayClient != nil {
_ = i.paymentGatewayClient.Close()
}
if i.mntxClient != nil {
_ = i.mntxClient.Close()
}
if i.oracleClient != nil {
_ = i.oracleClient.Close()
}
if i.feesConn != nil {
_ = i.feesConn.Close()
if i.discoveryClients != nil {
i.discoveryClients.Close()
i.discoveryClients = nil
}
}

View File

@@ -54,6 +54,8 @@ type gatewayCapabilitiesConfig struct {
CanReadBalance bool `yaml:"can_read_balance"`
CanSendFee bool `yaml:"can_send_fee"`
RequiresObserveConfirm bool `yaml:"requires_observe_confirm"`
CanBlock bool `yaml:"can_block"`
CanRelease bool `yaml:"can_release"`
}
type limitsConfig struct {

View File

@@ -2,7 +2,6 @@ package serverimp
import (
oracleclient "github.com/tech/sendico/fx/oracle/client"
chainclient "github.com/tech/sendico/gateway/chain/client"
mntxclient "github.com/tech/sendico/gateway/mntx/client"
ledgerclient "github.com/tech/sendico/ledger/client"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrator"
@@ -10,47 +9,28 @@ import (
)
type orchestratorDeps struct {
feesClient feesv1.FeeEngineClient
ledgerClient ledgerclient.Client
gatewayClient chainclient.Client
paymentGatewayClient chainclient.Client
mntxClient mntxclient.Client
oracleClient oracleclient.Client
feesClient feesv1.FeeEngineClient
ledgerClient ledgerclient.Client
mntxClient mntxclient.Client
oracleClient oracleclient.Client
gatewayInvokeResolver orchestrator.GatewayInvokeResolver
}
func (i *Imp) initDependencies(cfg *config) *orchestratorDeps {
deps := &orchestratorDeps{}
if cfg == nil {
if i.discoveryReg == nil {
if i.logger != nil {
i.logger.Warn("Discovery registry unavailable; downstream clients disabled")
}
return deps
}
deps.feesClient, i.feesConn = i.initFeesClient(cfg.Fees)
deps.ledgerClient = i.initLedgerClient(cfg.Ledger)
if deps.ledgerClient != nil {
i.ledgerClient = deps.ledgerClient
}
deps.gatewayClient = i.initGatewayClient(cfg.Gateway)
if deps.gatewayClient != nil {
i.gatewayClient = deps.gatewayClient
}
deps.paymentGatewayClient = i.initPaymentGatewayClient(cfg.PaymentGateway)
if deps.paymentGatewayClient != nil {
i.paymentGatewayClient = deps.paymentGatewayClient
}
deps.mntxClient = i.initMntxClient(cfg.Mntx)
if deps.mntxClient != nil {
i.mntxClient = deps.mntxClient
}
deps.oracleClient = i.initOracleClient(cfg.Oracle)
if deps.oracleClient != nil {
i.oracleClient = deps.oracleClient
}
i.discoveryClients = newDiscoveryClientResolver(i.logger, i.discoveryReg)
deps.feesClient = &discoveryFeeClient{resolver: i.discoveryClients}
deps.ledgerClient = &discoveryLedgerClient{resolver: i.discoveryClients}
deps.oracleClient = &discoveryOracleClient{resolver: i.discoveryClients}
deps.mntxClient = &discoveryMntxClient{resolver: i.discoveryClients}
deps.gatewayInvokeResolver = discoveryGatewayInvokeResolver{resolver: i.discoveryClients}
return deps
}
@@ -65,21 +45,15 @@ func (i *Imp) buildServiceOptions(cfg *config, deps *orchestratorDeps) []orchest
if deps.ledgerClient != nil {
opts = append(opts, orchestrator.WithLedgerClient(deps.ledgerClient))
}
if deps.gatewayClient != nil {
opts = append(opts, orchestrator.WithChainGatewayClient(deps.gatewayClient))
}
if deps.paymentGatewayClient != nil {
opts = append(opts, orchestrator.WithProviderSettlementGatewayClient(deps.paymentGatewayClient))
}
if railGateways := buildRailGateways(deps.gatewayClient, deps.paymentGatewayClient, cfg.GatewayInstances); len(railGateways) > 0 {
opts = append(opts, orchestrator.WithRailGateways(railGateways))
}
if deps.mntxClient != nil {
opts = append(opts, orchestrator.WithMntxGateway(deps.mntxClient))
}
if deps.oracleClient != nil {
opts = append(opts, orchestrator.WithOracleClient(deps.oracleClient))
}
if deps.gatewayInvokeResolver != nil {
opts = append(opts, orchestrator.WithGatewayInvokeResolver(deps.gatewayInvokeResolver))
}
if routes := buildCardGatewayRoutes(cfg.CardGateways); len(routes) > 0 {
opts = append(opts, orchestrator.WithCardGatewayRoutes(routes))
}

View File

@@ -0,0 +1,477 @@
package serverimp
import (
"context"
"fmt"
"net"
"net/url"
"sort"
"strings"
"sync"
"time"
oracleclient "github.com/tech/sendico/fx/oracle/client"
chainclient "github.com/tech/sendico/gateway/chain/client"
mntxclient "github.com/tech/sendico/gateway/mntx/client"
ledgerclient "github.com/tech/sendico/ledger/client"
"github.com/tech/sendico/pkg/discovery"
"github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/mservice"
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
)
const discoveryLogThrottle = 30 * time.Second
var (
feesServiceNames = []string{"BILLING_FEES", string(mservice.FeePlans)}
ledgerServiceNames = []string{"LEDGER", string(mservice.Ledger)}
oracleServiceNames = []string{"FX_ORACLE", string(mservice.FXOracle)}
mntxServiceNames = []string{"CARD_PAYOUT_RAIL_GATEWAY", string(mservice.MntxGateway)}
)
type discoveryEndpoint struct {
address string
insecure bool
raw string
}
func (e discoveryEndpoint) key() string {
return fmt.Sprintf("%s|%t", e.address, e.insecure)
}
type discoveryClientResolver struct {
logger mlogger.Logger
registry *discovery.Registry
mu sync.Mutex
feesConn *grpc.ClientConn
feesEndpoint discoveryEndpoint
ledgerClient ledgerclient.Client
ledgerEndpoint discoveryEndpoint
oracleClient oracleclient.Client
oracleEndpoint discoveryEndpoint
mntxClient mntxclient.Client
mntxEndpoint discoveryEndpoint
chainClients map[string]chainclient.Client
lastSelection map[string]string
lastMissing map[string]time.Time
}
func newDiscoveryClientResolver(logger mlogger.Logger, registry *discovery.Registry) *discoveryClientResolver {
if logger != nil {
logger = logger.Named("discovery_clients")
}
return &discoveryClientResolver{
logger: logger,
registry: registry,
chainClients: map[string]chainclient.Client{},
lastSelection: map[string]string{},
lastMissing: map[string]time.Time{},
}
}
func (r *discoveryClientResolver) Close() {
r.mu.Lock()
defer r.mu.Unlock()
if r.feesConn != nil {
_ = r.feesConn.Close()
r.feesConn = nil
}
if r.ledgerClient != nil {
_ = r.ledgerClient.Close()
r.ledgerClient = nil
}
if r.oracleClient != nil {
_ = r.oracleClient.Close()
r.oracleClient = nil
}
if r.mntxClient != nil {
_ = r.mntxClient.Close()
r.mntxClient = nil
}
for key, client := range r.chainClients {
if client != nil {
_ = client.Close()
}
delete(r.chainClients, key)
}
}
func (r *discoveryClientResolver) FeesAvailable() bool {
_, ok := r.findEntry("fees", feesServiceNames, "", "")
return ok
}
func (r *discoveryClientResolver) LedgerAvailable() bool {
_, ok := r.findEntry("ledger", ledgerServiceNames, "", "")
return ok
}
func (r *discoveryClientResolver) OracleAvailable() bool {
_, ok := r.findEntry("oracle", oracleServiceNames, "", "")
return ok
}
func (r *discoveryClientResolver) MntxAvailable() bool {
_, ok := r.findEntry("mntx", mntxServiceNames, "", "")
return ok
}
func (r *discoveryClientResolver) FeesClient(ctx context.Context) (feesv1.FeeEngineClient, error) {
entry, ok := r.findEntry("fees", feesServiceNames, "", "")
if !ok {
return nil, merrors.NoData("discovery: fees service unavailable")
}
endpoint, err := parseDiscoveryEndpoint(entry.InvokeURI)
if err != nil {
r.logMissing("fees", "invalid fees invoke uri", entry.InvokeURI, err)
return nil, err
}
r.mu.Lock()
defer r.mu.Unlock()
if r.feesConn == nil || r.feesEndpoint.key() != endpoint.key() || r.feesEndpoint.address != endpoint.address {
if r.feesConn != nil {
_ = r.feesConn.Close()
r.feesConn = nil
}
conn, dialErr := dialGrpc(ctx, endpoint)
if dialErr != nil {
r.logMissing("fees", "failed to dial fees service", endpoint.raw, dialErr)
return nil, dialErr
}
r.feesConn = conn
r.feesEndpoint = endpoint
}
return feesv1.NewFeeEngineClient(r.feesConn), nil
}
func (r *discoveryClientResolver) LedgerClient(ctx context.Context) (ledgerclient.Client, error) {
entry, ok := r.findEntry("ledger", ledgerServiceNames, "", "")
if !ok {
return nil, merrors.NoData("discovery: ledger service unavailable")
}
endpoint, err := parseDiscoveryEndpoint(entry.InvokeURI)
if err != nil {
r.logMissing("ledger", "invalid ledger invoke uri", entry.InvokeURI, err)
return nil, err
}
r.mu.Lock()
defer r.mu.Unlock()
if r.ledgerClient == nil || r.ledgerEndpoint.key() != endpoint.key() || r.ledgerEndpoint.address != endpoint.address {
if r.ledgerClient != nil {
_ = r.ledgerClient.Close()
r.ledgerClient = nil
}
client, dialErr := ledgerclient.New(ctx, ledgerclient.Config{
Address: endpoint.address,
Insecure: endpoint.insecure,
})
if dialErr != nil {
r.logMissing("ledger", "failed to dial ledger service", endpoint.raw, dialErr)
return nil, dialErr
}
r.ledgerClient = client
r.ledgerEndpoint = endpoint
}
return r.ledgerClient, nil
}
func (r *discoveryClientResolver) OracleClient(ctx context.Context) (oracleclient.Client, error) {
entry, ok := r.findEntry("oracle", oracleServiceNames, "", "")
if !ok {
return nil, merrors.NoData("discovery: oracle service unavailable")
}
endpoint, err := parseDiscoveryEndpoint(entry.InvokeURI)
if err != nil {
r.logMissing("oracle", "invalid oracle invoke uri", entry.InvokeURI, err)
return nil, err
}
r.mu.Lock()
defer r.mu.Unlock()
if r.oracleClient == nil || r.oracleEndpoint.key() != endpoint.key() || r.oracleEndpoint.address != endpoint.address {
if r.oracleClient != nil {
_ = r.oracleClient.Close()
r.oracleClient = nil
}
client, dialErr := oracleclient.New(ctx, oracleclient.Config{
Address: endpoint.address,
Insecure: endpoint.insecure,
})
if dialErr != nil {
r.logMissing("oracle", "failed to dial oracle service", endpoint.raw, dialErr)
return nil, dialErr
}
r.oracleClient = client
r.oracleEndpoint = endpoint
}
return r.oracleClient, nil
}
func (r *discoveryClientResolver) MntxClient(ctx context.Context) (mntxclient.Client, error) {
entry, ok := r.findEntry("mntx", mntxServiceNames, "", "")
if !ok {
return nil, merrors.NoData("discovery: mntx service unavailable")
}
endpoint, err := parseDiscoveryEndpoint(entry.InvokeURI)
if err != nil {
r.logMissing("mntx", "invalid mntx invoke uri", entry.InvokeURI, err)
return nil, err
}
r.mu.Lock()
defer r.mu.Unlock()
if r.mntxClient == nil || r.mntxEndpoint.key() != endpoint.key() || r.mntxEndpoint.address != endpoint.address {
if r.mntxClient != nil {
_ = r.mntxClient.Close()
r.mntxClient = nil
}
if !endpoint.insecure && r.logger != nil {
r.logger.Warn("Mntx gateway does not support TLS, falling back to insecure transport",
zap.String("invoke_uri", endpoint.raw))
}
client, dialErr := mntxclient.New(ctx, mntxclient.Config{
Address: endpoint.address,
})
if dialErr != nil {
r.logMissing("mntx", "failed to dial mntx service", endpoint.raw, dialErr)
return nil, dialErr
}
r.mntxClient = client
r.mntxEndpoint = endpoint
}
return r.mntxClient, nil
}
func (r *discoveryClientResolver) ChainClient(ctx context.Context, invokeURI string) (chainclient.Client, error) {
endpoint, err := parseDiscoveryEndpoint(invokeURI)
if err != nil {
r.logMissing("chain", "invalid chain gateway invoke uri", invokeURI, err)
return nil, err
}
r.mu.Lock()
defer r.mu.Unlock()
if client, ok := r.chainClients[endpoint.key()]; ok && client != nil {
return client, nil
}
client, dialErr := chainclient.New(ctx, chainclient.Config{
Address: endpoint.address,
Insecure: endpoint.insecure,
})
if dialErr != nil {
r.logMissing("chain", "failed to dial chain gateway", endpoint.raw, dialErr)
return nil, dialErr
}
r.chainClients[endpoint.key()] = client
return client, nil
}
func (r *discoveryClientResolver) PaymentGatewayClient(ctx context.Context, invokeURI string) (chainclient.Client, error) {
endpoint, err := parseDiscoveryEndpoint(invokeURI)
if err != nil {
r.logMissing("payment_gateway", "invalid payment gateway invoke uri", invokeURI, err)
return nil, err
}
r.mu.Lock()
defer r.mu.Unlock()
if client, ok := r.chainClients[endpoint.key()]; ok && client != nil {
return client, nil
}
client, dialErr := chainclient.New(ctx, chainclient.Config{
Address: endpoint.address,
Insecure: endpoint.insecure,
})
if dialErr != nil {
r.logMissing("payment_gateway", "failed to dial payment gateway", endpoint.raw, dialErr)
return nil, dialErr
}
r.chainClients[endpoint.key()] = client
return client, nil
}
func (r *discoveryClientResolver) findEntry(key string, services []string, rail string, network string) (*discovery.RegistryEntry, bool) {
if r == nil || r.registry == nil {
r.logMissing(key, "discovery registry unavailable", "", nil)
return nil, false
}
entries := r.registry.List(time.Now(), true)
matches := make([]discovery.RegistryEntry, 0)
for _, entry := range entries {
if !matchesService(entry.Service, services) {
continue
}
if rail != "" && !strings.EqualFold(strings.TrimSpace(entry.Rail), rail) {
continue
}
if network != "" && !strings.EqualFold(strings.TrimSpace(entry.Network), network) {
continue
}
matches = append(matches, entry)
}
if len(matches) == 0 {
r.logMissing(key, "discovery entry missing", "", nil)
return nil, false
}
sort.Slice(matches, func(i, j int) bool {
if matches[i].RoutingPriority != matches[j].RoutingPriority {
return matches[i].RoutingPriority > matches[j].RoutingPriority
}
if matches[i].ID != matches[j].ID {
return matches[i].ID < matches[j].ID
}
return matches[i].InstanceID < matches[j].InstanceID
})
entry := matches[0]
entryKey := discoveryEntryKey(entry)
r.logSelection(key, entryKey, entry)
return &entry, true
}
func (r *discoveryClientResolver) logSelection(key, entryKey string, entry discovery.RegistryEntry) {
if r.logger == nil {
return
}
r.mu.Lock()
last := r.lastSelection[key]
if last == entryKey {
r.mu.Unlock()
return
}
r.lastSelection[key] = entryKey
r.mu.Unlock()
r.logger.Info("Discovery endpoint selected",
zap.String("service_key", key),
zap.String("service", entry.Service),
zap.String("rail", entry.Rail),
zap.String("network", entry.Network),
zap.String("entry_id", entry.ID),
zap.String("instance_id", entry.InstanceID),
zap.String("invoke_uri", entry.InvokeURI))
}
func (r *discoveryClientResolver) logMissing(key, message, invokeURI string, err error) {
if r.logger == nil {
return
}
now := time.Now()
r.mu.Lock()
last := r.lastMissing[key]
if !last.IsZero() && now.Sub(last) < discoveryLogThrottle {
r.mu.Unlock()
return
}
r.lastMissing[key] = now
r.mu.Unlock()
fields := []zap.Field{zap.String("service_key", key)}
if invokeURI != "" {
fields = append(fields, zap.String("invoke_uri", strings.TrimSpace(invokeURI)))
}
if err != nil {
fields = append(fields, zap.Error(err))
}
r.logger.Warn(message, fields...)
}
func discoveryEntryKey(entry discovery.RegistryEntry) string {
return fmt.Sprintf("%s|%s|%s|%s|%s|%s",
strings.TrimSpace(entry.Service),
strings.TrimSpace(entry.ID),
strings.TrimSpace(entry.InstanceID),
strings.TrimSpace(entry.Rail),
strings.TrimSpace(entry.Network),
strings.TrimSpace(entry.InvokeURI))
}
func matchesService(service string, candidates []string) bool {
service = strings.TrimSpace(service)
if service == "" || len(candidates) == 0 {
return false
}
for _, candidate := range candidates {
if strings.EqualFold(service, strings.TrimSpace(candidate)) {
return true
}
}
return false
}
func parseDiscoveryEndpoint(raw string) (discoveryEndpoint, error) {
raw = strings.TrimSpace(raw)
if raw == "" {
return discoveryEndpoint{}, merrors.InvalidArgument("discovery: invoke uri is required")
}
parsed, err := url.Parse(raw)
if err != nil || parsed.Scheme == "" {
if _, _, splitErr := net.SplitHostPort(raw); splitErr != nil {
if err != nil {
return discoveryEndpoint{}, err
}
return discoveryEndpoint{}, merrors.InvalidArgument("discovery: invoke uri must include host:port")
}
return discoveryEndpoint{address: raw, insecure: true, raw: raw}, nil
}
scheme := strings.ToLower(strings.TrimSpace(parsed.Scheme))
switch scheme {
case "grpc":
address := strings.TrimSpace(parsed.Host)
if _, _, splitErr := net.SplitHostPort(address); splitErr != nil {
return discoveryEndpoint{}, merrors.InvalidArgument("discovery: invoke uri must include host:port")
}
return discoveryEndpoint{address: address, insecure: true, raw: raw}, nil
case "grpcs":
address := strings.TrimSpace(parsed.Host)
if _, _, splitErr := net.SplitHostPort(address); splitErr != nil {
return discoveryEndpoint{}, merrors.InvalidArgument("discovery: invoke uri must include host:port")
}
return discoveryEndpoint{address: address, insecure: false, raw: raw}, nil
case "dns", "passthrough":
return discoveryEndpoint{address: raw, insecure: true, raw: raw}, nil
default:
return discoveryEndpoint{}, merrors.InvalidArgument("discovery: unsupported invoke uri scheme")
}
}
func dialGrpc(ctx context.Context, endpoint discoveryEndpoint) (*grpc.ClientConn, error) {
dialOpts := []grpc.DialOption{}
if endpoint.insecure {
dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials()))
} else {
dialOpts = append(dialOpts, grpc.WithTransportCredentials(credentials.NewTLS(nil)))
}
if ctx == nil {
ctx = context.Background()
}
return grpc.DialContext(ctx, endpoint.address, dialOpts...)
}

View File

@@ -0,0 +1,19 @@
package serverimp
import (
"context"
chainclient "github.com/tech/sendico/gateway/chain/client"
"github.com/tech/sendico/pkg/merrors"
)
type discoveryGatewayInvokeResolver struct {
resolver *discoveryClientResolver
}
func (r discoveryGatewayInvokeResolver) Resolve(ctx context.Context, invokeURI string) (chainclient.Client, error) {
if r.resolver == nil {
return nil, merrors.Internal("discovery gateway resolver unavailable")
}
return r.resolver.ChainClient(ctx, invokeURI)
}

View File

@@ -0,0 +1,370 @@
package serverimp
import (
"context"
oracleclient "github.com/tech/sendico/fx/oracle/client"
chainclient "github.com/tech/sendico/gateway/chain/client"
mntxclient "github.com/tech/sendico/gateway/mntx/client"
ledgerclient "github.com/tech/sendico/ledger/client"
"github.com/tech/sendico/pkg/merrors"
"github.com/tech/sendico/pkg/payments/rail"
feesv1 "github.com/tech/sendico/pkg/proto/billing/fees/v1"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
mntxv1 "github.com/tech/sendico/pkg/proto/gateway/mntx/v1"
ledgerv1 "github.com/tech/sendico/pkg/proto/ledger/v1"
"google.golang.org/grpc"
)
type discoveryFeeClient struct {
resolver *discoveryClientResolver
}
func (c *discoveryFeeClient) Available() bool {
if c == nil || c.resolver == nil {
return false
}
return c.resolver.FeesAvailable()
}
func (c *discoveryFeeClient) QuoteFees(ctx context.Context, req *feesv1.QuoteFeesRequest, opts ...grpc.CallOption) (*feesv1.QuoteFeesResponse, error) {
client, err := c.resolver.FeesClient(ctx)
if err != nil {
return nil, err
}
return client.QuoteFees(ctx, req, opts...)
}
func (c *discoveryFeeClient) PrecomputeFees(ctx context.Context, req *feesv1.PrecomputeFeesRequest, opts ...grpc.CallOption) (*feesv1.PrecomputeFeesResponse, error) {
client, err := c.resolver.FeesClient(ctx)
if err != nil {
return nil, err
}
return client.PrecomputeFees(ctx, req, opts...)
}
func (c *discoveryFeeClient) ValidateFeeToken(ctx context.Context, req *feesv1.ValidateFeeTokenRequest, opts ...grpc.CallOption) (*feesv1.ValidateFeeTokenResponse, error) {
client, err := c.resolver.FeesClient(ctx)
if err != nil {
return nil, err
}
return client.ValidateFeeToken(ctx, req, opts...)
}
type discoveryLedgerClient struct {
resolver *discoveryClientResolver
}
func (c *discoveryLedgerClient) Available() bool {
if c == nil || c.resolver == nil {
return false
}
return c.resolver.LedgerAvailable()
}
func (c *discoveryLedgerClient) ReadBalance(ctx context.Context, accountID string) (*moneyv1.Money, error) {
client, err := c.resolver.LedgerClient(ctx)
if err != nil {
return nil, err
}
return client.ReadBalance(ctx, accountID)
}
func (c *discoveryLedgerClient) CreateTransaction(ctx context.Context, tx rail.LedgerTx) (string, error) {
client, err := c.resolver.LedgerClient(ctx)
if err != nil {
return "", err
}
return client.CreateTransaction(ctx, tx)
}
func (c *discoveryLedgerClient) TransferInternal(ctx context.Context, req *ledgerv1.TransferRequest) (*ledgerv1.PostResponse, error) {
client, err := c.resolver.LedgerClient(ctx)
if err != nil {
return nil, err
}
return client.TransferInternal(ctx, req)
}
func (c *discoveryLedgerClient) HoldBalance(ctx context.Context, accountID string, amount string) error {
client, err := c.resolver.LedgerClient(ctx)
if err != nil {
return err
}
return client.HoldBalance(ctx, accountID, amount)
}
func (c *discoveryLedgerClient) CreateAccount(ctx context.Context, req *ledgerv1.CreateAccountRequest) (*ledgerv1.CreateAccountResponse, error) {
client, err := c.resolver.LedgerClient(ctx)
if err != nil {
return nil, err
}
return client.CreateAccount(ctx, req)
}
func (c *discoveryLedgerClient) ListAccounts(ctx context.Context, req *ledgerv1.ListAccountsRequest) (*ledgerv1.ListAccountsResponse, error) {
client, err := c.resolver.LedgerClient(ctx)
if err != nil {
return nil, err
}
return client.ListAccounts(ctx, req)
}
func (c *discoveryLedgerClient) PostCreditWithCharges(ctx context.Context, req *ledgerv1.PostCreditRequest) (*ledgerv1.PostResponse, error) {
client, err := c.resolver.LedgerClient(ctx)
if err != nil {
return nil, err
}
return client.PostCreditWithCharges(ctx, req)
}
func (c *discoveryLedgerClient) PostDebitWithCharges(ctx context.Context, req *ledgerv1.PostDebitRequest) (*ledgerv1.PostResponse, error) {
client, err := c.resolver.LedgerClient(ctx)
if err != nil {
return nil, err
}
return client.PostDebitWithCharges(ctx, req)
}
func (c *discoveryLedgerClient) ApplyFXWithCharges(ctx context.Context, req *ledgerv1.FXRequest) (*ledgerv1.PostResponse, error) {
client, err := c.resolver.LedgerClient(ctx)
if err != nil {
return nil, err
}
return client.ApplyFXWithCharges(ctx, req)
}
func (c *discoveryLedgerClient) GetBalance(ctx context.Context, req *ledgerv1.GetBalanceRequest) (*ledgerv1.BalanceResponse, error) {
client, err := c.resolver.LedgerClient(ctx)
if err != nil {
return nil, err
}
return client.GetBalance(ctx, req)
}
func (c *discoveryLedgerClient) GetJournalEntry(ctx context.Context, req *ledgerv1.GetEntryRequest) (*ledgerv1.JournalEntryResponse, error) {
client, err := c.resolver.LedgerClient(ctx)
if err != nil {
return nil, err
}
return client.GetJournalEntry(ctx, req)
}
func (c *discoveryLedgerClient) GetStatement(ctx context.Context, req *ledgerv1.GetStatementRequest) (*ledgerv1.StatementResponse, error) {
client, err := c.resolver.LedgerClient(ctx)
if err != nil {
return nil, err
}
return client.GetStatement(ctx, req)
}
func (c *discoveryLedgerClient) Close() error {
if c == nil || c.resolver == nil {
return nil
}
client, err := c.resolver.LedgerClient(context.Background())
if err != nil {
return nil
}
return client.Close()
}
type discoveryOracleClient struct {
resolver *discoveryClientResolver
}
func (c *discoveryOracleClient) Available() bool {
if c == nil || c.resolver == nil {
return false
}
return c.resolver.OracleAvailable()
}
func (c *discoveryOracleClient) LatestRate(ctx context.Context, req oracleclient.LatestRateParams) (*oracleclient.RateSnapshot, error) {
client, err := c.resolver.OracleClient(ctx)
if err != nil {
return nil, err
}
return client.LatestRate(ctx, req)
}
func (c *discoveryOracleClient) GetQuote(ctx context.Context, req oracleclient.GetQuoteParams) (*oracleclient.Quote, error) {
client, err := c.resolver.OracleClient(ctx)
if err != nil {
return nil, err
}
return client.GetQuote(ctx, req)
}
func (c *discoveryOracleClient) Close() error {
if c == nil || c.resolver == nil {
return nil
}
client, err := c.resolver.OracleClient(context.Background())
if err != nil {
return nil
}
return client.Close()
}
type discoveryMntxClient struct {
resolver *discoveryClientResolver
}
func (c *discoveryMntxClient) Available() bool {
if c == nil || c.resolver == nil {
return false
}
return c.resolver.MntxAvailable()
}
func (c *discoveryMntxClient) CreateCardPayout(ctx context.Context, req *mntxv1.CardPayoutRequest) (*mntxv1.CardPayoutResponse, error) {
client, err := c.resolver.MntxClient(ctx)
if err != nil {
return nil, err
}
return client.CreateCardPayout(ctx, req)
}
func (c *discoveryMntxClient) CreateCardTokenPayout(ctx context.Context, req *mntxv1.CardTokenPayoutRequest) (*mntxv1.CardTokenPayoutResponse, error) {
client, err := c.resolver.MntxClient(ctx)
if err != nil {
return nil, err
}
return client.CreateCardTokenPayout(ctx, req)
}
func (c *discoveryMntxClient) GetCardPayoutStatus(ctx context.Context, req *mntxv1.GetCardPayoutStatusRequest) (*mntxv1.GetCardPayoutStatusResponse, error) {
client, err := c.resolver.MntxClient(ctx)
if err != nil {
return nil, err
}
return client.GetCardPayoutStatus(ctx, req)
}
func (c *discoveryMntxClient) ListGatewayInstances(ctx context.Context, req *mntxv1.ListGatewayInstancesRequest) (*mntxv1.ListGatewayInstancesResponse, error) {
client, err := c.resolver.MntxClient(ctx)
if err != nil {
return nil, err
}
return client.ListGatewayInstances(ctx, req)
}
func (c *discoveryMntxClient) Close() error {
if c == nil || c.resolver == nil {
return nil
}
client, err := c.resolver.MntxClient(context.Background())
if err != nil {
return nil
}
return client.Close()
}
type discoveryChainClient struct {
resolver *discoveryClientResolver
invokeURI string
}
func (c *discoveryChainClient) Available() bool {
return c != nil && c.resolver != nil && c.invokeURI != ""
}
func (c *discoveryChainClient) client(ctx context.Context) (chainclient.Client, error) {
if c == nil || c.resolver == nil {
return nil, merrors.Internal("discovery chain client unavailable")
}
return c.resolver.ChainClient(ctx, c.invokeURI)
}
func (c *discoveryChainClient) CreateManagedWallet(ctx context.Context, req *chainv1.CreateManagedWalletRequest) (*chainv1.CreateManagedWalletResponse, error) {
client, err := c.client(ctx)
if err != nil {
return nil, err
}
return client.CreateManagedWallet(ctx, req)
}
func (c *discoveryChainClient) GetManagedWallet(ctx context.Context, req *chainv1.GetManagedWalletRequest) (*chainv1.GetManagedWalletResponse, error) {
client, err := c.client(ctx)
if err != nil {
return nil, err
}
return client.GetManagedWallet(ctx, req)
}
func (c *discoveryChainClient) ListManagedWallets(ctx context.Context, req *chainv1.ListManagedWalletsRequest) (*chainv1.ListManagedWalletsResponse, error) {
client, err := c.client(ctx)
if err != nil {
return nil, err
}
return client.ListManagedWallets(ctx, req)
}
func (c *discoveryChainClient) GetWalletBalance(ctx context.Context, req *chainv1.GetWalletBalanceRequest) (*chainv1.GetWalletBalanceResponse, error) {
client, err := c.client(ctx)
if err != nil {
return nil, err
}
return client.GetWalletBalance(ctx, req)
}
func (c *discoveryChainClient) SubmitTransfer(ctx context.Context, req *chainv1.SubmitTransferRequest) (*chainv1.SubmitTransferResponse, error) {
client, err := c.client(ctx)
if err != nil {
return nil, err
}
return client.SubmitTransfer(ctx, req)
}
func (c *discoveryChainClient) GetTransfer(ctx context.Context, req *chainv1.GetTransferRequest) (*chainv1.GetTransferResponse, error) {
client, err := c.client(ctx)
if err != nil {
return nil, err
}
return client.GetTransfer(ctx, req)
}
func (c *discoveryChainClient) ListTransfers(ctx context.Context, req *chainv1.ListTransfersRequest) (*chainv1.ListTransfersResponse, error) {
client, err := c.client(ctx)
if err != nil {
return nil, err
}
return client.ListTransfers(ctx, req)
}
func (c *discoveryChainClient) EstimateTransferFee(ctx context.Context, req *chainv1.EstimateTransferFeeRequest) (*chainv1.EstimateTransferFeeResponse, error) {
client, err := c.client(ctx)
if err != nil {
return nil, err
}
return client.EstimateTransferFee(ctx, req)
}
func (c *discoveryChainClient) ComputeGasTopUp(ctx context.Context, req *chainv1.ComputeGasTopUpRequest) (*chainv1.ComputeGasTopUpResponse, error) {
client, err := c.client(ctx)
if err != nil {
return nil, err
}
return client.ComputeGasTopUp(ctx, req)
}
func (c *discoveryChainClient) EnsureGasTopUp(ctx context.Context, req *chainv1.EnsureGasTopUpRequest) (*chainv1.EnsureGasTopUpResponse, error) {
client, err := c.client(ctx)
if err != nil {
return nil, err
}
return client.EnsureGasTopUp(ctx, req)
}
func (c *discoveryChainClient) Close() error {
if c == nil || c.resolver == nil {
return nil
}
client, err := c.resolver.ChainClient(context.Background(), c.invokeURI)
if err != nil {
return nil
}
return client.Close()
}

View File

@@ -1,16 +1,11 @@
package serverimp
import (
oracleclient "github.com/tech/sendico/fx/oracle/client"
chainclient "github.com/tech/sendico/gateway/chain/client"
mntxclient "github.com/tech/sendico/gateway/mntx/client"
ledgerclient "github.com/tech/sendico/ledger/client"
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrator"
"github.com/tech/sendico/payments/orchestrator/storage"
"github.com/tech/sendico/pkg/discovery"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/server/grpcapp"
"google.golang.org/grpc"
)
type Imp struct {
@@ -23,11 +18,6 @@ type Imp struct {
discoveryWatcher *discovery.RegistryWatcher
discoveryReg *discovery.Registry
discoveryAnnouncer *discovery.Announcer
discoveryClients *discoveryClientResolver
service *orchestrator.Service
feesConn *grpc.ClientConn
ledgerClient ledgerclient.Client
gatewayClient chainclient.Client
paymentGatewayClient chainclient.Client
mntxClient mntxclient.Client
oracleClient oracleclient.Client
}