outbox for gateways
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
gatewayoutbox "github.com/tech/sendico/gateway/common/outbox"
|
||||
"github.com/tech/sendico/gateway/mntx/internal/service/monetix"
|
||||
"github.com/tech/sendico/gateway/mntx/storage"
|
||||
"github.com/tech/sendico/gateway/mntx/storage/model"
|
||||
@@ -17,6 +18,7 @@ import (
|
||||
"github.com/tech/sendico/pkg/merrors"
|
||||
msg "github.com/tech/sendico/pkg/messaging"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
pmodel "github.com/tech/sendico/pkg/model"
|
||||
gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1"
|
||||
mntxv1 "github.com/tech/sendico/pkg/proto/gateway/mntx/v1"
|
||||
"go.mongodb.org/mongo-driver/v2/bson"
|
||||
@@ -30,6 +32,8 @@ type cardPayoutProcessor struct {
|
||||
store storage.Repository
|
||||
httpClient *http.Client
|
||||
producer msg.Producer
|
||||
msgCfg pmodel.SettingsT
|
||||
outbox *gatewayoutbox.ReliableRuntime
|
||||
|
||||
perTxMinAmountMinor int64
|
||||
perTxMinAmountMinorByCurrency map[string]int64
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/tech/sendico/gateway/mntx/storage"
|
||||
"github.com/tech/sendico/pkg/clock"
|
||||
msg "github.com/tech/sendico/pkg/messaging"
|
||||
pmodel "github.com/tech/sendico/pkg/model"
|
||||
gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1"
|
||||
)
|
||||
|
||||
@@ -67,3 +68,12 @@ func WithDiscoveryInvokeURI(invokeURI string) Option {
|
||||
s.invokeURI = strings.TrimSpace(invokeURI)
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessagingSettings applies messaging driver settings.
|
||||
func WithMessagingSettings(settings pmodel.SettingsT) Option {
|
||||
return func(s *Service) {
|
||||
if settings != nil {
|
||||
s.msgCfg = settings
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
50
api/gateway/mntx/internal/service/gateway/outbox_reliable.go
Normal file
50
api/gateway/mntx/internal/service/gateway/outbox_reliable.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package gateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
gatewayoutbox "github.com/tech/sendico/gateway/common/outbox"
|
||||
"github.com/tech/sendico/pkg/db/transaction"
|
||||
me "github.com/tech/sendico/pkg/messaging/envelope"
|
||||
)
|
||||
|
||||
type mntxOutboxProvider interface {
|
||||
Outbox() gatewayoutbox.Store
|
||||
}
|
||||
|
||||
type mntxTransactionProvider interface {
|
||||
TransactionFactory() transaction.Factory
|
||||
}
|
||||
|
||||
func (p *cardPayoutProcessor) outboxStore() gatewayoutbox.Store {
|
||||
provider, ok := p.store.(mntxOutboxProvider)
|
||||
if !ok || provider == nil {
|
||||
return nil
|
||||
}
|
||||
return provider.Outbox()
|
||||
}
|
||||
|
||||
func (p *cardPayoutProcessor) startOutboxReliableProducer() error {
|
||||
if p == nil || p.outbox == nil {
|
||||
return nil
|
||||
}
|
||||
return p.outbox.Start(p.logger, p.producer, p.outboxStore(), p.msgCfg)
|
||||
}
|
||||
|
||||
func (p *cardPayoutProcessor) sendWithOutbox(ctx context.Context, env me.Envelope) error {
|
||||
if err := p.startOutboxReliableProducer(); err != nil {
|
||||
return err
|
||||
}
|
||||
if p.outbox == nil {
|
||||
return nil
|
||||
}
|
||||
return p.outbox.Send(ctx, env)
|
||||
}
|
||||
|
||||
func (p *cardPayoutProcessor) executeTransaction(ctx context.Context, cb transaction.Callback) (any, error) {
|
||||
provider, ok := p.store.(mntxTransactionProvider)
|
||||
if !ok || provider == nil || provider.TransactionFactory() == nil {
|
||||
return cb(ctx)
|
||||
}
|
||||
return provider.TransactionFactory().CreateTransaction().Execute(ctx, cb)
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
gatewayoutbox "github.com/tech/sendico/gateway/common/outbox"
|
||||
"github.com/tech/sendico/gateway/mntx/internal/appversion"
|
||||
"github.com/tech/sendico/gateway/mntx/internal/service/monetix"
|
||||
"github.com/tech/sendico/gateway/mntx/storage"
|
||||
@@ -14,6 +15,7 @@ import (
|
||||
"github.com/tech/sendico/pkg/discovery"
|
||||
msg "github.com/tech/sendico/pkg/messaging"
|
||||
"github.com/tech/sendico/pkg/mlogger"
|
||||
pmodel "github.com/tech/sendico/pkg/model"
|
||||
"github.com/tech/sendico/pkg/mservice"
|
||||
gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1"
|
||||
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
|
||||
@@ -25,10 +27,12 @@ type Service struct {
|
||||
logger mlogger.Logger
|
||||
clock clockpkg.Clock
|
||||
producer msg.Producer
|
||||
msgCfg pmodel.SettingsT
|
||||
storage storage.Repository
|
||||
config monetix.Config
|
||||
httpClient *http.Client
|
||||
card *cardPayoutProcessor
|
||||
outbox gatewayoutbox.ReliableRuntime
|
||||
gatewayDescriptor *gatewayv1.GatewayInstanceDescriptor
|
||||
announcer *discovery.Announcer
|
||||
invokeURI string
|
||||
@@ -64,6 +68,7 @@ func NewService(logger mlogger.Logger, opts ...Option) *Service {
|
||||
logger: logger.Named("service"),
|
||||
clock: clockpkg.NewSystem(),
|
||||
config: monetix.DefaultConfig(),
|
||||
msgCfg: map[string]any{},
|
||||
}
|
||||
|
||||
initMetrics()
|
||||
@@ -85,6 +90,11 @@ func NewService(logger mlogger.Logger, opts ...Option) *Service {
|
||||
}
|
||||
|
||||
svc.card = newCardPayoutProcessor(svc.logger, svc.config, svc.clock, svc.storage, svc.httpClient, svc.producer)
|
||||
svc.card.outbox = &svc.outbox
|
||||
svc.card.msgCfg = svc.msgCfg
|
||||
if err := svc.card.startOutboxReliableProducer(); err != nil {
|
||||
svc.logger.Warn("Failed to initialise outbox reliable producer", zap.Error(err))
|
||||
}
|
||||
svc.card.applyGatewayDescriptor(svc.gatewayDescriptor)
|
||||
svc.startDiscoveryAnnouncer()
|
||||
|
||||
@@ -102,6 +112,7 @@ func (s *Service) Shutdown() {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
s.outbox.Stop()
|
||||
if s.announcer != nil {
|
||||
s.announcer.Stop()
|
||||
}
|
||||
|
||||
@@ -38,27 +38,49 @@ func toOpStatus(t *model.CardPayout) (rail.OperationResult, error) {
|
||||
}
|
||||
|
||||
func (p *cardPayoutProcessor) updatePayoutStatus(ctx context.Context, state *model.CardPayout) error {
|
||||
if err := p.store.Payouts().Upsert(ctx, state); err != nil {
|
||||
if !isFinalStatus(state) {
|
||||
if err := p.store.Payouts().Upsert(ctx, state); err != nil {
|
||||
p.logger.Warn("Failed to update transfer status", zap.Error(err), mzap.ObjRef("payout_ref", state.ID),
|
||||
zap.String("payment_ref", state.PaymentRef), zap.String("status", string(state.Status)),
|
||||
)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
_, err := p.executeTransaction(ctx, func(txCtx context.Context) (any, error) {
|
||||
if upsertErr := p.store.Payouts().Upsert(txCtx, state); upsertErr != nil {
|
||||
return nil, upsertErr
|
||||
}
|
||||
if isFinalStatus(state) {
|
||||
if emitErr := p.emitTransferStatusEvent(txCtx, state); emitErr != nil {
|
||||
return nil, emitErr
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
})
|
||||
if err != nil {
|
||||
p.logger.Warn("Failed to update transfer status", zap.Error(err), mzap.ObjRef("payout_ref", state.ID),
|
||||
zap.String("payment_ref", state.PaymentRef), zap.String("status", string(state.Status)),
|
||||
)
|
||||
}
|
||||
if isFinalStatus(state) {
|
||||
p.emitTransferStatusEvent(state)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *cardPayoutProcessor) emitTransferStatusEvent(payout *model.CardPayout) {
|
||||
if p == nil || p.producer == nil || payout == nil {
|
||||
return
|
||||
func (p *cardPayoutProcessor) emitTransferStatusEvent(ctx context.Context, payout *model.CardPayout) error {
|
||||
if p == nil || payout == nil {
|
||||
return nil
|
||||
}
|
||||
if p.producer == nil || p.outboxStore() == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
status, err := toOpStatus(payout)
|
||||
if err != nil {
|
||||
p.logger.Warn("Failed to convert payout status to operation status for transfer status event", zap.Error(err),
|
||||
mzap.ObjRef("payout_ref", payout.ID), zap.String("payment_ref", payout.PaymentRef), zap.String("status", string(payout.Status)))
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
exec := pmodel.PaymentGatewayExecution{
|
||||
@@ -75,7 +97,9 @@ func (p *cardPayoutProcessor) emitTransferStatusEvent(payout *model.CardPayout)
|
||||
TransferRef: payout.GetID().Hex(),
|
||||
}
|
||||
env := paymentgateway.PaymentGatewayExecution(mservice.MntxGateway, &exec)
|
||||
if err := p.producer.SendMessage(env); err != nil {
|
||||
if err := p.sendWithOutbox(ctx, env); err != nil {
|
||||
p.logger.Warn("Failed to publish transfer status event", zap.Error(err), mzap.ObjRef("transfer_ref", payout.ID))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user