From 2711d601b0388a6eb363d57bebb57d6f79b05500 Mon Sep 17 00:00:00 2001 From: Stephan D Date: Tue, 10 Feb 2026 14:39:30 +0100 Subject: [PATCH] fixed code duplication --- .../service/gateway/card_processor.go | 181 +++++++++--------- 1 file changed, 92 insertions(+), 89 deletions(-) diff --git a/api/gateway/mntx/internal/service/gateway/card_processor.go b/api/gateway/mntx/internal/service/gateway/card_processor.go index 4212c786..db8a9c0a 100644 --- a/api/gateway/mntx/internal/service/gateway/card_processor.go +++ b/api/gateway/mntx/internal/service/gateway/card_processor.go @@ -29,6 +29,77 @@ type cardPayoutProcessor struct { producer msg.Producer } +func mergePayoutStateWithExisting(state, existing *model.CardPayout) { + if state == nil || existing == nil { + return + } + + state.ID = existing.ID // preserve ID for upsert + if !existing.CreatedAt.IsZero() { + state.CreatedAt = existing.CreatedAt + } + if state.OperationRef == "" { + state.OperationRef = existing.OperationRef + } + if state.IdempotencyKey == "" { + state.IdempotencyKey = existing.IdempotencyKey + } + if state.IntentRef == "" { + state.IntentRef = existing.IntentRef + } +} + +func (p *cardPayoutProcessor) findAndMergePayoutState(ctx context.Context, state *model.CardPayout) (*model.CardPayout, error) { + if p == nil || state == nil { + return nil, nil + } + existing, err := p.store.Payouts().FindByPaymentID(ctx, state.PaymentRef) + if err != nil { + return nil, err + } + mergePayoutStateWithExisting(state, existing) + return existing, nil +} + +func (p *cardPayoutProcessor) resolveProjectID(requestProjectID int64, logFieldKey, logFieldValue string) (int64, error) { + projectID := requestProjectID + if projectID == 0 { + projectID = p.config.ProjectID + } + if projectID == 0 { + p.logger.Warn("Monetix project_id is not configured", zap.String(logFieldKey, logFieldValue)) + return 0, merrors.Internal("monetix project_id is not configured") + } + return projectID, nil +} + +func applyCardPayoutSendResult(state *model.CardPayout, result *monetix.CardPayoutSendResult) { + if state == nil || result == nil { + return + } + state.ProviderPaymentID = strings.TrimSpace(result.ProviderRequestID) + if result.Accepted { + state.Status = model.PayoutStatusWaiting + return + } + state.Status = model.PayoutStatusFailed + state.ProviderCode = strings.TrimSpace(result.ErrorCode) + state.ProviderMessage = strings.TrimSpace(result.ErrorMessage) +} + +func payoutStateLogFields(state *model.CardPayout) []zap.Field { + if state == nil { + return nil + } + return []zap.Field{ + zap.String("payment_ref", state.PaymentRef), + zap.String("customer_id", state.CustomerID), + zap.String("operation_ref", state.OperationRef), + zap.String("idempotency_key", state.IdempotencyKey), + zap.String("intent_ref", state.IntentRef), + } +} + func newCardPayoutProcessor( logger mlogger.Logger, cfg monetix.Config, @@ -77,13 +148,9 @@ func (p *cardPayoutProcessor) Submit(ctx context.Context, req *mntxv1.CardPayout return nil, err } - projectID := req.GetProjectId() - if projectID == 0 { - projectID = p.config.ProjectID - } - if projectID == 0 { - p.logger.Warn("Monetix project_id is not configured", zap.String("payout_id", req.GetPayoutId())) - return nil, merrors.Internal("monetix project_id is not configured") + projectID, err := p.resolveProjectID(req.GetProjectId(), "payout_id", req.GetPayoutId()) + if err != nil { + return nil, err } now := p.clock.Now() @@ -106,18 +173,7 @@ func (p *cardPayoutProcessor) Submit(ctx context.Context, req *mntxv1.CardPayout } // Keep CreatedAt/refs if record already exists. - if existing, err := p.store.Payouts().FindByPaymentID(ctx, state.PaymentRef); err == nil && existing != nil { - state.ID = existing.ID // preserve ID for upsert - if !existing.CreatedAt.IsZero() { - state.CreatedAt = existing.CreatedAt - } - if state.OperationRef == "" { - state.OperationRef = existing.OperationRef - } - if state.IdempotencyKey == "" { - state.IdempotencyKey = existing.IdempotencyKey - } - } + _, _ = p.findAndMergePayoutState(ctx, state) client := monetix.NewClient(p.config, p.httpClient, p.logger) apiReq := buildCardPayoutRequest(projectID, req) @@ -129,39 +185,18 @@ func (p *cardPayoutProcessor) Submit(ctx context.Context, req *mntxv1.CardPayout state.UpdatedAt = p.clock.Now() if e := p.updatePayoutStatus(ctx, state); e != nil { - p.logger.Warn("Failed to update payout status", - zap.Error(e), - zap.String("payment_ref", state.PaymentRef), - 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), - ) + fields := append([]zap.Field{zap.Error(e)}, payoutStateLogFields(state)...) + p.logger.Warn("Failed to update payout status", fields...) } - p.logger.Warn("Monetix payout submission failed", - zap.Error(err), - zap.String("payment_ref", state.PaymentRef), - 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), - ) + fields := append([]zap.Field{zap.Error(err)}, payoutStateLogFields(state)...) + p.logger.Warn("Monetix payout submission failed", fields...) return nil, err } // Provider request id is the provider-side payment id in your model. - state.ProviderPaymentID = strings.TrimSpace(result.ProviderRequestID) - if result.Accepted { - state.Status = model.PayoutStatusWaiting - } else { - state.Status = model.PayoutStatusFailed - state.ProviderCode = strings.TrimSpace(result.ErrorCode) - state.ProviderMessage = strings.TrimSpace(result.ErrorMessage) - } + applyCardPayoutSendResult(state, result) state.UpdatedAt = p.clock.Now() if err := p.updatePayoutStatus(ctx, state); err != nil { @@ -223,13 +258,9 @@ func (p *cardPayoutProcessor) SubmitToken(ctx context.Context, req *mntxv1.CardT return nil, err } - projectID := req.GetProjectId() - if projectID == 0 { - projectID = p.config.ProjectID - } - if projectID == 0 { - p.logger.Warn("Monetix project_id is not configured", zap.String("payout_id", req.GetPayoutId())) - return nil, merrors.Internal("monetix project_id is not configured") + projectID, err := p.resolveProjectID(req.GetProjectId(), "payout_id", req.GetPayoutId()) + if err != nil { + return nil, err } now := p.clock.Now() @@ -246,17 +277,7 @@ func (p *cardPayoutProcessor) SubmitToken(ctx context.Context, req *mntxv1.CardT UpdatedAt: now, } - if existing, err := p.store.Payouts().FindByPaymentID(ctx, state.PaymentRef); err == nil && existing != nil { - if !existing.CreatedAt.IsZero() { - state.CreatedAt = existing.CreatedAt - } - if state.OperationRef == "" { - state.OperationRef = existing.OperationRef - } - if state.IdempotencyKey == "" { - state.IdempotencyKey = existing.IdempotencyKey - } - } + _, _ = p.findAndMergePayoutState(ctx, state) client := monetix.NewClient(p.config, p.httpClient, p.logger) apiReq := buildCardTokenPayoutRequest(projectID, req) @@ -278,14 +299,7 @@ func (p *cardPayoutProcessor) SubmitToken(ctx context.Context, req *mntxv1.CardT return nil, err } - state.ProviderPaymentID = strings.TrimSpace(result.ProviderRequestID) - if result.Accepted { - state.Status = model.PayoutStatusWaiting - } else { - state.Status = model.PayoutStatusFailed - state.ProviderCode = strings.TrimSpace(result.ErrorCode) - state.ProviderMessage = strings.TrimSpace(result.ErrorMessage) - } + applyCardPayoutSendResult(state, result) state.UpdatedAt = p.clock.Now() if err := p.updatePayoutStatus(ctx, state); err != nil { @@ -331,13 +345,9 @@ func (p *cardPayoutProcessor) Tokenize(ctx context.Context, req *mntxv1.CardToke return nil, err } - projectID := req.GetProjectId() - if projectID == 0 { - projectID = p.config.ProjectID - } - if projectID == 0 { - p.logger.Warn("Monetix project_id is not configured", zap.String("request_id", req.GetRequestId())) - return nil, merrors.Internal("monetix project_id is not configured") + projectID, err := p.resolveProjectID(req.GetProjectId(), "request_id", req.GetRequestId()) + if err != nil { + return nil, err } req = sanitizeCardTokenizeRequest(req) @@ -450,22 +460,15 @@ func (p *cardPayoutProcessor) ProcessCallback(ctx context.Context, payload []byt state := CardPayoutStateFromProto(p.clock, pbState) // Preserve CreatedAt + internal keys from existing record if present. - if existing, err := p.store.Payouts().FindByPaymentID(ctx, state.PaymentRef); err != nil { + existing, err := p.findAndMergePayoutState(ctx, state) + if err != nil { p.logger.Warn("Failed to fetch payout state while processing callback", zap.Error(err), zap.String("payment_ref", state.PaymentRef), ) return http.StatusInternalServerError, err - } else if existing != nil { - if !existing.CreatedAt.IsZero() { - state.CreatedAt = existing.CreatedAt - } - if state.OperationRef == "" { - state.OperationRef = existing.OperationRef - } - if state.IdempotencyKey == "" { - state.IdempotencyKey = existing.IdempotencyKey - } + } + if existing != nil { // keep failure reason if you want, or override depending on callback semantics if state.FailureReason == "" { state.FailureReason = existing.FailureReason