unified gateway interfaces
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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: []
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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 ""
|
||||
|
||||
Reference in New Issue
Block a user