You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by al...@apache.org on 2022/09/30 19:22:30 UTC

[fineract] branch develop updated: Chargeback Loan Transaction

This is an automated email from the ASF dual-hosted git repository.

aleks pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 40b6eb50f Chargeback Loan Transaction
40b6eb50f is described below

commit 40b6eb50f6b17b3f5bd00512bfc883384c83843d
Author: Jose Alberto Hernandez <al...@MacBook-Pro.local>
AuthorDate: Sun Sep 18 18:33:41 2022 -0500

    Chargeback Loan Transaction
---
 .../main/avro/payment/v1/PaymentTypeDataV1.avsc    |  16 +++
 .../commands/service/CommandWrapperBuilder.java    |   9 ++
 .../LoanChargebackTransactionBusinessEvent.java    |  40 ++++++
 .../interoperation/service/InteropServiceImpl.java |   4 +-
 .../service/NotificationDomainServiceImpl.java     |  17 +++
 .../fineract/portfolio/charge/domain/Charge.java   |   2 +-
 .../loanaccount/api/LoanApiConstants.java          |   5 +
 .../api/LoanTransactionsApiResource.java           |  11 +-
 .../loanaccount/data/LoanTransactionData.java      |   6 +
 .../data/LoanTransactionRelationData.java}         |  23 ++--
 .../portfolio/loanaccount/domain/Loan.java         |  18 ++-
 .../portfolio/loanaccount/domain/LoanEvent.java    |   3 +-
 .../loanaccount/domain/LoanTransaction.java        |  24 +++-
 .../domain/LoanTransactionRelation.java            |  65 ++++++++++
 .../domain/LoanTransactionRelationRepository.java} |   9 +-
 .../domain/LoanTransactionRelationTypeEnum.java    |  54 ++++++++
 .../loanaccount/domain/LoanTransactionType.java    |   5 +
 .../LoanRepaymentChargebackCommandHandler.java     |  43 +++++++
 .../mapper/LoanTransactionRelationMapper.java      |  39 ++++++
 .../serialization/LoanEventApiJsonValidator.java   |  44 ++++++-
 .../service/LoanReadPlatformService.java           |   4 +
 .../service/LoanReadPlatformServiceImpl.java       | 100 +++++++--------
 .../service/LoanWritePlatformService.java          |   2 +
 .../LoanWritePlatformServiceJpaRepositoryImpl.java | 104 +++++++++++++++-
 .../paymentdetail/domain/PaymentDetail.java        |   2 +-
 .../paymenttype/api/PaymentTypeApiResource.java    |  31 ++---
 .../api/PaymentTypeApiResourceConstants.java       |   2 +
 .../api/PaymentTypeApiResourceSwagger.java         |  16 +++
 .../paymenttype/data/PaymentTypeData.java          |  14 ++-
 .../paymenttype/data/PaymentTypeDataValidator.java |  34 ++++-
 .../portfolio/paymenttype/domain/PaymentType.java  |  34 ++---
 .../paymenttype/domain/PaymentTypeRepository.java  |   5 +
 .../domain/PaymentTypeRepositoryWrapper.java       |  13 +-
 .../PaymentTypeMapper.java}                        |  20 ++-
 .../service/PaymentTypeReadPlatformService.java    |   2 +
 .../PaymentTypeReadPlatformServiceImpl.java        |  58 +++------
 .../service/PaymentTypeWriteServiceImpl.java       |  19 ++-
 .../portfolio/savings/domain/SavingsAccount.java   |   4 +-
 .../SavingsAccountReadPlatformServiceImpl.java     |   3 +-
 .../db/changelog/tenant/changelog-tenant.xml       |   1 +
 .../parts/0052_loan_transaction_chargeback.xml     | 134 ++++++++++++++++++++
 .../LoanTransactionChargebackTest.java             | 138 +++++++++++++++++++++
 .../integrationtests/common/PaymentTypeHelper.java |  13 +-
 .../common/loans/LoanTransactionHelper.java        |   9 ++
 44 files changed, 1006 insertions(+), 193 deletions(-)

diff --git a/fineract-avro-schemas/src/main/avro/payment/v1/PaymentTypeDataV1.avsc b/fineract-avro-schemas/src/main/avro/payment/v1/PaymentTypeDataV1.avsc
index a1a02919c..0c96a5b7e 100644
--- a/fineract-avro-schemas/src/main/avro/payment/v1/PaymentTypeDataV1.avsc
+++ b/fineract-avro-schemas/src/main/avro/payment/v1/PaymentTypeDataV1.avsc
@@ -42,6 +42,22 @@
                 "null",
                 "long"
             ]
+        },
+        {
+            "default": null,
+            "name": "codeName",
+            "type": [
+                "null",
+                "string"
+            ]
+        },
+        {
+            "default": null,
+            "name": "isSystemDefined",
+            "type": [
+                "null",
+                "boolean"
+            ]
         }
     ]
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
index 952da0cec..28d523242 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/commands/service/CommandWrapperBuilder.java
@@ -905,6 +905,15 @@ public class CommandWrapperBuilder {
         return this;
     }
 
+    public CommandWrapperBuilder chargebackTransaction(final Long loanId, final Long transactionId) {
+        this.actionName = "CHARGEBACK";
+        this.entityName = "LOAN";
+        this.entityId = transactionId;
+        this.loanId = loanId;
+        this.href = "/loans/" + loanId + "/transactions/" + transactionId;
+        return this;
+    }
+
     public CommandWrapperBuilder loanForeclosure(final Long loanId) {
         this.actionName = "FORECLOSURE";
         this.entityName = "LOAN";
diff --git a/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/LoanChargebackTransactionBusinessEvent.java b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/LoanChargebackTransactionBusinessEvent.java
new file mode 100644
index 000000000..6cc63c795
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/infrastructure/event/business/domain/loan/LoanChargebackTransactionBusinessEvent.java
@@ -0,0 +1,40 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.infrastructure.event.business.domain.loan;
+
+import org.apache.fineract.infrastructure.event.business.domain.loan.transaction.LoanTransactionBusinessEvent;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+
+public class LoanChargebackTransactionBusinessEvent extends LoanTransactionBusinessEvent {
+
+    public static final String LOAN_CHARGEBACK_TRANSACTION_BUSINESS_EVENT = "LoanChargebackTransactionBusinessEvent";
+    public static final String LOAN_CHARGEBACK_TRANSACTION_PERMISSION = "LOAN_CHARGEBACK";
+    public static final String LOAN_CHARGEBACK_TRANSACTION_OBJECT_TYPE = "loanTransaction";
+    public static final String LOAN_CHARGEBACK_TRANSACTION_EVENT_TYPE = "loanTransactionChargeback";
+    public static final String LOAN_CHARGEBACK_TRANSACTION_NOTIFICATION = "Loan Transaction has been chargeback";
+
+    public LoanChargebackTransactionBusinessEvent(LoanTransaction transactionToChargeback) {
+        super(transactionToChargeback);
+    }
+
+    @Override
+    public String getType() {
+        return LOAN_CHARGEBACK_TRANSACTION_BUSINESS_EVENT;
+    }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropServiceImpl.java
index 81691513c..c045c09aa 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/interoperation/service/InteropServiceImpl.java
@@ -332,7 +332,7 @@ public class InteropServiceImpl implements InteropService {
         InteropTransactionRequestData request = dataValidator.validateAndParseCreateRequest(command);
 
         // TODO: error handling
-        SavingsAccount savingsAccount = validateAndGetSavingAccount(request);
+        validateAndGetSavingAccount(request);
 
         return InteropTransactionRequestResponseData.build(command.commandId(), request.getTransactionCode(), InteropActionState.ACCEPTED,
                 request.getExpiration(), request.getExtensionList(), request.getRequestCode());
@@ -617,7 +617,7 @@ public class InteropServiceImpl implements InteropService {
     PaymentType findPaymentType() {
         List<PaymentType> paymentTypes = paymentTypeRepository.findAll();
         for (PaymentType paymentType : paymentTypes) {
-            if (!paymentType.isCashPayment()) {
+            if (!paymentType.getIsCashPayment()) {
                 return paymentType;
             }
             // TODO: for now first not cash is retured:
diff --git a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationDomainServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationDomainServiceImpl.java
index e68eaeb30..9e607f73e 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationDomainServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/notification/service/NotificationDomainServiceImpl.java
@@ -34,6 +34,7 @@ import org.apache.fineract.infrastructure.event.business.domain.deposit.Recurrin
 import org.apache.fineract.infrastructure.event.business.domain.group.CentersCreateBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.group.GroupsCreateBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.LoanApprovedBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.domain.loan.LoanChargebackTransactionBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.LoanCloseAsRescheduleBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.LoanCloseBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.LoanCreatedBusinessEvent;
@@ -53,6 +54,7 @@ import org.apache.fineract.notification.data.NotificationData;
 import org.apache.fineract.notification.eventandlistener.NotificationEventPublisher;
 import org.apache.fineract.portfolio.client.domain.Client;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
 import org.apache.fineract.portfolio.savings.DepositAccountType;
 import org.apache.fineract.portfolio.savings.domain.FixedDepositAccount;
@@ -92,6 +94,8 @@ public class NotificationDomainServiceImpl implements NotificationDomainService
         businessEventNotifierService.addPostBusinessEventListener(LoanCreatedBusinessEvent.class, new LoanCreatedListener());
         businessEventNotifierService.addPostBusinessEventListener(LoanApprovedBusinessEvent.class, new LoanApprovedListener());
         businessEventNotifierService.addPostBusinessEventListener(LoanCloseBusinessEvent.class, new LoanClosedListener());
+        businessEventNotifierService.addPostBusinessEventListener(LoanChargebackTransactionBusinessEvent.class,
+                new LoanChargebackTransactionListener());
         businessEventNotifierService.addPostBusinessEventListener(LoanCloseAsRescheduleBusinessEvent.class,
                 new LoanCloseAsRescheduledListener());
         businessEventNotifierService.addPostBusinessEventListener(LoanTransactionMakeRepaymentPostBusinessEvent.class,
@@ -247,6 +251,19 @@ public class NotificationDomainServiceImpl implements NotificationDomainService
         }
     }
 
+    private class LoanChargebackTransactionListener implements BusinessEventListener<LoanChargebackTransactionBusinessEvent> {
+
+        @Override
+        public void onBusinessEvent(LoanChargebackTransactionBusinessEvent event) {
+            LoanTransaction loanTransaction = event.get();
+            buildNotification(LoanChargebackTransactionBusinessEvent.LOAN_CHARGEBACK_TRANSACTION_PERMISSION,
+                    LoanChargebackTransactionBusinessEvent.LOAN_CHARGEBACK_TRANSACTION_OBJECT_TYPE, loanTransaction.getId(),
+                    LoanChargebackTransactionBusinessEvent.LOAN_CHARGEBACK_TRANSACTION_NOTIFICATION,
+                    LoanChargebackTransactionBusinessEvent.LOAN_CHARGEBACK_TRANSACTION_EVENT_TYPE, context.authenticatedUser().getId(),
+                    loanTransaction.getLoan().getOfficeId());
+        }
+    }
+
     private class LoanMakeRepaymentListener implements BusinessEventListener<LoanTransactionMakeRepaymentPostBusinessEvent> {
 
         @Override
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/Charge.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/Charge.java
index 8d2bf69d8..45270e9d1 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/Charge.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/charge/domain/Charge.java
@@ -670,7 +670,7 @@ public class Charge extends AbstractPersistableCustom {
 
         PaymentTypeData paymentTypeData = null;
         if (this.paymentType != null) {
-            paymentTypeData = PaymentTypeData.instance(paymentType.getId(), paymentType.getPaymentName());
+            paymentTypeData = PaymentTypeData.instance(paymentType.getId(), paymentType.getName());
         }
 
         final CurrencyData currency = new CurrencyData(this.currencyCode, null, 0, 0, null, null);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
index 47eb7d89e..5c3281ee3 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanApiConstants.java
@@ -150,4 +150,9 @@ public interface LoanApiConstants {
 
     String DISALLOW_EXPECTED_DISBURSEMENTS = "disallowExpectedDisbursements";
 
+    String TRANSACTION_AMOUNT_PARAMNAME = "transactionAmount";
+    String PAYMENT_TYPE_PARAMNAME = "paymentTypeId";
+
+    // Commands
+    String CHARGEBACK_TRANSACTION_COMMAND = "chargeback";
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
index 62bc82943..a15b976a2 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoanTransactionsApiResource.java
@@ -275,10 +275,17 @@ public class LoanTransactionsApiResource {
             @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PostLoansLoanIdTransactionsTransactionIdResponse.class))) })
     public String adjustLoanTransaction(@PathParam("loanId") @Parameter(description = "loanId") final Long loanId,
             @PathParam("transactionId") @Parameter(description = "transactionId") final Long transactionId,
-            @Parameter(hidden = true) final String apiRequestBodyAsJson) {
+            @Parameter(hidden = true) final String apiRequestBodyAsJson,
+            @QueryParam("command") @Parameter(description = "command") final String commandParam) {
 
         final CommandWrapperBuilder builder = new CommandWrapperBuilder().withJson(apiRequestBodyAsJson);
-        final CommandWrapper commandRequest = builder.adjustTransaction(loanId, transactionId).build();
+
+        CommandWrapper commandRequest = null;
+        if (CommandParameterUtil.is(commandParam, LoanApiConstants.CHARGEBACK_TRANSACTION_COMMAND)) {
+            commandRequest = builder.chargebackTransaction(loanId, transactionId).build();
+        } else { // Default to adjust the Loan Transaction
+            commandRequest = builder.adjustTransaction(loanId, transactionId).build();
+        }
 
         final CommandProcessingResult result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
index 1d1006855..e32065d36 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionData.java
@@ -91,6 +91,8 @@ public class LoanTransactionData {
     private String reversalExternalId;
     private LocalDate reversedOnDate;
 
+    private List<LoanTransactionRelationData> transactionRelations;
+
     public static LoanTransactionData importInstance(BigDecimal repaymentAmount, LocalDate lastRepaymentDate, Long repaymentTypeId,
             Integer rowIndex, String locale, String dateFormat) {
         return new LoanTransactionData(repaymentAmount, lastRepaymentDate, repaymentTypeId, rowIndex, locale, dateFormat);
@@ -348,4 +350,8 @@ public class LoanTransactionData {
     public void setLoanChargePaidByList(Collection<LoanChargePaidByData> loanChargePaidByList) {
         this.loanChargePaidByList = loanChargePaidByList;
     }
+
+    public void setLoanTransactionRelations(List<LoanTransactionRelationData> transactionRelations) {
+        this.transactionRelations = transactionRelations;
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionRelationData.java
similarity index 58%
copy from fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformService.java
copy to fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionRelationData.java
index b3aead6d3..aa848ed72 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionRelationData.java
@@ -16,15 +16,24 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.portfolio.paymenttype.service;
+package org.apache.fineract.portfolio.loanaccount.data;
 
-import java.util.Collection;
-import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
 
-public interface PaymentTypeReadPlatformService {
+@Getter
+@Setter
+@AllArgsConstructor
+public class LoanTransactionRelationData implements Serializable {
 
-    Collection<PaymentTypeData> retrieveAllPaymentTypes();
-
-    PaymentTypeData retrieveOne(Long paymentTypeId);
+    private Long fromLoanTransaction;
+    private Long toLoanTransaction;
+    private LoanTransactionRelationTypeEnum relationType;
+    private BigDecimal amount;
+    private String paymentType;
 
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index f3d72a97d..d70c6ab28 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -1338,7 +1338,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
             final Money principal = this.loanRepaymentScheduleDetail.getPrincipal();
             this.summary.updateSummary(loanCurrency(), principal, getRepaymentScheduleInstallments(), this.loanSummaryWrapper,
                     isDisbursed(), this.charges);
-            updateLoanOutstandingBalaces();
+            updateLoanOutstandingBalances();
         }
     }
 
@@ -2850,7 +2850,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
             chargesPayment.updateComponentsAndTotal(zero, zero, disbursentMoney, zero);
             chargesPayment.updateLoan(this);
             addLoanTransaction(chargesPayment);
-            updateLoanOutstandingBalaces();
+            updateLoanOutstandingBalances();
         }
 
         if (getApprovedOnDate() != null && disbursedOn.isBefore(getApprovedOnDate())) {
@@ -2897,7 +2897,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         chargesPayment.updateComponents(zero, zero, charge.getAmount(getCurrency()), zero);
         chargesPayment.updateLoan(this);
         addLoanTransaction(chargesPayment);
-        updateLoanOutstandingBalaces();
+        updateLoanOutstandingBalances();
         charge.markAsFullyPaid();
         return chargesPayment;
     }
@@ -3426,7 +3426,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
                 addLoanTransaction(finalAccrual);
             }
         }
-        updateLoanOutstandingBalaces();
+        updateLoanOutstandingBalances();
     }
 
     private void determineCumulativeIncomeFromInstallments(HashMap<String, BigDecimal> cumulativeIncomeFromInstallments) {
@@ -3898,6 +3898,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         return changedTransactionDetail;
     }
 
+    public void handleChargebackTransaction(LoanTransaction chargebackTransaction,
+            final LoanLifecycleStateMachine loanLifecycleStateMachine) {
+        chargebackTransaction.updateLoan(this);
+        // TODO To be updated in the repayment schedule
+    }
+
     /**
      * Behaviour added to comply with capability of previous mifos product to support easier transition to fineract
      * platform.
@@ -5422,7 +5428,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
                 addLoanTransaction(accrual);
             }
         }
-        updateLoanOutstandingBalaces();
+        updateLoanOutstandingBalances();
     }
 
     private void determineFeeDetails(LocalDate fromDate, LocalDate toDate, HashMap<String, Object> feeDetails) {
@@ -5665,7 +5671,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
         return interestRecalculatedOn;
     }
 
-    private void updateLoanOutstandingBalaces() {
+    private void updateLoanOutstandingBalances() {
         Money outstanding = Money.zero(getCurrency());
         List<LoanTransaction> loanTransactions = retreiveListOfTransactionsExcludeAccruals();
         for (LoanTransaction loanTransaction : loanTransactions) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanEvent.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanEvent.java
index 9d7f1dfba..e0c11e21f 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanEvent.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanEvent.java
@@ -48,5 +48,6 @@ public enum LoanEvent {
     LOAN_INITIATE_TRANSFER, //
     LOAN_REJECT_TRANSFER, //
     LOAN_WITHDRAW_TRANSFER, //
-    LOAN_CREDIT_BALANCE_REFUND;
+    LOAN_CREDIT_BALANCE_REFUND, //
+    LOAN_CHARGEBACK;
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
index a4d5d3e00..22313cc2b 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java
@@ -130,6 +130,9 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
     @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "loanTransaction")
     private Set<LoanTransactionToRepaymentScheduleMapping> loanTransactionToRepaymentScheduleMappings = new HashSet<>();
 
+    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY, mappedBy = "fromTransaction")
+    private Set<LoanTransactionRelation> loanTransactionRelations = new HashSet<>();
+
     protected LoanTransaction() {}
 
     public static LoanTransaction incomePosting(final Loan loan, final Office office, final LocalDate dateOf, final BigDecimal amount,
@@ -155,6 +158,14 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
         return new LoanTransaction(null, office, LoanTransactionType.REPAYMENT, paymentDetail, amount.getAmount(), paymentDate, externalId);
     }
 
+    public static LoanTransaction chargeback(final Loan loan, final Money amount, final PaymentDetail paymentDetail,
+            final LocalDate paymentDate, final String externalId) {
+        LoanTransaction loanTransaction = new LoanTransaction(loan, loan.getOffice(), LoanTransactionType.CHARGEBACK, paymentDetail,
+                amount.getAmount(), paymentDate, externalId);
+        loanTransaction.principalPortion = amount.getAmount();
+        return loanTransaction;
+    }
+
     public static LoanTransaction repaymentType(final LoanTransactionType repaymentType, final Office office, final Money amount,
             final PaymentDetail paymentDetail, final LocalDate paymentDate, final String externalId, final String chargeRefundChargeType) {
         return new LoanTransaction(null, office, repaymentType, paymentDetail, amount.getAmount(), paymentDate, externalId,
@@ -170,7 +181,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
                 break;
             }
         }
-
     }
 
     public static LoanTransaction recoveryRepayment(final Office office, final Money amount, final PaymentDetail paymentDetail,
@@ -589,6 +599,10 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
         return getTypeOf().isChargePayment() && isNotReversed();
     }
 
+    public boolean isChargeback() {
+        return getTypeOf().isChargeback() && isNotReversed();
+    }
+
     public boolean isPenaltyPayment() {
         boolean isPenalty = false;
         if (isChargePayment()) {
@@ -849,6 +863,14 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
         return submittedOnDate;
     }
 
+    public Set<LoanTransactionRelation> getLoanTransactionRelations() {
+        return loanTransactionRelations;
+    }
+
+    public BigDecimal getAmount() {
+        return amount;
+    }
+
     // TODO missing hashCode(), equals(Object obj), but probably OK as long as
     // this is never stored in a Collection.
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelation.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelation.java
new file mode 100644
index 000000000..07cc3a57c
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelation.java
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.domain;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.EnumType;
+import javax.persistence.Enumerated;
+import javax.persistence.JoinColumn;
+import javax.persistence.Table;
+import javax.persistence.Version;
+import javax.validation.constraints.NotNull;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
+
+@Getter
+@Setter
+@Entity
+@Table(name = "m_loan_transaction_relation")
+public class LoanTransactionRelation extends AbstractAuditableWithUTCDateTimeCustom {
+
+    @JoinColumn(name = "from_loan_transaction_id", nullable = false)
+    private LoanTransaction fromTransaction;
+
+    @JoinColumn(name = "to_loan_transaction_id", nullable = false)
+    private LoanTransaction toTransaction;
+
+    @Enumerated(EnumType.ORDINAL)
+    @Column(name = "relation_type_enum", nullable = false)
+    private LoanTransactionRelationTypeEnum relationType;
+
+    @Version
+    private Long version;
+
+    protected LoanTransactionRelation() {}
+
+    protected LoanTransactionRelation(@NotNull LoanTransaction fromTransaction, @NotNull LoanTransaction toTransaction,
+            LoanTransactionRelationTypeEnum relationType) {
+        this.fromTransaction = fromTransaction;
+        this.toTransaction = toTransaction;
+        this.relationType = relationType;
+    }
+
+    public static LoanTransactionRelation instance(@NotNull LoanTransaction fromTransaction, @NotNull LoanTransaction toTransaction,
+            LoanTransactionRelationTypeEnum relation) {
+        return new LoanTransactionRelation(fromTransaction, toTransaction, relation);
+    }
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentTypeRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelationRepository.java
similarity index 69%
copy from fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentTypeRepository.java
copy to fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelationRepository.java
index d4119bdf2..6eeec08ea 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentTypeRepository.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelationRepository.java
@@ -16,11 +16,16 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.portfolio.paymenttype.domain;
+package org.apache.fineract.portfolio.loanaccount.domain;
 
+import java.util.List;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 
-public interface PaymentTypeRepository extends JpaRepository<PaymentType, Long>, JpaSpecificationExecutor<PaymentType> {
+public interface LoanTransactionRelationRepository
+        extends JpaRepository<LoanTransactionRelation, Long>, JpaSpecificationExecutor<LoanTransactionRelation> {
+
+    List<LoanTransactionRelation> findByFromTransactionAndRelationType(LoanTransaction loanTransaction,
+            LoanTransactionRelationTypeEnum relationType);
 
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelationTypeEnum.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelationTypeEnum.java
new file mode 100644
index 000000000..d07987798
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionRelationTypeEnum.java
@@ -0,0 +1,54 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.domain;
+
+public enum LoanTransactionRelationTypeEnum {
+
+    INVALID(0, "loanTransactionType.invalid"), //
+    CHARGEBACK(1, "loanTransactionRelationType.chargeback");
+
+    private final Integer value;
+    private final String code;
+
+    LoanTransactionRelationTypeEnum(final Integer value, final String code) {
+        this.value = value;
+        this.code = code;
+    }
+
+    public Integer getValue() {
+        return this.value;
+    }
+
+    public String getCode() {
+        return this.code;
+    }
+
+    public static LoanTransactionRelationTypeEnum fromInt(final Integer transactionType) {
+
+        if (transactionType == null) {
+            return LoanTransactionRelationTypeEnum.INVALID;
+        }
+
+        return switch (transactionType) {
+            case 1 -> LoanTransactionRelationTypeEnum.CHARGEBACK;
+            default -> LoanTransactionRelationTypeEnum.INVALID;
+        };
+    }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionType.java
index 1515d656e..7fee78b88 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionType.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionType.java
@@ -173,4 +173,9 @@ public enum LoanTransactionType {
     public boolean isIncomePosting() {
         return this.value.equals(LoanTransactionType.INCOME_POSTING.getValue());
     }
+
+    public boolean isChargeback() {
+        return this.value.equals(LoanTransactionType.CHARGEBACK.getValue());
+    }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentChargebackCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentChargebackCommandHandler.java
new file mode 100644
index 000000000..58991fac3
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentChargebackCommandHandler.java
@@ -0,0 +1,43 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.handler;
+
+import lombok.RequiredArgsConstructor;
+import org.apache.fineract.commands.annotation.CommandType;
+import org.apache.fineract.commands.handler.NewCommandSourceHandler;
+import org.apache.fineract.infrastructure.core.api.JsonCommand;
+import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
+import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+@Service
+@RequiredArgsConstructor
+@CommandType(entity = "LOAN", action = "CHARGEBACK")
+public class LoanRepaymentChargebackCommandHandler implements NewCommandSourceHandler {
+
+    private final LoanWritePlatformService writePlatformService;
+
+    @Transactional
+    @Override
+    public CommandProcessingResult processCommand(final JsonCommand command) {
+        return this.writePlatformService.chargebackLoanTransaction(command.getLoanId(), command.entityId(), command);
+    }
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTransactionRelationMapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTransactionRelationMapper.java
new file mode 100644
index 000000000..37f524e50
--- /dev/null
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/mapper/LoanTransactionRelationMapper.java
@@ -0,0 +1,39 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.portfolio.loanaccount.mapper;
+
+import java.util.List;
+import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionRelationData;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+
+@Mapper(componentModel = "spring")
+public interface LoanTransactionRelationMapper {
+
+    @Mapping(target = "fromLoanTransaction", source = "source.fromTransaction.id")
+    @Mapping(target = "toLoanTransaction", source = "source.toTransaction.id")
+    @Mapping(target = "relationType", source = "source.relationType")
+    @Mapping(target = "amount", source = "source.toTransaction.amount")
+    @Mapping(target = "paymentType", source = "source.toTransaction.paymentDetail.paymentType.name")
+    LoanTransactionRelationData map(LoanTransactionRelation source);
+
+    List<LoanTransactionRelationData> map(List<LoanTransactionRelation> sources);
+
+}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java
index 1e0ac6c90..1da8a069f 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanEventApiJsonValidator.java
@@ -212,7 +212,37 @@ public final class LoanEventApiJsonValidator {
         baseDataValidator.reset().parameter(LoanApiConstants.REVERSAL_EXTERNAL_ID_PARAMNAME).ignoreIfNull().value(reversalExternalId)
                 .notExceedingLengthOf(100);
 
-        validatePaymentDetails(baseDataValidator, element);
+        validatePaymentDetails(baseDataValidator, element, false);
+        throwExceptionIfValidationWarningsExist(dataValidationErrors);
+    }
+
+    public void validateChargebackTransaction(final String json) {
+
+        if (StringUtils.isBlank(json)) {
+            throw new InvalidJsonException();
+        }
+
+        final Set<String> transactionParameters = new HashSet<>(Arrays.asList(LoanApiConstants.TRANSACTION_AMOUNT_PARAMNAME,
+                LoanApiConstants.localeParameterName, LoanApiConstants.externalIdParameterName, LoanApiConstants.noteParameterName,
+                LoanApiConstants.PAYMENT_TYPE_PARAMNAME));
+
+        final Type typeOfMap = new TypeToken<Map<String, Object>>() {}.getType();
+        this.fromApiJsonHelper.checkForUnsupportedParameters(typeOfMap, json, transactionParameters);
+
+        final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
+        final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("loan.transaction");
+
+        final JsonElement element = this.fromApiJsonHelper.parse(json);
+
+        final BigDecimal transactionAmount = this.fromApiJsonHelper
+                .extractBigDecimalWithLocaleNamed(LoanApiConstants.TRANSACTION_AMOUNT_PARAMNAME, element);
+        baseDataValidator.reset().parameter(LoanApiConstants.TRANSACTION_AMOUNT_PARAMNAME).value(transactionAmount).notNull()
+                .zeroOrPositiveAmount();
+
+        final String note = this.fromApiJsonHelper.extractStringNamed(LoanApiConstants.noteParameterName, element);
+        baseDataValidator.reset().parameter(LoanApiConstants.noteParameterName).value(note).notExceedingLengthOf(1000);
+
+        validatePaymentDetails(baseDataValidator, element, true);
         throwExceptionIfValidationWarningsExist(dataValidationErrors);
     }
 
@@ -262,9 +292,19 @@ public final class LoanEventApiJsonValidator {
     }
 
     private void validatePaymentDetails(final DataValidatorBuilder baseDataValidator, final JsonElement element) {
+        final boolean paymentDetailRequired = false; // Default value for backward compatibility
+        validatePaymentDetails(baseDataValidator, element, paymentDetailRequired);
+    }
+
+    private void validatePaymentDetails(final DataValidatorBuilder baseDataValidator, final JsonElement element,
+            final boolean paymentDetailRequired) {
         // Validate all string payment detail fields for max length
         final Integer paymentTypeId = this.fromApiJsonHelper.extractIntegerWithLocaleNamed("paymentTypeId", element);
-        baseDataValidator.reset().parameter("paymentTypeId").value(paymentTypeId).ignoreIfNull().integerGreaterThanZero();
+        if (paymentDetailRequired) {
+            baseDataValidator.reset().parameter("paymentTypeId").value(paymentTypeId).notNull().integerGreaterThanZero();
+        } else {
+            baseDataValidator.reset().parameter("paymentTypeId").value(paymentTypeId).ignoreIfNull().integerGreaterThanZero();
+        }
         final Set<String> paymentDetailParameters = new HashSet<>(
                 Arrays.asList("accountNumber", "checkNumber", "routingCode", "receiptNumber", "bankNumber"));
         for (final String paymentDetailParameterName : paymentDetailParameters) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
index f52f20163..b47ff7547 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformService.java
@@ -36,6 +36,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleAccrualData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanScheduleDelinquencyData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
+import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionRelationData;
 import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
 import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
@@ -157,4 +158,7 @@ public interface LoanReadPlatformService {
     List<LoanRepaymentScheduleInstallmentData> getRepaymentDataResponse(Long loanId);
 
     CollectionData retrieveLoanCollectionData(Long loanId);
+
+    List<LoanTransactionRelationData> retrieveLoanTransactionRelationsByLoanTransactionId(Long loanTransactionId);
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index 9c0d3266e..7005b8adb 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -34,6 +34,7 @@ import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Optional;
+import lombok.AllArgsConstructor;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.fineract.accounting.common.AccountingRuleType;
 import org.apache.fineract.infrastructure.codes.data.CodeValueData;
@@ -97,6 +98,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanSummaryData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionEnumData;
+import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionRelationData;
 import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
 import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
 import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
@@ -108,12 +110,17 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanSubStatus;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import org.apache.fineract.portfolio.loanaccount.exception.LoanNotFoundException;
 import org.apache.fineract.portfolio.loanaccount.exception.LoanTransactionNotFoundException;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanScheduleData;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.data.LoanSchedulePeriodData;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
+import org.apache.fineract.portfolio.loanaccount.mapper.LoanTransactionRelationMapper;
 import org.apache.fineract.portfolio.loanproduct.data.LoanProductData;
 import org.apache.fineract.portfolio.loanproduct.data.TransactionProcessingStrategyData;
 import org.apache.fineract.portfolio.loanproduct.domain.InterestMethod;
@@ -125,7 +132,6 @@ import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
 import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadPlatformService;
 import org.apache.fineract.useradministration.domain.AppUser;
 import org.jetbrains.annotations.NotNull;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.DataAccessException;
 import org.springframework.dao.EmptyResultDataAccessException;
 import org.springframework.jdbc.core.JdbcTemplate;
@@ -136,6 +142,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.CollectionUtils;
 
+@AllArgsConstructor
 @Service
 @Transactional(readOnly = true)
 public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
@@ -154,7 +161,6 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
     private final CalendarReadPlatformService calendarReadPlatformService;
     private final StaffReadPlatformService staffReadPlatformService;
     private final PaginationHelper paginationHelper;
-    private final LoanMapper loanMapper;
     private final NamedParameterJdbcTemplate namedParameterJdbcTemplate;
     private final PaymentTypeReadPlatformService paymentTypeReadPlatformService;
     private final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory;
@@ -165,48 +171,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
     private final ColumnValidator columnValidator;
     private final DatabaseSpecificSQLGenerator sqlGenerator;
     private final DelinquencyReadPlatformService delinquencyReadPlatformService;
-
-    @Autowired
-    public LoanReadPlatformServiceImpl(final PlatformSecurityContext context,
-            final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepository,
-            final LoanProductReadPlatformService loanProductReadPlatformService, final ClientReadPlatformService clientReadPlatformService,
-            final GroupReadPlatformService groupReadPlatformService, final LoanDropdownReadPlatformService loanDropdownReadPlatformService,
-            final FundReadPlatformService fundReadPlatformService, final ChargeReadPlatformService chargeReadPlatformService,
-            final CodeValueReadPlatformService codeValueReadPlatformService, final JdbcTemplate jdbcTemplate,
-            final NamedParameterJdbcTemplate namedParameterJdbcTemplate, final CalendarReadPlatformService calendarReadPlatformService,
-            final StaffReadPlatformService staffReadPlatformService, final PaymentTypeReadPlatformService paymentTypeReadPlatformService,
-            final LoanRepaymentScheduleTransactionProcessorFactory loanRepaymentScheduleTransactionProcessorFactory,
-            final FloatingRatesReadPlatformService floatingRatesReadPlatformService, final LoanUtilService loanUtilService,
-            final ConfigurationDomainService configurationDomainService,
-            final DelinquencyReadPlatformService delinquencyReadPlatformService,
-            final AccountDetailsReadPlatformService accountDetailsReadPlatformService, final LoanRepositoryWrapper loanRepositoryWrapper,
-            final ColumnValidator columnValidator, DatabaseSpecificSQLGenerator sqlGenerator, PaginationHelper paginationHelper) {
-        this.context = context;
-        this.loanRepositoryWrapper = loanRepositoryWrapper;
-        this.applicationCurrencyRepository = applicationCurrencyRepository;
-        this.loanProductReadPlatformService = loanProductReadPlatformService;
-        this.clientReadPlatformService = clientReadPlatformService;
-        this.groupReadPlatformService = groupReadPlatformService;
-        this.loanDropdownReadPlatformService = loanDropdownReadPlatformService;
-        this.fundReadPlatformService = fundReadPlatformService;
-        this.chargeReadPlatformService = chargeReadPlatformService;
-        this.codeValueReadPlatformService = codeValueReadPlatformService;
-        this.calendarReadPlatformService = calendarReadPlatformService;
-        this.staffReadPlatformService = staffReadPlatformService;
-        this.jdbcTemplate = jdbcTemplate;
-        this.namedParameterJdbcTemplate = namedParameterJdbcTemplate;
-        this.paymentTypeReadPlatformService = paymentTypeReadPlatformService;
-        this.loanRepaymentScheduleTransactionProcessorFactory = loanRepaymentScheduleTransactionProcessorFactory;
-        this.floatingRatesReadPlatformService = floatingRatesReadPlatformService;
-        this.loanUtilService = loanUtilService;
-        this.configurationDomainService = configurationDomainService;
-        this.accountDetailsReadPlatformService = accountDetailsReadPlatformService;
-        this.columnValidator = columnValidator;
-        this.loanMapper = new LoanMapper(sqlGenerator, delinquencyReadPlatformService);
-        this.sqlGenerator = sqlGenerator;
-        this.paginationHelper = paginationHelper;
-        this.delinquencyReadPlatformService = delinquencyReadPlatformService;
-    }
+    private final LoanTransactionRepository loanTransactionRepository;
+    private final LoanTransactionRelationRepository loanTransactionRelationRepository;
+    private final LoanTransactionRelationMapper loanTransactionRelationMapper;
 
     @Override
     public LoanAccountData retrieveOne(final Long loanId) {
@@ -296,9 +263,16 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
             /***
              * TODO Vishwas: Remove references to "Contra" from the codebase
              ***/
-            final String sql = "select " + rm.loanPaymentsSchema()
-                    + " where tr.loan_id = ? and tr.transaction_type_enum not in (0, 3) and  (tr.is_reversed=false or tr.manually_adjusted_or_reversed = true) order by tr.transaction_date ASC,id ";
-            return this.jdbcTemplate.query(sql, rm, loanId); // NOSONAR
+            final String sql = "select " + rm.loanPaymentsSchema() + " where tr.loan_id = ? and tr.transaction_type_enum not in (0, 3) "
+                    + " and (tr.is_reversed=false or tr.manually_adjusted_or_reversed = true) " + " order by tr.transaction_date ASC,id ";
+            Collection<LoanTransactionData> loanTransactionData = this.jdbcTemplate.query(sql, rm, loanId); // NOSONAR
+            for (LoanTransactionData loanTransaction : loanTransactionData) {
+                if (loanTransaction.getType().isRepaymentType()) {
+                    loanTransaction
+                            .setLoanTransactionRelations(this.retrieveLoanTransactionRelationsByLoanTransactionId(loanTransaction.getId()));
+                }
+            }
+            return loanTransactionData;
         } catch (final EmptyResultDataAccessException e) {
             return null;
         }
@@ -310,10 +284,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
         final AppUser currentUser = this.context.authenticatedUser();
         final String hierarchy = currentUser.getOffice().getHierarchy();
         final String hierarchySearchString = hierarchy + "%";
+        final LoanMapper loanMapper = new LoanMapper(sqlGenerator, delinquencyReadPlatformService);
 
         final StringBuilder sqlBuilder = new StringBuilder(200);
         sqlBuilder.append("select " + sqlGenerator.calcFoundRows() + " ");
-        sqlBuilder.append(this.loanMapper.loanSchema());
+        sqlBuilder.append(loanMapper.loanSchema());
 
         // TODO - for time being this will data scope list of loans returned to
         // only loans that have a client associated.
@@ -377,7 +352,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
         }
         final Object[] objectArray = extraCriterias.toArray();
         final Object[] finalObjectArray = Arrays.copyOf(objectArray, arrayPos);
-        return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, this.loanMapper);
+        return this.paginationHelper.fetchPage(this.jdbcTemplate, sqlBuilder.toString(), finalObjectArray, loanMapper);
     }
 
     @Override
@@ -588,7 +563,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
         try {
             final LoanTransactionsMapper rm = new LoanTransactionsMapper(sqlGenerator);
             final String sql = "select " + rm.loanPaymentsSchema() + " where l.id = ? and tr.id = ? ";
-            return this.jdbcTemplate.queryForObject(sql, rm, loanId, transactionId); // NOSONAR
+            LoanTransactionData loanTransactionData = this.jdbcTemplate.queryForObject(sql, rm, loanId, transactionId); // NOSONAR
+            loanTransactionData.setLoanTransactionRelations(this.retrieveLoanTransactionRelationsByLoanTransactionId(transactionId));
+            return loanTransactionData;
         } catch (final EmptyResultDataAccessException e) {
             throw new LoanTransactionNotFoundException(transactionId, e);
         }
@@ -1310,10 +1287,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
         public String loanPaymentsSchema() {
 
             return " tr.id as id, tr.transaction_type_enum as transactionType, tr.transaction_date as " + sqlGenerator.escape("date")
-                    + ", tr.amount as total, " + " tr.principal_portion_derived as principal, tr.interest_portion_derived as interest, "
-                    + " tr.fee_charges_portion_derived as fees, tr.penalty_charges_portion_derived as penalties, "
+                    + ", tr.amount as total, tr.principal_portion_derived as principal, tr.interest_portion_derived as interest, "
+                    + " tr.fee_charges_portion_derived as fees, tr.penalty_charges_portion_derived as penalties,  "
                     + " tr.overpayment_portion_derived as overpayment, tr.outstanding_loan_balance_derived as outstandingLoanBalance, "
-                    + " tr.unrecognized_income_portion as unrecognizedIncome," + " tr.submitted_on_date as submittedOnDate, "
+                    + " tr.unrecognized_income_portion as unrecognizedIncome, tr.submitted_on_date as submittedOnDate, "
                     + " tr.manually_adjusted_or_reversed as manuallyReversed, tr.reversal_external_id as reversalExternalId, tr.reversed_on_date as reversedOnDate, "
                     + " pd.payment_type_id as paymentType,pd.account_number as accountNumber,pd.check_number as checkNumber, "
                     + " pd.receipt_number as receiptNumber, pd.bank_number as bankNumber,pd.routing_code as routingCode, l.net_disbursal_amount as netDisbursalAmount,"
@@ -1323,10 +1300,10 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
                     + " pt.value as paymentTypeName, tr.external_id as externalId, tr.office_id as officeId, office.name as officeName, "
                     + " fromtran.id as fromTransferId, fromtran.is_reversed as fromTransferReversed,"
                     + " fromtran.transaction_date as fromTransferDate, fromtran.amount as fromTransferAmount,"
-                    + " fromtran.description as fromTransferDescription,"
-                    + " totran.id as toTransferId, totran.is_reversed as toTransferReversed,"
+                    + " fromtran.description as fromTransferDescription, "
+                    + " totran.id as toTransferId, totran.is_reversed as toTransferReversed, "
                     + " totran.transaction_date as toTransferDate, totran.amount as toTransferAmount,"
-                    + " totran.description as toTransferDescription " + " from m_loan l join m_loan_transaction tr on tr.loan_id = l.id"
+                    + " totran.description as toTransferDescription from m_loan l join m_loan_transaction tr on tr.loan_id = l.id "
                     + " join m_currency rc on rc." + sqlGenerator.escape("code") + " = l.currency_code "
                     + " left JOIN m_payment_detail pd ON tr.payment_detail_id = pd.id"
                     + " left join m_payment_type pt on pd.payment_type_id = pt.id" + " left join m_office office on office.id=tr.office_id"
@@ -1405,6 +1382,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
                 transfer = AccountTransferData.transferBasicDetails(toTransferId, currencyData, toTransferAmount, toTransferDate,
                         toTransferDescription, toTransferReversed);
             }
+
             return new LoanTransactionData(id, officeId, officeName, transactionType, paymentDetailData, currencyData, date, totalAmount,
                     netDisbursalAmount, principalPortion, interestPortion, feeChargesPortion, penaltyChargesPortion, overPaymentPortion,
                     unrecognizedIncomePortion, externalId, transfer, null, outstandingLoanBalance, submittedOnDate, manuallyReversed,
@@ -2066,7 +2044,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
         public String schema() {
 
             return " tr.id as id, tr.transaction_type_enum as transactionType, tr.transaction_date as " + sqlGenerator.escape("date")
-                    + ", tr.amount as total, " + " tr.principal_portion_derived as principal, tr.interest_portion_derived as interest, "
+                    + ", tr.amount as total, tr.principal_portion_derived as principal, tr.interest_portion_derived as interest, "
                     + " tr.fee_charges_portion_derived as fees, tr.penalty_charges_portion_derived as penalties, "
                     + " tr.overpayment_portion_derived as overpayment, tr.outstanding_loan_balance_derived as outstandingLoanBalance, "
                     + " tr.unrecognized_income_portion as unrecognizedIncome " + " from m_loan_transaction tr ";
@@ -2492,4 +2470,12 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
                     delinquentAmount, lastPaymentDate, lastPaymentAmount);
         }
     }
+
+    @Override
+    public List<LoanTransactionRelationData> retrieveLoanTransactionRelationsByLoanTransactionId(Long loanTransactionId) {
+        final LoanTransaction loanTransaction = this.loanTransactionRepository.getReferenceById(loanTransactionId);
+        List<LoanTransactionRelation> loanTransactionRelations = this.loanTransactionRelationRepository
+                .findByFromTransactionAndRelationType(loanTransaction, LoanTransactionRelationTypeEnum.CHARGEBACK);
+        return loanTransactionRelationMapper.map(loanTransactionRelations);
+    }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java
index d8e8a29c1..fb419edd7 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformService.java
@@ -50,6 +50,8 @@ public interface LoanWritePlatformService {
 
     CommandProcessingResult adjustLoanTransaction(Long loanId, Long transactionId, JsonCommand command);
 
+    CommandProcessingResult chargebackLoanTransaction(Long loanId, Long transactionId, JsonCommand command);
+
     CommandProcessingResult waiveInterestOnLoan(Long loanId, JsonCommand command);
 
     CommandProcessingResult writeOff(Long loanId, JsonCommand command);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
index 7115b2d90..7d010b191 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanWritePlatformServiceJpaRepositoryImpl.java
@@ -59,6 +59,7 @@ import org.apache.fineract.infrastructure.event.business.domain.loan.LoanAcceptT
 import org.apache.fineract.infrastructure.event.business.domain.loan.LoanAdjustTransactionBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.LoanApplyOverdueChargeBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBalanceChangedBusinessEvent;
+import org.apache.fineract.infrastructure.event.business.domain.loan.LoanChargebackTransactionBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.LoanCloseAsRescheduleBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.LoanCloseBusinessEvent;
 import org.apache.fineract.infrastructure.event.business.domain.loan.LoanDisbursalBusinessEvent;
@@ -180,6 +181,9 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanSubStatus;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTrancheDisbursementCharge;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelation;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationRepository;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import org.apache.fineract.portfolio.loanaccount.exception.DateMismatchException;
@@ -237,6 +241,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
     private final LoanAccountDomainService loanAccountDomainService;
     private final NoteRepository noteRepository;
     private final LoanTransactionRepository loanTransactionRepository;
+    private final LoanTransactionRelationRepository loanTransactionRelationRepository;
     private final LoanAssembler loanAssembler;
     private final ChargeRepositoryWrapper chargeRepository;
     private final LoanChargeRepository loanChargeRepository;
@@ -392,7 +397,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
         final Map<String, Object> changes = new LinkedHashMap<>();
 
         final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createAndPersistPaymentDetail(command, changes);
-        if (paymentDetail != null && paymentDetail.getPaymentType() != null && paymentDetail.getPaymentType().isCashPayment()) {
+        if (paymentDetail != null && paymentDetail.getPaymentType() != null && paymentDetail.getPaymentType().getIsCashPayment()) {
             BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed("transactionAmount");
             this.cashierTransactionDataValidator.validateOnLoanDisbursal(currentUser, loan.getCurrencyCode(), transactionAmount);
         }
@@ -1184,6 +1189,103 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
                 .with(changes).build();
     }
 
+    @Transactional
+    @Override
+    public CommandProcessingResult chargebackLoanTransaction(final Long loanId, final Long transactionId, final JsonCommand command) {
+        this.loanEventApiJsonValidator.validateChargebackTransaction(command.json());
+
+        Loan loan = this.loanAssembler.assembleFrom(loanId);
+        if (this.accountTransfersReadPlatformService.isAccountTransfer(transactionId, PortfolioAccountType.LOAN)) {
+            throw new PlatformServiceUnavailableException("error.msg.loan.transfer.transaction.update.not.allowed",
+                    "Loan transaction:" + transactionId + " chargeback not allowed as it involves in account transfer", transactionId);
+        }
+        if (loan.isClosedWrittenOff()) {
+            throw new PlatformServiceUnavailableException("error.msg.loan.chargeback.operation.not.allowed",
+                    "Loan transaction:" + transactionId + " chargeback not allowed as loan status is written off", transactionId);
+        }
+        if (loan.repaymentScheduleDetail().isInterestRecalculationEnabled()) {
+            throw new PlatformServiceUnavailableException("error.msg.loan.chargeback.operation.not.allowed",
+                    "Loan transaction:" + transactionId + " chargeback not allowed as loan product is interest recalculation enabled",
+                    transactionId);
+        }
+
+        final List<Long> existingTransactionIds = new ArrayList<>();
+        final List<Long> existingReversedTransactionIds = new ArrayList<>();
+
+        checkClientOrGroupActive(loan);
+
+        LoanTransaction loanTransaction = this.loanTransactionRepository.findById(transactionId)
+                .orElseThrow(() -> new LoanTransactionNotFoundException(transactionId));
+
+        if (!loanTransaction.isRepayment()) {
+            throw new PlatformServiceUnavailableException("error.msg.loan.chargeback.operation.not.allowed",
+                    "Loan transaction:" + transactionId + " chargeback not allowed as loan transaction is not repayment", transactionId);
+        }
+
+        businessEventNotifierService.notifyPreBusinessEvent(new LoanChargebackTransactionBusinessEvent(loanTransaction));
+
+        final LocalDate transactionDate = DateUtils.getBusinessLocalDate();
+        final BigDecimal transactionAmount = command.bigDecimalValueOfParameterNamed(LoanApiConstants.TRANSACTION_AMOUNT_PARAMNAME);
+        final String txnExternalId = command.stringValueOfParameterNamedAllowingNull(LoanApiConstants.externalIdParameterName);
+
+        final Map<String, Object> changes = new LinkedHashMap<>();
+        changes.put("transactionAmount", command.stringValueOfParameterNamed(LoanApiConstants.TRANSACTION_AMOUNT_PARAMNAME));
+        changes.put("locale", command.locale());
+        changes.put("dateFormat", command.dateFormat());
+        changes.put("paymentTypeId", command.stringValueOfParameterNamed(LoanApiConstants.PAYMENT_TYPE_PARAMNAME));
+
+        final Money transactionAmountAsMoney = Money.of(loan.getCurrency(), transactionAmount);
+        final PaymentDetail paymentDetail = this.paymentDetailWritePlatformService.createPaymentDetail(command, changes);
+        LoanTransaction newTransaction = LoanTransaction.chargeback(loan, transactionAmountAsMoney, paymentDetail, transactionDate,
+                txnExternalId);
+
+        validateLoanTransactionAmountChargeBack(loanTransaction, newTransaction);
+
+        this.paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail);
+
+        loan.handleChargebackTransaction(newTransaction, defaultLoanLifecycleStateMachine());
+
+        loan = saveAndFlushLoanWithDataIntegrityViolationChecks(loan);
+
+        // Store the Loan Transaction Relation
+        LoanTransactionRelation loanTransactionRelation = LoanTransactionRelation.instance(loanTransaction, newTransaction,
+                LoanTransactionRelationTypeEnum.CHARGEBACK);
+        this.loanTransactionRelationRepository.save(loanTransactionRelation);
+
+        this.loanTransactionRepository.saveAndFlush(newTransaction);
+
+        final String noteText = command.stringValueOfParameterNamed(LoanApiConstants.noteParamName);
+        if (StringUtils.isNotBlank(noteText)) {
+            changes.put("note", noteText);
+            Note note = Note.loanTransactionNote(loan, newTransaction, noteText);
+            this.noteRepository.save(note);
+        }
+
+        postJournalEntries(loan, existingTransactionIds, existingReversedTransactionIds);
+        this.loanAccountDomainService.setLoanDelinquencyTag(loan, DateUtils.getBusinessLocalDate());
+
+        businessEventNotifierService.notifyPostBusinessEvent(new LoanChargebackTransactionBusinessEvent(loanTransaction));
+
+        return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(transactionId)
+                .withOfficeId(loan.getOfficeId()).withClientId(loan.getClientId()).withGroupId(loan.getGroupId()).withLoanId(loanId)
+                .with(changes).build();
+    }
+
+    private void validateLoanTransactionAmountChargeBack(LoanTransaction loanTransaction, LoanTransaction chargebackTransaction) {
+        BigDecimal actualAmount = BigDecimal.ZERO;
+        for (LoanTransactionRelation loanTransactionRelation : loanTransaction.getLoanTransactionRelations()) {
+            if (loanTransactionRelation.getRelationType().equals(LoanTransactionRelationTypeEnum.CHARGEBACK)) {
+                actualAmount = actualAmount.add(loanTransactionRelation.getToTransaction().getPrincipalPortion());
+            }
+        }
+        actualAmount = actualAmount.add(chargebackTransaction.getPrincipalPortion());
+        if (actualAmount.compareTo(loanTransaction.getPrincipalPortion()) > 0) {
+            throw new PlatformServiceUnavailableException("error.msg.loan.chargeback.operation.not.allowed",
+                    "Loan transaction:" + loanTransaction.getId() + " chargeback not allowed as loan transaction amount is not enough",
+                    loanTransaction.getId());
+        }
+    }
+
     @Transactional
     @Override
     public CommandProcessingResult waiveInterestOnLoan(final Long loanId, final JsonCommand command) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymentdetail/domain/PaymentDetail.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymentdetail/domain/PaymentDetail.java
index 8c0cbaecf..b737c7631 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymentdetail/domain/PaymentDetail.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymentdetail/domain/PaymentDetail.java
@@ -103,7 +103,7 @@ public final class PaymentDetail extends AbstractPersistableCustom {
     }
 
     public PaymentDetailData toData() {
-        final PaymentTypeData paymentTypeData = this.paymentType.toData();
+        final PaymentTypeData paymentTypeData = null; // this.paymentType.toData();
         final PaymentDetailData paymentDetailData = new PaymentDetailData(getId(), paymentTypeData, this.accountNumber, this.checkNumber,
                 this.routingCode, this.receiptNumber, this.bankNumber);
         return paymentDetailData;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/api/PaymentTypeApiResource.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/api/PaymentTypeApiResource.java
index f967457cf..aeb359e77 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/api/PaymentTypeApiResource.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/api/PaymentTypeApiResource.java
@@ -36,9 +36,11 @@ import javax.ws.rs.PUT;
 import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
+import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.UriInfo;
+import lombok.AllArgsConstructor;
 import org.apache.fineract.commands.domain.CommandWrapper;
 import org.apache.fineract.commands.service.CommandWrapperBuilder;
 import org.apache.fineract.commands.service.PortfolioCommandSourceWritePlatformService;
@@ -50,9 +52,9 @@ import org.apache.fineract.infrastructure.security.service.PlatformSecurityConte
 import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
 import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
 import org.apache.fineract.portfolio.paymenttype.service.PaymentTypeReadPlatformService;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+@AllArgsConstructor
 @Path("/paymenttypes")
 @Component
 
@@ -64,34 +66,23 @@ public class PaymentTypeApiResource {
     private final PaymentTypeReadPlatformService readPlatformService;
     private final PortfolioCommandSourceWritePlatformService commandWritePlatformService;
     private final ApiRequestParameterHelper apiRequestParameterHelper;
-    // private final String resourceNameForPermissions = "PAYMENT_TYPE";
     private final PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper;
 
-    // private final Set<String> RESPONSE_DATA_PARAMETERS = new
-    // HashSet<>(Arrays.asList("id", "value", "description", "isCashPayment"));
-
-    @Autowired
-    public PaymentTypeApiResource(PlatformSecurityContext securityContext, DefaultToApiJsonSerializer<PaymentTypeData> jsonSerializer,
-            PaymentTypeReadPlatformService readPlatformService, PaymentTypeRepositoryWrapper paymentTypeRepositoryWrapper,
-            ApiRequestParameterHelper apiRequestParameterHelper, PortfolioCommandSourceWritePlatformService commandWritePlatformService) {
-
-        this.securityContext = securityContext;
-        this.jsonSerializer = jsonSerializer;
-        this.readPlatformService = readPlatformService;
-        this.paymentTypeRepositoryWrapper = paymentTypeRepositoryWrapper;
-        this.apiRequestParameterHelper = apiRequestParameterHelper;
-        this.commandWritePlatformService = commandWritePlatformService;
-    }
-
     @GET
     @Consumes({ MediaType.TEXT_HTML, MediaType.APPLICATION_JSON })
     @Produces(MediaType.APPLICATION_JSON)
     @Operation(summary = "Retrieve all Payment Types", description = "Retrieve list of payment types")
     @ApiResponses({
             @ApiResponse(responseCode = "200", description = "OK", content = @Content(array = @ArraySchema(schema = @Schema(implementation = PaymentTypeApiResourceSwagger.GetPaymentTypesResponse.class)))) })
-    public String getAllPaymentTypes(@Context final UriInfo uriInfo) {
+    public String getAllPaymentTypes(@Context final UriInfo uriInfo,
+            @QueryParam("onlyWithCode") @Parameter(description = "onlyWithCode") final boolean onlyWithCode) {
         this.securityContext.authenticatedUser().validateHasReadPermission(PaymentTypeApiResourceConstants.resourceNameForPermissions);
-        final Collection<PaymentTypeData> paymentTypes = this.readPlatformService.retrieveAllPaymentTypes();
+        Collection<PaymentTypeData> paymentTypes = null;
+        if (onlyWithCode) {
+            paymentTypes = this.readPlatformService.retrieveAllPaymentTypesWithCode();
+        } else {
+            paymentTypes = this.readPlatformService.retrieveAllPaymentTypes();
+        }
         final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
         return this.jsonSerializer.serialize(settings, paymentTypes, PaymentTypeApiResourceConstants.RESPONSE_DATA_PARAMETERS);
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/api/PaymentTypeApiResourceConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/api/PaymentTypeApiResourceConstants.java
index 8d8440983..be7b4f00f 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/api/PaymentTypeApiResourceConstants.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/api/PaymentTypeApiResourceConstants.java
@@ -37,6 +37,8 @@ public final class PaymentTypeApiResourceConstants {
     public static final String DESCRIPTION = "description";
     public static final String ISCASHPAYMENT = "isCashPayment";
     public static final String POSITION = "position";
+    public static final String CODE_NAME = "code_name";
+    public static final String IS_SYSTEM_DEFINED = "system_defined";
 
     static final Set<String> RESPONSE_DATA_PARAMETERS = new HashSet<>(Arrays.asList(ID, NAME, DESCRIPTION, ISCASHPAYMENT));
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/api/PaymentTypeApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/api/PaymentTypeApiResourceSwagger.java
index ead8bd51d..8813ec286 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/api/PaymentTypeApiResourceSwagger.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/api/PaymentTypeApiResourceSwagger.java
@@ -42,6 +42,10 @@ final class PaymentTypeApiResourceSwagger {
         public Boolean isCashPayment;
         @Schema(example = "0")
         public Integer position;
+        @Schema(example = "REPAYMENT_REFUND")
+        public String codeName;
+        @Schema(example = "false")
+        public Boolean isSystemDefined;
     }
 
     @Schema(description = "GetPaymentTypesPaymentTypeIdResponse")
@@ -59,6 +63,10 @@ final class PaymentTypeApiResourceSwagger {
         public Boolean isCashPayment;
         @Schema(example = "1")
         public Integer position;
+        @Schema(example = "REPAYMENT_REFUND")
+        public String codeName;
+        @Schema(example = "false")
+        public Boolean isSystemDefined;
     }
 
     @Schema(description = "PostPaymentTypesRequest")
@@ -74,6 +82,10 @@ final class PaymentTypeApiResourceSwagger {
         public Boolean isCashPayment;
         @Schema(example = "1")
         public Integer position;
+        @Schema(example = "REPAYMENT_REFUND")
+        public String codeName;
+        @Schema(example = "false")
+        public Boolean isSystemDefined;
     }
 
     @Schema(description = "PostPaymentTypesResponse")
@@ -98,6 +110,10 @@ final class PaymentTypeApiResourceSwagger {
         public Boolean isCashPayment;
         @Schema(example = "3")
         public Integer position;
+        @Schema(example = "REPAYMENT_REFUND")
+        public String codeName;
+        @Schema(example = "false")
+        public Boolean isSystemDefined;
     }
 
     @Schema(description = "PutPaymentTypesPaymentTypeIdResponse")
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/data/PaymentTypeData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/data/PaymentTypeData.java
index ee18a19ff..b626469bb 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/data/PaymentTypeData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/data/PaymentTypeData.java
@@ -22,8 +22,12 @@ import java.io.Serializable;
 import lombok.AllArgsConstructor;
 import lombok.EqualsAndHashCode;
 import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
 
+@ToString
 @Getter
+@Setter
 @EqualsAndHashCode
 @AllArgsConstructor
 public class PaymentTypeData implements Serializable {
@@ -33,16 +37,22 @@ public class PaymentTypeData implements Serializable {
     private String description;
     private Boolean isCashPayment;
     private Long position;
+    private String codeName;
+    private Boolean isSystemDefined;
 
     public static PaymentTypeData instance(final Long id, final String name, final String description, final Boolean isCashPayment,
             final Long position) {
-        return new PaymentTypeData(id, name, description, isCashPayment, position);
+        String codeName = null;
+        Boolean isSystemDefined = false;
+        return new PaymentTypeData(id, name, description, isCashPayment, position, codeName, isSystemDefined);
     }
 
     public static PaymentTypeData instance(final Long id, final String name) {
         String description = null;
         Boolean isCashPayment = null;
         Long position = null;
-        return new PaymentTypeData(id, name, description, isCashPayment, position);
+        String codeName = null;
+        Boolean isSystemDefined = false;
+        return new PaymentTypeData(id, name, description, isCashPayment, position, codeName, isSystemDefined);
     }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/data/PaymentTypeDataValidator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/data/PaymentTypeDataValidator.java
index b735af4a2..23a570242 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/data/PaymentTypeDataValidator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/data/PaymentTypeDataValidator.java
@@ -43,11 +43,13 @@ public class PaymentTypeDataValidator {
     private final FromJsonHelper fromApiJsonHelper;
     private static final Set<String> CREATE_PAYMENT_TYPE_REQUEST_DATA_PARAMETERS = new HashSet<>(
             Arrays.asList(PaymentTypeApiResourceConstants.NAME, PaymentTypeApiResourceConstants.DESCRIPTION,
-                    PaymentTypeApiResourceConstants.ISCASHPAYMENT, PaymentTypeApiResourceConstants.POSITION));
+                    PaymentTypeApiResourceConstants.ISCASHPAYMENT, PaymentTypeApiResourceConstants.POSITION,
+                    PaymentTypeApiResourceConstants.CODE_NAME, PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED));
 
     private static final Set<String> UPDATE_PAYMENT_TYPE_REQUEST_DATA_PARAMETERS = new HashSet<>(
             Arrays.asList(PaymentTypeApiResourceConstants.NAME, PaymentTypeApiResourceConstants.DESCRIPTION,
-                    PaymentTypeApiResourceConstants.ISCASHPAYMENT, PaymentTypeApiResourceConstants.POSITION));
+                    PaymentTypeApiResourceConstants.ISCASHPAYMENT, PaymentTypeApiResourceConstants.POSITION,
+                    PaymentTypeApiResourceConstants.CODE_NAME, PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED));
 
     @Autowired
     public PaymentTypeDataValidator(final FromJsonHelper fromApiJsonHelper) {
@@ -93,6 +95,19 @@ public class PaymentTypeDataValidator {
                     .integerZeroOrGreater();
         }
 
+        if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.CODE_NAME, element)) {
+            final String codeName = this.fromApiJsonHelper.extractStringNamed(PaymentTypeApiResourceConstants.CODE_NAME, element);
+            baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.CODE_NAME).value(codeName).ignoreIfNull()
+                    .notExceedingLengthOf(100);
+        }
+
+        if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED, element)) {
+            final Boolean isSystemDefined = this.fromApiJsonHelper.extractBooleanNamed(PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED,
+                    element);
+            baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED).value(isSystemDefined)
+                    .validateForBooleanValue();
+        }
+
         throwExceptionIfValidationWarningsExist(dataValidationErrors);
     }
 
@@ -142,7 +157,20 @@ public class PaymentTypeDataValidator {
                     .integerZeroOrGreater();
         }
 
-        throwExceptionIfValidationWarningsExist(dataValidationErrors);
+        if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.CODE_NAME, element)) {
+            final String codeName = this.fromApiJsonHelper.extractStringNamed(PaymentTypeApiResourceConstants.CODE_NAME, element);
+            baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.CODE_NAME).value(codeName).ignoreIfNull()
+                    .notExceedingLengthOf(100);
+        }
 
+        if (this.fromApiJsonHelper.parameterExists(PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED, element)) {
+            final Boolean isSystemDefined = this.fromApiJsonHelper.extractBooleanNamed(PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED,
+                    element);
+            baseDataValidator.reset().parameter(PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED).value(isSystemDefined)
+                    .validateForBooleanValue();
+        }
+
+        throwExceptionIfValidationWarningsExist(dataValidationErrors);
     }
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentType.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentType.java
index 1207eba64..ab1bd383b 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentType.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentType.java
@@ -23,14 +23,19 @@ import java.util.Map;
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.Table;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.Setter;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
 import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
 import org.apache.fineract.portfolio.paymenttype.api.PaymentTypeApiResourceConstants;
-import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
 
+@Getter
+@Setter
 @Entity
 @Table(name = "m_payment_type")
+@AllArgsConstructor
 public class PaymentType extends AbstractPersistableCustom {
 
     @Column(name = "value")
@@ -45,18 +50,13 @@ public class PaymentType extends AbstractPersistableCustom {
     @Column(name = "order_position")
     private Long position;
 
-    protected PaymentType() {}
+    @Column(name = "code_name")
+    private String codeName;
 
-    public PaymentType(final String name, final String description, final Boolean isCashPayment, final Long position) {
-        this.name = name;
-        this.description = description;
-        this.isCashPayment = isCashPayment;
-        this.position = position;
-    }
+    @Column(name = "is_system_defined")
+    private Boolean isSystemDefined;
 
-    public static PaymentType create(String name, String description, Boolean isCashPayment, Long position) {
-        return new PaymentType(name, description, isCashPayment, position);
-    }
+    protected PaymentType() {}
 
     public Map<String, Object> update(final JsonCommand command) {
 
@@ -89,16 +89,4 @@ public class PaymentType extends AbstractPersistableCustom {
         return actualChanges;
     }
 
-    public PaymentTypeData toData() {
-        return PaymentTypeData.instance(getId(), this.name, this.description, this.isCashPayment, this.position);
-    }
-
-    public Boolean isCashPayment() {
-        return isCashPayment;
-    }
-
-    public String getPaymentName() {
-        return name;
-    }
-
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentTypeRepository.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentTypeRepository.java
index d4119bdf2..eaa8ed285 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentTypeRepository.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentTypeRepository.java
@@ -18,9 +18,14 @@
  */
 package org.apache.fineract.portfolio.paymenttype.domain;
 
+import java.util.List;
 import org.springframework.data.jpa.repository.JpaRepository;
 import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
 
 public interface PaymentTypeRepository extends JpaRepository<PaymentType, Long>, JpaSpecificationExecutor<PaymentType> {
 
+    List<PaymentType> findAllByOrderByPositionAsc();
+
+    List<PaymentType> findAllByCodeNameIsNotNullOrderByPositionAsc();
+
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentTypeRepositoryWrapper.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentTypeRepositoryWrapper.java
index 6b03bdb71..8bcf81d02 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentTypeRepositoryWrapper.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/domain/PaymentTypeRepositoryWrapper.java
@@ -18,18 +18,23 @@
  */
 package org.apache.fineract.portfolio.paymenttype.domain;
 
+import java.util.List;
+import lombok.RequiredArgsConstructor;
 import org.apache.fineract.portfolio.paymenttype.exception.PaymentTypeNotFoundException;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 @Service
+@RequiredArgsConstructor
 public class PaymentTypeRepositoryWrapper {
 
     private final PaymentTypeRepository repository;
 
-    @Autowired
-    public PaymentTypeRepositoryWrapper(final PaymentTypeRepository repository) {
-        this.repository = repository;
+    public List<PaymentType> findAll() {
+        return this.repository.findAllByOrderByPositionAsc();
+    }
+
+    public List<PaymentType> findAllWithCodeName() {
+        return this.repository.findAllByCodeNameIsNotNullOrderByPositionAsc();
     }
 
     public PaymentType findOneWithNotFoundDetection(final Long id) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/mapper/PaymentTypeMapper.java
similarity index 53%
copy from fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformService.java
copy to fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/mapper/PaymentTypeMapper.java
index b3aead6d3..af48e1336 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/mapper/PaymentTypeMapper.java
@@ -16,15 +16,25 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.fineract.portfolio.paymenttype.service;
+package org.apache.fineract.portfolio.paymenttype.mapper;
 
-import java.util.Collection;
+import java.util.List;
 import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
+import org.apache.fineract.portfolio.paymenttype.domain.PaymentType;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
 
-public interface PaymentTypeReadPlatformService {
+@Mapper(componentModel = "spring")
+public interface PaymentTypeMapper {
 
-    Collection<PaymentTypeData> retrieveAllPaymentTypes();
+    @Mapping(target = "id", source = "source.id")
+    @Mapping(target = "name", source = "source.name")
+    @Mapping(target = "description", source = "source.description")
+    @Mapping(target = "isCashPayment", source = "source.isCashPayment")
+    @Mapping(target = "codeName", source = "source.codeName")
+    @Mapping(target = "isSystemDefined", source = "source.isSystemDefined")
+    PaymentTypeData map(PaymentType source);
 
-    PaymentTypeData retrieveOne(Long paymentTypeId);
+    List<PaymentTypeData> map(List<PaymentType> sources);
 
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformService.java
index b3aead6d3..072f5a903 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformService.java
@@ -25,6 +25,8 @@ public interface PaymentTypeReadPlatformService {
 
     Collection<PaymentTypeData> retrieveAllPaymentTypes();
 
+    Collection<PaymentTypeData> retrieveAllPaymentTypesWithCode();
+
     PaymentTypeData retrieveOne(Long paymentTypeId);
 
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformServiceImpl.java
index 65fd525b2..a7edbd5d4 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeReadPlatformServiceImpl.java
@@ -18,28 +18,24 @@
  */
 package org.apache.fineract.portfolio.paymenttype.service;
 
-import java.sql.ResultSet;
-import java.sql.SQLException;
 import java.util.Collection;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
 import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
-import org.springframework.beans.factory.annotation.Autowired;
+import org.apache.fineract.portfolio.paymenttype.domain.PaymentType;
+import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
+import org.apache.fineract.portfolio.paymenttype.mapper.PaymentTypeMapper;
 import org.springframework.cache.annotation.Cacheable;
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.jdbc.core.RowMapper;
 import org.springframework.stereotype.Service;
 
 @Service
+@RequiredArgsConstructor
 public class PaymentTypeReadPlatformServiceImpl implements PaymentTypeReadPlatformService {
 
-    private final JdbcTemplate jdbcTemplate;
     private final PlatformSecurityContext context;
-
-    @Autowired
-    public PaymentTypeReadPlatformServiceImpl(final PlatformSecurityContext context, final JdbcTemplate jdbcTemplate) {
-        this.context = context;
-        this.jdbcTemplate = jdbcTemplate;
-    }
+    private final PaymentTypeMapper paymentTypeMapper;
+    private final PaymentTypeRepositoryWrapper paymentTypeRepository;
 
     @Override
     @Cacheable(value = "payment_types", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('payment_types')")
@@ -47,41 +43,27 @@ public class PaymentTypeReadPlatformServiceImpl implements PaymentTypeReadPlatfo
         // TODO Auto-generated method stub
         this.context.authenticatedUser();
 
-        final PaymentTypeMapper ptm = new PaymentTypeMapper();
-        final String sql = "select " + ptm.schema() + "order by position";
-
-        return this.jdbcTemplate.query(sql, ptm); // NOSONAR
+        List<PaymentType> paymentType = this.paymentTypeRepository.findAll();
+        return this.paymentTypeMapper.map(paymentType);
     }
 
     @Override
-    public PaymentTypeData retrieveOne(Long paymentTypeId) {
+    @Cacheable(value = "paymentTypesWithCode", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('payment_types')")
+    public Collection<PaymentTypeData> retrieveAllPaymentTypesWithCode() {
         // TODO Auto-generated method stub
         this.context.authenticatedUser();
 
-        final PaymentTypeMapper ptm = new PaymentTypeMapper();
-        final String sql = "select " + ptm.schema() + "where pt.id = ?";
-
-        return this.jdbcTemplate.queryForObject(sql, ptm, new Object[] { paymentTypeId }); // NOSONAR
+        List<PaymentType> paymentType = this.paymentTypeRepository.findAllWithCodeName();
+        return this.paymentTypeMapper.map(paymentType);
     }
 
-    private static final class PaymentTypeMapper implements RowMapper<PaymentTypeData> {
-
-        public String schema() {
-            return " pt.id as id, pt.value as name, pt.description as description,pt.is_cash_payment as isCashPayment,pt.order_position as position from m_payment_type pt ";
-        }
-
-        @Override
-        public PaymentTypeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
-
-            final Long id = rs.getLong("id");
-            final String name = rs.getString("name");
-            final String description = rs.getString("description");
-            final boolean isCashPayment = rs.getBoolean("isCashPayment");
-            final Long position = rs.getLong("position");
-
-            return PaymentTypeData.instance(id, name, description, isCashPayment, position);
-        }
+    @Override
+    public PaymentTypeData retrieveOne(Long paymentTypeId) {
+        // TODO Auto-generated method stub
+        this.context.authenticatedUser();
 
+        final PaymentType paymentType = this.paymentTypeRepository.findOneWithNotFoundDetection(paymentTypeId);
+        return this.paymentTypeMapper.map(paymentType);
     }
 
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeWriteServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeWriteServiceImpl.java
index 0fcf4f26c..aa5a893c6 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeWriteServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/paymenttype/service/PaymentTypeWriteServiceImpl.java
@@ -19,6 +19,7 @@
 package org.apache.fineract.portfolio.paymenttype.service;
 
 import java.util.Map;
+import lombok.RequiredArgsConstructor;
 import org.apache.fineract.infrastructure.core.api.JsonCommand;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
 import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
@@ -28,28 +29,19 @@ import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeDataValidator;
 import org.apache.fineract.portfolio.paymenttype.domain.PaymentType;
 import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepository;
 import org.apache.fineract.portfolio.paymenttype.domain.PaymentTypeRepositoryWrapper;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.cache.annotation.CacheEvict;
 import org.springframework.dao.DataIntegrityViolationException;
 import org.springframework.orm.jpa.JpaSystemException;
 import org.springframework.stereotype.Service;
 
 @Service
+@RequiredArgsConstructor
 public class PaymentTypeWriteServiceImpl implements PaymentTypeWriteService {
 
     private final PaymentTypeRepository repository;
     private final PaymentTypeRepositoryWrapper repositoryWrapper;
     private final PaymentTypeDataValidator fromApiJsonDeserializer;
 
-    @Autowired
-    public PaymentTypeWriteServiceImpl(PaymentTypeRepository repository, PaymentTypeRepositoryWrapper repositoryWrapper,
-            PaymentTypeDataValidator fromApiJsonDeserializer) {
-        this.repository = repository;
-        this.repositoryWrapper = repositoryWrapper;
-        this.fromApiJsonDeserializer = fromApiJsonDeserializer;
-
-    }
-
     @Override
     @CacheEvict(value = "payment_types", key = "T(org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil).getTenant().getTenantIdentifier().concat('payment_types')")
     public CommandProcessingResult createPaymentType(JsonCommand command) {
@@ -58,8 +50,13 @@ public class PaymentTypeWriteServiceImpl implements PaymentTypeWriteService {
         String description = command.stringValueOfParameterNamed(PaymentTypeApiResourceConstants.DESCRIPTION);
         Boolean isCashPayment = command.booleanObjectValueOfParameterNamed(PaymentTypeApiResourceConstants.ISCASHPAYMENT);
         Long position = command.longValueOfParameterNamed(PaymentTypeApiResourceConstants.POSITION);
+        String codeName = command.stringValueOfParameterNamed(PaymentTypeApiResourceConstants.CODE_NAME);
+        Boolean isSystemDefined = command.booleanObjectValueOfParameterNamed(PaymentTypeApiResourceConstants.IS_SYSTEM_DEFINED);
+        if (isSystemDefined == null) {
+            isSystemDefined = false;
+        }
 
-        PaymentType newPaymentType = PaymentType.create(name, description, isCashPayment, position);
+        PaymentType newPaymentType = new PaymentType(name, description, isCashPayment, position, codeName, isSystemDefined);
         this.repository.saveAndFlush(newPaymentType);
         return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(newPaymentType.getId()).build();
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
index 459fac396..ae10f099a 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/domain/SavingsAccount.java
@@ -1322,11 +1322,11 @@ public class SavingsAccount extends AbstractPersistableCustom {
 
                 if (charge.isEnablePaymentType() && charge.isEnableFreeWithdrawal()) { // discount transaction to
                                                                                        // specific paymentType
-                    if (paymentDetail.getPaymentType().getPaymentName().equals(charge.getCharge().getPaymentType().getPaymentName())) {
+                    if (paymentDetail.getPaymentType().getName().equals(charge.getCharge().getPaymentType().getName())) {
                         resetFreeChargeDaysCount(charge, transactionAmount, transactionDate, user, refNo);
                     }
                 } else if (charge.isEnablePaymentType()) { // normal charge-transaction to specific paymentType
-                    if (paymentDetail.getPaymentType().getPaymentName().equals(charge.getCharge().getPaymentType().getPaymentName())) {
+                    if (paymentDetail.getPaymentType().getName().equals(charge.getCharge().getPaymentType().getName())) {
                         charge.updateWithdralFeeAmount(transactionAmount);
                         this.payCharge(charge, charge.getAmountOutstanding(this.getCurrency()), transactionDate, user,
                                 backdatedTxnsAllowedTill, refNo);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
index a6fd7cb16..7a8d19d9d 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/savings/service/SavingsAccountReadPlatformServiceImpl.java
@@ -616,7 +616,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
                         final Long paymentTypeId = JdbcSupport.getLong(rs, "paymentType");
                         if (paymentTypeId != null) {
                             final String typeName = rs.getString("paymentTypeName");
-                            final PaymentTypeData paymentTypeData = new PaymentTypeData(paymentTypeId, typeName, null, false, null);
+                            final PaymentTypeData paymentTypeData = new PaymentTypeData(paymentTypeId, typeName, null, false, null, null,
+                                    false);
                             paymentDetailData = new PaymentDetailData(id, paymentTypeData, null, null, null, null, null);
                         }
                     }
diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
index 6ec9cf739..ba4e3bfc4 100644
--- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
+++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml
@@ -71,4 +71,5 @@
     <include file="parts/0049_add_send_asynchronous_events_job.xml" relativeToChangelogFile="true"/>
     <include file="parts/0050_add_reverse_flag_disbursement_details.xml" relativeToChangelogFile="true"/>
     <include file="parts/0051_external_event_table_category_info.xml" relativeToChangelogFile="true"/>
+    <include file="parts/0052_loan_transaction_chargeback.xml" relativeToChangelogFile="true"/>
 </databaseChangeLog>
diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0052_loan_transaction_chargeback.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0052_loan_transaction_chargeback.xml
new file mode 100644
index 000000000..638f126fe
--- /dev/null
+++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0052_loan_transaction_chargeback.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements. See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership. The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License. You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied. See the License for the
+    specific language governing permissions and limitations
+    under the License.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+                   xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd">
+    <changeSet author="fineract" id="1" context="mysql">
+        <createTable tableName="m_loan_transaction_relation">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="from_loan_transaction_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="to_loan_transaction_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="relation_type_enum" type="INT">
+                <constraints nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column name="last_modified_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="last_modified_on_utc" type="DATETIME">
+                <constraints nullable="false" />
+            </column>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="1" context="postgresql">
+        <createTable tableName="m_loan_transaction_relation">
+            <column autoIncrement="true" name="id" type="BIGINT">
+                <constraints nullable="false" primaryKey="true"/>
+            </column>
+            <column name="from_loan_transaction_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="to_loan_transaction_id" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="relation_type_enum" type="INT">
+                <constraints nullable="false"/>
+            </column>
+            <column name="created_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE">
+                <constraints nullable="false" />
+            </column>
+            <column name="version" type="BIGINT">
+                <constraints nullable="false"/>
+            </column>
+            <column name="last_modified_by" type="BIGINT">
+                <constraints nullable="false" />
+            </column>
+            <column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE">
+                <constraints nullable="false" />
+            </column>
+        </createTable>
+    </changeSet>
+    <changeSet author="fineract" id="2">
+        <addForeignKeyConstraint baseColumnNames="from_loan_transaction_id" baseTableName="m_loan_transaction_relation"
+                                 constraintName="FK_m_loan_transaction_from_relation" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_loan_transaction" validate="true"/>
+        <addForeignKeyConstraint baseColumnNames="to_loan_transaction_id" baseTableName="m_loan_transaction_relation"
+                                 constraintName="FK_m_loan_transaction_to_relation" deferrable="false"
+                                 initiallyDeferred="false" onDelete="RESTRICT" onUpdate="RESTRICT"
+                                 referencedColumnNames="id" referencedTableName="m_loan_transaction" validate="true"/>
+    </changeSet>
+    <changeSet author="fineract" id="3">
+        <insert tableName="m_permission">
+            <column name="grouping" value="transaction_loan"/>
+            <column name="code" value="CHARGEBACK_LOAN"/>
+            <column name="entity_name" value="LOAN"/>
+            <column name="action_name" value="CHARGEBACK"/>
+            <column name="can_maker_checker" valueBoolean="false"/>
+        </insert>
+    </changeSet>
+    <changeSet author="fineract" id="4">
+        <addColumn tableName="m_payment_type">
+            <column name="code_name" type="VARCHAR(100)"/>
+        </addColumn>
+        <addColumn tableName="m_payment_type">
+            <column name="is_system_defined" type="boolean" defaultValueComputed="false">
+                <constraints nullable="false"/>
+            </column>
+        </addColumn>
+    </changeSet>
+    <changeSet author="fineract" id="5">
+        <insert tableName="m_payment_type">
+            <column name="value" value="Repayment Adjustment Chargeback"/>
+            <column name="description" value="Repayment Adjustment Chargeback"/>
+            <column name="is_cash_payment" valueBoolean="false"/>
+            <column name="order_position" valueNumeric="1"/>
+            <column name="code_name" value="REPAYMENT_ADJUSTMENT_CHARGEBACK"/>
+            <column name="is_system_defined" valueNumeric="true"/>
+        </insert>
+        <insert tableName="m_payment_type">
+            <column name="value" value="Repayment Adjustment Refund"/>
+            <column name="description" value="Repayment Adjustment Refund"/>
+            <column name="is_cash_payment" valueBoolean="false"/>
+            <column name="order_position" valueNumeric="1"/>
+            <column name="code_name" value="REPAYMENT_ADJUSTMENT_REFUND"/>
+            <column name="is_system_defined" valueBoolean="true"/>
+        </insert>
+    </changeSet>
+</databaseChangeLog>
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionChargebackTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionChargebackTest.java
new file mode 100644
index 000000000..cd07d376d
--- /dev/null
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanTransactionChargebackTest.java
@@ -0,0 +1,138 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import com.google.gson.Gson;
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.time.LocalDate;
+import java.util.HashMap;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.GetPaymentTypesResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.CommonConstants;
+import org.apache.fineract.integrationtests.common.PaymentTypeHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
+import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class LoanTransactionChargebackTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build();
+    }
+
+    @Test
+    public void applyLoanTransactionChargeback() {
+        final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
+
+        // Client and Loan account creation
+        final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
+        final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper, null);
+        assertNotNull(getLoanProductsProductResponse);
+        log.info("Loan Product Bucket Name: {}", getLoanProductsProductResponse.getDelinquencyBucket().getName());
+
+        final LocalDate todaysDate = Utils.getLocalDateOfTenant();
+        // Older date to have more than one overdue installment
+        final LocalDate transactionDate = todaysDate.minusDays(45);
+        String operationDate = Utils.dateFormatter.format(transactionDate);
+
+        final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
+                getLoanProductsProductResponse.getId().toString(), operationDate, "1000");
+
+        GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+
+        loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+
+        operationDate = Utils.dateFormatter.format(todaysDate);
+        Float amount = Float.valueOf("1100.0");
+        PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = loanTransactionHelper.makeLoanRepayment(operationDate, amount,
+                loanId);
+        assertNotNull(loanIdTransactionsResponse);
+        final Integer transactionId = loanIdTransactionsResponse.getResourceId();
+
+        List<GetPaymentTypesResponse> paymentTypeList = PaymentTypeHelper.getSystemPaymentType(requestSpec, responseSpec);
+        assertTrue(!paymentTypeList.isEmpty());
+
+        amount = Float.valueOf("800.0");
+        final String payload = createChargebackPayload(amount.toString(), paymentTypeList.get(0).getId());
+        PostLoansLoanIdTransactionsTransactionIdRequest postLoansTransactionCommandRequest = loanTransactionHelper
+                .applyLoanTransactionCommand(loanId, transactionId, "chargeback", payload);
+        assertNotNull(postLoansTransactionCommandRequest);
+
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+        assertNotNull(getLoansLoanIdResponse);
+    }
+
+    private String createChargebackPayload(final String transactionAmount, final Integer paymentTypeId) {
+        final HashMap<String, Object> map = new HashMap<>();
+        map.put("transactionAmount", transactionAmount);
+        map.put("paymentTypeId", paymentTypeId);
+        map.put("locale", CommonConstants.LOCALE);
+        final String chargebackPayload = new Gson().toJson(map);
+        log.info("{}", chargebackPayload);
+        return chargebackPayload;
+    }
+
+    private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper,
+            final Integer delinquencyBucketId) {
+        final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().build(null, delinquencyBucketId);
+        final Integer loanProductId = loanTransactionHelper.getLoanProductId(Utils.convertToJson(loanProductMap));
+        return loanTransactionHelper.getLoanProduct(loanProductId);
+    }
+
+    private Integer createLoanAccount(final LoanTransactionHelper loanTransactionHelper, final String clientId, final String loanProductId,
+            final String operationDate, final String principalAmount) {
+        final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal(principalAmount).withLoanTermFrequency("12")
+                .withLoanTermFrequencyAsMonths().withNumberOfRepayments("12").withRepaymentEveryAfter("1")
+                .withRepaymentFrequencyTypeAsMonths() //
+                .withInterestRatePerPeriod("2") //
+                .withExpectedDisbursementDate(operationDate) //
+                .withInterestTypeAsDecliningBalance() //
+                .withSubmittedOnDate(operationDate) //
+                .build(clientId, loanProductId, null);
+        final Integer loanId = loanTransactionHelper.getLoanId(loanApplicationJSON);
+        loanTransactionHelper.approveLoan(operationDate, principalAmount, loanId, null);
+        loanTransactionHelper.disburseLoanWithNetDisbursalAmount(operationDate, loanId, principalAmount);
+        return loanId;
+    }
+}
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/PaymentTypeHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/PaymentTypeHelper.java
index 9a54fa5e2..0f2c396dc 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/PaymentTypeHelper.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/PaymentTypeHelper.java
@@ -24,7 +24,10 @@ import com.google.common.reflect.TypeToken;
 import com.google.gson.Gson;
 import io.restassured.specification.RequestSpecification;
 import io.restassured.specification.ResponseSpecification;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
 import java.util.HashMap;
+import org.apache.fineract.client.models.GetPaymentTypesResponse;
 
 @SuppressWarnings({ "rawtypes", "unchecked" })
 public final class PaymentTypeHelper {
@@ -33,8 +36,16 @@ public final class PaymentTypeHelper {
 
     }
 
-    private static final String CREATE_PAYMENTTYPE_URL = "/fineract-provider/api/v1/paymenttypes?" + Utils.TENANT_IDENTIFIER;
     private static final String PAYMENTTYPE_URL = "/fineract-provider/api/v1/paymenttypes";
+    private static final String CREATE_PAYMENTTYPE_URL = PAYMENTTYPE_URL + "?" + Utils.TENANT_IDENTIFIER;
+
+    public static ArrayList<GetPaymentTypesResponse> getSystemPaymentType(final RequestSpecification requestSpec,
+            final ResponseSpecification responseSpec) {
+        String response = Utils.performServerGet(requestSpec, responseSpec,
+                PAYMENTTYPE_URL + "?onlyWithCode=true&" + Utils.TENANT_IDENTIFIER);
+        Type paymentTypeList = new TypeToken<ArrayList<GetPaymentTypesResponse>>() {}.getType();
+        return new Gson().fromJson(response, paymentTypeList);
+    }
 
     public static Integer createPaymentType(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
             final String name, final String description, final Boolean isCashPayment, final Integer position) {
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index 96c51a0f5..aeb79b8d9 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -47,6 +47,7 @@ import org.apache.fineract.client.models.GetLoansLoanIdRepaymentSchedule;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
 import org.apache.fineract.client.models.PutLoansLoanIdResponse;
 import org.apache.fineract.client.util.JSON;
 import org.apache.fineract.integrationtests.common.CommonConstants;
@@ -239,6 +240,14 @@ public class LoanTransactionHelper {
         return Utils.performServerGet(requestSpec, responseSpec, GET_REPAYMENTS_URL, "loanRepaymentScheduleInstallments");
     }
 
+    public PostLoansLoanIdTransactionsTransactionIdRequest applyLoanTransactionCommand(final Integer loanId, final Integer transactionId,
+            final String command, final String payload) {
+        final String LOAN_TRANSACTION_URL = "/fineract-provider/api/v1/loans/" + loanId + "/transactions/" + transactionId + "?command="
+                + command + "&" + Utils.TENANT_IDENTIFIER;
+        final String response = Utils.performServerPost(requestSpec, responseSpec, LOAN_TRANSACTION_URL, payload, null);
+        return GSON.fromJson(response, PostLoansLoanIdTransactionsTransactionIdRequest.class);
+    }
+
     public HashMap approveLoan(final String approvalDate, final Integer loanID) {
         String loanApprovalCommand = createLoanOperationURL(APPROVE_LOAN_COMMAND, loanID);
         String loanApprovalRequest = getApproveLoanAsJSON(approvalDate);