fixed mntx discovery
This commit is contained in:
@@ -1,14 +1,12 @@
|
||||
package serverimp
|
||||
|
||||
import (
|
||||
mntxclient "github.com/tech/sendico/gateway/mntx/client"
|
||||
ledgerclient "github.com/tech/sendico/ledger/client"
|
||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrator"
|
||||
)
|
||||
|
||||
type orchestratorDeps struct {
|
||||
ledgerClient ledgerclient.Client
|
||||
mntxClient mntxclient.Client
|
||||
gatewayInvokeResolver orchestrator.GatewayInvokeResolver
|
||||
}
|
||||
|
||||
@@ -23,7 +21,6 @@ func (i *Imp) initDependencies(_ *config) *orchestratorDeps {
|
||||
|
||||
i.discoveryClients = newDiscoveryClientResolver(i.logger, i.discoveryReg)
|
||||
deps.ledgerClient = &discoveryLedgerClient{resolver: i.discoveryClients}
|
||||
deps.mntxClient = &discoveryMntxClient{resolver: i.discoveryClients}
|
||||
deps.gatewayInvokeResolver = discoveryGatewayInvokeResolver{resolver: i.discoveryClients}
|
||||
return deps
|
||||
}
|
||||
@@ -36,9 +33,6 @@ func (i *Imp) buildServiceOptions(cfg *config, deps *orchestratorDeps) []orchest
|
||||
if deps.ledgerClient != nil {
|
||||
opts = append(opts, orchestrator.WithLedgerClient(deps.ledgerClient))
|
||||
}
|
||||
if deps.mntxClient != nil {
|
||||
opts = append(opts, orchestrator.WithMntxGateway(deps.mntxClient))
|
||||
}
|
||||
|
||||
if deps.gatewayInvokeResolver != nil {
|
||||
opts = append(opts, orchestrator.WithGatewayInvokeResolver(deps.gatewayInvokeResolver))
|
||||
|
||||
@@ -32,7 +32,7 @@ func (i *Imp) initDiscovery(cfg *config) {
|
||||
i.logger.Info("Discovery registry watcher started")
|
||||
}
|
||||
announce := discovery.Announcement{
|
||||
Service: "PAYMENTS_ORCHESTRATOR",
|
||||
Service: mservice.PaymentOrchestrator,
|
||||
Operations: []string{discovery.OperationPaymentInitiate},
|
||||
InvokeURI: cfg.GRPC.DiscoveryInvokeURI(),
|
||||
Version: appversion.Create().Short(),
|
||||
|
||||
@@ -12,7 +12,6 @@ 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/pkg/discovery"
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
@@ -28,15 +27,9 @@ import (
|
||||
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{discovery.RailCardPayout, string(mservice.MntxGateway)}
|
||||
|
||||
feesRequiredOps = []string{discovery.OperationFeeCalc}
|
||||
ledgerRequiredOps = discovery.LedgerServiceOperations()
|
||||
oracleRequiredOps = []string{discovery.OperationFXQuote}
|
||||
mntxRequiredOps = discovery.CardPayoutRailGatewayOperations()
|
||||
)
|
||||
|
||||
type discoveryEndpoint struct {
|
||||
@@ -64,9 +57,6 @@ type discoveryClientResolver struct {
|
||||
oracleClient oracleclient.Client
|
||||
oracleEndpoint discoveryEndpoint
|
||||
|
||||
mntxClient mntxclient.Client
|
||||
mntxEndpoint discoveryEndpoint
|
||||
|
||||
chainClients map[string]chainclient.Client
|
||||
|
||||
lastSelection map[string]string
|
||||
@@ -101,10 +91,6 @@ func (r *discoveryClientResolver) Close() {
|
||||
_ = 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()
|
||||
@@ -114,27 +100,22 @@ func (r *discoveryClientResolver) Close() {
|
||||
}
|
||||
|
||||
func (r *discoveryClientResolver) FeesAvailable() bool {
|
||||
_, ok := r.findEntry("fees", feesServiceNames, "", "", feesRequiredOps)
|
||||
_, ok := r.findEntry(mservice.BillingFees, "", "", feesRequiredOps)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *discoveryClientResolver) LedgerAvailable() bool {
|
||||
_, ok := r.findEntry("ledger", ledgerServiceNames, "", "", ledgerRequiredOps)
|
||||
_, ok := r.findEntry(mservice.Ledger, "", "", ledgerRequiredOps)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *discoveryClientResolver) OracleAvailable() bool {
|
||||
_, ok := r.findEntry("oracle", oracleServiceNames, "", "", oracleRequiredOps)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *discoveryClientResolver) MntxAvailable() bool {
|
||||
_, ok := r.findEntry("mntx", mntxServiceNames, "", "", mntxRequiredOps)
|
||||
_, ok := r.findEntry(mservice.FXOracle, "", "", oracleRequiredOps)
|
||||
return ok
|
||||
}
|
||||
|
||||
func (r *discoveryClientResolver) FeesClient(ctx context.Context) (feesv1.FeeEngineClient, error) {
|
||||
entry, ok := r.findEntry("fees", feesServiceNames, "", "", feesRequiredOps)
|
||||
entry, ok := r.findEntry(mservice.BillingFees, "", "", feesRequiredOps)
|
||||
if !ok {
|
||||
return nil, merrors.NoData("discovery: fees service unavailable")
|
||||
}
|
||||
@@ -154,7 +135,7 @@ func (r *discoveryClientResolver) FeesClient(ctx context.Context) (feesv1.FeeEng
|
||||
}
|
||||
conn, dialErr := dialGrpc(ctx, endpoint)
|
||||
if dialErr != nil {
|
||||
r.logMissing("fees", "failed to dial fees service", endpoint.raw, dialErr)
|
||||
r.logMissing("fees", "Failed to dial fees service", endpoint.raw, dialErr)
|
||||
return nil, dialErr
|
||||
}
|
||||
r.feesConn = conn
|
||||
@@ -165,13 +146,13 @@ func (r *discoveryClientResolver) FeesClient(ctx context.Context) (feesv1.FeeEng
|
||||
}
|
||||
|
||||
func (r *discoveryClientResolver) LedgerClient(ctx context.Context) (ledgerclient.Client, error) {
|
||||
entry, ok := r.findEntry("ledger", ledgerServiceNames, "", "", ledgerRequiredOps)
|
||||
entry, ok := r.findEntry(mservice.Ledger, "", "", ledgerRequiredOps)
|
||||
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)
|
||||
r.logMissing("ledger", "Invalid ledger invoke uri", entry.InvokeURI, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -188,7 +169,7 @@ func (r *discoveryClientResolver) LedgerClient(ctx context.Context) (ledgerclien
|
||||
Insecure: endpoint.insecure,
|
||||
})
|
||||
if dialErr != nil {
|
||||
r.logMissing("ledger", "failed to dial ledger service", endpoint.raw, dialErr)
|
||||
r.logMissing("ledger", "Failed to dial ledger service", endpoint.raw, dialErr)
|
||||
return nil, dialErr
|
||||
}
|
||||
r.ledgerClient = client
|
||||
@@ -199,13 +180,13 @@ func (r *discoveryClientResolver) LedgerClient(ctx context.Context) (ledgerclien
|
||||
}
|
||||
|
||||
func (r *discoveryClientResolver) OracleClient(ctx context.Context) (oracleclient.Client, error) {
|
||||
entry, ok := r.findEntry("oracle", oracleServiceNames, "", "", oracleRequiredOps)
|
||||
entry, ok := r.findEntry(mservice.FXOracle, "", "", oracleRequiredOps)
|
||||
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)
|
||||
r.logMissing("oracle", "Invalid oracle invoke uri", entry.InvokeURI, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -222,7 +203,7 @@ func (r *discoveryClientResolver) OracleClient(ctx context.Context) (oracleclien
|
||||
Insecure: endpoint.insecure,
|
||||
})
|
||||
if dialErr != nil {
|
||||
r.logMissing("oracle", "failed to dial oracle service", endpoint.raw, dialErr)
|
||||
r.logMissing("oracle", "Failed to dial oracle service", endpoint.raw, dialErr)
|
||||
return nil, dialErr
|
||||
}
|
||||
r.oracleClient = client
|
||||
@@ -232,47 +213,10 @@ func (r *discoveryClientResolver) OracleClient(ctx context.Context) (oracleclien
|
||||
return r.oracleClient, nil
|
||||
}
|
||||
|
||||
func (r *discoveryClientResolver) MntxClient(ctx context.Context) (mntxclient.Client, error) {
|
||||
entry, ok := r.findEntry("mntx", mntxServiceNames, "", "", mntxRequiredOps)
|
||||
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)
|
||||
r.logMissing("chain", "Invalid chain gateway invoke uri", invokeURI, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -288,7 +232,7 @@ func (r *discoveryClientResolver) ChainClient(ctx context.Context, invokeURI str
|
||||
Insecure: endpoint.insecure,
|
||||
})
|
||||
if dialErr != nil {
|
||||
r.logMissing("chain", "failed to dial chain gateway", endpoint.raw, dialErr)
|
||||
r.logMissing("chain", "Failed to dial chain gateway", endpoint.raw, dialErr)
|
||||
return nil, dialErr
|
||||
}
|
||||
r.chainClients[endpoint.key()] = client
|
||||
@@ -298,7 +242,7 @@ func (r *discoveryClientResolver) ChainClient(ctx context.Context, invokeURI str
|
||||
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)
|
||||
r.logMissing("payment_gateway", "Invalid payment gateway invoke uri", invokeURI, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -314,16 +258,18 @@ func (r *discoveryClientResolver) PaymentGatewayClient(ctx context.Context, invo
|
||||
Insecure: endpoint.insecure,
|
||||
})
|
||||
if dialErr != nil {
|
||||
r.logMissing("payment_gateway", "failed to dial payment gateway", endpoint.raw, dialErr)
|
||||
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, requiredOps []string) (*discovery.RegistryEntry, bool) {
|
||||
func (r *discoveryClientResolver) findEntry(service mservice.Type, rail string, network string, requiredOps []string) (*discovery.RegistryEntry, bool) {
|
||||
key := discoverySelectionKey(service, rail, network)
|
||||
|
||||
if r == nil || r.registry == nil {
|
||||
r.logMissing(key, "discovery registry unavailable", "", nil)
|
||||
r.logMissing(key, "Discovery registry unavailable", "", nil)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@@ -335,7 +281,7 @@ func (r *discoveryClientResolver) findEntry(key string, services []string, rail
|
||||
entries := r.registry.List(time.Now(), true)
|
||||
matches := make([]discoveryMatch, 0)
|
||||
for _, entry := range entries {
|
||||
if !matchesService(entry.Service, services) {
|
||||
if service != "" && !matchesService(entry.Service, service) {
|
||||
continue
|
||||
}
|
||||
if rail != "" && !strings.EqualFold(strings.TrimSpace(entry.Rail), rail) {
|
||||
@@ -351,7 +297,7 @@ func (r *discoveryClientResolver) findEntry(key string, services []string, rail
|
||||
}
|
||||
|
||||
if len(matches) == 0 {
|
||||
r.logMissing(key, "discovery entry missing", "", nil)
|
||||
r.logMissing(key, "Discovery entry missing", "", nil)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
@@ -374,6 +320,27 @@ func (r *discoveryClientResolver) findEntry(key string, services []string, rail
|
||||
return &entry, true
|
||||
}
|
||||
|
||||
func discoverySelectionKey(service mservice.Type, rail, network string) string {
|
||||
serviceName := strings.TrimSpace(string(service))
|
||||
railName := strings.ToUpper(strings.TrimSpace(rail))
|
||||
networkName := strings.ToUpper(strings.TrimSpace(network))
|
||||
|
||||
switch {
|
||||
case serviceName != "" && railName != "" && networkName != "":
|
||||
return fmt.Sprintf("%s|%s|%s", serviceName, railName, networkName)
|
||||
case serviceName != "" && railName != "":
|
||||
return fmt.Sprintf("%s|%s", serviceName, railName)
|
||||
case serviceName != "":
|
||||
return serviceName
|
||||
case railName != "" && networkName != "":
|
||||
return fmt.Sprintf("rail:%s|%s", railName, networkName)
|
||||
case railName != "":
|
||||
return "rail:" + railName
|
||||
default:
|
||||
return "discovery"
|
||||
}
|
||||
}
|
||||
|
||||
func (r *discoveryClientResolver) logSelection(key, entryKey string, entry discovery.RegistryEntry) {
|
||||
r.mu.Lock()
|
||||
last := r.lastSelection[key]
|
||||
@@ -423,17 +390,12 @@ func discoveryEntryKey(entry discovery.RegistryEntry) string {
|
||||
strings.TrimSpace(entry.InvokeURI))
|
||||
}
|
||||
|
||||
func matchesService(service string, candidates []string) bool {
|
||||
func matchesService(service string, candidate mservice.Type) bool {
|
||||
service = strings.TrimSpace(service)
|
||||
if service == "" || len(candidates) == 0 {
|
||||
if service == "" || strings.TrimSpace(string(candidate)) == "" {
|
||||
return false
|
||||
}
|
||||
for _, candidate := range candidates {
|
||||
if strings.EqualFold(service, strings.TrimSpace(candidate)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return strings.EqualFold(service, strings.TrimSpace(string(candidate)))
|
||||
}
|
||||
|
||||
func parseDiscoveryEndpoint(raw string) (discoveryEndpoint, error) {
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
|
||||
connectorv1 "github.com/tech/sendico/pkg/proto/connector/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"
|
||||
)
|
||||
@@ -247,60 +246,6 @@ func (c *discoveryOracleClient) Close() error {
|
||||
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
|
||||
|
||||
@@ -3,8 +3,11 @@ package orchestrator
|
||||
import (
|
||||
"context"
|
||||
"github.com/tech/sendico/pkg/discovery"
|
||||
"net"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
mntxclient "github.com/tech/sendico/gateway/mntx/client"
|
||||
@@ -20,7 +23,12 @@ import (
|
||||
)
|
||||
|
||||
type gatewayCardPayoutExecutor struct {
|
||||
mntxClient mntxclient.Client
|
||||
gatewayRegistry GatewayRegistry
|
||||
|
||||
mu sync.Mutex
|
||||
clients map[string]mntxclient.Client
|
||||
|
||||
dialClient func(ctx context.Context, address string) (mntxclient.Client, error)
|
||||
}
|
||||
|
||||
type cardPayoutCustomer struct {
|
||||
@@ -40,12 +48,20 @@ func (e *gatewayCardPayoutExecutor) ExecuteCardPayout(ctx context.Context, req s
|
||||
if req.Payment == nil {
|
||||
return nil, merrors.InvalidArgument("card payout send: payment is required")
|
||||
}
|
||||
if e == nil || e.mntxClient == nil {
|
||||
return nil, merrors.InvalidArgument("card payout send: mntx client is required")
|
||||
if e == nil || e.gatewayRegistry == nil {
|
||||
return nil, merrors.InvalidArgument("card payout send: gateway registry is required")
|
||||
}
|
||||
if model.ParseRailOperation(string(req.Step.Action)) != discovery.RailOperationSend {
|
||||
return nil, merrors.InvalidArgument("card payout send: unsupported action")
|
||||
}
|
||||
gateway, err := e.resolveGateway(ctx, req.Step)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
client, err := e.client(ctx, strings.TrimSpace(gateway.InvokeURI))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
card, err := payoutDestinationCard(req.Payment, req.Step.Metadata)
|
||||
if err != nil {
|
||||
@@ -68,7 +84,7 @@ func (e *gatewayCardPayoutExecutor) ExecuteCardPayout(ctx context.Context, req s
|
||||
|
||||
var responsePayout *mntxv1.CardPayoutState
|
||||
if token := strings.TrimSpace(card.Token); token != "" {
|
||||
resp, createErr := e.mntxClient.CreateCardTokenPayout(ctx, &mntxv1.CardTokenPayoutRequest{
|
||||
resp, createErr := client.CreateCardTokenPayout(ctx, &mntxv1.CardTokenPayoutRequest{
|
||||
PayoutId: payoutRef,
|
||||
ProjectId: projectID,
|
||||
CustomerId: customer.id,
|
||||
@@ -106,7 +122,7 @@ func (e *gatewayCardPayoutExecutor) ExecuteCardPayout(ctx context.Context, req s
|
||||
if card.ExpMonth == 0 || card.ExpYear == 0 {
|
||||
return nil, merrors.InvalidArgument("card payout send: card expiry is required")
|
||||
}
|
||||
resp, createErr := e.mntxClient.CreateCardPayout(ctx, &mntxv1.CardPayoutRequest{
|
||||
resp, createErr := client.CreateCardPayout(ctx, &mntxv1.CardPayoutRequest{
|
||||
PayoutId: payoutRef,
|
||||
ProjectId: projectID,
|
||||
CustomerId: customer.id,
|
||||
@@ -141,7 +157,12 @@ func (e *gatewayCardPayoutExecutor) ExecuteCardPayout(ctx context.Context, req s
|
||||
|
||||
resolvedPayoutRef := firstNonEmpty(strings.TrimSpace(responsePayout.GetPayoutId()), payoutRef)
|
||||
resolvedOperationRef := firstNonEmpty(strings.TrimSpace(responsePayout.GetOperationRef()), operationRef)
|
||||
gatewayInstanceID := firstNonEmpty(strings.TrimSpace(req.Step.InstanceID), strings.TrimSpace(req.Step.Gateway))
|
||||
gatewayInstanceID := firstNonEmpty(
|
||||
strings.TrimSpace(req.Step.InstanceID),
|
||||
strings.TrimSpace(gateway.InstanceID),
|
||||
strings.TrimSpace(req.Step.Gateway),
|
||||
strings.TrimSpace(gateway.ID),
|
||||
)
|
||||
externalRefs, refsErr := cardPayoutExternalRefs(resolvedPayoutRef, resolvedOperationRef, gatewayInstanceID)
|
||||
if refsErr != nil {
|
||||
return nil, refsErr
|
||||
@@ -155,6 +176,122 @@ func (e *gatewayCardPayoutExecutor) ExecuteCardPayout(ctx context.Context, req s
|
||||
return &sexec.ExecuteOutput{StepExecution: step}, nil
|
||||
}
|
||||
|
||||
func (e *gatewayCardPayoutExecutor) resolveGateway(ctx context.Context, step xplan.Step) (*model.GatewayInstanceDescriptor, error) {
|
||||
if e.gatewayRegistry == nil {
|
||||
return nil, merrors.InvalidArgument("card payout send: gateway registry is required")
|
||||
}
|
||||
items, err := e.gatewayRegistry.List(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stepGateway := strings.TrimSpace(step.Gateway)
|
||||
stepInstance := strings.TrimSpace(step.InstanceID)
|
||||
|
||||
var byInstance *model.GatewayInstanceDescriptor
|
||||
var byGateway *model.GatewayInstanceDescriptor
|
||||
var single *model.GatewayInstanceDescriptor
|
||||
cardCount := 0
|
||||
for i := range items {
|
||||
item := items[i]
|
||||
if item == nil || model.ParseRail(string(item.Rail)) != discovery.RailCardPayout || !item.IsEnabled {
|
||||
continue
|
||||
}
|
||||
cardCount++
|
||||
single = item
|
||||
if stepInstance != "" && (strings.EqualFold(strings.TrimSpace(item.InstanceID), stepInstance) || strings.EqualFold(strings.TrimSpace(item.ID), stepInstance)) {
|
||||
byInstance = item
|
||||
break
|
||||
}
|
||||
if stepGateway != "" && (strings.EqualFold(strings.TrimSpace(item.ID), stepGateway) || strings.EqualFold(strings.TrimSpace(item.InstanceID), stepGateway)) {
|
||||
byGateway = item
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case byInstance != nil:
|
||||
if strings.TrimSpace(byInstance.InvokeURI) == "" {
|
||||
return nil, merrors.InvalidArgument("card payout send: gateway invoke uri is missing")
|
||||
}
|
||||
return byInstance, nil
|
||||
case byGateway != nil:
|
||||
if strings.TrimSpace(byGateway.InvokeURI) == "" {
|
||||
return nil, merrors.InvalidArgument("card payout send: gateway invoke uri is missing")
|
||||
}
|
||||
return byGateway, nil
|
||||
case stepGateway == "" && stepInstance == "" && cardCount == 1:
|
||||
if strings.TrimSpace(single.InvokeURI) == "" {
|
||||
return nil, merrors.InvalidArgument("card payout send: gateway invoke uri is missing")
|
||||
}
|
||||
return single, nil
|
||||
default:
|
||||
return nil, merrors.InvalidArgument("card payout send: gateway instance not found")
|
||||
}
|
||||
}
|
||||
|
||||
func (e *gatewayCardPayoutExecutor) client(ctx context.Context, invokeURI string) (mntxclient.Client, error) {
|
||||
address, err := parseGatewayAddress(invokeURI)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
e.mu.Lock()
|
||||
defer e.mu.Unlock()
|
||||
|
||||
if e.clients == nil {
|
||||
e.clients = map[string]mntxclient.Client{}
|
||||
}
|
||||
if c := e.clients[address]; c != nil {
|
||||
return c, nil
|
||||
}
|
||||
|
||||
dial := e.dialClient
|
||||
if dial == nil {
|
||||
dial = func(ctx context.Context, address string) (mntxclient.Client, error) {
|
||||
return mntxclient.New(ctx, mntxclient.Config{Address: address})
|
||||
}
|
||||
}
|
||||
|
||||
client, dialErr := dial(ctx, address)
|
||||
if dialErr != nil {
|
||||
return nil, dialErr
|
||||
}
|
||||
e.clients[address] = client
|
||||
return client, nil
|
||||
}
|
||||
|
||||
func parseGatewayAddress(raw string) (string, error) {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return "", merrors.InvalidArgument("card payout send: gateway invoke uri is required")
|
||||
}
|
||||
|
||||
if !strings.Contains(raw, "://") {
|
||||
if _, _, splitErr := net.SplitHostPort(raw); splitErr != nil {
|
||||
return "", merrors.InvalidArgument("card payout send: gateway invoke uri must include host:port")
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
|
||||
parsed, err := url.Parse(raw)
|
||||
if err != nil || parsed.Scheme == "" {
|
||||
return "", merrors.InvalidArgument("card payout send: gateway invoke uri must include host:port")
|
||||
}
|
||||
|
||||
switch strings.ToLower(strings.TrimSpace(parsed.Scheme)) {
|
||||
case "grpc", "grpcs":
|
||||
address := strings.TrimSpace(parsed.Host)
|
||||
if _, _, splitErr := net.SplitHostPort(address); splitErr != nil {
|
||||
return "", merrors.InvalidArgument("card payout send: gateway invoke uri must include host:port")
|
||||
}
|
||||
return address, nil
|
||||
case "dns", "passthrough":
|
||||
return raw, nil
|
||||
default:
|
||||
return "", merrors.InvalidArgument("card payout send: unsupported gateway invoke uri scheme")
|
||||
}
|
||||
}
|
||||
|
||||
func payoutDestinationCard(payment *agg.Payment, metadata map[string]string) (*model.CardEndpoint, error) {
|
||||
if card, ok := batchmeta.CardFromMetadata(metadata); ok && card != nil {
|
||||
return card, nil
|
||||
|
||||
@@ -23,17 +23,32 @@ func TestGatewayCardPayoutExecutor_ExecuteCardPayout_SubmitsCardPayout(t *testin
|
||||
orgID := bson.NewObjectID()
|
||||
|
||||
var payoutReq *mntxv1.CardPayoutRequest
|
||||
var dialAddress string
|
||||
executor := &gatewayCardPayoutExecutor{
|
||||
mntxClient: &mntxclient.Fake{
|
||||
CreateCardPayoutFn: func(_ context.Context, req *mntxv1.CardPayoutRequest) (*mntxv1.CardPayoutResponse, error) {
|
||||
payoutReq = req
|
||||
return &mntxv1.CardPayoutResponse{
|
||||
Payout: &mntxv1.CardPayoutState{
|
||||
PayoutId: "payout-remote-1",
|
||||
},
|
||||
}, nil
|
||||
gatewayRegistry: &fakeGatewayRegistry{
|
||||
items: []*model.GatewayInstanceDescriptor{
|
||||
{
|
||||
ID: paymenttypes.DefaultCardsGatewayID,
|
||||
InstanceID: paymenttypes.DefaultCardsGatewayID,
|
||||
Rail: discovery.RailCardPayout,
|
||||
InvokeURI: "grpc://mntx-gateway:50051",
|
||||
IsEnabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
dialClient: func(_ context.Context, address string) (mntxclient.Client, error) {
|
||||
dialAddress = address
|
||||
return &mntxclient.Fake{
|
||||
CreateCardPayoutFn: func(_ context.Context, req *mntxv1.CardPayoutRequest) (*mntxv1.CardPayoutResponse, error) {
|
||||
payoutReq = req
|
||||
return &mntxv1.CardPayoutResponse{
|
||||
Payout: &mntxv1.CardPayoutState{
|
||||
PayoutId: "payout-remote-1",
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
req := sexec.StepRequest{
|
||||
@@ -104,6 +119,9 @@ func TestGatewayCardPayoutExecutor_ExecuteCardPayout_SubmitsCardPayout(t *testin
|
||||
if payoutReq == nil {
|
||||
t.Fatal("expected payout request to be submitted")
|
||||
}
|
||||
if got, want := dialAddress, "mntx-gateway:50051"; got != want {
|
||||
t.Fatalf("dial address mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
if got, want := payoutReq.GetPayoutId(), "payment-1"; got != want {
|
||||
t.Fatalf("payout_id mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
@@ -146,17 +164,32 @@ func TestGatewayCardPayoutExecutor_ExecuteCardPayout_UsesStepMetadataOverrides(t
|
||||
orgID := bson.NewObjectID()
|
||||
|
||||
var payoutReq *mntxv1.CardPayoutRequest
|
||||
var dialAddress string
|
||||
executor := &gatewayCardPayoutExecutor{
|
||||
mntxClient: &mntxclient.Fake{
|
||||
CreateCardPayoutFn: func(_ context.Context, req *mntxv1.CardPayoutRequest) (*mntxv1.CardPayoutResponse, error) {
|
||||
payoutReq = req
|
||||
return &mntxv1.CardPayoutResponse{
|
||||
Payout: &mntxv1.CardPayoutState{
|
||||
PayoutId: "payout-remote-2",
|
||||
},
|
||||
}, nil
|
||||
gatewayRegistry: &fakeGatewayRegistry{
|
||||
items: []*model.GatewayInstanceDescriptor{
|
||||
{
|
||||
ID: paymenttypes.DefaultCardsGatewayID,
|
||||
InstanceID: paymenttypes.DefaultCardsGatewayID,
|
||||
Rail: discovery.RailCardPayout,
|
||||
InvokeURI: "grpc://mntx-gateway:50051",
|
||||
IsEnabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
dialClient: func(_ context.Context, address string) (mntxclient.Client, error) {
|
||||
dialAddress = address
|
||||
return &mntxclient.Fake{
|
||||
CreateCardPayoutFn: func(_ context.Context, req *mntxv1.CardPayoutRequest) (*mntxv1.CardPayoutResponse, error) {
|
||||
payoutReq = req
|
||||
return &mntxv1.CardPayoutResponse{
|
||||
Payout: &mntxv1.CardPayoutState{
|
||||
PayoutId: "payout-remote-2",
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}, nil
|
||||
},
|
||||
}
|
||||
|
||||
req := sexec.StepRequest{
|
||||
@@ -221,6 +254,9 @@ func TestGatewayCardPayoutExecutor_ExecuteCardPayout_UsesStepMetadataOverrides(t
|
||||
if payoutReq == nil {
|
||||
t.Fatal("expected payout request to be submitted")
|
||||
}
|
||||
if got, want := dialAddress, "mntx-gateway:50051"; got != want {
|
||||
t.Fatalf("dial address mismatch: got=%q want=%q", got, want)
|
||||
}
|
||||
if got, want := payoutReq.GetAmountMinor(), int64(15000); got != want {
|
||||
t.Fatalf("amount_minor mismatch: got=%d want=%d", got, want)
|
||||
}
|
||||
@@ -235,7 +271,7 @@ func TestGatewayCardPayoutExecutor_ExecuteCardPayout_UsesStepMetadataOverrides(t
|
||||
}
|
||||
}
|
||||
|
||||
func TestGatewayCardPayoutExecutor_ExecuteCardPayout_RequiresMntxClient(t *testing.T) {
|
||||
func TestGatewayCardPayoutExecutor_ExecuteCardPayout_RequiresGatewayRegistry(t *testing.T) {
|
||||
orgID := bson.NewObjectID()
|
||||
|
||||
executor := &gatewayCardPayoutExecutor{}
|
||||
@@ -270,7 +306,7 @@ func TestGatewayCardPayoutExecutor_ExecuteCardPayout_RequiresMntxClient(t *testi
|
||||
if err == nil {
|
||||
t.Fatal("expected error")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "mntx client is required") {
|
||||
if !strings.Contains(err.Error(), "gateway registry is required") {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ import (
|
||||
"time"
|
||||
|
||||
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/storage/model"
|
||||
clockpkg "github.com/tech/sendico/pkg/clock"
|
||||
@@ -54,16 +53,6 @@ func WithLedgerClient(client ledgerclient.Client) Option {
|
||||
}
|
||||
}
|
||||
|
||||
// WithMntxGateway configures card payout execution for card-bound steps.
|
||||
func WithMntxGateway(client mntxclient.Client) Option {
|
||||
return func(s *Service) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.mntxClient = client
|
||||
}
|
||||
}
|
||||
|
||||
// WithPaymentGatewayBroker wires broker subscription for payment gateway execution events.
|
||||
func WithPaymentGatewayBroker(broker mb.Broker) Option {
|
||||
return func(s *Service) {
|
||||
|
||||
@@ -3,7 +3,6 @@ package orchestrator
|
||||
import (
|
||||
"context"
|
||||
|
||||
mntxclient "github.com/tech/sendico/gateway/mntx/client"
|
||||
ledgerclient "github.com/tech/sendico/ledger/client"
|
||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/prepo"
|
||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/psvc"
|
||||
@@ -26,7 +25,6 @@ type Service struct {
|
||||
producer msg.Producer
|
||||
|
||||
ledgerClient ledgerclient.Client
|
||||
mntxClient mntxclient.Client
|
||||
gatewayInvokeResolver GatewayInvokeResolver
|
||||
gatewayRegistry GatewayRegistry
|
||||
cardGatewayRoutes map[string]CardGatewayRoute
|
||||
@@ -56,7 +54,6 @@ func NewService(logger mlogger.Logger, repo storage.Repository, producer msg.Pro
|
||||
var err error
|
||||
svc.v2, svc.paymentRepo, err = newOrchestrationV2Service(svc.logger, repo, v2RuntimeDeps{
|
||||
LedgerClient: svc.ledgerClient,
|
||||
MntxClient: svc.mntxClient,
|
||||
GatewayInvokeResolver: svc.gatewayInvokeResolver,
|
||||
GatewayRegistry: svc.gatewayRegistry,
|
||||
CardGatewayRoutes: svc.cardGatewayRoutes,
|
||||
|
||||
@@ -3,7 +3,6 @@ package orchestrator
|
||||
import (
|
||||
"context"
|
||||
|
||||
mntxclient "github.com/tech/sendico/gateway/mntx/client"
|
||||
ledgerclient "github.com/tech/sendico/ledger/client"
|
||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/oobs"
|
||||
"github.com/tech/sendico/payments/orchestrator/internal/service/orchestrationv2/pquery"
|
||||
@@ -27,7 +26,6 @@ type v2MongoDBProvider interface {
|
||||
|
||||
type v2RuntimeDeps struct {
|
||||
LedgerClient ledgerclient.Client
|
||||
MntxClient mntxclient.Client
|
||||
GatewayInvokeResolver GatewayInvokeResolver
|
||||
GatewayRegistry GatewayRegistry
|
||||
CardGatewayRoutes map[string]CardGatewayRoute
|
||||
@@ -114,9 +112,9 @@ func buildOrchestrationV2Executors(logger mlogger.Logger, runtimeDeps v2RuntimeD
|
||||
ledgerClient: runtimeDeps.LedgerClient,
|
||||
}
|
||||
var cardPayoutExecutor sexec.CardPayoutExecutor
|
||||
if runtimeDeps.MntxClient != nil {
|
||||
if runtimeDeps.GatewayRegistry != nil {
|
||||
cardPayoutExecutor = &gatewayCardPayoutExecutor{
|
||||
mntxClient: runtimeDeps.MntxClient,
|
||||
gatewayRegistry: runtimeDeps.GatewayRegistry,
|
||||
}
|
||||
}
|
||||
return psvc.NewDefaultExecutors(execLogger, sexec.Dependencies{
|
||||
|
||||
Reference in New Issue
Block a user