unified gateway interfaces

This commit is contained in:
Stephan D
2026-01-04 12:47:43 +01:00
parent 743f683d92
commit 59c83e414a
41 changed files with 927 additions and 186 deletions

View File

@@ -9,6 +9,7 @@ import (
"github.com/tech/sendico/pkg/merrors"
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
unifiedv1 "github.com/tech/sendico/pkg/proto/gateway/unified/v1"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
"google.golang.org/grpc/credentials/insecure"
@@ -75,7 +76,7 @@ func New(ctx context.Context, cfg Config, opts ...grpc.DialOption) (Client, erro
return &chainGatewayClient{
cfg: cfg,
conn: conn,
client: chainv1.NewChainGatewayServiceClient(conn),
client: unifiedv1.NewUnifiedGatewayServiceClient(conn),
}, nil
}

View File

@@ -22,7 +22,7 @@ require (
require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251230134950-44c893854e3f // indirect
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260104020744-7268a54d0358 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.24.4 // indirect
github.com/bmatcuk/doublestar/v4 v4.9.1 // indirect

View File

@@ -6,8 +6,8 @@ github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ=
github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251230134950-44c893854e3f h1:a5PUgHGinaD6XrLmIDLQmGHocjIjBsBAcR5gALjZvMU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20251230134950-44c893854e3f/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260104020744-7268a54d0358 h1:B6uGMdZ4maUTJm+LYgBwEIDuJxgOUACw8K0Yg6jpNbY=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260104020744-7268a54d0358/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0=
github.com/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=

View File

@@ -20,6 +20,7 @@ import (
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/mservice"
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
unifiedv1 "github.com/tech/sendico/pkg/proto/gateway/unified/v1"
"google.golang.org/grpc"
)
@@ -33,7 +34,7 @@ var (
errStorageUnavailable = serviceError("chain_gateway: storage not initialised")
)
// Service implements the ChainGatewayService RPC contract.
// Service implements the UnifiedGatewayService RPC contract for chain operations.
type Service struct {
logger mlogger.Logger
storage storage.Repository
@@ -51,7 +52,7 @@ type Service struct {
commands commands.Registry
announcers []*discovery.Announcer
chainv1.UnimplementedChainGatewayServiceServer
unifiedv1.UnimplementedUnifiedGatewayServiceServer
}
// NewService constructs the chain gateway service skeleton.
@@ -94,7 +95,7 @@ func NewService(logger mlogger.Logger, repo storage.Repository, producer msg.Pro
// Register wires the service onto the provided gRPC router.
func (s *Service) Register(router routers.GRPC) error {
return router.Register(func(reg grpc.ServiceRegistrar) {
chainv1.RegisterChainGatewayServiceServer(reg, s)
unifiedv1.RegisterUnifiedGatewayServiceServer(reg, s)
})
}
@@ -208,6 +209,7 @@ func (s *Service) startDiscoveryAnnouncers() {
Network: network.Name,
Operations: []string{"balance.read", "payin.crypto", "payout.crypto", "fee.send"},
Currencies: currencies,
InvokeURI: discovery.DefaultInvokeURI(string(mservice.ChainGateway)),
Version: version,
}
announcer := discovery.NewAnnouncer(s.logger, s.producer, string(mservice.ChainGateway), announce)

View File

@@ -7,6 +7,7 @@ import (
"github.com/tech/sendico/pkg/merrors"
mntxv1 "github.com/tech/sendico/pkg/proto/gateway/mntx/v1"
unifiedv1 "github.com/tech/sendico/pkg/proto/gateway/unified/v1"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
@@ -23,7 +24,7 @@ type Client interface {
type gatewayClient struct {
conn *grpc.ClientConn
client mntxv1.MntxGatewayServiceClient
client unifiedv1.UnifiedGatewayServiceClient
cfg Config
logger *zap.Logger
}
@@ -48,7 +49,7 @@ func New(ctx context.Context, cfg Config, opts ...grpc.DialOption) (Client, erro
return &gatewayClient{
conn: conn,
client: mntxv1.NewMntxGatewayServiceClient(conn),
client: unifiedv1.NewUnifiedGatewayServiceClient(conn),
cfg: cfg,
logger: cfg.Logger,
}, nil

View File

@@ -7,7 +7,6 @@ replace github.com/tech/sendico/pkg => ../../pkg
require (
github.com/go-chi/chi/v5 v5.2.3
github.com/prometheus/client_golang v1.23.2
github.com/shopspring/decimal v1.4.0
github.com/tech/sendico/pkg v0.1.0
go.uber.org/zap v1.27.1
google.golang.org/grpc v1.78.0
@@ -22,8 +21,8 @@ require (
github.com/casbin/govaluate v1.10.0 // indirect
github.com/casbin/mongodb-adapter/v3 v3.7.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/golang/snappy v1.0.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/klauspost/compress v1.18.2 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect

View File

@@ -40,8 +40,6 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/go-chi/chi/v5 v5.2.3 h1:WQIt9uxdsAbgIYgid+BpYc+liqQZGMHRaUwp0JUcvdE=
github.com/go-chi/chi/v5 v5.2.3/go.mod h1:L2yAIGWB3H+phAw1NxKwWM+7eUH/lU8pOMm5hHcoops=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
@@ -59,6 +57,8 @@ github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -125,8 +125,6 @@ github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/i
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=

View File

@@ -16,6 +16,7 @@ import (
"github.com/tech/sendico/pkg/mservice"
gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1"
mntxv1 "github.com/tech/sendico/pkg/proto/gateway/mntx/v1"
unifiedv1 "github.com/tech/sendico/pkg/proto/gateway/unified/v1"
"go.uber.org/zap"
"google.golang.org/grpc"
)
@@ -31,7 +32,7 @@ type Service struct {
gatewayDescriptor *gatewayv1.GatewayInstanceDescriptor
announcer *discovery.Announcer
mntxv1.UnimplementedMntxGatewayServiceServer
unifiedv1.UnimplementedUnifiedGatewayServiceServer
}
type payoutFailure interface {
@@ -96,7 +97,7 @@ func NewService(logger mlogger.Logger, opts ...Option) *Service {
// Register wires the service onto the provided gRPC router.
func (s *Service) Register(router routers.GRPC) error {
return router.Register(func(reg grpc.ServiceRegistrar) {
mntxv1.RegisterMntxGatewayServiceServer(reg, s)
unifiedv1.RegisterUnifiedGatewayServiceServer(reg, s)
})
}
@@ -145,6 +146,7 @@ func (s *Service) startDiscoveryAnnouncer() {
Service: "CARD_PAYOUT_RAIL_GATEWAY",
Rail: "CARD_PAYOUT",
Operations: []string{"payout.card"},
InvokeURI: discovery.DefaultInvokeURI(string(mservice.MntxGateway)),
Version: appversion.Create().Short(),
}
if s.gatewayDescriptor != nil {

View File

@@ -34,7 +34,7 @@ messaging:
reconnect_wait: 5
gateway:
rail: "card"
rail: "provider_settlement"
target_chat_id_env: TGSETTLE_GATEWAY_CHAT_ID
timeout_seconds: 120
accepted_user_ids: []

View File

@@ -8,6 +8,8 @@ require (
github.com/tech/sendico/pkg v0.1.0
go.mongodb.org/mongo-driver v1.17.6
go.uber.org/zap v1.27.1
google.golang.org/grpc v1.78.0
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v3 v3.0.1
)
@@ -46,6 +48,4 @@ require (
golang.org/x/sys v0.39.0 // indirect
golang.org/x/text v0.32.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect
google.golang.org/grpc v1.78.0 // indirect
google.golang.org/protobuf v1.36.11 // indirect
)

View File

@@ -20,8 +20,16 @@ import (
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/model"
"github.com/tech/sendico/pkg/mservice"
paymenttypes "github.com/tech/sendico/pkg/payments/types"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
unifiedv1 "github.com/tech/sendico/pkg/proto/gateway/unified/v1"
"github.com/tech/sendico/pkg/server/grpcapp"
"go.uber.org/zap"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/types/known/timestamppb"
)
const (
@@ -29,6 +37,13 @@ const (
executedStatus = "executed"
)
const (
metadataPaymentIntentID = "payment_intent_id"
metadataQuoteRef = "quote_ref"
metadataTargetChatID = "target_chat_id"
metadataOutgoingLeg = "outgoing_leg"
)
type Config struct {
Rail string
TargetChatIDEnv string
@@ -49,6 +64,8 @@ type Service struct {
mu sync.Mutex
pending map[string]*model.PaymentGatewayIntent
consumers []msg.Consumer
unifiedv1.UnimplementedUnifiedGatewayServiceServer
}
func NewService(logger mlogger.Logger, repo storage.Repository, producer msg.Producer, broker mb.Broker, cfg Config) *Service {
@@ -56,13 +73,13 @@ func NewService(logger mlogger.Logger, repo storage.Repository, producer msg.Pro
logger = logger.Named("tgsettle_gateway")
}
svc := &Service{
logger: logger,
repo: repo,
logger: logger,
repo: repo,
producer: producer,
broker: broker,
cfg: cfg,
rail: strings.TrimSpace(cfg.Rail),
pending: map[string]*model.PaymentGatewayIntent{},
broker: broker,
cfg: cfg,
rail: strings.TrimSpace(cfg.Rail),
pending: map[string]*model.PaymentGatewayIntent{},
}
svc.chatID = strings.TrimSpace(readEnv(cfg.TargetChatIDEnv))
svc.startConsumers()
@@ -70,8 +87,10 @@ func NewService(logger mlogger.Logger, repo storage.Repository, producer msg.Pro
return svc
}
func (s *Service) Register(_ routers.GRPC) error {
return nil
func (s *Service) Register(router routers.GRPC) error {
return router.Register(func(reg grpc.ServiceRegistrar) {
unifiedv1.RegisterUnifiedGatewayServiceServer(reg, s)
})
}
func (s *Service) Shutdown() {
@@ -95,8 +114,6 @@ func (s *Service) startConsumers() {
}
return
}
intentProcessor := paymentgateway.NewPaymentGatewayIntentProcessor(s.logger, s.onIntent)
s.consumeProcessor(intentProcessor)
resultProcessor := confirmations.NewConfirmationResultProcessor(s.logger, string(mservice.PaymentGateway), s.rail, s.onConfirmationResult)
s.consumeProcessor(resultProcessor)
}
@@ -115,6 +132,62 @@ func (s *Service) consumeProcessor(processor np.EnvelopeProcessor) {
}()
}
func (s *Service) SubmitTransfer(ctx context.Context, req *chainv1.SubmitTransferRequest) (*chainv1.SubmitTransferResponse, error) {
if req == nil {
return nil, merrors.InvalidArgument("submit_transfer: request is required")
}
idempotencyKey := strings.TrimSpace(req.GetIdempotencyKey())
if idempotencyKey == "" {
return nil, merrors.InvalidArgument("submit_transfer: idempotency_key is required")
}
amount := req.GetAmount()
if amount == nil || strings.TrimSpace(amount.GetAmount()) == "" || strings.TrimSpace(amount.GetCurrency()) == "" {
return nil, merrors.InvalidArgument("submit_transfer: amount is required")
}
intent, err := intentFromSubmitTransfer(req, s.rail, s.chatID)
if err != nil {
return nil, err
}
if s.repo == nil || s.repo.Payments() == nil {
return nil, merrors.Internal("payment gateway storage unavailable")
}
existing, err := s.repo.Payments().FindByIdempotencyKey(ctx, idempotencyKey)
if err != nil {
return nil, err
}
if existing != nil {
return &chainv1.SubmitTransferResponse{Transfer: transferFromExecution(existing, req)}, nil
}
if err := s.onIntent(ctx, intent); err != nil {
return nil, err
}
return &chainv1.SubmitTransferResponse{Transfer: transferFromRequest(req)}, nil
}
func (s *Service) GetTransfer(ctx context.Context, req *chainv1.GetTransferRequest) (*chainv1.GetTransferResponse, error) {
if req == nil {
return nil, merrors.InvalidArgument("get_transfer: request is required")
}
transferRef := strings.TrimSpace(req.GetTransferRef())
if transferRef == "" {
return nil, merrors.InvalidArgument("get_transfer: transfer_ref is required")
}
if s.repo == nil || s.repo.Payments() == nil {
return nil, merrors.Internal("payment gateway storage unavailable")
}
existing, err := s.repo.Payments().FindByIdempotencyKey(ctx, transferRef)
if err != nil {
return nil, err
}
if existing != nil {
return &chainv1.GetTransferResponse{Transfer: transferFromExecution(existing, nil)}, nil
}
if s.hasPending(transferRef) {
return &chainv1.GetTransferResponse{Transfer: transferPending(transferRef)}, nil
}
return nil, status.Error(codes.NotFound, "transfer not found")
}
func (s *Service) onIntent(ctx context.Context, intent *model.PaymentGatewayIntent) error {
if intent == nil {
return merrors.InvalidArgument("payment gateway intent is nil", "intent")
@@ -178,11 +251,11 @@ func (s *Service) onConfirmationResult(ctx context.Context, result *model.Confir
if result.Status == model.ConfirmationStatusConfirmed || result.Status == model.ConfirmationStatusClarified {
exec := &storagemodel.PaymentExecution{
IdempotencyKey: intent.IdempotencyKey,
IdempotencyKey: intent.IdempotencyKey,
PaymentIntentID: intent.PaymentIntentID,
ExecutedMoney: result.Money,
QuoteRef: intent.QuoteRef,
Status: executedStatus,
ExecutedMoney: result.Money,
QuoteRef: intent.QuoteRef,
Status: executedStatus,
}
if err := s.repo.Payments().InsertExecution(ctx, exec); err != nil && err != storage.ErrDuplicate {
return err
@@ -290,11 +363,22 @@ func (s *Service) removeIntent(requestID string) {
s.mu.Unlock()
}
func (s *Service) hasPending(requestID string) bool {
requestID = strings.TrimSpace(requestID)
if requestID == "" {
return false
}
s.mu.Lock()
defer s.mu.Unlock()
_, ok := s.pending[requestID]
return ok
}
func (s *Service) startAnnouncer() {
if s == nil || s.producer == nil {
return
}
caps := []string{"telegram_confirmation", "money_persistence"}
caps := []string{"telegram_confirmation", "money_persistence", "observe.confirm", "payout.fiat"}
if s.rail != "" {
caps = append(caps, "confirmations."+strings.ToLower(string(mservice.PaymentGateway))+"."+strings.ToLower(s.rail))
}
@@ -302,6 +386,7 @@ func (s *Service) startAnnouncer() {
Service: string(mservice.PaymentGateway),
Rail: s.rail,
Operations: caps,
InvokeURI: discovery.DefaultInvokeURI(string(mservice.PaymentGateway)),
}
s.announcer = discovery.NewAnnouncer(s.logger, s.producer, string(mservice.PaymentGateway), announce)
s.announcer.Start()
@@ -324,6 +409,128 @@ func normalizeIntent(intent *model.PaymentGatewayIntent) *model.PaymentGatewayIn
return &cp
}
func intentFromSubmitTransfer(req *chainv1.SubmitTransferRequest, defaultRail, defaultChatID string) (*model.PaymentGatewayIntent, error) {
if req == nil {
return nil, merrors.InvalidArgument("submit_transfer: request is required")
}
idempotencyKey := strings.TrimSpace(req.GetIdempotencyKey())
if idempotencyKey == "" {
return nil, merrors.InvalidArgument("submit_transfer: idempotency_key is required")
}
amount := req.GetAmount()
if amount == nil {
return nil, merrors.InvalidArgument("submit_transfer: amount is required")
}
requestedMoney := &paymenttypes.Money{
Amount: strings.TrimSpace(amount.GetAmount()),
Currency: strings.TrimSpace(amount.GetCurrency()),
}
if requestedMoney.Amount == "" || requestedMoney.Currency == "" {
return nil, merrors.InvalidArgument("submit_transfer: amount is required")
}
metadata := req.GetMetadata()
paymentIntentID := strings.TrimSpace(req.GetClientReference())
if paymentIntentID == "" {
paymentIntentID = strings.TrimSpace(metadata[metadataPaymentIntentID])
}
if paymentIntentID == "" {
return nil, merrors.InvalidArgument("submit_transfer: payment_intent_id is required")
}
quoteRef := strings.TrimSpace(metadata[metadataQuoteRef])
targetChatID := strings.TrimSpace(metadata[metadataTargetChatID])
outgoingLeg := strings.TrimSpace(metadata[metadataOutgoingLeg])
if outgoingLeg == "" {
outgoingLeg = strings.TrimSpace(defaultRail)
}
if targetChatID == "" {
targetChatID = strings.TrimSpace(defaultChatID)
}
return &model.PaymentGatewayIntent{
PaymentIntentID: paymentIntentID,
IdempotencyKey: idempotencyKey,
OutgoingLeg: outgoingLeg,
QuoteRef: quoteRef,
RequestedMoney: requestedMoney,
TargetChatID: targetChatID,
}, nil
}
func transferFromRequest(req *chainv1.SubmitTransferRequest) *chainv1.Transfer {
if req == nil {
return nil
}
amount := req.GetAmount()
return &chainv1.Transfer{
TransferRef: strings.TrimSpace(req.GetIdempotencyKey()),
IdempotencyKey: strings.TrimSpace(req.GetIdempotencyKey()),
OrganizationRef: strings.TrimSpace(req.GetOrganizationRef()),
SourceWalletRef: strings.TrimSpace(req.GetSourceWalletRef()),
Destination: req.GetDestination(),
RequestedAmount: amount,
Status: chainv1.TransferStatus_TRANSFER_SUBMITTED,
}
}
func transferFromExecution(exec *storagemodel.PaymentExecution, req *chainv1.SubmitTransferRequest) *chainv1.Transfer {
if exec == nil {
return nil
}
var requested *moneyv1.Money
if req != nil && req.GetAmount() != nil {
requested = req.GetAmount()
}
net := moneyFromPayment(exec.ExecutedMoney)
status := chainv1.TransferStatus_TRANSFER_CONFIRMED
if strings.TrimSpace(exec.Status) != "" && !strings.EqualFold(exec.Status, executedStatus) {
status = chainv1.TransferStatus_TRANSFER_PENDING
}
transfer := &chainv1.Transfer{
TransferRef: strings.TrimSpace(exec.IdempotencyKey),
IdempotencyKey: strings.TrimSpace(exec.IdempotencyKey),
RequestedAmount: requested,
NetAmount: net,
Status: status,
}
if req != nil {
transfer.OrganizationRef = strings.TrimSpace(req.GetOrganizationRef())
transfer.SourceWalletRef = strings.TrimSpace(req.GetSourceWalletRef())
transfer.Destination = req.GetDestination()
}
if !exec.ExecutedAt.IsZero() {
ts := timestamppb.New(exec.ExecutedAt)
transfer.CreatedAt = ts
transfer.UpdatedAt = ts
}
return transfer
}
func transferPending(requestID string) *chainv1.Transfer {
ref := strings.TrimSpace(requestID)
if ref == "" {
return nil
}
return &chainv1.Transfer{
TransferRef: ref,
IdempotencyKey: ref,
Status: chainv1.TransferStatus_TRANSFER_SUBMITTED,
}
}
func moneyFromPayment(m *paymenttypes.Money) *moneyv1.Money {
if m == nil {
return nil
}
currency := strings.TrimSpace(m.Currency)
amount := strings.TrimSpace(m.Amount)
if currency == "" || amount == "" {
return nil
}
return &moneyv1.Money{
Currency: currency,
Amount: amount,
}
}
func readEnv(env string) string {
if strings.TrimSpace(env) == "" {
return ""