diff --git a/.woodpecker/bff.yml b/.woodpecker/bff.yml index 90982716..25c29c7a 100644 --- a/.woodpecker/bff.yml +++ b/.woodpecker/bff.yml @@ -14,7 +14,7 @@ when: - api/server/** - api/proto/** - api/pkg/** - ignore_message: '[REBUILD]' + ignore_message: '[rebuild]' steps: - name: version diff --git a/.woodpecker/billing_documents.yml b/.woodpecker/billing_documents.yml index 57102408..38f67056 100644 --- a/.woodpecker/billing_documents.yml +++ b/.woodpecker/billing_documents.yml @@ -13,7 +13,7 @@ when: - api/billing/documents/** - api/proto/** - api/pkg/** - ignore_message: '[REBUILD]' + ignore_message: '[rebuild]' steps: - name: version diff --git a/.woodpecker/billing_fees.yml b/.woodpecker/billing_fees.yml index d9cd9f55..10e303e5 100644 --- a/.woodpecker/billing_fees.yml +++ b/.woodpecker/billing_fees.yml @@ -13,7 +13,7 @@ when: - api/billing/fees/** - api/proto/** - api/pkg/** - ignore_message: '[REBUILD]' + ignore_message: '[rebuild]' steps: - name: version diff --git a/.woodpecker/db.yml b/.woodpecker/db.yml index 77925254..aa601be4 100644 --- a/.woodpecker/db.yml +++ b/.woodpecker/db.yml @@ -1,6 +1,9 @@ when: - event: push branch: main + path: + exclude: ['**'] + ignore_message: '[infra]' steps: - name: version diff --git a/.woodpecker/discovery.yml b/.woodpecker/discovery.yml index df075aae..9cd58c4f 100644 --- a/.woodpecker/discovery.yml +++ b/.woodpecker/discovery.yml @@ -12,7 +12,7 @@ when: - api/discovery/** - api/proto/** - api/pkg/** - ignore_message: '[REBUILD]' + ignore_message: '[rebuild]' steps: - name: version diff --git a/.woodpecker/frontend.yml b/.woodpecker/frontend.yml index 6e8087c4..331e02e2 100644 --- a/.woodpecker/frontend.yml +++ b/.woodpecker/frontend.yml @@ -13,7 +13,7 @@ when: - api/pkg/** - api/proto/** - frontend/** - ignore_message: '[REBUILD]' + ignore_message: '[rebuild]' steps: - name: version diff --git a/.woodpecker/fx_ingestor.yml b/.woodpecker/fx_ingestor.yml index 5b0a7dc7..b063fb6d 100644 --- a/.woodpecker/fx_ingestor.yml +++ b/.woodpecker/fx_ingestor.yml @@ -17,7 +17,7 @@ when: - api/fx/storage/** - api/proto/** - api/pkg/** - ignore_message: '[REBUILD]' + ignore_message: '[rebuild]' steps: - name: version diff --git a/.woodpecker/fx_oracle.yml b/.woodpecker/fx_oracle.yml index 569450ec..38fb8e63 100644 --- a/.woodpecker/fx_oracle.yml +++ b/.woodpecker/fx_oracle.yml @@ -17,7 +17,7 @@ when: - api/fx/storage/** - api/proto/** - api/pkg/** - ignore_message: '[REBUILD]' + ignore_message: '[rebuild]' steps: diff --git a/.woodpecker/gateway_chain.yml b/.woodpecker/gateway_chain.yml index 34565d8b..d8103392 100644 --- a/.woodpecker/gateway_chain.yml +++ b/.woodpecker/gateway_chain.yml @@ -16,7 +16,7 @@ when: - api/gateway/chain/** - api/proto/** - api/pkg/** - ignore_message: '[REBUILD]' + ignore_message: '[rebuild]' steps: - name: version diff --git a/.woodpecker/gateway_mntx.yml b/.woodpecker/gateway_mntx.yml index 4535df17..f5de3e00 100644 --- a/.woodpecker/gateway_mntx.yml +++ b/.woodpecker/gateway_mntx.yml @@ -15,7 +15,7 @@ when: - api/gateway/mntx/** - api/proto/** - api/pkg/** - ignore_message: '[REBUILD]' + ignore_message: '[rebuild]' steps: - name: version diff --git a/.woodpecker/gateway_tgsettle.yml b/.woodpecker/gateway_tgsettle.yml index dc3e118a..ef9c5cd6 100644 --- a/.woodpecker/gateway_tgsettle.yml +++ b/.woodpecker/gateway_tgsettle.yml @@ -13,7 +13,7 @@ when: - api/gateway/tgsettle/** - api/proto/** - api/pkg/** - ignore_message: '[REBUILD]' + ignore_message: '[rebuild]' steps: - name: version diff --git a/.woodpecker/gateway_tron.yml b/.woodpecker/gateway_tron.yml index 82d977d6..958e7a69 100644 --- a/.woodpecker/gateway_tron.yml +++ b/.woodpecker/gateway_tron.yml @@ -16,7 +16,7 @@ when: - api/gateway/tron/** - api/proto/** - api/pkg/** - ignore_message: '[REBUILD]' + ignore_message: '[rebuild]' steps: - name: version diff --git a/.woodpecker/ledger.yml b/.woodpecker/ledger.yml index d1bcfdd3..b0d7fdc6 100644 --- a/.woodpecker/ledger.yml +++ b/.woodpecker/ledger.yml @@ -13,7 +13,7 @@ when: - api/ledger/** - api/proto/** - api/pkg/** - ignore_message: '[REBUILD]' + ignore_message: '[rebuild]' steps: - name: version diff --git a/.woodpecker/nats.yml b/.woodpecker/nats.yml index 692df8ba..c6651e60 100644 --- a/.woodpecker/nats.yml +++ b/.woodpecker/nats.yml @@ -1,6 +1,10 @@ when: - event: push branch: main + path: + exclude: ['**'] + ignore_message: '[infra]' + steps: - name: version diff --git a/.woodpecker/notification.yml b/.woodpecker/notification.yml index fefb0425..761859eb 100644 --- a/.woodpecker/notification.yml +++ b/.woodpecker/notification.yml @@ -16,7 +16,7 @@ when: - api/notification/** - api/proto/** - api/pkg/** - ignore_message: '[REBUILD]' + ignore_message: '[rebuild]' steps: - name: version diff --git a/.woodpecker/payments_orchestrator.yml b/.woodpecker/payments_orchestrator.yml index 09522e04..7558af26 100644 --- a/.woodpecker/payments_orchestrator.yml +++ b/.woodpecker/payments_orchestrator.yml @@ -13,7 +13,7 @@ when: - api/payments/orchestrator/** - api/proto/** - api/pkg/** - ignore_message: '[REBUILD]' + ignore_message: '[rebuild]' steps: - name: version diff --git a/api/gateway/mntx/client/client.go b/api/gateway/mntx/client/client.go index dcee28b2..4837d793 100644 --- a/api/gateway/mntx/client/client.go +++ b/api/gateway/mntx/client/client.go @@ -151,7 +151,9 @@ func operationFromCardPayout(req *mntxv1.CardPayoutRequest) (*connectorv1.Operat money := moneyFromMinor(req.GetAmountMinor(), req.GetCurrency()) op := &connectorv1.Operation{ Type: connectorv1.OperationType_PAYOUT, - IdempotencyKey: strings.TrimSpace(req.GetPayoutId()), + IdempotencyKey: strings.TrimSpace(req.GetIdempotencyKey()), + OperationRef: strings.TrimSpace(req.GetOperationRef()), + IntentRef: strings.TrimSpace(req.GetIntentRef()), Money: money, Params: structFromMap(params), } diff --git a/api/gateway/mntx/config.dev.yml b/api/gateway/mntx/config.dev.yml index befe15fd..06bd17dd 100644 --- a/api/gateway/mntx/config.dev.yml +++ b/api/gateway/mntx/config.dev.yml @@ -48,7 +48,7 @@ monetix: gateway: id: "monetix" is_enabled: true - network: "VISA_DIRECT" + network: "MIR" currencies: ["RUB"] limits: min_amount: "0" diff --git a/api/gateway/mntx/config.yml b/api/gateway/mntx/config.yml index 29039442..ae265c35 100644 --- a/api/gateway/mntx/config.yml +++ b/api/gateway/mntx/config.yml @@ -48,7 +48,7 @@ monetix: gateway: id: "monetix" is_enabled: true - network: "VISA_DIRECT" + network: "MIR" currencies: ["RUB"] limits: min_amount: "0" diff --git a/api/gateway/mntx/internal/service/gateway/card_processor.go b/api/gateway/mntx/internal/service/gateway/card_processor.go index 4b106b9c..391efd70 100644 --- a/api/gateway/mntx/internal/service/gateway/card_processor.go +++ b/api/gateway/mntx/internal/service/gateway/card_processor.go @@ -129,6 +129,8 @@ func (p *cardPayoutProcessor) Submit(ctx context.Context, req *mntxv1.CardPayout zap.String("customer_id", state.CustomerID), zap.String("operation_ref", state.OperationRef), zap.String("idempotency_key", state.IdempotencyKey), + zap.String("operation_ref", state.OperationRef), + zap.String("intent_ref", state.IntentRef), ) } @@ -138,6 +140,8 @@ func (p *cardPayoutProcessor) Submit(ctx context.Context, req *mntxv1.CardPayout zap.String("customer_id", state.CustomerID), zap.String("operation_ref", state.OperationRef), zap.String("idempotency_key", state.IdempotencyKey), + zap.String("operation_ref", state.OperationRef), + zap.String("intent_ref", state.IntentRef), ) return nil, err diff --git a/api/gateway/mntx/internal/service/gateway/connector.go b/api/gateway/mntx/internal/service/gateway/connector.go index 59de971d..a87e39ba 100644 --- a/api/gateway/mntx/internal/service/gateway/connector.go +++ b/api/gateway/mntx/internal/service/gateway/connector.go @@ -49,9 +49,18 @@ func (s *Service) SubmitOperation(ctx context.Context, req *connectorv1.SubmitOp return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "submit_operation: operation is required", nil, "")}}, nil } op := req.GetOperation() - if strings.TrimSpace(op.GetIdempotencyKey()) == "" { + idempotencyKey := strings.TrimSpace(op.GetIdempotencyKey()) + if idempotencyKey == "" { return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "submit_operation: idempotency_key is required", op, "")}}, nil } + operationRef := strings.TrimSpace(op.GetOperationRef()) + if operationRef == "" { + return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "submit_operation: operation_ref is required", op, "")}}, nil + } + intentRef := strings.TrimSpace(op.GetIntentRef()) + if intentRef == "" { + return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_INVALID_PARAMS, "submit_operation: intent_ref is required", op, "")}}, nil + } if op.GetType() != connectorv1.OperationType_PAYOUT { return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(connectorv1.ErrorCode_UNSUPPORTED_OPERATION, "submit_operation: unsupported operation type", op, "")}}, nil } @@ -73,7 +82,8 @@ func (s *Service) SubmitOperation(ctx context.Context, req *connectorv1.SubmitOp } return &connectorv1.SubmitOperationResponse{Receipt: payoutReceipt(resp.GetPayout())}, nil } - resp, err := s.CreateCardPayout(ctx, buildCardPayoutRequestFromParams(reader, payoutID, amountMinor, currency)) + cr := buildCardPayoutRequestFromParams(reader, payoutID, idempotencyKey, operationRef, intentRef, amountMinor, currency) + resp, err := s.CreateCardPayout(ctx, cr) if err != nil { return &connectorv1.SubmitOperationResponse{Receipt: &connectorv1.OperationReceipt{Error: connectorError(mapErrorCode(err), err.Error(), op, "")}}, nil } @@ -183,7 +193,9 @@ func buildCardTokenPayoutRequestFromParams(reader params.Reader, payoutID string return req } -func buildCardPayoutRequestFromParams(reader params.Reader, payoutID string, amountMinor int64, currency string) *mntxv1.CardPayoutRequest { +func buildCardPayoutRequestFromParams(reader params.Reader, + payoutID, idempotencyKey, operationRef, intentRef string, + amountMinor int64, currency string) *mntxv1.CardPayoutRequest { return &mntxv1.CardPayoutRequest{ PayoutId: payoutID, ProjectId: readerInt64(reader, "project_id"), @@ -204,6 +216,9 @@ func buildCardPayoutRequestFromParams(reader params.Reader, payoutID string, amo CardExpMonth: uint32(readerInt64(reader, "card_exp_month")), CardHolder: strings.TrimSpace(reader.String("card_holder")), Metadata: reader.StringMap("metadata"), + OperationRef: operationRef, + IdempotencyKey: idempotencyKey, + IntentRef: intentRef, } } diff --git a/api/gateway/mntx/internal/service/gateway/transfer_notifications.go b/api/gateway/mntx/internal/service/gateway/transfer_notifications.go index 2c674317..f2e1fcb0 100644 --- a/api/gateway/mntx/internal/service/gateway/transfer_notifications.go +++ b/api/gateway/mntx/internal/service/gateway/transfer_notifications.go @@ -11,7 +11,6 @@ import ( "github.com/tech/sendico/pkg/mutil/mzap" "github.com/tech/sendico/pkg/payments/rail" paytypes "github.com/tech/sendico/pkg/payments/types" - gatewayv1 "github.com/tech/sendico/pkg/proto/common/gateway/v1" "go.uber.org/zap" ) @@ -37,15 +36,6 @@ func toOpStatus(t *model.CardPayout) rail.OperationResult { } } -func toError(t *model.CardPayout) *gatewayv1.OperationError { - if t.Status == model.PayoutStatusSuccess { - return nil - } - return &gatewayv1.OperationError{ - Message: t.FailureReason, - } -} - func (p *cardPayoutProcessor) updatePayoutStatus(ctx context.Context, state *model.CardPayout) error { if err := p.store.Payouts().Upsert(ctx, state); err != nil { p.logger.Warn("Failed to update transfer status", zap.String("transfer_ref", state.PayoutID), zap.String("status", string(state.Status)), zap.Error(err)) diff --git a/api/payments/orchestrator/internal/service/orchestrator/card_payout_submit.go b/api/payments/orchestrator/internal/service/orchestrator/card_payout_submit.go index fb6e1acd..0f3407ed 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/card_payout_submit.go +++ b/api/payments/orchestrator/internal/service/orchestrator/card_payout_submit.go @@ -13,7 +13,7 @@ import ( "go.uber.org/zap" ) -func (s *Service) submitCardPayout(ctx context.Context, payment *model.Payment) error { +func (s *Service) submitCardPayout(ctx context.Context, operationRef string, payment *model.Payment) error { if payment == nil { return merrors.InvalidArgument("payment is required") } @@ -133,6 +133,8 @@ func (s *Service) submitCardPayout(ctx context.Context, payment *model.Payment) CardExpMonth: card.ExpMonth, CardHolder: holder, Metadata: meta, + IntentRef: payment.Intent.Ref, + OperationRef: operationRef, } resp, err := s.deps.mntx.client.CreateCardPayout(ctx, req) if err != nil { @@ -173,7 +175,8 @@ func (s *Service) submitCardPayout(ctx context.Context, payment *model.Payment) updateExecutionPlanTotalNetworkFee(plan) } - s.logger.Info("card payout submitted", zap.String("payment_ref", payment.PaymentRef), zap.String("payout_id", exec.CardPayoutRef)) + s.logger.Info("card payout submitted", zap.String("payment_ref", payment.PaymentRef), + zap.String("payout_id", exec.CardPayoutRef), zap.String("operation_ref", state.OperationRef)) return nil } diff --git a/api/payments/orchestrator/internal/service/orchestrator/card_payout_test.go b/api/payments/orchestrator/internal/service/orchestrator/card_payout_test.go index 71e21dfa..2e2b8ad3 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/card_payout_test.go +++ b/api/payments/orchestrator/internal/service/orchestrator/card_payout_test.go @@ -281,7 +281,7 @@ func TestSubmitCardPayout_UsesSettlementAmount(t *testing.T) { }, } - if err := svc.submitCardPayout(ctx, payment); err != nil { + if err := svc.submitCardPayout(ctx, "op-ref", payment); err != nil { t.Fatalf("submitCardPayout error: %v", err) } diff --git a/api/payments/orchestrator/internal/service/orchestrator/handlers_events.go b/api/payments/orchestrator/internal/service/orchestrator/handlers_events.go index 2cb83211..becd52a3 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/handlers_events.go +++ b/api/payments/orchestrator/internal/service/orchestrator/handlers_events.go @@ -20,12 +20,12 @@ type paymentEventHandler struct { repo storage.Repository ensureRepo func(ctx context.Context) error logger mlogger.Logger - submitCardPayout func(ctx context.Context, payment *model.Payment) error + submitCardPayout func(ctx context.Context, operationRef string, payment *model.Payment) error resumePlan func(ctx context.Context, store storage.PaymentsStore, payment *model.Payment) error releaseHold func(ctx context.Context, store storage.PaymentsStore, payment *model.Payment) error } -func newPaymentEventHandler(repo storage.Repository, ensure func(ctx context.Context) error, logger mlogger.Logger, submitCardPayout func(ctx context.Context, payment *model.Payment) error, resumePlan func(ctx context.Context, store storage.PaymentsStore, payment *model.Payment) error, releaseHold func(ctx context.Context, store storage.PaymentsStore, payment *model.Payment) error) *paymentEventHandler { +func newPaymentEventHandler(repo storage.Repository, ensure func(ctx context.Context) error, logger mlogger.Logger, submitCardPayout func(ctx context.Context, operationRef string, payment *model.Payment) error, resumePlan func(ctx context.Context, store storage.PaymentsStore, payment *model.Payment) error, releaseHold func(ctx context.Context, store storage.PaymentsStore, payment *model.Payment) error) *paymentEventHandler { return &paymentEventHandler{ repo: repo, ensureRepo: ensure, @@ -140,7 +140,7 @@ func (h *paymentEventHandler) processTransferUpdate(ctx context.Context, req *or payment.State = model.PaymentStateFundsReserved if h.submitCardPayout == nil { h.logger.Warn("card payout execution skipped", zap.String("payment_ref", payment.PaymentRef)) - } else if err := h.submitCardPayout(ctx, payment); err != nil { + } else if err := h.submitCardPayout(ctx, transfer.GetOperationRef(), payment); err != nil { payment.State = model.PaymentStateFailed payment.FailureCode = model.PaymentFailureCodePolicy payment.FailureReason = strings.TrimSpace(err.Error()) diff --git a/api/payments/orchestrator/internal/service/orchestrator/service_test.go b/api/payments/orchestrator/internal/service/orchestrator/service_test.go index 742f4f82..9903a93c 100644 --- a/api/payments/orchestrator/internal/service/orchestrator/service_test.go +++ b/api/payments/orchestrator/internal/service/orchestrator/service_test.go @@ -269,7 +269,7 @@ func TestProcessTransferUpdateHandler_CardFundingWaitsForSources(t *testing.T) { } payoutCalls := 0 - submit := func(ctx context.Context, payment *model.Payment) error { + submit := func(ctx context.Context, operationRef string, payment *model.Payment) error { payoutCalls++ if payment.Execution == nil { payment.Execution = &model.ExecutionRefs{} @@ -279,6 +279,7 @@ func TestProcessTransferUpdateHandler_CardFundingWaitsForSources(t *testing.T) { step := ensureExecutionStep(plan, stepCodeCardPayout) setExecutionStepRole(step, executionStepRoleConsumer) step.TransferRef = "payout-1" + step.OperationRef = operationRef setExecutionStepStatus(step, model.OperationStateWaiting) return nil }