package gateway import ( "context" "github.com/tech/sendico/gateway/chsettle/storage/model" "github.com/tech/sendico/pkg/merrors" paymentgateway "github.com/tech/sendico/pkg/messaging/notifications/paymentgateway" pmodel "github.com/tech/sendico/pkg/model" "github.com/tech/sendico/pkg/mservice" "github.com/tech/sendico/pkg/mutil/mzap" "github.com/tech/sendico/pkg/payments/rail" "go.uber.org/zap" ) func isFinalStatus(t *model.PaymentRecord) bool { switch t.Status { case model.PaymentStatusFailed, model.PaymentStatusSuccess, model.PaymentStatusCancelled: return true default: return false } } func toOpStatus(t *model.PaymentRecord) (rail.OperationResult, error) { switch t.Status { case model.PaymentStatusFailed: return rail.OperationResultFailed, nil case model.PaymentStatusSuccess: return rail.OperationResultSuccess, nil case model.PaymentStatusCancelled: return rail.OperationResultCancelled, nil default: return rail.OperationResultFailed, merrors.InvalidArgument("unexpected transfer status", "payment.status") } } func (s *Service) updateTransferStatus(ctx context.Context, record *model.PaymentRecord) error { if record == nil { return merrors.InvalidArgument("payment record is required", "record") } s.logger.Debug("Persisting transfer status", zap.String("idempotency_key", record.IdempotencyKey), zap.String("payment_ref", record.PaymentIntentID), zap.String("status", string(record.Status)), zap.Bool("is_final", isFinalStatus(record))) if !isFinalStatus(record) { if err := s.repo.Payments().Upsert(ctx, record); err != nil { s.logger.Warn("Failed to update transfer status", zap.String("payment_ref", record.PaymentIntentID), zap.String("status", string(record.Status)), zap.Error(err)) return err } s.logger.Debug("Transfer status persisted (non-final)", zap.String("idempotency_key", record.IdempotencyKey), zap.String("status", string(record.Status))) return nil } _, err := s.executeTransaction(ctx, func(txCtx context.Context) (any, error) { if upsertErr := s.repo.Payments().Upsert(txCtx, record); upsertErr != nil { return nil, upsertErr } if isFinalStatus(record) { if emitErr := s.emitTransferStatusEvent(txCtx, record); emitErr != nil { return nil, emitErr } } return struct{}{}, nil }) if err != nil { s.logger.Warn("Failed to update transfer status", zap.String("payment_ref", record.PaymentIntentID), zap.String("status", string(record.Status)), zap.Error(err)) return err } s.logger.Info("Transfer status persisted (final)", zap.String("idempotency_key", record.IdempotencyKey), zap.String("status", string(record.Status))) return nil } func (s *Service) emitTransferStatusEvent(ctx context.Context, record *model.PaymentRecord) error { if s == nil || record == nil { return nil } if s.producer == nil || s.outboxStore() == nil { return nil } status, err := toOpStatus(record) if err != nil { s.logger.Warn("Failed to map transfer status for transfer status event", zap.Error(err), mzap.ObjRef("transfer_ref", record.ID)) return err } exec := pmodel.PaymentGatewayExecution{ PaymentIntentID: record.PaymentIntentID, IdempotencyKey: record.IdempotencyKey, ExecutedMoney: record.ExecutedMoney, PaymentRef: record.PaymentRef, Status: status, OperationRef: record.OperationRef, Error: record.FailureReason, TransferRef: record.ID.Hex(), } env := paymentgateway.PaymentGatewayExecution(mservice.ChSettle, &exec) if sendErr := s.sendWithOutbox(ctx, env); sendErr != nil { s.logger.Warn("Failed to publish transfer status event", zap.Error(sendErr), mzap.ObjRef("transfer_ref", record.ID)) return sendErr } return nil }