Repository: pagopa/p4pa-payhub-activities
Pull request #230: feat: P4ADEV-2592 payment notification activity
Author: login: domenicogi
Labels: name: released
Created: 2025-04-09T09:27:08Z | Merged: 2025-04-09T13:38:05Z

#### Description
<!--- Please always add a PR description as if nobody knows anything about the context these changes come from. -->
<!--- Even if we are all from our internal team, we may not be on the same page. -->
<!--- Write this PR as you were contributing to a public OSS project, where nobody knows you and you have to earn their trust. -->
<!--- This will improve our projects in the long run! Thanks. -->

#### List of Changes
<!--- Describe your changes in detail -->

#### Motivation and Context
<!--- Why is this change required? What problem does it solve? -->

#### How Has This Been Tested?
<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, tests ran to see how -->
<!--- your change affects other areas of the code, etc. -->
- Pre-Deploy Test
    - [X] Unit
    - [ ] Integration (Narrow)
- Post-Deploy Test
    - [ ] Isolated Microservice
    - [ ] Broader Integration
    - [ ] Acceptance
    - [ ] Performance & Load

#### Types of changes
<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

- [ ] PATCH - Bug fix (backwards compatible bug fixes)
- [X] MINOR - New feature (add functionality in a backwards compatible manner)
- [ ] MAJOR - Breaking change (fix or feature that would cause existing functionality to not work as expected)

#### Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->

- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.



Diff:
diff --git a/src/main/java/it/gov/pagopa/payhub/activities/activity/ingestionflow/paymentnotification/PaymentNotificationIngestionActivity.java b/src/main/java/it/gov/pagopa/payhub/activities/activity/ingestionflow/paymentnotification/PaymentNotificationIngestionActivity.java
index 95dac80bb..244df3a55 100644
--- a/src/main/java/it/gov/pagopa/payhub/activities/activity/ingestionflow/paymentnotification/PaymentNotificationIngestionActivity.java
+++ b/src/main/java/it/gov/pagopa/payhub/activities/activity/ingestionflow/paymentnotification/PaymentNotificationIngestionActivity.java
@@ -3,6 +3,7 @@
 import io.temporal.activity.ActivityInterface;
 import io.temporal.activity.ActivityMethod;
 import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileActivityResult;
+
 /**
  * Interface for the PaymentNotificationIngestionActivity.
  * Defines methods for processing payment notification files based on an IngestionFlowFile ID.
diff --git a/src/main/java/it/gov/pagopa/payhub/activities/activity/ingestionflow/paymentnotification/PaymentNotificationIngestionActivityImpl.java b/src/main/java/it/gov/pagopa/payhub/activities/activity/ingestionflow/paymentnotification/PaymentNotificationIngestionActivityImpl.java
index a6a8046b2..7768721ac 100644
--- a/src/main/java/it/gov/pagopa/payhub/activities/activity/ingestionflow/paymentnotification/PaymentNotificationIngestionActivityImpl.java
+++ b/src/main/java/it/gov/pagopa/payhub/activities/activity/ingestionflow/paymentnotification/PaymentNotificationIngestionActivityImpl.java
@@ -4,11 +4,13 @@
 import it.gov.pagopa.payhub.activities.connector.processexecutions.IngestionFlowFileService;
 import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileActivityResult;
 import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileDTO;
+import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileResult;
 import it.gov.pagopa.payhub.activities.exception.ingestionflow.InvalidIngestionFileException;
 import it.gov.pagopa.payhub.activities.service.files.CsvService;
 import it.gov.pagopa.payhub.activities.service.files.FileArchiverService;
 import it.gov.pagopa.payhub.activities.service.ingestionflow.IngestionFlowFileRetrieverService;
 import it.gov.pagopa.payhub.activities.service.ingestionflow.paymentnotification.PaymentNotificationProcessingService;
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
 import it.gov.pagopa.pu.processexecutions.dto.generated.IngestionFlowFile;
 import it.gov.pagopa.pu.processexecutions.dto.generated.IngestionFlowFile.IngestionFlowFileTypeEnum;
 import java.nio.file.Path;
@@ -20,7 +22,7 @@
 @Slf4j
 @Lazy
 @Component
-public class PaymentNotificationIngestionActivityImpl extends BaseIngestionFlowFileActivity<PaymentNotificationIngestionFlowFileActivityResult> implements PaymentNotificationIngestionActivity{
+public class PaymentNotificationIngestionActivityImpl extends BaseIngestionFlowFileActivity<PaymentNotificationIngestionFlowFileActivityResult> implements PaymentNotificationIngestionActivity {
 
   private final CsvService csvService;
   private final PaymentNotificationProcessingService paymentNotificationProcessingService;
@@ -43,20 +45,30 @@ protected IngestionFlowFileTypeEnum getHandledIngestionFlowFileType() {
   }
 
   @Override
-  protected PaymentNotificationIngestionFlowFileActivityResult handleRetrievedFiles(List<Path> retrievedFiles, IngestionFlowFile ingestionFlowFileDTO) {
+  protected PaymentNotificationIngestionFlowFileActivityResult handleRetrievedFiles(
+      List<Path> retrievedFiles, IngestionFlowFile ingestionFlowFileDTO) {
     Path filePath = retrievedFiles.getFirst();
     Path workingDirectory = filePath.getParent();
     log.info("Processing file: {}", filePath);
 
     try {
-      return csvService.readCsv(filePath, PaymentNotificationIngestionFlowFileDTO.class, csvIterator ->
-          paymentNotificationProcessingService.processPaymentNotification(csvIterator, ingestionFlowFileDTO, workingDirectory));
+      PaymentNotificationIngestionFlowFileResult result = csvService.readCsv(filePath,
+          PaymentNotificationIngestionFlowFileDTO.class, csvIterator ->
+              paymentNotificationProcessingService.processPaymentNotification(csvIterator,
+                  ingestionFlowFileDTO, workingDirectory));
+
+      List<String> iudList = result.getPaymentNotificationList().stream().map(
+          PaymentNotificationDTO::getIud).toList();
+
+      return new PaymentNotificationIngestionFlowFileActivityResult(
+          iudList,
+          result.getPaymentNotificationList().getFirst().getOrganizationId()
+      );
     } catch (Exception e) {
       log.error("Error processing file {}: {}", filePath, e.getMessage());
       throw new InvalidIngestionFileException(String.format("Error processing file %s: %s", filePath, e.getMessage()));
     }
 
-
   }
 
 
diff --git a/src/main/java/it/gov/pagopa/payhub/activities/connector/classification/PaymentNotificationService.java b/src/main/java/it/gov/pagopa/payhub/activities/connector/classification/PaymentNotificationService.java
new file mode 100644
index 000000000..4ba7e207d
--- /dev/null
+++ b/src/main/java/it/gov/pagopa/payhub/activities/connector/classification/PaymentNotificationService.java
@@ -0,0 +1,18 @@
+package it.gov.pagopa.payhub.activities.connector.classification;
+
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
+
+
+/**
+ * Interface for managing payment notifications.
+ * Provides methods to create payment notifications.
+ */
+public interface PaymentNotificationService {
+    /**
+     * Creates a new payment notification.
+     *
+     * @param dto the {@link PaymentNotificationDTO} object containing the details of the payment notification
+     * @return the created {@link PaymentNotificationDTO} object
+     */
+    PaymentNotificationDTO createPaymentNotification(PaymentNotificationDTO dto);
+}
diff --git a/src/main/java/it/gov/pagopa/payhub/activities/connector/classification/PaymentNotificationServiceImpl.java b/src/main/java/it/gov/pagopa/payhub/activities/connector/classification/PaymentNotificationServiceImpl.java
new file mode 100644
index 000000000..ee803063f
--- /dev/null
+++ b/src/main/java/it/gov/pagopa/payhub/activities/connector/classification/PaymentNotificationServiceImpl.java
@@ -0,0 +1,23 @@
+package it.gov.pagopa.payhub.activities.connector.classification;
+
+import it.gov.pagopa.payhub.activities.connector.auth.AuthnService;
+import it.gov.pagopa.payhub.activities.connector.classification.client.PaymentNotificationClient;
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
+
+public class PaymentNotificationServiceImpl implements PaymentNotificationService{
+
+  private final PaymentNotificationClient paymentNotificationClient;
+  private final AuthnService authnService;
+
+  public PaymentNotificationServiceImpl(PaymentNotificationClient paymentNotificationClient,
+      AuthnService authnService) {
+    this.paymentNotificationClient = paymentNotificationClient;
+    this.authnService = authnService;
+  }
+
+
+  @Override
+  public PaymentNotificationDTO createPaymentNotification(PaymentNotificationDTO dto) {
+    return paymentNotificationClient.createPaymentNotification(dto, authnService.getAccessToken());
+  }
+}
diff --git a/src/main/java/it/gov/pagopa/payhub/activities/connector/classification/client/PaymentNotificationClient.java b/src/main/java/it/gov/pagopa/payhub/activities/connector/classification/client/PaymentNotificationClient.java
new file mode 100644
index 000000000..2df90cab9
--- /dev/null
+++ b/src/main/java/it/gov/pagopa/payhub/activities/connector/classification/client/PaymentNotificationClient.java
@@ -0,0 +1,29 @@
+package it.gov.pagopa.payhub.activities.connector.classification.client;
+
+import it.gov.pagopa.payhub.activities.connector.classification.config.ClassificationApisHolder;
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+
+@Lazy
+@Service
+@Slf4j
+public class PaymentNotificationClient {
+
+    private final ClassificationApisHolder classificationApisHolder;
+
+    public PaymentNotificationClient(ClassificationApisHolder classificationApisHolder) {
+        this.classificationApisHolder = classificationApisHolder;
+    }
+
+    public PaymentNotificationDTO createPaymentNotification(PaymentNotificationDTO dtos, String accessToken) {
+        return classificationApisHolder.getPaymentNotificationApi(accessToken)
+                .createPaymentNotification(dtos);
+    }
+
+
+
+
+
+}
diff --git a/src/main/java/it/gov/pagopa/payhub/activities/connector/classification/config/ClassificationApisHolder.java b/src/main/java/it/gov/pagopa/payhub/activities/connector/classification/config/ClassificationApisHolder.java
index e460651ef..382fe4411 100644
--- a/src/main/java/it/gov/pagopa/payhub/activities/connector/classification/config/ClassificationApisHolder.java
+++ b/src/main/java/it/gov/pagopa/payhub/activities/connector/classification/config/ClassificationApisHolder.java
@@ -27,6 +27,8 @@ public class ClassificationApisHolder {
 
     private final AssessmentsControllerApi assessmentsControllerApi;
 
+    private final PaymentNotificationApi paymentNotificationApi;
+
     private final ThreadLocal<String> bearerTokenHolder = new ThreadLocal<>();
 
     public ClassificationApisHolder(
@@ -55,6 +57,8 @@ public ClassificationApisHolder(
         this.treasuryEntityExtendedControllerApi = new TreasuryEntityExtendedControllerApi(apiClient);
 
         this.assessmentsControllerApi = new AssessmentsControllerApi(apiClient);
+
+        this.paymentNotificationApi = new PaymentNotificationApi(apiClient);
     }
 
     @PreDestroy
@@ -95,6 +99,10 @@ public AssessmentsControllerApi getAssessmentsControllerApi(String accessToken){
         return getApi(accessToken, assessmentsControllerApi);
     }
 
+    public PaymentNotificationApi getPaymentNotificationApi(String accessToken){
+        return getApi(accessToken, paymentNotificationApi);
+    }
+
     private <T extends BaseApi> T getApi(String accessToken, T api) {
         bearerTokenHolder.set(accessToken);
         return api;
diff --git a/src/main/java/it/gov/pagopa/payhub/activities/dto/paymentnotification/PaymentNotificationErrorDTO.java b/src/main/java/it/gov/pagopa/payhub/activities/dto/paymentnotification/PaymentNotificationErrorDTO.java
new file mode 100644
index 000000000..abb4afd6a
--- /dev/null
+++ b/src/main/java/it/gov/pagopa/payhub/activities/dto/paymentnotification/PaymentNotificationErrorDTO.java
@@ -0,0 +1,38 @@
+package it.gov.pagopa.payhub.activities.dto.paymentnotification;
+
+import it.gov.pagopa.payhub.activities.dto.ErrorFileDTO;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+import lombok.experimental.SuperBuilder;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@SuperBuilder
+@AllArgsConstructor
+@NoArgsConstructor
+public class PaymentNotificationErrorDTO extends ErrorFileDTO {
+
+    private String iuv;
+    private String iud;
+    private String workflowStatus;
+    private Long rowNumber;
+
+    public PaymentNotificationErrorDTO(String fileName, String iuv, String iud, String workflowStatus, Long rowNumber, String errorCode, String errorMessage) {
+        super(fileName, errorCode, errorMessage);
+        this.iuv = iuv;
+        this.iud = iud;
+        this.workflowStatus = workflowStatus;
+        this.rowNumber = rowNumber;
+    }
+
+    @Override
+    public String[] toCsvRow() {
+        return new String[]{
+                getFileName(), iuv, iud, workflowStatus,
+                rowNumber.toString(),
+                getErrorCode(), getErrorMessage()
+        };
+    }
+}
diff --git a/src/main/java/it/gov/pagopa/payhub/activities/dto/paymentnotification/PaymentNotificationIngestionFlowFileResult.java b/src/main/java/it/gov/pagopa/payhub/activities/dto/paymentnotification/PaymentNotificationIngestionFlowFileResult.java
new file mode 100644
index 000000000..20c8056e9
--- /dev/null
+++ b/src/main/java/it/gov/pagopa/payhub/activities/dto/paymentnotification/PaymentNotificationIngestionFlowFileResult.java
@@ -0,0 +1,26 @@
+package it.gov.pagopa.payhub.activities.dto.paymentnotification;
+
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
+import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+public class PaymentNotificationIngestionFlowFileResult {
+
+    private Long totalRows;
+
+    private Long processedRows;
+
+    private String errorDescription;
+
+    private String discardedFileName;
+
+    private List<PaymentNotificationDTO> paymentNotificationList;
+}
diff --git a/src/main/java/it/gov/pagopa/payhub/activities/mapper/ingestionflow/paymentnotification/PaymentNotificationMapper.java b/src/main/java/it/gov/pagopa/payhub/activities/mapper/ingestionflow/paymentnotification/PaymentNotificationMapper.java
new file mode 100644
index 000000000..99d8161b2
--- /dev/null
+++ b/src/main/java/it/gov/pagopa/payhub/activities/mapper/ingestionflow/paymentnotification/PaymentNotificationMapper.java
@@ -0,0 +1,50 @@
+package it.gov.pagopa.payhub.activities.mapper.ingestionflow.paymentnotification;
+
+import io.nexusrpc.Service;
+import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileDTO;
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
+import it.gov.pagopa.pu.classification.dto.generated.Person;
+import it.gov.pagopa.pu.classification.dto.generated.PersonEntityType;
+import it.gov.pagopa.pu.processexecutions.dto.generated.IngestionFlowFile;
+
+@Service
+public class PaymentNotificationMapper {
+
+  public PaymentNotificationDTO map(PaymentNotificationIngestionFlowFileDTO dto, IngestionFlowFile ingestionFlowFile) {
+
+    return PaymentNotificationDTO.builder()
+        .organizationId(ingestionFlowFile.getOrganizationId())
+        .ingestionFlowFileId(ingestionFlowFile.getIngestionFlowFileId())
+        .iud(dto.getIud())
+        .iuv(dto.getIuv())
+        .paymentExecutionDate(dto.getPaymentExecutionDate())
+        .paymentType(dto.getPaymentType())
+        .amountPaidCents(dto.getAmountPaidCents().longValue())
+        .paCommission(dto.getPaCommissionCents().longValue())
+        .remittanceInformation(dto.getRemittanceInformation())
+        .transferCategory(dto.getTransferCategory())
+        .debtPositionTypeOrgCode(dto.getDebtPositionTypeOrgCode())
+        .balance(dto.getBalance())
+        .debtor(mapPersonalDataFromPaymentNotification(dto))
+        .build();
+  }
+
+
+  public static Person mapPersonalDataFromPaymentNotification(PaymentNotificationIngestionFlowFileDTO dto) {
+    return Person.builder()
+        .entityType(PersonEntityType.valueOf((dto.getDebtorUniqueIdentifierType())))
+        .fiscalCode(dto.getDebtorUniqueIdentifierCode())
+        .fullName(dto.getDebtorFullName())
+        .address(dto.getDebtorAddress())
+        .civic(dto.getDebtorCivic())
+        .postalCode(dto.getDebtorPostalCode())
+        .location(dto.getDebtorLocation())
+        .province(dto.getDebtorProvince())
+        .nation(dto.getDebtorNation())
+        .email(dto.getDebtorEmail())
+        .build();
+  }
+
+}
+
+
diff --git a/src/main/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationErrorsArchiverService.java b/src/main/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationErrorsArchiverService.java
new file mode 100644
index 000000000..24c79f199
--- /dev/null
+++ b/src/main/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationErrorsArchiverService.java
@@ -0,0 +1,30 @@
+package it.gov.pagopa.payhub.activities.service.ingestionflow.paymentnotification;
+
+import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationErrorDTO;
+import it.gov.pagopa.payhub.activities.service.files.CsvService;
+import it.gov.pagopa.payhub.activities.service.files.ErrorArchiverService;
+import it.gov.pagopa.payhub.activities.service.files.FileArchiverService;
+import java.util.Collections;
+import java.util.List;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Lazy;
+import org.springframework.stereotype.Service;
+
+@Lazy
+@Service
+public class PaymentNotificationErrorsArchiverService extends
+    ErrorArchiverService<PaymentNotificationErrorDTO> {
+
+    protected PaymentNotificationErrorsArchiverService(@Value("${folders.shared}") String sharedFolder,
+                                               @Value("${folders.process-target-sub-folders.errors}") String errorFolder,
+                                               FileArchiverService fileArchiverService,
+                                               CsvService csvService) {
+        super(sharedFolder, errorFolder, fileArchiverService, csvService);
+    }
+
+    @Override
+    protected List<String[]> getHeaders() {
+        return Collections.singletonList(
+                new String[]{"File Name", "IUV", "IUD", "Workflow Status", "Row Number", "Error Code", "Error Message"});
+    }
+}
diff --git a/src/main/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationProcessingService.java b/src/main/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationProcessingService.java
index d307b8fc1..c62248757 100644
--- a/src/main/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationProcessingService.java
+++ b/src/main/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationProcessingService.java
@@ -1,11 +1,16 @@
 package it.gov.pagopa.payhub.activities.service.ingestionflow.paymentnotification;
 
-import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileActivityResult;
+import it.gov.pagopa.payhub.activities.connector.classification.PaymentNotificationService;
+import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationErrorDTO;
 import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileDTO;
+import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileResult;
+import it.gov.pagopa.payhub.activities.mapper.ingestionflow.paymentnotification.PaymentNotificationMapper;
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
 import it.gov.pagopa.pu.processexecutions.dto.generated.IngestionFlowFile;
 import java.nio.file.Path;
-import java.util.Collections;
+import java.util.ArrayList;
 import java.util.Iterator;
+import java.util.List;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
@@ -15,12 +20,74 @@
 @Slf4j
 public class PaymentNotificationProcessingService {
 
-  public PaymentNotificationIngestionFlowFileActivityResult processPaymentNotification(
+  private final PaymentNotificationMapper paymentNotificationMapper;
+  private final PaymentNotificationErrorsArchiverService paymentNotificationErrorsArchiverService;
+  private final PaymentNotificationService paymentNotificationService;
+
+  public PaymentNotificationProcessingService(
+      PaymentNotificationMapper paymentNotificationMapper,
+      PaymentNotificationErrorsArchiverService paymentNotificationErrorsArchiverService,
+      PaymentNotificationService paymentNotificationService) {
+    this.paymentNotificationMapper = paymentNotificationMapper;
+    this.paymentNotificationErrorsArchiverService = paymentNotificationErrorsArchiverService;
+    this.paymentNotificationService = paymentNotificationService;
+  }
+
+
+  public PaymentNotificationIngestionFlowFileResult processPaymentNotification(
       Iterator<PaymentNotificationIngestionFlowFileDTO> iterator,
       IngestionFlowFile ingestionFlowFile, Path workingDirectory) {
-      return new PaymentNotificationIngestionFlowFileActivityResult(Collections.emptyList(),0L);
+    List<PaymentNotificationErrorDTO> errorList = new ArrayList<>();
+    List<PaymentNotificationDTO> paymentNotificationList = new ArrayList<>();
+    long processedRows = 0;
+    long totalRows = 0;
+
+    while (iterator.hasNext()) {
+      totalRows++;
+
+      PaymentNotificationIngestionFlowFileDTO paymentNotificationDTO = iterator.next();
+
+      try {
+
+        PaymentNotificationDTO paymentNotificationCreated = paymentNotificationService.createPaymentNotification(
+            paymentNotificationMapper.map(paymentNotificationDTO, ingestionFlowFile));
+
+        paymentNotificationList.add(paymentNotificationCreated);
+
+        processedRows++;
+
+      } catch (Exception e) {
+        log.error("Error processing payment notice with iud {} and iuv {}: {}",
+            paymentNotificationDTO.getIud(), paymentNotificationDTO.getIuv(),
+            e.getMessage());
+        PaymentNotificationErrorDTO error = new PaymentNotificationErrorDTO(
+            ingestionFlowFile.getFileName(), paymentNotificationDTO.getIuv(),
+            paymentNotificationDTO.getIud(), null, totalRows, "PROCESS_EXCEPTION", e.getMessage());
+        errorList.add(error);
+        log.info("Current error list size after handleProcessingError: {}", errorList.size());
+      }
+    }
+
+    String errorsZipFileName = archiveErrorFiles(ingestionFlowFile, workingDirectory, errorList);
+    return new PaymentNotificationIngestionFlowFileResult(totalRows, processedRows,
+        errorsZipFileName != null ? "Some rows have failed" : null, errorsZipFileName,
+        paymentNotificationList);
   }
 
+  private String archiveErrorFiles(IngestionFlowFile ingestionFlowFile, Path workingDirectory,
+      List<PaymentNotificationErrorDTO> errorList) {
+    if (errorList.isEmpty()) {
+      log.info("No errors to archive for file: {}", ingestionFlowFile.getFileName());
+      return null;
+    }
+
+    paymentNotificationErrorsArchiverService.writeErrors(workingDirectory, ingestionFlowFile,
+        errorList);
+    String errorsZipFileName = paymentNotificationErrorsArchiverService.archiveErrorFiles(
+        workingDirectory, ingestionFlowFile);
+    log.info("Error file archived at: {}", errorsZipFileName);
 
+    return errorsZipFileName;
+  }
 }
 
diff --git a/src/test/java/it/gov/pagopa/payhub/activities/activity/ingestionflow/paymentnotification/PaymentNotificationIngestionActivityImplTest.java b/src/test/java/it/gov/pagopa/payhub/activities/activity/ingestionflow/paymentnotification/PaymentNotificationIngestionActivityImplTest.java
index afa847fc8..adf33139d 100644
--- a/src/test/java/it/gov/pagopa/payhub/activities/activity/ingestionflow/paymentnotification/PaymentNotificationIngestionActivityImplTest.java
+++ b/src/test/java/it/gov/pagopa/payhub/activities/activity/ingestionflow/paymentnotification/PaymentNotificationIngestionActivityImplTest.java
@@ -10,11 +10,13 @@
 import it.gov.pagopa.payhub.activities.connector.processexecutions.IngestionFlowFileService;
 import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileActivityResult;
 import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileDTO;
+import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileResult;
 import it.gov.pagopa.payhub.activities.exception.ingestionflow.InvalidIngestionFileException;
 import it.gov.pagopa.payhub.activities.service.files.CsvService;
 import it.gov.pagopa.payhub.activities.service.files.FileArchiverService;
 import it.gov.pagopa.payhub.activities.service.ingestionflow.IngestionFlowFileRetrieverService;
 import it.gov.pagopa.payhub.activities.service.ingestionflow.paymentnotification.PaymentNotificationProcessingService;
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
 import it.gov.pagopa.pu.processexecutions.dto.generated.IngestionFlowFile;
 import it.gov.pagopa.pu.processexecutions.dto.generated.IngestionFlowFile.IngestionFlowFileTypeEnum;
 import java.io.IOException;
@@ -147,10 +149,15 @@ void givenValidIngestionFlowWhenExceptionThenThrowInvalidIngestionFileException(
 
 
 
-  private PaymentNotificationIngestionFlowFileActivityResult buildPaymentNotificationIngestionFlowFileResult() {
-    return PaymentNotificationIngestionFlowFileActivityResult.builder()
-        .iudList(List.of("iud1", "iud2"))
-        .organizationId(1L)
+  private PaymentNotificationIngestionFlowFileResult buildPaymentNotificationIngestionFlowFileResult() {
+    return PaymentNotificationIngestionFlowFileResult.builder()
+        .processedRows(20L)
+        .totalRows(30L)
+        .discardedFileName("dicardedFileName")
+        .errorDescription("errorDescription")
+        .paymentNotificationList(List.of(
+            buildPaymentNotificationDTO("1"),
+            buildPaymentNotificationDTO("2")))
         .build();
   }
 
@@ -191,5 +198,13 @@ private Iterator<PaymentNotificationIngestionFlowFileDTO> buildPaymentNotificati
 
 
 
+  public static PaymentNotificationDTO buildPaymentNotificationDTO(String suffix) {
+    return PaymentNotificationDTO.builder()
+        .iud("iud" + suffix)
+        .iuv("iuv" + suffix)
+        .organizationId(Long.valueOf(suffix))
+        .build();
+  }
+
 
-}
\ No newline at end of file
+}
diff --git a/src/test/java/it/gov/pagopa/payhub/activities/connector/classification/PaymentNotificationServiceTest.java b/src/test/java/it/gov/pagopa/payhub/activities/connector/classification/PaymentNotificationServiceTest.java
new file mode 100644
index 000000000..d7c3c211b
--- /dev/null
+++ b/src/test/java/it/gov/pagopa/payhub/activities/connector/classification/PaymentNotificationServiceTest.java
@@ -0,0 +1,60 @@
+package it.gov.pagopa.payhub.activities.connector.classification;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import it.gov.pagopa.payhub.activities.connector.auth.AuthnService;
+import it.gov.pagopa.payhub.activities.connector.classification.client.PaymentNotificationClient;
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class PaymentNotificationServiceTest {
+
+    @Mock
+    private PaymentNotificationClient paymentNotificationClientMock;
+    @Mock
+    private AuthnService authnServiceMock;
+    private PaymentNotificationServiceImpl paymentNotificationService;
+
+    @BeforeEach
+    void setUp() {
+        paymentNotificationService = new PaymentNotificationServiceImpl(
+            paymentNotificationClientMock, authnServiceMock);
+    }
+
+    @AfterEach
+    void verifyNoMoreInteractions() {
+        Mockito.verifyNoMoreInteractions(
+            paymentNotificationClientMock,
+                authnServiceMock);
+    }
+
+    @Test
+    void testCreatePaymentNotification() {
+        // Given
+        PaymentNotificationDTO dto = new PaymentNotificationDTO();
+        String accessToken = "accessToken";
+        PaymentNotificationDTO expectedResponse = new PaymentNotificationDTO();
+
+        when(paymentNotificationClientMock.createPaymentNotification(dto, accessToken)).thenReturn(expectedResponse);
+        Mockito.when(authnServiceMock.getAccessToken())
+                .thenReturn(accessToken);
+
+        // When
+        PaymentNotificationDTO result = paymentNotificationService.createPaymentNotification(dto);
+
+        // Then
+        assertEquals(expectedResponse, result);
+        verify(paymentNotificationClientMock, times(1)).createPaymentNotification(dto, accessToken);
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/it/gov/pagopa/payhub/activities/connector/classification/client/PaymentNotificationClientTest.java b/src/test/java/it/gov/pagopa/payhub/activities/connector/classification/client/PaymentNotificationClientTest.java
new file mode 100644
index 000000000..6fcedb196
--- /dev/null
+++ b/src/test/java/it/gov/pagopa/payhub/activities/connector/classification/client/PaymentNotificationClientTest.java
@@ -0,0 +1,56 @@
+package it.gov.pagopa.payhub.activities.connector.classification.client;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import it.gov.pagopa.payhub.activities.connector.classification.config.ClassificationApisHolder;
+import it.gov.pagopa.pu.classification.client.generated.PaymentNotificationApi;
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class PaymentNotificationClientTest {
+
+    @Mock
+    private ClassificationApisHolder classificationApisHolderMock;
+
+    @Mock
+    private PaymentNotificationApi paymentNotificationApiMock;
+
+    private PaymentNotificationClient paymentNotificationClient;
+
+    @BeforeEach
+    void setUp() {
+        paymentNotificationClient = new PaymentNotificationClient(classificationApisHolderMock);
+    }
+
+    @Test
+    void testCreatePaymentNotification() {
+        // Given
+        PaymentNotificationDTO dto = new PaymentNotificationDTO();
+        String accessToken = "accessToken";
+        PaymentNotificationDTO expectedResponse = new PaymentNotificationDTO();
+
+        when(classificationApisHolderMock.getPaymentNotificationApi(accessToken))
+                .thenReturn(paymentNotificationApiMock);
+        when(paymentNotificationApiMock.createPaymentNotification(dto)).thenReturn(expectedResponse);
+
+        when(classificationApisHolderMock.getPaymentNotificationApi(accessToken))
+                .thenReturn(paymentNotificationApiMock);
+        when(paymentNotificationApiMock.createPaymentNotification(dto)).thenReturn(expectedResponse);
+
+        // When
+        PaymentNotificationDTO result = paymentNotificationClient.createPaymentNotification(dto, accessToken);
+
+        // Then
+        assertEquals(expectedResponse, result);
+        verify(paymentNotificationApiMock, times(1)).createPaymentNotification(dto);
+    }
+
+}
\ No newline at end of file
diff --git a/src/test/java/it/gov/pagopa/payhub/activities/connector/classification/config/ClassificationApisHolderTest.java b/src/test/java/it/gov/pagopa/payhub/activities/connector/classification/config/ClassificationApisHolderTest.java
index 32829a319..3a6c930c4 100644
--- a/src/test/java/it/gov/pagopa/payhub/activities/connector/classification/config/ClassificationApisHolderTest.java
+++ b/src/test/java/it/gov/pagopa/payhub/activities/connector/classification/config/ClassificationApisHolderTest.java
@@ -127,4 +127,15 @@ void whenGetAssessmentsControllerApiThenAuthenticationShouldBeSetInThreadSafeMod
                 classificationApisHolder::unload);
     }
 //endregion
+
+    //region Assessments entity
+    @Test
+    void whenGetPaymentNotificationApiThenAuthenticationShouldBeSetInThreadSafeMode() throws InterruptedException {
+        assertAuthenticationShouldBeSetInThreadSafeMode(
+            accessToken -> classificationApisHolder.getPaymentNotificationApi(accessToken)
+                .createPaymentNotification(new PaymentNotificationDTO()),
+            new ParameterizedTypeReference<>() {},
+            classificationApisHolder::unload);
+    }
+    //endregion
 }
\ No newline at end of file
diff --git a/src/test/java/it/gov/pagopa/payhub/activities/mapper/ingestionflow/paymentnotification/PaymentNotificationMapperTest.java b/src/test/java/it/gov/pagopa/payhub/activities/mapper/ingestionflow/paymentnotification/PaymentNotificationMapperTest.java
new file mode 100644
index 000000000..824bf26a0
--- /dev/null
+++ b/src/test/java/it/gov/pagopa/payhub/activities/mapper/ingestionflow/paymentnotification/PaymentNotificationMapperTest.java
@@ -0,0 +1,101 @@
+package it.gov.pagopa.payhub.activities.mapper.ingestionflow.paymentnotification;
+
+import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileDTO;
+import it.gov.pagopa.payhub.activities.util.TestUtils;
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
+import it.gov.pagopa.pu.classification.dto.generated.Person;
+import it.gov.pagopa.pu.classification.dto.generated.PersonEntityType;
+import it.gov.pagopa.pu.processexecutions.dto.generated.IngestionFlowFile;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class PaymentNotificationMapperTest {
+
+  @Test
+  void mapReturnPaymentNotificationDTOWithCorrectValues() {
+    PaymentNotificationIngestionFlowFileDTO dto = new PaymentNotificationIngestionFlowFileDTO();
+    dto.setIud("testIud");
+    dto.setIuv("testIuv");
+    dto.setPaymentExecutionDate(LocalDate.now());
+    dto.setPaymentType("testType");
+    dto.setAmountPaidCents(BigDecimal.valueOf(1000));
+    dto.setPaCommissionCents(BigDecimal.valueOf(100));
+    dto.setRemittanceInformation("testInfo");
+    dto.setTransferCategory("testCategory");
+    dto.setDebtPositionTypeOrgCode("testCode");
+    dto.setBalance("balance");
+    dto.setDebtorUniqueIdentifierType("F");
+    dto.setDebtorUniqueIdentifierCode("testCode");
+    dto.setDebtorFullName("testName");
+    dto.setDebtorAddress("testAddress");
+    dto.setDebtorCivic("testCivic");
+    dto.setDebtorPostalCode("12345");
+    dto.setDebtorLocation("testLocation");
+    dto.setDebtorProvince("testProvince");
+    dto.setDebtorNation("testNation");
+
+    IngestionFlowFile ingestionFlowFile = new IngestionFlowFile();
+    ingestionFlowFile.setOrganizationId(2L);
+    ingestionFlowFile.setIngestionFlowFileId(1L);
+
+    PaymentNotificationDTO result = new PaymentNotificationMapper().map(dto, ingestionFlowFile);
+
+    Assertions.assertEquals(2L, result.getOrganizationId());
+    Assertions.assertEquals(1L, result.getIngestionFlowFileId());
+    Assertions.assertEquals("testIud", result.getIud());
+    Assertions.assertEquals("testIuv", result.getIuv());
+    Assertions.assertEquals(dto.getPaymentExecutionDate(), result.getPaymentExecutionDate());
+    Assertions.assertEquals("testType", result.getPaymentType());
+    Assertions.assertEquals(1000L, result.getAmountPaidCents());
+    Assertions.assertEquals(100L, result.getPaCommission());
+    Assertions.assertEquals("testInfo", result.getRemittanceInformation());
+    Assertions.assertEquals("testCategory", result.getTransferCategory());
+    Assertions.assertEquals("testCode", result.getDebtPositionTypeOrgCode());
+    Assertions.assertEquals(dto.getBalance(), result.getBalance());
+    Assertions.assertNotNull(result.getDebtor());
+    Assertions.assertEquals(PersonEntityType.F, result.getDebtor().getEntityType());
+    Assertions.assertEquals("testCode", result.getDebtor().getFiscalCode());
+    Assertions.assertEquals("testName", result.getDebtor().getFullName());
+    Assertions.assertEquals("testAddress", result.getDebtor().getAddress());
+    Assertions.assertEquals("testCivic", result.getDebtor().getCivic());
+    Assertions.assertEquals("12345", result.getDebtor().getPostalCode());
+    Assertions.assertEquals("testLocation", result.getDebtor().getLocation());
+    Assertions.assertEquals("testProvince", result.getDebtor().getProvince());
+    Assertions.assertEquals("testNation", result.getDebtor().getNation());
+    TestUtils.checkNotNullFields(result,"paymentNotificationId","creationDate","updateDate","updateOperatorExternalId");
+  }
+
+  @Test
+  void mapPersonalDataFromPaymentNotificationReturnPersonWithCorrectValues() {
+    PaymentNotificationIngestionFlowFileDTO dto = new PaymentNotificationIngestionFlowFileDTO();
+    dto.setDebtorUniqueIdentifierType("F");
+    dto.setDebtorUniqueIdentifierCode("testCode");
+    dto.setDebtorFullName("testName");
+    dto.setDebtorAddress("testAddress");
+    dto.setDebtorCivic("testCivic");
+    dto.setDebtorPostalCode("12345");
+    dto.setDebtorLocation("testLocation");
+    dto.setDebtorProvince("testProvince");
+    dto.setDebtorNation("testNation");
+    dto.setDebtorEmail("email@email.com");
+
+    Person result = PaymentNotificationMapper.mapPersonalDataFromPaymentNotification(dto);
+
+    Assertions.assertEquals(PersonEntityType.F, result.getEntityType());
+    Assertions.assertEquals("testCode", result.getFiscalCode());
+    Assertions.assertEquals("testName", result.getFullName());
+    Assertions.assertEquals("testAddress", result.getAddress());
+    Assertions.assertEquals("testCivic", result.getCivic());
+    Assertions.assertEquals("12345", result.getPostalCode());
+    Assertions.assertEquals("testLocation", result.getLocation());
+    Assertions.assertEquals("testProvince", result.getProvince());
+    Assertions.assertEquals("testNation", result.getNation());
+    TestUtils.checkNotNullFields(result);
+  }
+
+}
\ No newline at end of file
diff --git a/src/test/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationErrorsArchiverServiceTest.java b/src/test/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationErrorsArchiverServiceTest.java
new file mode 100644
index 000000000..83bf0a143
--- /dev/null
+++ b/src/test/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationErrorsArchiverServiceTest.java
@@ -0,0 +1,158 @@
+package it.gov.pagopa.payhub.activities.service.ingestionflow.paymentnotification;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+
+import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationErrorDTO;
+import it.gov.pagopa.payhub.activities.exception.NotRetryableActivityException;
+import it.gov.pagopa.payhub.activities.service.files.CsvService;
+import it.gov.pagopa.payhub.activities.service.files.FileArchiverService;
+import it.gov.pagopa.payhub.activities.util.faker.IngestionFlowFileFaker;
+import it.gov.pagopa.pu.processexecutions.dto.generated.IngestionFlowFile;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class PaymentNotificationErrorsArchiverServiceTest {
+
+  @Mock
+  private FileArchiverService fileArchiverServiceMock;
+
+  @Mock
+  private CsvService csvServiceMock;
+
+  private PaymentNotificationErrorsArchiverService service;
+
+  public static final String FILE_NAME = "fileName";
+  public static final String WORKFLOW_STATUS = "workflowStatus";
+  public static final String ERROR_CODE = "errorCode";
+  public static final String ERROR_MESSAGE = "errorMessage";
+  private final String errorFolder = "error";
+  private final String sharedDirectory = "/tmp";
+
+  @BeforeEach
+  void setUp() {
+    service = new PaymentNotificationErrorsArchiverService(sharedDirectory, errorFolder, fileArchiverServiceMock, csvServiceMock);
+  }
+
+  @Test
+  void testWriteErrors_whenValidInput_thenCreatesAndArchivesCsv() throws IOException {
+    List<PaymentNotificationErrorDTO> errorDTOList = List.of(
+        new PaymentNotificationErrorDTO(FILE_NAME, "iupdOrg", "iud", WORKFLOW_STATUS, 1L, ERROR_CODE, ERROR_MESSAGE),
+        new PaymentNotificationErrorDTO(FILE_NAME, "iupdOrg", "iud", WORKFLOW_STATUS, 1L, ERROR_CODE, ERROR_MESSAGE)
+    );
+    Path workingDirectory = Path.of("build", "test");
+    IngestionFlowFile ingestionFlowFileDTO = IngestionFlowFileFaker.buildIngestionFlowFile();
+    Path expectedErrorFilePath = workingDirectory.resolve("ERROR-fileName.csv");
+
+    // When
+    service.writeErrors(workingDirectory, ingestionFlowFileDTO, errorDTOList);
+
+    // Then
+    Mockito.verify(csvServiceMock)
+        .createCsv(eq(expectedErrorFilePath), any(), any());
+  }
+
+  @Test
+  void testWriteErrors_whenErrorListEmpty_thenReturn() throws IOException {
+    Path workingDirectory = Path.of("build", "test");
+    IngestionFlowFile ingestionFlowFileDTO = IngestionFlowFileFaker.buildIngestionFlowFile();
+    Path expectedErrorFilePath = workingDirectory.resolve("ERROR-fileName.csv");
+
+    // When
+    service.writeErrors(workingDirectory, ingestionFlowFileDTO, List.of());
+
+    // Then
+    Mockito.verify(csvServiceMock, Mockito.times(0))
+        .createCsv(eq(expectedErrorFilePath), any(), any());
+  }
+
+  @Test
+  void testWriteErrors_whenIOException_thenThrowsActivitiesException() throws IOException {
+    List<PaymentNotificationErrorDTO> errorDTOList = List.of(
+        new PaymentNotificationErrorDTO(FILE_NAME, "iupdOrg", "iud", WORKFLOW_STATUS, 1L, ERROR_CODE, ERROR_MESSAGE)
+    );
+    Path workingDirectory = Path.of("build", "test");
+    IngestionFlowFile ingestionFlowFileDTO = IngestionFlowFileFaker.buildIngestionFlowFile();
+    Path expectedErrorFilePath = workingDirectory.resolve("ERROR-fileName.csv");
+
+    Mockito.doThrow(new IOException("Error creating CSV"))
+        .when(csvServiceMock)
+        .createCsv(eq(expectedErrorFilePath), any(), any());
+
+    // When & Then
+    NotRetryableActivityException exception = assertThrows(NotRetryableActivityException.class, () ->
+        service.writeErrors(workingDirectory, ingestionFlowFileDTO, errorDTOList));
+    assertEquals("Error creating CSV", exception.getMessage());
+  }
+
+  @Test
+  void givenNoErrorsWhenArchiveErrorFilesThenReturnNull() {
+    // Given
+    Path workingDirectory = Path.of("build");
+
+    // When
+    String result = service.archiveErrorFiles(workingDirectory, new IngestionFlowFile());
+
+    // Then
+    Assertions.assertNull(result);
+  }
+
+  @Test
+  void givenErrorsWhenArchiveErrorFilesThenCompressAndArchiveThem() throws IOException {
+    // Given
+    Path workingDirectory = Path.of("build", "test");
+    Files.createDirectories(workingDirectory);
+    Path errorFile = Files.createTempFile(workingDirectory, "ERROR-", ".csv");
+    try {
+      IngestionFlowFile ingestionFlowFileDTO = IngestionFlowFileFaker.buildIngestionFlowFile();
+      String expectedZipErrorFileName = "ERROR-fileName.zip";
+
+      // When
+      String result = service.archiveErrorFiles(workingDirectory, ingestionFlowFileDTO);
+
+      // Then
+      Assertions.assertEquals(expectedZipErrorFileName, result);
+
+      Mockito.verify(fileArchiverServiceMock)
+          .compressAndArchive(List.of(errorFile), Path.of("build/test/" + expectedZipErrorFileName), Path.of(sharedDirectory, ingestionFlowFileDTO.getOrganizationId() + "", ingestionFlowFileDTO.getFilePathName(), errorFolder));
+    } finally {
+      Files.delete(errorFile);
+    }
+  }
+
+  @Test
+  void givenArchiveErrorFilesWhenIOExceptionThenReturnNull() throws IOException {
+    // Given
+    Path workingDirectory = Path.of("build", "test");
+    Files.createDirectories(workingDirectory);
+    Path errorFile = Files.createTempFile(workingDirectory, "ERROR-", ".csv");
+    try {
+      IngestionFlowFile ingestionFlowFileDTO = IngestionFlowFileFaker.buildIngestionFlowFile();
+      String expectedZipErrorFileName = "ERROR-fileName.zip";
+
+      Mockito.doThrow(new IOException("Error")).when(fileArchiverServiceMock)
+          .compressAndArchive(List.of(errorFile), Path.of("build/test/" + expectedZipErrorFileName), Path.of(sharedDirectory, ingestionFlowFileDTO.getOrganizationId() + "", ingestionFlowFileDTO.getFilePathName(), errorFolder));
+
+      // When
+      String result = service.archiveErrorFiles(workingDirectory, ingestionFlowFileDTO);
+
+      // Then
+      Assertions.assertNull(result);
+
+    } finally {
+      Files.delete(errorFile);
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/test/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationProcessingServiceTest.java b/src/test/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationProcessingServiceTest.java
index c7d604836..365bb491a 100644
--- a/src/test/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationProcessingServiceTest.java
+++ b/src/test/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationProcessingServiceTest.java
@@ -1,44 +1,115 @@
 package it.gov.pagopa.payhub.activities.service.ingestionflow.paymentnotification;
 
 
+import static it.gov.pagopa.payhub.activities.util.faker.IngestionFlowFileFaker.buildIngestionFlowFile;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
 
-import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileActivityResult;
+import it.gov.pagopa.payhub.activities.connector.classification.PaymentNotificationService;
 import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileDTO;
+import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileResult;
+import it.gov.pagopa.payhub.activities.mapper.ingestionflow.paymentnotification.PaymentNotificationMapper;
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
 import it.gov.pagopa.pu.processexecutions.dto.generated.IngestionFlowFile;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.nio.file.Path;
-import java.util.Collections;
 import java.util.Iterator;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
 import org.mockito.Mock;
+import org.mockito.Mockito;
 import org.mockito.junit.jupiter.MockitoExtension;
 
 @ExtendWith(MockitoExtension.class)
 class PaymentNotificationProcessingServiceTest {
 
   @Mock
-  private Iterator<PaymentNotificationIngestionFlowFileDTO> iterator;
+  private Iterator<PaymentNotificationIngestionFlowFileDTO> iteratorMock;
 
   @Mock
-  private IngestionFlowFile ingestionFlowFile;
+  private IngestionFlowFile ingestionFlowFileMock;
+
+  @Mock
+  private PaymentNotificationErrorsArchiverService errorsArchiverServiceMock;
+
 
   @Mock
   private Path workingDirectory;
+  
+  @Mock
+  private PaymentNotificationMapper mapperMock;
+
+  @Mock
+  private PaymentNotificationService paymentNotificationServiceMock;
 
   private PaymentNotificationProcessingService service;
 
   @BeforeEach
   void setUp() {
-    service = new PaymentNotificationProcessingService();
+    service = new PaymentNotificationProcessingService(mapperMock, errorsArchiverServiceMock, paymentNotificationServiceMock);
   }
 
   @Test
-  void processPaymentNotificationWithEmptyIterator() {
-    PaymentNotificationIngestionFlowFileActivityResult result = service.processPaymentNotification(iterator, ingestionFlowFile, workingDirectory);
+  void processPaymentNotificationWithNoErrors() {
+    PaymentNotificationIngestionFlowFileDTO dto = mock(PaymentNotificationIngestionFlowFileDTO.class);
+    PaymentNotificationDTO mappedNotification = mock(PaymentNotificationDTO.class);
+    Mockito.when(mapperMock.map(dto, ingestionFlowFileMock)).thenReturn(mappedNotification);
+    Mockito.when(paymentNotificationServiceMock.createPaymentNotification(mappedNotification)).thenReturn(mappedNotification);
+
+    PaymentNotificationIngestionFlowFileResult result = service.processPaymentNotification(
+        Stream.of(dto).iterator(),
+        ingestionFlowFileMock, workingDirectory);
+
+    Assertions.assertEquals(1L, result.getProcessedRows());
+    Assertions.assertEquals(1L, result.getTotalRows());
+    Assertions.assertNotNull(result.getPaymentNotificationList());
+    Assertions.assertEquals(1, result.getPaymentNotificationList().size());
+    Mockito.verify(mapperMock).map(dto, ingestionFlowFileMock);
+    Mockito.verify(paymentNotificationServiceMock).createPaymentNotification(mappedNotification);
+    Mockito.verifyNoInteractions(errorsArchiverServiceMock);
+  }
+  
+  @Test
+  void givenThrowExceptionWhenProcessPaymentNotificationThenAddError() throws URISyntaxException {
+    // Given
+    PaymentNotificationIngestionFlowFileDTO paymentNotificationIngestionFlowFileDTO = mock(PaymentNotificationIngestionFlowFileDTO.class);
+    Mockito.when(paymentNotificationIngestionFlowFileDTO.getIud()).thenReturn("testIud");
+    Mockito.when(paymentNotificationIngestionFlowFileDTO.getIuv()).thenReturn("testIuv");
+
+    IngestionFlowFile ingestionFlowFile = buildIngestionFlowFile();
+    workingDirectory = Path.of(new URI("file:///tmp"));
+
+    PaymentNotificationDTO mappedNotification = mock(PaymentNotificationDTO.class);
+    Mockito.when(mapperMock.map(paymentNotificationIngestionFlowFileDTO, ingestionFlowFile)).thenReturn(mappedNotification);
+    Mockito.when(paymentNotificationServiceMock.createPaymentNotification(mappedNotification))
+        .thenThrow(new RuntimeException("Processing error"));
+
+    Mockito.when(errorsArchiverServiceMock.archiveErrorFiles(workingDirectory, ingestionFlowFile))
+        .thenReturn("zipFileName.csv");
+
+    // When
+    PaymentNotificationIngestionFlowFileResult result = service.processPaymentNotification(
+        Stream.of(paymentNotificationIngestionFlowFileDTO).iterator(),
+        ingestionFlowFile,
+        workingDirectory
+    );
+
+    // Then
+    assertEquals(1, result.getTotalRows());
+    assertEquals(0, result.getProcessedRows());
+    assertEquals("Some rows have failed", result.getErrorDescription());
+    assertEquals("zipFileName.csv", result.getDiscardedFileName());
 
-    assertEquals(0L, result.getOrganizationId());
-    assertEquals(Collections.emptyList(), result.getIudList());
+    verify(mapperMock).map(paymentNotificationIngestionFlowFileDTO, ingestionFlowFile);
+    verify(paymentNotificationServiceMock).createPaymentNotification(mappedNotification);
+    verify(errorsArchiverServiceMock).writeErrors(eq(workingDirectory), eq(ingestionFlowFile), any());
+    verify(errorsArchiverServiceMock).archiveErrorFiles(workingDirectory, ingestionFlowFile);
   }
 }
\ No newline at end of file
diff --git a/version b/version
index 251afd9a0..df5ea3bc1 100644
--- a/version
+++ b/version
@@ -1 +1 @@
-1.93.3
+1.93.3-SNAPSHOT


Reviews:
user: login: antonioT90
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T10:03:34Z
user: login: antonioT90
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T11:12:52Z
user: login: antonioT90
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T13:26:49Z
user: login: antonioT90
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-04-09T13:37:16Z

Review comments:
diff_hunk: @@ -15,12 +19,65 @@
 @Slf4j
 public class PaymentNotificationProcessingService {
 
-  public PaymentNotificationIngestionFlowFileActivityResult processPaymentNotification(
+  private final PaymentNotificationMapper paymentNotificationMapper;
+  private final PaymentNotificationErrorsArchiverService paymentNotificationErrorsArchiverService;
+
+  public PaymentNotificationProcessingService(
+      PaymentNotificationMapper paymentNotificationMapper,
+      PaymentNotificationErrorsArchiverService paymentNotificationErrorsArchiverService) {
+    this.paymentNotificationMapper = paymentNotificationMapper;
+    this.paymentNotificationErrorsArchiverService = paymentNotificationErrorsArchiverService;
+  }
+
+
+  public PaymentNotificationIngestionFlowFileResult processPaymentNotification(
       Iterator<PaymentNotificationIngestionFlowFileDTO> iterator,
       IngestionFlowFile ingestionFlowFile, Path workingDirectory) {
-      return new PaymentNotificationIngestionFlowFileActivityResult(Collections.emptyList(),0L);
+    List<PaymentNotificationErrorDTO> errorList = new ArrayList<>();
+    List<PaymentNotificationDTO> paymentNotificationList = new ArrayList<>();
+    long processedRows = 0;
+    long totalRows = 0;
+
+    while (iterator.hasNext()) {
+      totalRows++;
+
+      PaymentNotificationIngestionFlowFileDTO paymentNotificationDTO = iterator.next();
+
+      try {
+        paymentNotificationList.add(paymentNotificationMapper.map(paymentNotificationDTO, ingestionFlowFile));
+
path: src/main/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationProcessingService.java
user: login: antonioT90
body: here you should save the entity calling the save API?
can we put a TODO with the JIRA ticket?
created_at: 2025-04-09T10:01:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+package it.gov.pagopa.payhub.activities.mapper.ingestionflow.paymentnotification;
+
+import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileDTO;
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
+import it.gov.pagopa.pu.classification.dto.generated.Person;
+import it.gov.pagopa.pu.classification.dto.generated.PersonEntityType;
+import it.gov.pagopa.pu.processexecutions.dto.generated.IngestionFlowFile;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class PaymentNotificationMapperTest {
+
+  @Test
+  void mapReturnPaymentNotificationDTOWithCorrectValues() {
+    PaymentNotificationIngestionFlowFileDTO dto = new PaymentNotificationIngestionFlowFileDTO();
+    dto.setIud("testIud");
+    dto.setIuv("testIuv");
+    dto.setPaymentExecutionDate(LocalDate.now());
+    dto.setPaymentType("testType");
+    dto.setAmountPaidCents(BigDecimal.valueOf(1000));
+    dto.setPaCommissionCents(BigDecimal.valueOf(100));
+    dto.setRemittanceInformation("testInfo");
+    dto.setTransferCategory("testCategory");
+    dto.setDebtPositionTypeOrgCode("testCode");
+    dto.setBalance("balance");
+    dto.setDebtorUniqueIdentifierType("F");
+    dto.setDebtorUniqueIdentifierCode("testCode");
+    dto.setDebtorFullName("testName");
+    dto.setDebtorAddress("testAddress");
+    dto.setDebtorCivic("testCivic");
+    dto.setDebtorPostalCode("12345");
+    dto.setDebtorLocation("testLocation");
+    dto.setDebtorProvince("testProvince");
+    dto.setDebtorNation("testNation");
+
+    IngestionFlowFile ingestionFlowFile = new IngestionFlowFile();
+    ingestionFlowFile.setOrganizationId(2L);
+    ingestionFlowFile.setIngestionFlowFileId(1L);
+
+    PaymentNotificationDTO result = new PaymentNotificationMapper().map(dto, ingestionFlowFile);
+
+    Assertions.assertEquals(2L, result.getOrganizationId());
+    Assertions.assertEquals(1L, result.getIngestionFlowFileId());
+    Assertions.assertEquals("testIud", result.getIud());
+    Assertions.assertEquals("testIuv", result.getIuv());
+    Assertions.assertEquals(dto.getPaymentExecutionDate(), result.getPaymentExecutionDate());
+    Assertions.assertEquals("testType", result.getPaymentType());
+    Assertions.assertEquals(1000L, result.getAmountPaidCents());
+    Assertions.assertEquals(100L, result.getPaCommission());
+    Assertions.assertEquals("testInfo", result.getRemittanceInformation());
+    Assertions.assertEquals("testCategory", result.getTransferCategory());
+    Assertions.assertEquals("testCode", result.getDebtPositionTypeOrgCode());
+    Assertions.assertEquals(dto.getBalance(), result.getBalance());
+    Assertions.assertNotNull(result.getDebtor());
+    Assertions.assertEquals(PersonEntityType.F, result.getDebtor().getEntityType());
+    Assertions.assertEquals("testCode", result.getDebtor().getFiscalCode());
+    Assertions.assertEquals("testName", result.getDebtor().getFullName());
+    Assertions.assertEquals("testAddress", result.getDebtor().getAddress());
+    Assertions.assertEquals("testCivic", result.getDebtor().getCivic());
+    Assertions.assertEquals("12345", result.getDebtor().getPostalCode());
+    Assertions.assertEquals("testLocation", result.getDebtor().getLocation());
+    Assertions.assertEquals("testProvince", result.getDebtor().getProvince());
+    Assertions.assertEquals("testNation", result.getDebtor().getNation());
+  }
path: src/test/java/it/gov/pagopa/payhub/activities/mapper/ingestionflow/paymentnotification/PaymentNotificationMapperTest.java
user: login: antonioT90
body: always checkNotNull fields on mapper tests
created_at: 2025-04-09T11:12:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,97 @@
+package it.gov.pagopa.payhub.activities.mapper.ingestionflow.paymentnotification;
+
+import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileDTO;
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
+import it.gov.pagopa.pu.classification.dto.generated.Person;
+import it.gov.pagopa.pu.classification.dto.generated.PersonEntityType;
+import it.gov.pagopa.pu.processexecutions.dto.generated.IngestionFlowFile;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class PaymentNotificationMapperTest {
+
+  @Test
+  void mapReturnPaymentNotificationDTOWithCorrectValues() {
+    PaymentNotificationIngestionFlowFileDTO dto = new PaymentNotificationIngestionFlowFileDTO();
+    dto.setIud("testIud");
+    dto.setIuv("testIuv");
+    dto.setPaymentExecutionDate(LocalDate.now());
+    dto.setPaymentType("testType");
+    dto.setAmountPaidCents(BigDecimal.valueOf(1000));
+    dto.setPaCommissionCents(BigDecimal.valueOf(100));
+    dto.setRemittanceInformation("testInfo");
+    dto.setTransferCategory("testCategory");
+    dto.setDebtPositionTypeOrgCode("testCode");
+    dto.setBalance("balance");
+    dto.setDebtorUniqueIdentifierType("F");
+    dto.setDebtorUniqueIdentifierCode("testCode");
+    dto.setDebtorFullName("testName");
+    dto.setDebtorAddress("testAddress");
+    dto.setDebtorCivic("testCivic");
+    dto.setDebtorPostalCode("12345");
+    dto.setDebtorLocation("testLocation");
+    dto.setDebtorProvince("testProvince");
+    dto.setDebtorNation("testNation");
+
+    IngestionFlowFile ingestionFlowFile = new IngestionFlowFile();
+    ingestionFlowFile.setOrganizationId(2L);
+    ingestionFlowFile.setIngestionFlowFileId(1L);
+
+    PaymentNotificationDTO result = new PaymentNotificationMapper().map(dto, ingestionFlowFile);
+
+    Assertions.assertEquals(2L, result.getOrganizationId());
+    Assertions.assertEquals(1L, result.getIngestionFlowFileId());
+    Assertions.assertEquals("testIud", result.getIud());
+    Assertions.assertEquals("testIuv", result.getIuv());
+    Assertions.assertEquals(dto.getPaymentExecutionDate(), result.getPaymentExecutionDate());
+    Assertions.assertEquals("testType", result.getPaymentType());
+    Assertions.assertEquals(1000L, result.getAmountPaidCents());
+    Assertions.assertEquals(100L, result.getPaCommission());
+    Assertions.assertEquals("testInfo", result.getRemittanceInformation());
+    Assertions.assertEquals("testCategory", result.getTransferCategory());
+    Assertions.assertEquals("testCode", result.getDebtPositionTypeOrgCode());
+    Assertions.assertEquals(dto.getBalance(), result.getBalance());
+    Assertions.assertNotNull(result.getDebtor());
+    Assertions.assertEquals(PersonEntityType.F, result.getDebtor().getEntityType());
+    Assertions.assertEquals("testCode", result.getDebtor().getFiscalCode());
+    Assertions.assertEquals("testName", result.getDebtor().getFullName());
+    Assertions.assertEquals("testAddress", result.getDebtor().getAddress());
+    Assertions.assertEquals("testCivic", result.getDebtor().getCivic());
+    Assertions.assertEquals("12345", result.getDebtor().getPostalCode());
+    Assertions.assertEquals("testLocation", result.getDebtor().getLocation());
+    Assertions.assertEquals("testProvince", result.getDebtor().getProvince());
+    Assertions.assertEquals("testNation", result.getDebtor().getNation());
+  }
+
+  @Test
+  void mapPersonalDataFromPaymentNotificationReturnPersonWithCorrectValues() {
+    PaymentNotificationIngestionFlowFileDTO dto = new PaymentNotificationIngestionFlowFileDTO();
+    dto.setDebtorUniqueIdentifierType("F");
+    dto.setDebtorUniqueIdentifierCode("testCode");
+    dto.setDebtorFullName("testName");
+    dto.setDebtorAddress("testAddress");
+    dto.setDebtorCivic("testCivic");
+    dto.setDebtorPostalCode("12345");
+    dto.setDebtorLocation("testLocation");
+    dto.setDebtorProvince("testProvince");
+    dto.setDebtorNation("testNation");
+
+    Person result = PaymentNotificationMapper.mapPersonalDataFromPaymentNotification(dto);
+
+    Assertions.assertEquals(PersonEntityType.F, result.getEntityType());
+    Assertions.assertEquals("testCode", result.getFiscalCode());
+    Assertions.assertEquals("testName", result.getFullName());
+    Assertions.assertEquals("testAddress", result.getAddress());
+    Assertions.assertEquals("testCivic", result.getCivic());
+    Assertions.assertEquals("12345", result.getPostalCode());
+    Assertions.assertEquals("testLocation", result.getLocation());
+    Assertions.assertEquals("testProvince", result.getProvince());
+    Assertions.assertEquals("testNation", result.getNation());
+  }
path: src/test/java/it/gov/pagopa/payhub/activities/mapper/ingestionflow/paymentnotification/PaymentNotificationMapperTest.java
user: login: antonioT90
body: always checkNotNull fields on mapper tests
created_at: 2025-04-09T11:12:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -15,12 +19,65 @@
 @Slf4j
 public class PaymentNotificationProcessingService {
 
-  public PaymentNotificationIngestionFlowFileActivityResult processPaymentNotification(
+  private final PaymentNotificationMapper paymentNotificationMapper;
+  private final PaymentNotificationErrorsArchiverService paymentNotificationErrorsArchiverService;
+
+  public PaymentNotificationProcessingService(
+      PaymentNotificationMapper paymentNotificationMapper,
+      PaymentNotificationErrorsArchiverService paymentNotificationErrorsArchiverService) {
+    this.paymentNotificationMapper = paymentNotificationMapper;
+    this.paymentNotificationErrorsArchiverService = paymentNotificationErrorsArchiverService;
+  }
+
+
+  public PaymentNotificationIngestionFlowFileResult processPaymentNotification(
       Iterator<PaymentNotificationIngestionFlowFileDTO> iterator,
       IngestionFlowFile ingestionFlowFile, Path workingDirectory) {
-      return new PaymentNotificationIngestionFlowFileActivityResult(Collections.emptyList(),0L);
+    List<PaymentNotificationErrorDTO> errorList = new ArrayList<>();
+    List<PaymentNotificationDTO> paymentNotificationList = new ArrayList<>();
+    long processedRows = 0;
+    long totalRows = 0;
+
+    while (iterator.hasNext()) {
+      totalRows++;
+
+      PaymentNotificationIngestionFlowFileDTO paymentNotificationDTO = iterator.next();
+
+      try {
+        paymentNotificationList.add(paymentNotificationMapper.map(paymentNotificationDTO, ingestionFlowFile));
+
path: src/main/java/it/gov/pagopa/payhub/activities/service/ingestionflow/paymentnotification/PaymentNotificationProcessingService.java
user: login: antonioT90
body: still I don't see the invocation of the save API, am I missing something?
created_at: 2025-04-09T11:12:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,100 @@
+package it.gov.pagopa.payhub.activities.mapper.ingestionflow.paymentnotification;
+
+import it.gov.pagopa.payhub.activities.dto.paymentnotification.PaymentNotificationIngestionFlowFileDTO;
+import it.gov.pagopa.payhub.activities.util.TestUtils;
+import it.gov.pagopa.pu.classification.dto.generated.PaymentNotificationDTO;
+import it.gov.pagopa.pu.classification.dto.generated.Person;
+import it.gov.pagopa.pu.classification.dto.generated.PersonEntityType;
+import it.gov.pagopa.pu.processexecutions.dto.generated.IngestionFlowFile;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class PaymentNotificationMapperTest {
+
+  @Test
+  void mapReturnPaymentNotificationDTOWithCorrectValues() {
+    PaymentNotificationIngestionFlowFileDTO dto = new PaymentNotificationIngestionFlowFileDTO();
+    dto.setIud("testIud");
+    dto.setIuv("testIuv");
+    dto.setPaymentExecutionDate(LocalDate.now());
+    dto.setPaymentType("testType");
+    dto.setAmountPaidCents(BigDecimal.valueOf(1000));
+    dto.setPaCommissionCents(BigDecimal.valueOf(100));
+    dto.setRemittanceInformation("testInfo");
+    dto.setTransferCategory("testCategory");
+    dto.setDebtPositionTypeOrgCode("testCode");
+    dto.setBalance("balance");
+    dto.setDebtorUniqueIdentifierType("F");
+    dto.setDebtorUniqueIdentifierCode("testCode");
+    dto.setDebtorFullName("testName");
+    dto.setDebtorAddress("testAddress");
+    dto.setDebtorCivic("testCivic");
+    dto.setDebtorPostalCode("12345");
+    dto.setDebtorLocation("testLocation");
+    dto.setDebtorProvince("testProvince");
+    dto.setDebtorNation("testNation");
+
+    IngestionFlowFile ingestionFlowFile = new IngestionFlowFile();
+    ingestionFlowFile.setOrganizationId(2L);
+    ingestionFlowFile.setIngestionFlowFileId(1L);
+
+    PaymentNotificationDTO result = new PaymentNotificationMapper().map(dto, ingestionFlowFile);
+
+    Assertions.assertEquals(2L, result.getOrganizationId());
+    Assertions.assertEquals(1L, result.getIngestionFlowFileId());
+    Assertions.assertEquals("testIud", result.getIud());
+    Assertions.assertEquals("testIuv", result.getIuv());
+    Assertions.assertEquals(dto.getPaymentExecutionDate(), result.getPaymentExecutionDate());
+    Assertions.assertEquals("testType", result.getPaymentType());
+    Assertions.assertEquals(1000L, result.getAmountPaidCents());
+    Assertions.assertEquals(100L, result.getPaCommission());
+    Assertions.assertEquals("testInfo", result.getRemittanceInformation());
+    Assertions.assertEquals("testCategory", result.getTransferCategory());
+    Assertions.assertEquals("testCode", result.getDebtPositionTypeOrgCode());
+    Assertions.assertEquals(dto.getBalance(), result.getBalance());
+    Assertions.assertNotNull(result.getDebtor());
+    Assertions.assertEquals(PersonEntityType.F, result.getDebtor().getEntityType());
+    Assertions.assertEquals("testCode", result.getDebtor().getFiscalCode());
+    Assertions.assertEquals("testName", result.getDebtor().getFullName());
+    Assertions.assertEquals("testAddress", result.getDebtor().getAddress());
+    Assertions.assertEquals("testCivic", result.getDebtor().getCivic());
+    Assertions.assertEquals("12345", result.getDebtor().getPostalCode());
+    Assertions.assertEquals("testLocation", result.getDebtor().getLocation());
+    Assertions.assertEquals("testProvince", result.getDebtor().getProvince());
+    Assertions.assertEquals("testNation", result.getDebtor().getNation());
+    TestUtils.checkNotNullFields(result,"paymentNotificationId","creationDate","updateDate","updateOperatorExternalId");
+  }
+
+  @Test
+  void mapPersonalDataFromPaymentNotificationReturnPersonWithCorrectValues() {
+    PaymentNotificationIngestionFlowFileDTO dto = new PaymentNotificationIngestionFlowFileDTO();
+    dto.setDebtorUniqueIdentifierType("F");
+    dto.setDebtorUniqueIdentifierCode("testCode");
+    dto.setDebtorFullName("testName");
+    dto.setDebtorAddress("testAddress");
+    dto.setDebtorCivic("testCivic");
+    dto.setDebtorPostalCode("12345");
+    dto.setDebtorLocation("testLocation");
+    dto.setDebtorProvince("testProvince");
+    dto.setDebtorNation("testNation");
+
+    Person result = PaymentNotificationMapper.mapPersonalDataFromPaymentNotification(dto);
+
+    Assertions.assertEquals(PersonEntityType.F, result.getEntityType());
+    Assertions.assertEquals("testCode", result.getFiscalCode());
+    Assertions.assertEquals("testName", result.getFullName());
+    Assertions.assertEquals("testAddress", result.getAddress());
+    Assertions.assertEquals("testCivic", result.getCivic());
+    Assertions.assertEquals("12345", result.getPostalCode());
+    Assertions.assertEquals("testLocation", result.getLocation());
+    Assertions.assertEquals("testProvince", result.getProvince());
+    Assertions.assertEquals("testNation", result.getNation());
+    TestUtils.checkNotNullFields(result,"email");
path: src/test/java/it/gov/pagopa/payhub/activities/mapper/ingestionflow/paymentnotification/PaymentNotificationMapperTest.java
user: login: antonioT90
body: why email field should be null?
created_at: 2025-04-09T13:26:44Z
author_association: CONTRIBUTOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-04-09T13:37:54Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=pagopa_p4pa-payhub-activities&pullRequest=230) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=pagopa_p4pa-payhub-activities&pullRequest=230&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=pagopa_p4pa-payhub-activities&pullRequest=230&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=pagopa_p4pa-payhub-activities&pullRequest=230&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=pagopa_p4pa-payhub-activities&pullRequest=230&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=pagopa_p4pa-payhub-activities&pullRequest=230&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=pagopa_p4pa-payhub-activities&pullRequest=230)


author_association: NONE
user: login: github-actions[bot]
created_at: 2025-04-09T13:38:48Z
body: :tada: This PR is included in version 1.94.0 :tada:

The release is available on [GitHub release](https://github.com/pagopa/p4pa-payhub-activities/releases/tag/v1.94.0)

Your **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package::rocket:
author_association: CONTRIBUTOR