Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[PIDM-42] fix(payment-status): payment position/option status not coherent #289

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

dylantangredi-bvt
Copy link
Collaborator

@dylantangredi-bvt dylantangredi-bvt commented Jan 31, 2025

List of Changes

These changes are relevant in case the user pays the full amount for a payment position in presence of already paid installments/payment options:

  • Fixed payment option status not changing from PO_UNPAID to PO_PAID
  • Added detailed logs for this specific case, instead of throwing an exception that makes the db not coherent with the payment process on the node
  • Fixed payment position status not changing from PARTIALLY_PAID to PAID
  • Moved the exception handling from the /pay to the /get (activate) phase to prevent the issue

Motivation and Context

This is a hotfix that was necessary to make the db coherent with what the user experiences during checkout. Before the fix, if the user paid using a NAV corresponding to the full amount due for the payment position but one or more installment/payment options were already paid for the same payment position, then the payment option corresponding to the full amount did not change status from PO_UNPAID to PAID, causing a discrepancy in the process. This happened because the checkout ended correctly up to the receipt generation for the user payment, but in the backend a HttpStatus.CONFLICT, "Existing related payment found" was being thrown.

How Has This Been Tested?

Tested locally with Postman making the following use case:

  • Create debt position with 4 payment options, option 1 being for the full amount and option 2/3/4 for the 3 installments
  • Publish debt position
  • Pay payment option 2 (corresponding to one installment)
  • Get payment option 2 by NAV and check db status on the option (PO_PAID)
  • Get payment option 1 by NAV (corresponding to the full amount) -> error 409
  • Pay payment option 1 (corresponding to the full amount), skipping activate
  • Retrieve payment option by NAV and check db status on the option -> PO_PAID and debtPositionStatus -> PAID

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)

Checklist:

  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.

…position status not changing to PAID when paying the full amount in presence of already paid installments;
@dylantangredi-bvt dylantangredi-bvt added bug Something isn't working gpd patch minor labels Jan 31, 2025
…fix-payment-status

# Conflicts:
#	src/main/java/it/gov/pagopa/debtposition/util/DebtPositionValidation.java
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[google-java-format] reported by reviewdog 🐶

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Copy link
Contributor

@cap-ang cap-ang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great,

How about preventing these cases by blocking the activation phase with a 409 if the activation is not related to an installment (payment option with isPartialPayment true)?

… PIDM-42-fix-payment-status

# Conflicts:
#	src/main/java/it/gov/pagopa/debtposition/util/DebtPositionValidation.java
…on to get/activate phase; adapt existing unit test;
@@ -69,6 +70,7 @@ public PaymentOption getPaymentOptionByNAV(@NotBlank String organizationFiscalCo
// PaymentPosition used when converting PaymentOption to POWithDebtor
DebtPositionStatus.validityCheckAndUpdate(paymentOption);
DebtPositionStatus.expirationCheckAndUpdate(paymentOption);
DebtPositionStatus.checkAlreadyPaidInstallments(paymentOption, nav);

return paymentOption;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[google-java-format] reported by reviewdog 🐶

Suggested change
return paymentOption;
if (po.isEmpty()) {
throw new AppException(AppError.PAYMENT_OPTION_NOT_FOUND, organizationFiscalCode, nav);

@@ -233,7 +235,10 @@
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[google-java-format] reported by reviewdog 🐶

Suggested change
}
pp.setLastUpdatedDate(currentDate);
// salvo l'aggiornamento del pagamento
paymentPositionRepository.saveAndFlush(pp);
return poToPay;
}
private Transfer updateTransferStatus(PaymentPosition pp, String iuv, String transferId) {
LocalDateTime currentDate = LocalDateTime.now(ZoneOffset.UTC);
long numberPOTransfers = 0;
long countReportedTransfer = 0;
Transfer reportedTransfer = null;
for (PaymentOption po : pp.getPaymentOption()) {
if (po.getIuv().equals(iuv)) {
// numero totale dei transfer per la PO
numberPOTransfers = po.getTransfer().stream().count();
// numero dei transfer della PO in stato T_REPORTED
countReportedTransfer =
po.getTransfer().stream()
.filter(t -> t.getStatus().equals(TransferStatus.T_REPORTED))
.count();
// recupero il transfer oggetto di rendicontazione
Optional<Transfer> transferToReport =
po.getTransfer().stream().filter(t -> t.getIdTransfer().equals(transferId)).findFirst();
if (transferToReport.isEmpty()) {
log.error(
"Obtained unexpected empty transfer - "
+ "[organizationFiscalCode= "
+ pp.getOrganizationFiscalCode()
+ "; "
+ "iupd= "
+ pp.getIupd()
+ "; "
+ "iuv= "
+ iuv
+ "; "
+ "idTransfer= "
+ transferId
+ "]");
throw new AppException(
AppError.TRANSFER_REPORTING_FAILED, pp.getOrganizationFiscalCode(), iuv, transferId);
}

Comment on lines 237 to 242
// aggiorno lo stato della payment position
if (countPaidPartialPayment > 0 && countPaidPartialPayment < numberOfPartialPayment) {
// PIDM-42 if paying the full amount when there is already a paid partial payment
// then update the payment position status to PAID
if (countPaidPartialPayment > 0 && countPaidPartialPayment < numberOfPartialPayment
&& Boolean.TRUE.equals(Objects.requireNonNull(poToPay).getIsPartialPayment())) {
pp.setStatus(DebtPositionStatus.PARTIALLY_PAID);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[google-java-format] reported by reviewdog 🐶

Suggested change
// aggiorno lo stato della payment position
if (countPaidPartialPayment > 0 && countPaidPartialPayment < numberOfPartialPayment) {
// PIDM-42 if paying the full amount when there is already a paid partial payment
// then update the payment position status to PAID
if (countPaidPartialPayment > 0 && countPaidPartialPayment < numberOfPartialPayment
&& Boolean.TRUE.equals(Objects.requireNonNull(poToPay).getIsPartialPayment())) {
pp.setStatus(DebtPositionStatus.PARTIALLY_PAID);
transferToReport.get().setStatus(TransferStatus.T_REPORTED);
transferToReport.get().setLastUpdatedDate(currentDate);
countReportedTransfer++;
// aggiorno lo stato della PO
if (countReportedTransfer < numberPOTransfers) {
po.setStatus(PaymentOptionStatus.PO_PARTIALLY_REPORTED);

Comment on lines +659 to +660
+ auxDigit
+ "123456IUVMULTIPLEMOCK3")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[google-java-format] reported by reviewdog 🐶

Suggested change
+ auxDigit
+ "123456IUVMULTIPLEMOCK3")
+ auxDigit
+ "123456IUVMULTIPLEMOCK3")

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[google-java-format] reported by reviewdog 🐶

PaymentOption paymentOption = po.get();
// Update PaymentPosition instance only in memory
// PaymentPosition used when converting PaymentOption to POWithDebtor
DebtPositionStatus.validityCheckAndUpdate(paymentOption);
DebtPositionStatus.expirationCheckAndUpdate(paymentOption);
DebtPositionStatus.checkAlreadyPaidInstallments(paymentOption, nav);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[google-java-format] reported by reviewdog 🐶

pp.setStatus(DebtPositionStatus.PAID);
pp.setPaymentDate(paymentOptionModel.getPaymentDate());

@dylantangredi-bvt
Copy link
Collaborator Author

Great,

How about preventing these cases by blocking the activation phase with a 409 if the activation is not related to an installment (payment option with isPartialPayment true)?

I assume you meant isPartialPayment false because that indicates the full amount payment (so not related to an installment). In this case it has been done with the latest commit

@dylantangredi-bvt dylantangredi-bvt marked this pull request as ready for review January 31, 2025 11:50
@dylantangredi-bvt dylantangredi-bvt requested a review from a team as a code owner January 31, 2025 11:50
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Comment on lines +282 to +297
log.warn(
"Potential payment state inconsistency detected || "
+ "Organization: {} || "
+ "IUPD: {} || "
+ "NAV: {} || "
+ "Position Status: {} || "
+ "Payment Option Status: {} || "
+ "Is Partial Payment: {} || "
+ "Timestamp: {}",
ppToPay.getOrganizationFiscalCode(),
ppToPay.getIupd(),
nav,
ppToPay.getStatus(),
poToPay.getStatus(),
poToPay.getIsPartialPayment(),
LocalDateTime.now());

Check failure

Code scanning / CodeQL

Log Injection High

This log entry depends on a
user-provided value
.

Copilot Autofix AI about 21 hours ago

To fix the log injection issue, we need to sanitize the nav parameter before logging it. The best way to do this is to remove any potentially harmful characters from the nav parameter, such as new-line characters, which can be used to forge log entries. We can use a utility method to sanitize the input by replacing or removing such characters.

  1. Create a utility method to sanitize the nav parameter by removing new-line characters and other potentially harmful characters.
  2. Use this utility method to sanitize the nav parameter before logging it in the DebtPositionValidation class.
Suggested changeset 1
src/main/java/it/gov/pagopa/debtposition/util/DebtPositionValidation.java

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/main/java/it/gov/pagopa/debtposition/util/DebtPositionValidation.java b/src/main/java/it/gov/pagopa/debtposition/util/DebtPositionValidation.java
--- a/src/main/java/it/gov/pagopa/debtposition/util/DebtPositionValidation.java
+++ b/src/main/java/it/gov/pagopa/debtposition/util/DebtPositionValidation.java
@@ -25,2 +25,3 @@
 import org.apache.logging.log4j.util.Strings;
+import org.apache.commons.lang3.StringUtils;
 
@@ -29,2 +30,9 @@
 
+  private static String sanitize(String input) {
+    if (input == null) {
+      return null;
+    }
+    return StringUtils.replaceChars(input, "\n\r", "");
+  }
+
   private static final String LOG_BASE_PARAMS_DETAIL =
@@ -281,2 +289,3 @@
       // log detailed information about this edge case
+      String sanitizedNav = sanitize(nav);
       log.warn(
@@ -292,3 +301,3 @@
           ppToPay.getIupd(),
-          nav,
+          sanitizedNav,
           ppToPay.getStatus(),
EOF
@@ -25,2 +25,3 @@
import org.apache.logging.log4j.util.Strings;
import org.apache.commons.lang3.StringUtils;

@@ -29,2 +30,9 @@

private static String sanitize(String input) {
if (input == null) {
return null;
}
return StringUtils.replaceChars(input, "\n\r", "");
}

private static final String LOG_BASE_PARAMS_DETAIL =
@@ -281,2 +289,3 @@
// log detailed information about this edge case
String sanitizedNav = sanitize(nav);
log.warn(
@@ -292,3 +301,3 @@
ppToPay.getIupd(),
nav,
sanitizedNav,
ppToPay.getStatus(),
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants