TG settlement service

This commit is contained in:
Stephan D
2026-01-02 14:54:18 +01:00
parent ea1c69f14a
commit 743f683d92
82 changed files with 4693 additions and 503 deletions

View File

@@ -93,7 +93,13 @@ func (s *RegistryService) Start() {
return
}
s.startOnce.Do(func() {
s.logInfo("Discovery registry service starting", zap.Int("consumers", len(s.consumers)), zap.Bool("kv_enabled", s.kv != nil))
fields := []zap.Field{zap.Int("consumers", len(s.consumers)), zap.Bool("kv_enabled", s.kv != nil)}
if s.kv != nil {
if bucket := s.kv.Bucket(); bucket != "" {
fields = append(fields, zap.String("kv_bucket", bucket))
}
}
s.logInfo("Discovery registry service starting", fields...)
for _, ch := range s.consumers {
ch := ch
go func() {
@@ -130,6 +136,12 @@ func (s *RegistryService) handleAnnounce(_ context.Context, env me.Envelope) err
s.logWarn("Failed to decode discovery announce payload", fields...)
return err
}
s.logDebug("Discovery announce received", append(envelopeFields(env), announcementFields(payload)...)...)
if strings.TrimSpace(payload.InstanceID) == "" && strings.TrimSpace(payload.ID) == "" {
fields := append(envelopeFields(env), announcementFields(payload)...)
s.logWarn("Discovery announce missing id and instance id", fields...)
return nil
}
if strings.TrimSpace(payload.InstanceID) == "" {
fields := append(envelopeFields(env), announcementFields(payload)...)
s.logWarn("Discovery announce missing instance id", fields...)
@@ -151,6 +163,7 @@ func (s *RegistryService) handleHeartbeat(_ context.Context, env me.Envelope) er
s.logWarn("Failed to decode discovery heartbeat payload", fields...)
return err
}
s.logDebug("Discovery heartbeat received", append(envelopeFields(env), zap.String("id", payload.ID), zap.String("instance_id", payload.InstanceID), zap.String("status", payload.Status))...)
if strings.TrimSpace(payload.InstanceID) == "" && strings.TrimSpace(payload.ID) == "" {
return nil
}
@@ -163,6 +176,10 @@ func (s *RegistryService) handleHeartbeat(_ context.Context, env me.Envelope) er
ts = time.Now()
}
results := s.registry.UpdateHeartbeat(payload.ID, payload.InstanceID, strings.TrimSpace(payload.Status), ts, time.Now())
if len(results) == 0 {
s.logDebug("Discovery heartbeat ignored: entry not found", zap.String("id", payload.ID), zap.String("instance_id", payload.InstanceID))
return nil
}
for _, result := range results {
if result.BecameHealthy {
s.logInfo("Discovery registry entry became healthy", append(entryFields(result.Entry), zap.String("status", result.Entry.Status))...)
@@ -186,6 +203,7 @@ func (s *RegistryService) handleLookup(_ context.Context, env me.Envelope) error
}
resp := s.registry.Lookup(time.Now())
resp.RequestID = strings.TrimSpace(payload.RequestID)
s.logDebug("Discovery lookup prepared", zap.String("request_id", resp.RequestID), zap.Int("services", len(resp.Services)), zap.Int("gateways", len(resp.Gateways)))
if err := s.producer.SendMessage(NewLookupResponseEnvelope(s.sender, resp)); err != nil {
fields := []zap.Field{zap.String("request_id", resp.RequestID), zap.Error(err)}
s.logWarn("Failed to publish discovery lookup response", fields...)
@@ -221,10 +239,12 @@ func (s *RegistryService) initKV(msgBroker mb.Broker) {
}
provider, ok := msgBroker.(jetStreamProvider)
if !ok {
s.logInfo("Discovery KV disabled: broker does not support JetStream")
return
}
js := provider.JetStream()
if js == nil {
s.logWarn("Discovery KV disabled: JetStream not configured")
return
}
store, err := NewKVStore(s.logger, js, "")
@@ -255,10 +275,25 @@ func (s *RegistryService) consumeKVUpdates(watcher nats.KeyWatcher) {
if s == nil || watcher == nil {
return
}
initial := true
initialCount := 0
for entry := range watcher.Updates() {
if entry == nil {
if initial {
fields := []zap.Field{zap.Int("entries", initialCount)}
if s.kv != nil {
if bucket := s.kv.Bucket(); bucket != "" {
fields = append(fields, zap.String("bucket", bucket))
}
}
s.logInfo("Discovery KV initial sync complete", fields...)
initial = false
}
continue
}
if initial && entry.Operation() == nats.KeyValuePut {
initialCount++
}
switch entry.Operation() {
case nats.KeyValueDelete, nats.KeyValuePurge:
key := registryKeyFromKVKey(entry.Key())
@@ -302,6 +337,13 @@ func (s *RegistryService) logWarn(message string, fields ...zap.Field) {
s.logger.Warn(message, fields...)
}
func (s *RegistryService) logDebug(message string, fields ...zap.Field) {
if s.logger == nil {
return
}
s.logger.Debug(message, fields...)
}
func (s *RegistryService) logInfo(message string, fields ...zap.Field) {
if s.logger == nil {
return

View File

@@ -92,10 +92,23 @@ func (w *RegistryWatcher) consume(watcher nats.KeyWatcher) {
if w == nil || watcher == nil {
return
}
initial := true
initialCount := 0
for entry := range watcher.Updates() {
if entry == nil {
if initial && w.logger != nil {
fields := []zap.Field{zap.Int("entries", initialCount)}
if w.kv != nil {
fields = append(fields, zap.String("bucket", w.kv.Bucket()))
}
w.logger.Info("Discovery registry watcher initial sync complete", fields...)
initial = false
}
continue
}
if initial && entry.Operation() == nats.KeyValuePut {
initialCount++
}
switch entry.Operation() {
case nats.KeyValueDelete, nats.KeyValuePurge:
key := registryKeyFromKVKey(entry.Key())

View File

@@ -0,0 +1,77 @@
package notifications
import (
"encoding/json"
"strings"
messaging "github.com/tech/sendico/pkg/messaging/envelope"
"github.com/tech/sendico/pkg/model"
nm "github.com/tech/sendico/pkg/model/notification"
"github.com/tech/sendico/pkg/mservice"
)
type ConfirmationRequestNotification struct {
messaging.Envelope
payload model.ConfirmationRequest
}
func (crn *ConfirmationRequestNotification) Serialize() ([]byte, error) {
data, err := json.Marshal(crn.payload)
if err != nil {
return nil, err
}
return crn.Envelope.Wrap(data)
}
type ConfirmationResultNotification struct {
messaging.Envelope
payload model.ConfirmationResult
}
func (crn *ConfirmationResultNotification) Serialize() ([]byte, error) {
data, err := json.Marshal(crn.payload)
if err != nil {
return nil, err
}
return crn.Envelope.Wrap(data)
}
func confirmationRequestEvent() model.NotificationEvent {
return model.NewNotification(mservice.Notifications, nm.NAConfirmationRequest)
}
func confirmationResultEvent(sourceService, rail string) model.NotificationEvent {
action := strings.TrimSpace(sourceService)
if action == "" {
action = "unknown"
}
action = strings.ToLower(action)
rail = strings.TrimSpace(rail)
if rail == "" {
rail = "default"
}
rail = strings.ToLower(rail)
return model.NewNotification(mservice.Confirmations, nm.NotificationAction(action+"."+rail))
}
func NewConfirmationRequestEnvelope(sender string, request *model.ConfirmationRequest) messaging.Envelope {
var payload model.ConfirmationRequest
if request != nil {
payload = *request
}
return &ConfirmationRequestNotification{
Envelope: messaging.CreateEnvelope(sender, confirmationRequestEvent()),
payload: payload,
}
}
func NewConfirmationResultEnvelope(sender string, result *model.ConfirmationResult, sourceService, rail string) messaging.Envelope {
var payload model.ConfirmationResult
if result != nil {
payload = *result
}
return &ConfirmationResultNotification{
Envelope: messaging.CreateEnvelope(sender, confirmationResultEvent(sourceService, rail)),
payload: payload,
}
}

View File

@@ -0,0 +1,81 @@
package notifications
import (
"context"
"encoding/json"
me "github.com/tech/sendico/pkg/messaging/envelope"
ch "github.com/tech/sendico/pkg/messaging/notifications/confirmations/handler"
np "github.com/tech/sendico/pkg/messaging/notifications/processor"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/model"
"go.uber.org/zap"
)
type ConfirmationRequestProcessor struct {
logger mlogger.Logger
handler ch.ConfirmationRequestHandler
event model.NotificationEvent
}
func (crp *ConfirmationRequestProcessor) Process(ctx context.Context, envelope me.Envelope) error {
var msg model.ConfirmationRequest
if err := json.Unmarshal(envelope.GetData(), &msg); err != nil {
crp.logger.Warn("Failed to decode confirmation request envelope", zap.Error(err), zap.String("topic", crp.event.ToString()))
return err
}
if crp.handler == nil {
crp.logger.Warn("Confirmation request handler is not configured", zap.String("topic", crp.event.ToString()))
return nil
}
return crp.handler(ctx, &msg)
}
func (crp *ConfirmationRequestProcessor) GetSubject() model.NotificationEvent {
return crp.event
}
type ConfirmationResultProcessor struct {
logger mlogger.Logger
handler ch.ConfirmationResultHandler
event model.NotificationEvent
}
func (crp *ConfirmationResultProcessor) Process(ctx context.Context, envelope me.Envelope) error {
var msg model.ConfirmationResult
if err := json.Unmarshal(envelope.GetData(), &msg); err != nil {
crp.logger.Warn("Failed to decode confirmation result envelope", zap.Error(err), zap.String("topic", crp.event.ToString()))
return err
}
if crp.handler == nil {
crp.logger.Warn("Confirmation result handler is not configured", zap.String("topic", crp.event.ToString()))
return nil
}
return crp.handler(ctx, &msg)
}
func (crp *ConfirmationResultProcessor) GetSubject() model.NotificationEvent {
return crp.event
}
func NewConfirmationRequestProcessor(logger mlogger.Logger, handler ch.ConfirmationRequestHandler) np.EnvelopeProcessor {
if logger != nil {
logger = logger.Named("confirmation_request_processor")
}
return &ConfirmationRequestProcessor{
logger: logger,
handler: handler,
event: confirmationRequestEvent(),
}
}
func NewConfirmationResultProcessor(logger mlogger.Logger, sourceService, rail string, handler ch.ConfirmationResultHandler) np.EnvelopeProcessor {
if logger != nil {
logger = logger.Named("confirmation_result_processor")
}
return &ConfirmationResultProcessor{
logger: logger,
handler: handler,
event: confirmationResultEvent(sourceService, rail),
}
}

View File

@@ -0,0 +1,66 @@
package notifications
import (
"encoding/json"
messaging "github.com/tech/sendico/pkg/messaging/envelope"
"github.com/tech/sendico/pkg/model"
nm "github.com/tech/sendico/pkg/model/notification"
"github.com/tech/sendico/pkg/mservice"
)
type PaymentGatewayIntentNotification struct {
messaging.Envelope
payload model.PaymentGatewayIntent
}
func (pgn *PaymentGatewayIntentNotification) Serialize() ([]byte, error) {
data, err := json.Marshal(pgn.payload)
if err != nil {
return nil, err
}
return pgn.Envelope.Wrap(data)
}
type PaymentGatewayExecutionNotification struct {
messaging.Envelope
payload model.PaymentGatewayExecution
}
func (pgn *PaymentGatewayExecutionNotification) Serialize() ([]byte, error) {
data, err := json.Marshal(pgn.payload)
if err != nil {
return nil, err
}
return pgn.Envelope.Wrap(data)
}
func intentEvent() model.NotificationEvent {
return model.NewNotification(mservice.PaymentGateway, nm.NAPaymentGatewayIntent)
}
func executionEvent() model.NotificationEvent {
return model.NewNotification(mservice.PaymentGateway, nm.NAPaymentGatewayExecution)
}
func NewPaymentGatewayIntentEnvelope(sender string, intent *model.PaymentGatewayIntent) messaging.Envelope {
var payload model.PaymentGatewayIntent
if intent != nil {
payload = *intent
}
return &PaymentGatewayIntentNotification{
Envelope: messaging.CreateEnvelope(sender, intentEvent()),
payload: payload,
}
}
func NewPaymentGatewayExecutionEnvelope(sender string, exec *model.PaymentGatewayExecution) messaging.Envelope {
var payload model.PaymentGatewayExecution
if exec != nil {
payload = *exec
}
return &PaymentGatewayExecutionNotification{
Envelope: messaging.CreateEnvelope(sender, executionEvent()),
payload: payload,
}
}

View File

@@ -0,0 +1,81 @@
package notifications
import (
"context"
"encoding/json"
me "github.com/tech/sendico/pkg/messaging/envelope"
ch "github.com/tech/sendico/pkg/messaging/notifications/paymentgateway/handler"
np "github.com/tech/sendico/pkg/messaging/notifications/processor"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/model"
"go.uber.org/zap"
)
type PaymentGatewayIntentProcessor struct {
logger mlogger.Logger
handler ch.PaymentGatewayIntentHandler
event model.NotificationEvent
}
func (pgp *PaymentGatewayIntentProcessor) Process(ctx context.Context, envelope me.Envelope) error {
var msg model.PaymentGatewayIntent
if err := json.Unmarshal(envelope.GetData(), &msg); err != nil {
pgp.logger.Warn("Failed to decode payment gateway intent envelope", zap.Error(err), zap.String("topic", pgp.event.ToString()))
return err
}
if pgp.handler == nil {
pgp.logger.Warn("Payment gateway intent handler is not configured", zap.String("topic", pgp.event.ToString()))
return nil
}
return pgp.handler(ctx, &msg)
}
func (pgp *PaymentGatewayIntentProcessor) GetSubject() model.NotificationEvent {
return pgp.event
}
type PaymentGatewayExecutionProcessor struct {
logger mlogger.Logger
handler ch.PaymentGatewayExecutionHandler
event model.NotificationEvent
}
func (pgp *PaymentGatewayExecutionProcessor) Process(ctx context.Context, envelope me.Envelope) error {
var msg model.PaymentGatewayExecution
if err := json.Unmarshal(envelope.GetData(), &msg); err != nil {
pgp.logger.Warn("Failed to decode payment gateway execution envelope", zap.Error(err), zap.String("topic", pgp.event.ToString()))
return err
}
if pgp.handler == nil {
pgp.logger.Warn("Payment gateway execution handler is not configured", zap.String("topic", pgp.event.ToString()))
return nil
}
return pgp.handler(ctx, &msg)
}
func (pgp *PaymentGatewayExecutionProcessor) GetSubject() model.NotificationEvent {
return pgp.event
}
func NewPaymentGatewayIntentProcessor(logger mlogger.Logger, handler ch.PaymentGatewayIntentHandler) np.EnvelopeProcessor {
if logger != nil {
logger = logger.Named("payment_gateway_intent_processor")
}
return &PaymentGatewayIntentProcessor{
logger: logger,
handler: handler,
event: intentEvent(),
}
}
func NewPaymentGatewayExecutionProcessor(logger mlogger.Logger, handler ch.PaymentGatewayExecutionHandler) np.EnvelopeProcessor {
if logger != nil {
logger = logger.Named("payment_gateway_execution_processor")
}
return &PaymentGatewayExecutionProcessor{
logger: logger,
handler: handler,
event: executionEvent(),
}
}

View File

@@ -0,0 +1,26 @@
package notifications
import (
messaging "github.com/tech/sendico/pkg/messaging/envelope"
cinternal "github.com/tech/sendico/pkg/messaging/internal/notifications/confirmations"
ch "github.com/tech/sendico/pkg/messaging/notifications/confirmations/handler"
np "github.com/tech/sendico/pkg/messaging/notifications/processor"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/model"
)
func ConfirmationRequest(sender string, request *model.ConfirmationRequest) messaging.Envelope {
return cinternal.NewConfirmationRequestEnvelope(sender, request)
}
func ConfirmationResult(sender string, result *model.ConfirmationResult, sourceService, rail string) messaging.Envelope {
return cinternal.NewConfirmationResultEnvelope(sender, result, sourceService, rail)
}
func NewConfirmationRequestProcessor(logger mlogger.Logger, handler ch.ConfirmationRequestHandler) np.EnvelopeProcessor {
return cinternal.NewConfirmationRequestProcessor(logger, handler)
}
func NewConfirmationResultProcessor(logger mlogger.Logger, sourceService, rail string, handler ch.ConfirmationResultHandler) np.EnvelopeProcessor {
return cinternal.NewConfirmationResultProcessor(logger, sourceService, rail, handler)
}

View File

@@ -0,0 +1,11 @@
package notifications
import (
"context"
"github.com/tech/sendico/pkg/model"
)
type ConfirmationRequestHandler = func(context.Context, *model.ConfirmationRequest) error
type ConfirmationResultHandler = func(context.Context, *model.ConfirmationResult) error

View File

@@ -0,0 +1,11 @@
package notifications
import (
"context"
"github.com/tech/sendico/pkg/model"
)
type PaymentGatewayIntentHandler = func(context.Context, *model.PaymentGatewayIntent) error
type PaymentGatewayExecutionHandler = func(context.Context, *model.PaymentGatewayExecution) error

View File

@@ -0,0 +1,26 @@
package notifications
import (
messaging "github.com/tech/sendico/pkg/messaging/envelope"
pinternal "github.com/tech/sendico/pkg/messaging/internal/notifications/paymentgateway"
ch "github.com/tech/sendico/pkg/messaging/notifications/paymentgateway/handler"
np "github.com/tech/sendico/pkg/messaging/notifications/processor"
"github.com/tech/sendico/pkg/mlogger"
"github.com/tech/sendico/pkg/model"
)
func PaymentGatewayIntent(sender string, intent *model.PaymentGatewayIntent) messaging.Envelope {
return pinternal.NewPaymentGatewayIntentEnvelope(sender, intent)
}
func PaymentGatewayExecution(sender string, exec *model.PaymentGatewayExecution) messaging.Envelope {
return pinternal.NewPaymentGatewayExecutionEnvelope(sender, exec)
}
func NewPaymentGatewayIntentProcessor(logger mlogger.Logger, handler ch.PaymentGatewayIntentHandler) np.EnvelopeProcessor {
return pinternal.NewPaymentGatewayIntentProcessor(logger, handler)
}
func NewPaymentGatewayExecutionProcessor(logger mlogger.Logger, handler ch.PaymentGatewayExecutionHandler) np.EnvelopeProcessor {
return pinternal.NewPaymentGatewayExecutionProcessor(logger, handler)
}

View File

@@ -1,38 +1,42 @@
package model
import (
"time"
import paymenttypes "github.com/tech/sendico/pkg/payments/types"
"github.com/tech/sendico/pkg/db/storable"
"github.com/tech/sendico/pkg/mservice"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type ConfirmationTarget string
type ConfirmationStatus string
const (
ConfirmationTargetLogin ConfirmationTarget = "login"
ConfirmationTargetPayout ConfirmationTarget = "payout"
ConfirmationStatusConfirmed ConfirmationStatus = "CONFIRMED"
ConfirmationStatusClarified ConfirmationStatus = "CLARIFIED"
ConfirmationStatusTimeout ConfirmationStatus = "TIMEOUT"
ConfirmationStatusRejected ConfirmationStatus = "REJECTED"
)
// ConfirmationCode stores verification codes for operations like login or payouts.
type ConfirmationCode struct {
storable.Base `bson:",inline" json:",inline"`
AccountRef primitive.ObjectID `bson:"accountRef" json:"accountRef"`
Destination string `bson:"destination" json:"destination"`
Target ConfirmationTarget `bson:"target" json:"target"`
CodeHash []byte `bson:"codeHash" json:"-"`
Salt []byte `bson:"salt" json:"-"`
ExpiresAt time.Time `bson:"expiresAt" json:"expiresAt"`
Attempts int `bson:"attempts" json:"attempts"`
MaxAttempts int `bson:"maxAttempts" json:"maxAttempts"`
ResendCount int `bson:"resendCount" json:"resendCount"`
ResendLimit int `bson:"resendLimit" json:"resendLimit"`
CooldownUntil time.Time `bson:"cooldownUntil" json:"cooldownUntil"`
Used bool `bson:"used" json:"used"`
type ConfirmationRequest struct {
RequestID string `bson:"requestId,omitempty" json:"request_id,omitempty"`
TargetChatID string `bson:"targetChatId,omitempty" json:"target_chat_id,omitempty"`
RequestedMoney *paymenttypes.Money `bson:"requestedMoney,omitempty" json:"requested_money,omitempty"`
PaymentIntentID string `bson:"paymentIntentId,omitempty" json:"payment_intent_id,omitempty"`
QuoteRef string `bson:"quoteRef,omitempty" json:"quote_ref,omitempty"`
AcceptedUserIDs []string `bson:"acceptedUserIds,omitempty" json:"accepted_user_ids,omitempty"`
TimeoutSeconds int32 `bson:"timeoutSeconds,omitempty" json:"timeout_seconds,omitempty"`
SourceService string `bson:"sourceService,omitempty" json:"source_service,omitempty"`
Rail string `bson:"rail,omitempty" json:"rail,omitempty"`
}
func (c *ConfirmationCode) Collection() string {
return mservice.Confirmations
type ConfirmationResult struct {
RequestID string `bson:"requestId,omitempty" json:"request_id,omitempty"`
Money *paymenttypes.Money `bson:"money,omitempty" json:"money,omitempty"`
RawReply *TelegramMessage `bson:"rawReply,omitempty" json:"raw_reply,omitempty"`
Status ConfirmationStatus `bson:"status,omitempty" json:"status,omitempty"`
ParseError string `bson:"parseError,omitempty" json:"parse_error,omitempty"`
}
type TelegramMessage struct {
ChatID string `bson:"chatId,omitempty" json:"chat_id,omitempty"`
MessageID string `bson:"messageId,omitempty" json:"message_id,omitempty"`
ReplyToMessageID string `bson:"replyToMessageId,omitempty" json:"reply_to_message_id,omitempty"`
FromUserID string `bson:"fromUserId,omitempty" json:"from_user_id,omitempty"`
FromUsername string `bson:"fromUsername,omitempty" json:"from_username,omitempty"`
Text string `bson:"text,omitempty" json:"text,omitempty"`
SentAt int64 `bson:"sentAt,omitempty" json:"sent_at,omitempty"`
}

View File

@@ -0,0 +1,36 @@
package model
import (
"time"
"github.com/tech/sendico/pkg/db/storable"
"github.com/tech/sendico/pkg/mservice"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type ConfirmationTarget string
const (
ConfirmationTargetLogin ConfirmationTarget = "login"
ConfirmationTargetPayout ConfirmationTarget = "payout"
)
type ConfirmationCode struct {
storable.Base `bson:",inline" json:",inline"`
AccountRef primitive.ObjectID `bson:"accountRef" json:"accountRef"`
Destination string `bson:"destination" json:"destination"`
Target ConfirmationTarget `bson:"target" json:"target"`
CodeHash []byte `bson:"codeHash" json:"codeHash,omitempty"`
Salt []byte `bson:"salt" json:"salt,omitempty"`
ExpiresAt time.Time `bson:"expiresAt" json:"expiresAt"`
MaxAttempts int `bson:"maxAttempts" json:"maxAttempts"`
ResendLimit int `bson:"resendLimit" json:"resendLimit"`
CooldownUntil time.Time `bson:"cooldownUntil" json:"cooldownUntil"`
Used bool `bson:"used" json:"used"`
Attempts int `bson:"attempts" json:"attempts"`
ResendCount int `bson:"resendCount" json:"resendCount"`
}
func (*ConfirmationCode) Collection() string {
return mservice.Confirmations
}

View File

@@ -12,6 +12,10 @@ const (
NASent NotificationAction = "sent"
NAPasswordReset NotificationAction = "password_reset"
NAConfirmationRequest NotificationAction = "confirmation.request"
NAPaymentGatewayIntent NotificationAction = "intent.request"
NAPaymentGatewayExecution NotificationAction = "execution.result"
NADiscoveryServiceAnnounce NotificationAction = "service.announce"
NADiscoveryGatewayAnnounce NotificationAction = "gateway.announce"
NADiscoveryHeartbeat NotificationAction = "service.heartbeat"

View File

@@ -81,6 +81,9 @@ func StringToNotificationAction(s string) (nm.NotificationAction, error) {
nm.NADeleted,
nm.NAAssigned,
nm.NAPasswordReset,
nm.NAConfirmationRequest,
nm.NAPaymentGatewayIntent,
nm.NAPaymentGatewayExecution,
nm.NADiscoveryServiceAnnounce,
nm.NADiscoveryGatewayAnnounce,
nm.NADiscoveryHeartbeat,
@@ -99,8 +102,15 @@ func StringToNotificationEvent(eventType, eventAction string) (NotificationEvent
return nil, err
}
ea, err := StringToNotificationAction(eventAction)
if err != nil {
return nil, err
if err == nil {
return NewNotification(et, ea), nil
}
return NewNotification(et, ea), nil
if et == mservice.Confirmations {
action := strings.TrimSpace(eventAction)
if action == "" {
return nil, err
}
return &NotificationEventImp{nType: et, nAction: nm.NotificationAction(action)}, nil
}
return nil, err
}

View File

@@ -0,0 +1,22 @@
package model
import paymenttypes "github.com/tech/sendico/pkg/payments/types"
type PaymentGatewayIntent struct {
PaymentIntentID string `bson:"paymentIntentId,omitempty" json:"payment_intent_id,omitempty"`
IdempotencyKey string `bson:"idempotencyKey,omitempty" json:"idempotency_key,omitempty"`
OutgoingLeg string `bson:"outgoingLeg,omitempty" json:"outgoing_leg,omitempty"`
QuoteRef string `bson:"quoteRef,omitempty" json:"quote_ref,omitempty"`
RequestedMoney *paymenttypes.Money `bson:"requestedMoney,omitempty" json:"requested_money,omitempty"`
TargetChatID string `bson:"targetChatId,omitempty" json:"target_chat_id,omitempty"`
}
type PaymentGatewayExecution struct {
PaymentIntentID string `bson:"paymentIntentId,omitempty" json:"payment_intent_id,omitempty"`
IdempotencyKey string `bson:"idempotencyKey,omitempty" json:"idempotency_key,omitempty"`
QuoteRef string `bson:"quoteRef,omitempty" json:"quote_ref,omitempty"`
ExecutedMoney *paymenttypes.Money `bson:"executedMoney,omitempty" json:"executed_money,omitempty"`
Status ConfirmationStatus `bson:"status,omitempty" json:"status,omitempty"`
RequestID string `bson:"requestId,omitempty" json:"request_id,omitempty"`
RawReply *TelegramMessage `bson:"rawReply,omitempty" json:"raw_reply,omitempty"`
}

View File

@@ -14,6 +14,7 @@ const (
Clients Type = "clients" // Represents client information
ChainGateway Type = "chain_gateway" // Represents chain gateway microservice
MntxGateway Type = "mntx_gateway" // Represents Monetix gateway microservice
PaymentGateway Type = "payment_gateway" // Represents payment gateway microservice
FXOracle Type = "fx_oracle" // Represents FX oracle microservice
FeePlans Type = "fee_plans" // Represents fee plans microservice
FilterProjects Type = "filter_projects" // Represents comments on tasks or other resources
@@ -36,6 +37,7 @@ const (
Organizations Type = "organizations" // Represents organizations in the system
Payments Type = "payments" // Represents payments service
PaymentRoutes Type = "payment_routes" // Represents payment routing definitions
PaymentPlanTemplates Type = "payment_plan_templates" // Represents payment plan templates
PaymentMethods Type = "payment_methods" // Represents payment methods service
Permissions Type = "permissions" // Represents permissiosns service
Policies Type = "policies" // Represents access control policies
@@ -52,9 +54,9 @@ const (
func StringToSType(s string) (Type, error) {
switch Type(s) {
case Accounts, Confirmations, Amplitude, Site, Changes, Clients, ChainGateway, ChainWallets, ChainWalletBalances,
ChainTransfers, ChainDeposits, MntxGateway, FXOracle, FeePlans, FilterProjects, Invitations, Invoices, Logo, Ledger,
ChainTransfers, ChainDeposits, MntxGateway, PaymentGateway, FXOracle, FeePlans, FilterProjects, Invitations, Invoices, Logo, Ledger,
LedgerAccounts, LedgerBalances, LedgerEntries, LedgerOutbox, LedgerParties, LedgerPlines, Notifications,
Organizations, Payments, PaymentRoutes, PaymentOrchestrator, Permissions, Policies, PolicyAssignements,
Organizations, Payments, PaymentRoutes, PaymentPlanTemplates, PaymentOrchestrator, Permissions, Policies, PolicyAssignements,
RefreshTokens, Roles, Storage, Tenants, Workflows, Discovery:
return Type(s), nil
default: