fixed tgsettle logging

This commit is contained in:
Stephan D
2026-01-19 19:56:11 +01:00
parent d9d2fded8a
commit 5737ebf89a
4 changed files with 153 additions and 25 deletions

View File

@@ -22,7 +22,7 @@ require (
require ( require (
github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260118001747-ff01f1c4e24d // indirect github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260119175649-084b3e29eee1 // indirect
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/bits-and-blooms/bitset v1.24.4 // indirect github.com/bits-and-blooms/bitset v1.24.4 // indirect
github.com/bmatcuk/doublestar/v4 v4.9.2 // indirect github.com/bmatcuk/doublestar/v4 v4.9.2 // 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/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 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260118001747-ff01f1c4e24d h1:xetq9B0q0HG5s4NOlR4T7oOltvrV7FhjAru+qcKcIhI= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260119175649-084b3e29eee1 h1:uq8tHimZusJXFqiZG2EyQhyLYUWXkOumwNRFAyWcyHw=
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260118001747-ff01f1c4e24d/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI= github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260119175649-084b3e29eee1/go.mod h1:ioLG6R+5bUSO1oeGSDxOV3FADARuMoytZCSX6MEMQkI=
github.com/VictoriaMetrics/fastcache v1.13.0 h1:AW4mheMR5Vd9FkAPUv+NH6Nhw+fmbTMGMsNAoA/+4G0= 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/VictoriaMetrics/fastcache v1.13.0/go.mod h1:hHXhl4DA2fTL2HTZDJFXWgW0LNjo6B+4aj2Wmng3TjU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=

View File

@@ -10,6 +10,7 @@ import (
connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1" connectorv1 "github.com/tech/sendico/pkg/proto/connector/v1"
moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1" moneyv1 "github.com/tech/sendico/pkg/proto/common/money/v1"
chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1" chainv1 "github.com/tech/sendico/pkg/proto/gateway/chain/v1"
"go.uber.org/zap"
) )
const tgsettleConnectorID = "tgsettle" const tgsettleConnectorID = "tgsettle"
@@ -44,61 +45,94 @@ func (s *Service) GetBalance(_ context.Context, _ *connectorv1.GetBalanceRequest
func (s *Service) SubmitOperation(ctx context.Context, req *connectorv1.SubmitOperationRequest) (*connectorv1.SubmitOperationResponse, error) { func (s *Service) SubmitOperation(ctx context.Context, req *connectorv1.SubmitOperationRequest) (*connectorv1.SubmitOperationResponse, error) {
if req == nil || req.GetOperation() == nil { if req == nil || req.GetOperation() == nil {
s.logger.Warn("Submit operation rejected", zap.String("reason", "operation is required"))
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "submit_operation: operation is required", nil, "")}}, nil return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "submit_operation: operation is required", nil, "")}}, nil
} }
op := req.GetOperation() op := req.GetOperation()
if strings.TrimSpace(op.GetIdempotencyKey()) == "" { if strings.TrimSpace(op.GetIdempotencyKey()) == "" {
s.logger.Warn("Submit operation rejected", append(operationLogFields(op), zap.String("reason", "idempotency_key is required"))...)
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "submit_operation: idempotency_key is required", op, "")}}, nil return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "submit_operation: idempotency_key is required", op, "")}}, nil
} }
if op.GetType() != connectorv1.OperationType_TRANSFER { if op.GetType() != connectorv1.OperationType_TRANSFER {
s.logger.Warn("Submit operation rejected", append(operationLogFields(op), zap.String("reason", "unsupported operation type"))...)
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_UNSUPPORTED_OPERATION, "submit_operation: unsupported operation type", op, "")}}, nil return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_UNSUPPORTED_OPERATION, "submit_operation: unsupported operation type", op, "")}}, nil
} }
reader := params.New(op.GetParams()) reader := params.New(op.GetParams())
paymentIntentID := strings.TrimSpace(reader.String("payment_intent_id"))
if paymentIntentID == "" {
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "submit_operation: payment_intent_id is required", op, "")}}, nil
}
source := operationAccountID(op.GetFrom())
if source == "" {
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "transfer: from.account is required", op, "")}}, nil
}
dest, err := transferDestinationFromOperation(op)
if err != nil {
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, err.Error(), op, "")}}, nil
}
amount := op.GetMoney()
if amount == nil {
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "transfer: money is required", op, "")}}, nil
}
metadata := reader.StringMap("metadata") metadata := reader.StringMap("metadata")
if metadata == nil { if metadata == nil {
metadata = map[string]string{} metadata = map[string]string{}
} }
paymentIntentID := strings.TrimSpace(reader.String("payment_intent_id"))
if paymentIntentID == "" {
paymentIntentID = strings.TrimSpace(reader.String("client_reference"))
}
if paymentIntentID == "" {
paymentIntentID = strings.TrimSpace(metadata[metadataPaymentIntentID])
}
if paymentIntentID == "" {
s.logger.Warn("Submit operation rejected", append(operationLogFields(op), zap.String("reason", "payment_intent_id is required"))...)
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "submit_operation: payment_intent_id is required", op, "")}}, nil
}
source := operationAccountID(op.GetFrom())
if source == "" {
s.logger.Warn("Submit operation rejected", append(operationLogFields(op), zap.String("reason", "from.account is required"))...)
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "transfer: from.account is required", op, "")}}, nil
}
dest, err := transferDestinationFromOperation(op)
if err != nil {
s.logger.Warn("Submit operation rejected", append(operationLogFields(op), zap.Error(err))...)
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, err.Error(), op, "")}}, nil
}
amount := op.GetMoney()
if amount == nil {
s.logger.Warn("Submit operation rejected", append(operationLogFields(op), zap.String("reason", "money is required"))...)
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "transfer: money is required", op, "")}}, nil
}
metadata[metadataPaymentIntentID] = paymentIntentID metadata[metadataPaymentIntentID] = paymentIntentID
if quoteRef := strings.TrimSpace(reader.String("quote_ref")); quoteRef != "" { quoteRef := strings.TrimSpace(reader.String("quote_ref"))
if quoteRef != "" {
metadata[metadataQuoteRef] = quoteRef metadata[metadataQuoteRef] = quoteRef
} }
if targetChatID := strings.TrimSpace(reader.String("target_chat_id")); targetChatID != "" { targetChatID := strings.TrimSpace(reader.String("target_chat_id"))
if targetChatID != "" {
metadata[metadataTargetChatID] = targetChatID metadata[metadataTargetChatID] = targetChatID
} }
if outgoingLeg := strings.TrimSpace(reader.String("outgoing_leg")); outgoingLeg != "" { outgoingLeg := strings.TrimSpace(reader.String("outgoing_leg"))
if outgoingLeg != "" {
metadata[metadataOutgoingLeg] = outgoingLeg metadata[metadataOutgoingLeg] = outgoingLeg
} }
normalizedAmount := normalizeMoneyForTransfer(amount)
logFields := append(operationLogFields(op),
zap.String("payment_intent_id", paymentIntentID),
zap.String("organization_ref", strings.TrimSpace(reader.String("organization_ref"))),
zap.String("source_wallet_ref", source),
zap.String("amount", strings.TrimSpace(normalizedAmount.GetAmount())),
zap.String("currency", strings.TrimSpace(normalizedAmount.GetCurrency())),
zap.String("quote_ref", quoteRef),
zap.String("outgoing_leg", outgoingLeg),
)
logFields = append(logFields, transferDestinationLogFields(dest)...)
resp, err := s.SubmitTransfer(ctx, &chainv1.SubmitTransferRequest{ resp, err := s.SubmitTransfer(ctx, &chainv1.SubmitTransferRequest{
IdempotencyKey: strings.TrimSpace(op.GetIdempotencyKey()), IdempotencyKey: strings.TrimSpace(op.GetIdempotencyKey()),
OrganizationRef: strings.TrimSpace(reader.String("organization_ref")), OrganizationRef: strings.TrimSpace(reader.String("organization_ref")),
SourceWalletRef: source, SourceWalletRef: source,
Destination: dest, Destination: dest,
Amount: normalizeMoneyForTransfer(amount), Amount: normalizedAmount,
Metadata: metadata, Metadata: metadata,
ClientReference: paymentIntentID, ClientReference: paymentIntentID,
}) })
if err != nil { if err != nil {
s.logger.Warn("Submit operation transfer failed", append(logFields, zap.Error(err))...)
return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(mapErrorCode(err), err.Error(), op, "")}}, nil return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(mapErrorCode(err), err.Error(), op, "")}}, nil
} }
transfer := resp.GetTransfer() transfer := resp.GetTransfer()
s.logger.Info("Submit operation transfer submitted", append(logFields,
zap.String("transfer_ref", strings.TrimSpace(transfer.GetTransferRef())),
zap.String("status", transfer.GetStatus().String()),
)...)
return &connectorv1.SubmitOperationResponse{ return &connectorv1.SubmitOperationResponse{
Receipt: &connectorv1.OperationReceipt{ Receipt: &connectorv1.OperationReceipt{
OperationId: strings.TrimSpace(transfer.GetTransferRef()), OperationId: strings.TrimSpace(transfer.GetTransferRef()),
@@ -110,10 +144,13 @@ func (s *Service) SubmitOperation(ctx context.Context, req *connectorv1.SubmitOp
func (s *Service) GetOperation(ctx context.Context, req *connectorv1.GetOperationRequest) (*connectorv1.GetOperationResponse, error) { func (s *Service) GetOperation(ctx context.Context, req *connectorv1.GetOperationRequest) (*connectorv1.GetOperationResponse, error) {
if req == nil || strings.TrimSpace(req.GetOperationId()) == "" { if req == nil || strings.TrimSpace(req.GetOperationId()) == "" {
s.logger.Warn("Get operation rejected", zap.String("reason", "operation_id is required"))
return nil, merrors.InvalidArgument("get_operation: operation_id is required") return nil, merrors.InvalidArgument("get_operation: operation_id is required")
} }
resp, err := s.GetTransfer(ctx, &chainv1.GetTransferRequest{TransferRef: strings.TrimSpace(req.GetOperationId())}) operationID := strings.TrimSpace(req.GetOperationId())
resp, err := s.GetTransfer(ctx, &chainv1.GetTransferRequest{TransferRef: operationID})
if err != nil { if err != nil {
s.logger.Warn("Get operation failed", zap.String("operation_id", operationID), zap.Error(err))
return nil, err return nil, err
} }
return &connectorv1.GetOperationResponse{Operation: transferToOperation(resp.GetTransfer())}, nil return &connectorv1.GetOperationResponse{Operation: transferToOperation(resp.GetTransfer())}, nil
@@ -223,6 +260,39 @@ func operationAccountID(party *connectorv1.OperationParty) string {
return "" return ""
} }
func operationLogFields(op *connectorv1.Operation) []zap.Field {
if op == nil {
return nil
}
return []zap.Field{
zap.String("operation_id", strings.TrimSpace(op.GetOperationId())),
zap.String("idempotency_key", strings.TrimSpace(op.GetIdempotencyKey())),
zap.String("correlation_id", strings.TrimSpace(op.GetCorrelationId())),
zap.String("parent_intent_id", strings.TrimSpace(op.GetParentIntentId())),
zap.String("operation_type", op.GetType().String()),
}
}
func transferDestinationLogFields(dest *chainv1.TransferDestination) []zap.Field {
if dest == nil {
return nil
}
switch d := dest.GetDestination().(type) {
case *chainv1.TransferDestination_ManagedWalletRef:
return []zap.Field{
zap.String("destination_type", "managed_wallet"),
zap.String("destination_ref", strings.TrimSpace(d.ManagedWalletRef)),
}
case *chainv1.TransferDestination_ExternalAddress:
return []zap.Field{
zap.String("destination_type", "external_address"),
zap.String("destination_ref", strings.TrimSpace(d.ExternalAddress)),
}
default:
return []zap.Field{zap.String("destination_type", "unknown")}
}
}
func connectorError(code connectorv1.ErrorCode, message string, op *connectorv1.Operation, accountID string) *connectorv1.ConnectorError { func connectorError(code connectorv1.ErrorCode, message string, op *connectorv1.Operation, accountID string) *connectorv1.ConnectorError {
err := &connectorv1.ConnectorError{ err := &connectorv1.ConnectorError{
Code: code, Code: code,

View File

@@ -139,75 +139,115 @@ func (s *Service) consumeProcessor(processor np.EnvelopeProcessor) {
func (s *Service) SubmitTransfer(ctx context.Context, req *chainv1.SubmitTransferRequest) (*chainv1.SubmitTransferResponse, error) { func (s *Service) SubmitTransfer(ctx context.Context, req *chainv1.SubmitTransferRequest) (*chainv1.SubmitTransferResponse, error) {
if req == nil { if req == nil {
s.logger.Warn("Submit transfer rejected", zap.String("reason", "request is required"))
return nil, merrors.InvalidArgument("submit_transfer: request is required") return nil, merrors.InvalidArgument("submit_transfer: request is required")
} }
idempotencyKey := strings.TrimSpace(req.GetIdempotencyKey()) idempotencyKey := strings.TrimSpace(req.GetIdempotencyKey())
if idempotencyKey == "" { if idempotencyKey == "" {
s.logger.Warn("Submit transfer rejected", zap.String("reason", "idempotency_key is required"))
return nil, merrors.InvalidArgument("submit_transfer: idempotency_key is required") return nil, merrors.InvalidArgument("submit_transfer: idempotency_key is required")
} }
amount := req.GetAmount() amount := req.GetAmount()
if amount == nil || strings.TrimSpace(amount.GetAmount()) == "" || strings.TrimSpace(amount.GetCurrency()) == "" { if amount == nil || strings.TrimSpace(amount.GetAmount()) == "" || strings.TrimSpace(amount.GetCurrency()) == "" {
s.logger.Warn("Submit transfer rejected", zap.String("reason", "amount is required"), zap.String("idempotency_key", idempotencyKey))
return nil, merrors.InvalidArgument("submit_transfer: amount is required") return nil, merrors.InvalidArgument("submit_transfer: amount is required")
} }
intent, err := intentFromSubmitTransfer(req, s.rail, s.chatID) intent, err := intentFromSubmitTransfer(req, s.rail, s.chatID)
if err != nil { if err != nil {
s.logger.Warn("Submit transfer rejected", zap.Error(err), zap.String("idempotency_key", idempotencyKey))
return nil, err return nil, err
} }
logFields := []zap.Field{
zap.String("idempotency_key", intent.IdempotencyKey),
zap.String("payment_intent_id", intent.PaymentIntentID),
zap.String("quote_ref", intent.QuoteRef),
zap.String("rail", intent.OutgoingLeg),
zap.String("organization_ref", strings.TrimSpace(req.GetOrganizationRef())),
zap.String("source_wallet_ref", strings.TrimSpace(req.GetSourceWalletRef())),
}
if intent.RequestedMoney != nil {
logFields = append(logFields,
zap.String("amount", strings.TrimSpace(intent.RequestedMoney.Amount)),
zap.String("currency", strings.TrimSpace(intent.RequestedMoney.Currency)),
)
}
logFields = append(logFields, transferDestinationLogFields(req.GetDestination())...)
if s.repo == nil || s.repo.Payments() == nil { if s.repo == nil || s.repo.Payments() == nil {
s.logger.Warn("Payment gateway storage unavailable", logFields...)
return nil, merrors.Internal("payment gateway storage unavailable") return nil, merrors.Internal("payment gateway storage unavailable")
} }
existing, err := s.repo.Payments().FindByIdempotencyKey(ctx, idempotencyKey) existing, err := s.repo.Payments().FindByIdempotencyKey(ctx, idempotencyKey)
if err != nil { if err != nil {
s.logger.Warn("Submit transfer lookup failed", append(logFields, zap.Error(err))...)
return nil, err return nil, err
} }
if existing != nil { if existing != nil {
s.logger.Info("Submit transfer idempotent hit", append(logFields, zap.String("status", strings.TrimSpace(existing.Status)))...)
return &chainv1.SubmitTransferResponse{Transfer: transferFromExecution(existing, req)}, nil return &chainv1.SubmitTransferResponse{Transfer: transferFromExecution(existing, req)}, nil
} }
if err := s.onIntent(ctx, intent); err != nil { if err := s.onIntent(ctx, intent); err != nil {
s.logger.Warn("Submit transfer intent handling failed", append(logFields, zap.Error(err))...)
return nil, err return nil, err
} }
s.logger.Info("Submit transfer accepted", logFields...)
return &chainv1.SubmitTransferResponse{Transfer: transferFromRequest(req)}, nil return &chainv1.SubmitTransferResponse{Transfer: transferFromRequest(req)}, nil
} }
func (s *Service) GetTransfer(ctx context.Context, req *chainv1.GetTransferRequest) (*chainv1.GetTransferResponse, error) { func (s *Service) GetTransfer(ctx context.Context, req *chainv1.GetTransferRequest) (*chainv1.GetTransferResponse, error) {
if req == nil { if req == nil {
s.logger.Warn("Get transfer rejected", zap.String("reason", "request is required"))
return nil, merrors.InvalidArgument("get_transfer: request is required") return nil, merrors.InvalidArgument("get_transfer: request is required")
} }
transferRef := strings.TrimSpace(req.GetTransferRef()) transferRef := strings.TrimSpace(req.GetTransferRef())
if transferRef == "" { if transferRef == "" {
s.logger.Warn("Get transfer rejected", zap.String("reason", "transfer_ref is required"))
return nil, merrors.InvalidArgument("get_transfer: transfer_ref is required") return nil, merrors.InvalidArgument("get_transfer: transfer_ref is required")
} }
logFields := []zap.Field{zap.String("transfer_ref", transferRef)}
if s.repo == nil || s.repo.Payments() == nil { if s.repo == nil || s.repo.Payments() == nil {
s.logger.Warn("Payment gateway storage unavailable", logFields...)
return nil, merrors.Internal("payment gateway storage unavailable") return nil, merrors.Internal("payment gateway storage unavailable")
} }
existing, err := s.repo.Payments().FindByIdempotencyKey(ctx, transferRef) existing, err := s.repo.Payments().FindByIdempotencyKey(ctx, transferRef)
if err != nil { if err != nil {
s.logger.Warn("Get transfer lookup failed", append(logFields, zap.Error(err))...)
return nil, err return nil, err
} }
if existing != nil { if existing != nil {
s.logger.Info("Get transfer resolved from execution", append(logFields,
zap.String("payment_intent_id", strings.TrimSpace(existing.PaymentIntentID)),
zap.String("status", strings.TrimSpace(existing.Status)),
)...)
return &chainv1.GetTransferResponse{Transfer: transferFromExecution(existing, nil)}, nil return &chainv1.GetTransferResponse{Transfer: transferFromExecution(existing, nil)}, nil
} }
if s.hasPending(transferRef) { if s.hasPending(transferRef) {
s.logger.Info("Get transfer pending", logFields...)
return &chainv1.GetTransferResponse{Transfer: transferPending(transferRef)}, nil return &chainv1.GetTransferResponse{Transfer: transferPending(transferRef)}, nil
} }
s.logger.Warn("Get transfer not found", logFields...)
return nil, status.Error(codes.NotFound, "transfer not found") return nil, status.Error(codes.NotFound, "transfer not found")
} }
func (s *Service) onIntent(ctx context.Context, intent *model.PaymentGatewayIntent) error { func (s *Service) onIntent(ctx context.Context, intent *model.PaymentGatewayIntent) error {
if intent == nil { if intent == nil {
s.logger.Warn("Payment gateway intent rejected", zap.String("reason", "intent is nil"))
return merrors.InvalidArgument("payment gateway intent is nil", "intent") return merrors.InvalidArgument("payment gateway intent is nil", "intent")
} }
intent = normalizeIntent(intent) intent = normalizeIntent(intent)
if intent.IdempotencyKey == "" { if intent.IdempotencyKey == "" {
s.logger.Warn("Payment gateway intent rejected", zap.String("reason", "idempotency_key is required"))
return merrors.InvalidArgument("idempotency_key is required", "idempotency_key") return merrors.InvalidArgument("idempotency_key is required", "idempotency_key")
} }
if intent.PaymentIntentID == "" { if intent.PaymentIntentID == "" {
s.logger.Warn("Payment gateway intent rejected", zap.String("reason", "payment_intent_id is required"), zap.String("idempotency_key", intent.IdempotencyKey))
return merrors.InvalidArgument("payment_intent_id is required", "payment_intent_id") return merrors.InvalidArgument("payment_intent_id is required", "payment_intent_id")
} }
if intent.RequestedMoney == nil || strings.TrimSpace(intent.RequestedMoney.Amount) == "" || strings.TrimSpace(intent.RequestedMoney.Currency) == "" { if intent.RequestedMoney == nil || strings.TrimSpace(intent.RequestedMoney.Amount) == "" || strings.TrimSpace(intent.RequestedMoney.Currency) == "" {
s.logger.Warn("Payment gateway intent rejected", zap.String("reason", "requested_money is required"), zap.String("idempotency_key", intent.IdempotencyKey))
return merrors.InvalidArgument("requested_money is required", "requested_money") return merrors.InvalidArgument("requested_money is required", "requested_money")
} }
if s.repo == nil || s.repo.Payments() == nil { if s.repo == nil || s.repo.Payments() == nil {
s.logger.Warn("Payment gateway storage unavailable", zap.String("idempotency_key", intent.IdempotencyKey))
return merrors.Internal("payment gateway storage unavailable") return merrors.Internal("payment gateway storage unavailable")
} }
@@ -226,6 +266,7 @@ func (s *Service) onIntent(ctx context.Context, intent *model.PaymentGatewayInte
confirmReq, err := s.buildConfirmationRequest(intent) confirmReq, err := s.buildConfirmationRequest(intent)
if err != nil { if err != nil {
s.logger.Warn("Failed to build confirmation request", zap.Error(err), zap.String("idempotency_key", intent.IdempotencyKey), zap.String("payment_intent_id", intent.PaymentIntentID))
return err return err
} }
if err := s.sendConfirmationRequest(confirmReq); err != nil { if err := s.sendConfirmationRequest(confirmReq); err != nil {
@@ -237,10 +278,12 @@ func (s *Service) onIntent(ctx context.Context, intent *model.PaymentGatewayInte
func (s *Service) onConfirmationResult(ctx context.Context, result *model.ConfirmationResult) error { func (s *Service) onConfirmationResult(ctx context.Context, result *model.ConfirmationResult) error {
if result == nil { if result == nil {
s.logger.Warn("Confirmation result rejected", zap.String("reason", "result is nil"))
return merrors.InvalidArgument("confirmation result is nil", "result") return merrors.InvalidArgument("confirmation result is nil", "result")
} }
requestID := strings.TrimSpace(result.RequestID) requestID := strings.TrimSpace(result.RequestID)
if requestID == "" { if requestID == "" {
s.logger.Warn("Confirmation result rejected", zap.String("reason", "request_id is required"))
return merrors.InvalidArgument("confirmation request_id is required", "request_id") return merrors.InvalidArgument("confirmation request_id is required", "request_id")
} }
intent := s.lookupIntent(requestID) intent := s.lookupIntent(requestID)
@@ -314,9 +357,11 @@ func (s *Service) buildConfirmationRequest(intent *model.PaymentGatewayIntent) (
func (s *Service) sendConfirmationRequest(request *model.ConfirmationRequest) error { func (s *Service) sendConfirmationRequest(request *model.ConfirmationRequest) error {
if request == nil { if request == nil {
s.logger.Warn("Confirmation request rejected", zap.String("reason", "request is nil"))
return merrors.InvalidArgument("confirmation request is nil", "request") return merrors.InvalidArgument("confirmation request is nil", "request")
} }
if s.producer == nil { if s.producer == nil {
s.logger.Warn("Messaging producer not configured")
return merrors.Internal("messaging producer is not configured") return merrors.Internal("messaging producer is not configured")
} }
env := confirmations.ConfirmationRequest(string(mservice.PaymentGateway), request) env := confirmations.ConfirmationRequest(string(mservice.PaymentGateway), request)
@@ -330,6 +375,12 @@ func (s *Service) sendConfirmationRequest(request *model.ConfirmationRequest) er
zap.Int32("timeout_seconds", request.TimeoutSeconds)) zap.Int32("timeout_seconds", request.TimeoutSeconds))
return err return err
} }
s.logger.Info("Published confirmation request",
zap.String("request_id", request.RequestID),
zap.String("payment_intent_id", request.PaymentIntentID),
zap.String("quote_ref", request.QuoteRef),
zap.String("rail", request.Rail),
zap.Int32("timeout_seconds", request.TimeoutSeconds))
return nil return nil
} }
@@ -355,7 +406,14 @@ func (s *Service) publishExecution(intent *model.PaymentGatewayIntent, result *m
zap.String("payment_intent_id", intent.PaymentIntentID), zap.String("payment_intent_id", intent.PaymentIntentID),
zap.String("quote_ref", intent.QuoteRef), zap.String("quote_ref", intent.QuoteRef),
zap.String("status", string(result.Status))) zap.String("status", string(result.Status)))
return
} }
s.logger.Info("Published gateway execution result",
zap.String("request_id", result.RequestID),
zap.String("idempotency_key", intent.IdempotencyKey),
zap.String("payment_intent_id", intent.PaymentIntentID),
zap.String("quote_ref", intent.QuoteRef),
zap.String("status", string(result.Status)))
} }
func (s *Service) trackIntent(requestID string, intent *model.PaymentGatewayIntent) { func (s *Service) trackIntent(requestID string, intent *model.PaymentGatewayIntent) {