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/05/04 13:13:36 UTC

[fineract] branch develop updated: FINERACT-1596 New Merchant Issued and Payout Refunds and Goodwill Credit Transactions

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 c2be3d9b4 FINERACT-1596 New Merchant Issued and Payout Refunds and Goodwill Credit Transactions
c2be3d9b4 is described below

commit c2be3d9b4b0560f170deebe39605d4717ea80700
Author: John Woodlock <jo...@gmail.com>
AuthorDate: Wed May 4 13:07:20 2022 +0100

    FINERACT-1596 New Merchant Issued and Payout Refunds and Goodwill Credit Transactions
---
 .../accounting/common/AccountingConstants.java     |  6 +--
 .../AccrualBasedAccountingProcessorForLoan.java    | 18 +++++--
 .../commands/service/CommandWrapperBuilder.java    | 27 ++++++++++
 .../AccountTransfersWritePlatformServiceImpl.java  | 18 +++----
 .../common/BusinessEventNotificationConstants.java |  4 ++
 .../api/LoanTransactionsApiResource.java           | 59 ++++++++++++++------
 .../loanaccount/data/LoanTransactionEnumData.java  | 27 +++++++++-
 .../portfolio/loanaccount/domain/Loan.java         | 24 ++++-----
 .../domain/LoanAccountDomainService.java           | 13 ++---
 .../domain/LoanAccountDomainServiceJpa.java        | 47 +++++++++++-----
 .../loanaccount/domain/LoanTransaction.java        | 27 ++++++++--
 .../loanaccount/domain/LoanTransactionType.java    | 30 ++++++++++-
 ...tLoanRepaymentScheduleTransactionProcessor.java |  6 +--
 ...AndDeleteLoanDisburseDetailsCommandHandler.java |  8 +--
 .../handler/AddLoanChargeCommandHandler.java       | 16 ++----
 .../BulkUpdateLoanOfficerCommandHandler.java       |  8 +--
 .../CloseLoanAsRescheduledCommandHandler.java      |  8 +--
 .../handler/CloseLoanCommandHandler.java           |  8 +--
 .../handler/CreditBalanceRefundCommandHandler.java |  4 +-
 .../handler/DeleteLoanChargeCommandHandler.java    |  8 +--
 .../handler/DisburseLoanCommandHandler.java        |  8 +--
 .../DisburseLoanToSavingsCommandHandler.java       |  8 +--
 .../handler/ForeClosureCommandHandler.java         |  8 +--
 ...r.java => LoanGoodwilCreditCommandHandler.java} | 16 +++---
 ...=> LoanMerchantIssuedRefundCommandHandler.java} | 16 +++---
 ...er.java => LoanPayoutRefundCommandHandler.java} | 18 +++----
 .../handler/LoanRecoveryPaymentCommandHandler.java |  3 +-
 .../handler/LoanRepaymentCommandHandler.java       | 12 ++---
 ...ationWritePlatformServiceJpaRepositoryImpl.java | 11 ++--
 .../service/LoanReadPlatformService.java           |  3 +-
 .../service/LoanReadPlatformServiceImpl.java       |  6 ++-
 .../loanaccount/service/LoanUtilService.java       | 10 ++++
 .../service/LoanWritePlatformService.java          |  4 +-
 .../LoanWritePlatformServiceJpaRepositoryImpl.java | 26 ++++-----
 .../loanproduct/service/LoanEnumerations.java      | 12 +++++
 .../db/changelog/tenant/changelog-tenant.xml       |  1 +
 ...und_payoutrefund_goodwillcredit_permissions.xml | 46 ++++++++++++++++
 ...anceRefundandRepaymentTypeIntegrationTest.java} | 63 +++++++++++++++++++---
 .../common/loans/LoanTransactionHelper.java        |  6 +++
 39 files changed, 444 insertions(+), 199 deletions(-)

diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java
index 9de490cc5..320ff34fc 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/common/AccountingConstants.java
@@ -73,9 +73,9 @@ public final class AccountingConstants {
      ***/
     public enum AccrualAccountsForLoan {
 
-        FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), LOSSES_WRITTEN_OFF(
-                6), INTEREST_RECEIVABLE(
-                        7), FEES_RECEIVABLE(8), PENALTIES_RECEIVABLE(9), TRANSFERS_SUSPENSE(10), OVERPAYMENT(11), INCOME_FROM_RECOVERY(12);
+        FUND_SOURCE(1), LOAN_PORTFOLIO(2), INTEREST_ON_LOANS(3), INCOME_FROM_FEES(4), INCOME_FROM_PENALTIES(5), //
+        LOSSES_WRITTEN_OFF(6), INTEREST_RECEIVABLE(7), FEES_RECEIVABLE(8), PENALTIES_RECEIVABLE(9), //
+        TRANSFERS_SUSPENSE(10), OVERPAYMENT(11), INCOME_FROM_RECOVERY(12);
 
         private final Integer value;
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java
index 330651ac8..537d906ea 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/accounting/journalentry/service/AccrualBasedAccountingProcessorForLoan.java
@@ -61,9 +61,10 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
             }
 
             /***
-             * Handle repayments, repayments at disbursement and reversal of Repayments and Repayments at disbursement
+             * Handle repayments, loan refunds, repayments at disbursement and reversal of Repayments and Repayments at
+             * disbursement
              ***/
-            else if (loanTransactionDTO.getTransactionType().isRepayment()
+            else if (loanTransactionDTO.getTransactionType().isRepaymentType()
                     || loanTransactionDTO.getTransactionType().isRepaymentAtDisbursement()
                     || loanTransactionDTO.getTransactionType().isChargePayment()) {
                 createJournalEntriesForRepaymentsAndWriteOffs(loanDTO, loanTransactionDTO, office, false,
@@ -293,9 +294,16 @@ public class AccrualBasedAccountingProcessorForLoan implements AccountingProcess
                             FinancialActivity.LIABILITY_TRANSFER.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
                             transactionDate, totalDebitAmount, isReversal);
                 } else {
-                    this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode,
-                            AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
-                            transactionDate, totalDebitAmount, isReversal);
+                    if (loanTransactionDTO.getTransactionType().isGoodwillCredit()) {
+                        this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode,
+                                AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
+                                transactionDate, totalDebitAmount, isReversal);
+
+                    } else {
+                        this.helper.createDebitJournalEntryOrReversalForLoan(office, currencyCode,
+                                AccrualAccountsForLoan.FUND_SOURCE.getValue(), loanProductId, paymentTypeId, loanId, transactionId,
+                                transactionDate, totalDebitAmount, isReversal);
+                    }
                 }
             }
         }
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 775cd84e1..5a2b9239a 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
@@ -797,6 +797,33 @@ public class CommandWrapperBuilder {
         return this;
     }
 
+    public CommandWrapperBuilder loanMerchantIssuedRefundTransaction(final Long loanId) {
+        this.actionName = "MERCHANTISSUEDREFUND";
+        this.entityName = "LOAN";
+        this.entityId = null;
+        this.loanId = loanId;
+        this.href = "/loans/" + loanId + "/transactions/template?command=merchantissuedrefund";
+        return this;
+    }
+
+    public CommandWrapperBuilder loanPayoutRefundTransaction(final Long loanId) {
+        this.actionName = "PAYOUTREFUND";
+        this.entityName = "LOAN";
+        this.entityId = null;
+        this.loanId = loanId;
+        this.href = "/loans/" + loanId + "/transactions/template?command=payoutrefund";
+        return this;
+    }
+
+    public CommandWrapperBuilder loanGoodwillCreditTransaction(final Long loanId) {
+        this.actionName = "GOODWILLCREDIT";
+        this.entityName = "LOAN";
+        this.entityId = null;
+        this.loanId = loanId;
+        this.href = "/loans/" + loanId + "/transactions/template?command=goodwillcredit";
+        return this;
+    }
+
     public CommandWrapperBuilder loanRecoveryPaymentTransaction(final Long loanId) {
         this.actionName = "RECOVERYPAYMENT";
         this.entityName = "LOAN";
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java
index 98b2162dd..4bfc81c18 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/account/service/AccountTransfersWritePlatformServiceImpl.java
@@ -177,8 +177,8 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
             final Boolean isHolidayValidationDone = false;
             final HolidayDetailDTO holidayDetailDto = null;
             final boolean isRecoveryRepayment = false;
-            final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccount,
-                    new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, null,
+            final LoanTransaction loanRepaymentTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT,
+                    toLoanAccount, new CommandProcessingResultBuilder(), transactionDate, transactionAmount, paymentDetail, null, null,
                     isRecoveryRepayment, isAccountTransfer, holidayDetailDto, isHolidayValidationDone);
 
             final AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleSavingsToLoanTransfer(command,
@@ -340,10 +340,10 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
                 final boolean isRecoveryRepayment = false;
                 final Boolean isHolidayValidationDone = false;
                 final HolidayDetailDTO holidayDetailDto = null;
-                loanTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccount, new CommandProcessingResultBuilder(),
-                        accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
-                        accountTransferDTO.getPaymentDetail(), null, null, isRecoveryRepayment, isAccountTransfer, holidayDetailDto,
-                        isHolidayValidationDone);
+                loanTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, toLoanAccount,
+                        new CommandProcessingResultBuilder(), accountTransferDTO.getTransactionDate(),
+                        accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(), null, null, isRecoveryRepayment,
+                        isAccountTransfer, holidayDetailDto, isHolidayValidationDone);
             }
 
             accountTransferDetails = this.accountTransferAssembler.assembleSavingsToLoanTransfer(accountTransferDTO, fromSavingsAccount,
@@ -476,9 +476,9 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
                 accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(),
                 accountTransferDTO.getNoteText(), accountTransferDTO.getTxnExternalId(), true);
 
-        LoanTransaction repayTransaction = this.loanAccountDomainService.makeRepayment(toLoanAccount, new CommandProcessingResultBuilder(),
-                accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(), accountTransferDTO.getPaymentDetail(),
-                null, null, false, isAccountTransfer, null, false, true);
+        LoanTransaction repayTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, toLoanAccount,
+                new CommandProcessingResultBuilder(), accountTransferDTO.getTransactionDate(), accountTransferDTO.getTransactionAmount(),
+                accountTransferDTO.getPaymentDetail(), null, null, false, isAccountTransfer, null, false, true);
 
         AccountTransferDetails accountTransferDetails = this.accountTransferAssembler.assembleLoanToLoanTransfer(accountTransferDTO,
                 fromLoanAccount, toLoanAccount, disburseTransaction, repayTransaction);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/BusinessEventNotificationConstants.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/BusinessEventNotificationConstants.java
index 4c4af1cf1..e7799abbd 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/BusinessEventNotificationConstants.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/common/BusinessEventNotificationConstants.java
@@ -33,6 +33,10 @@ public class BusinessEventNotificationConstants {
         LOAN_UNDO_TRANSACTION("loan_undo_transaction"), //
         LOAN_ADJUST_TRANSACTION("loan_adjust_transaction"), //
         LOAN_MAKE_REPAYMENT("loan_repayment_transaction"), //
+        LOAN_MERCHANT_ISSUED_REFUND("loan_merchant_issued_refund"), //
+        LOAN_PAYOUT_REFUND("loan_payout_refund"), //
+        LOAN_GOODWILL_CREDIT("loan_goodwill_credit"), //
+        LOAN_RECOVERY_PAYMENT("loan_recovery_payment"), //
         LOAN_WRITTEN_OFF("loan_writtenoff"), //
         LOAN_UNDO_WRITTEN_OFF("loan_undo_writtenoff"), //
         LOAN_DISBURSAL("loan_disbursal"), //
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 646ff12de..985566905 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
@@ -57,6 +57,7 @@ import org.apache.fineract.infrastructure.core.service.DateUtils;
 import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
 import org.apache.fineract.portfolio.loanaccount.data.LoanRepaymentScheduleInstallmentData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import org.apache.fineract.portfolio.loanaccount.service.LoanChargePaidByReadPlatformService;
 import org.apache.fineract.portfolio.loanaccount.service.LoanReadPlatformService;
 import org.apache.fineract.portfolio.paymenttype.data.PaymentTypeData;
@@ -110,12 +111,14 @@ public class LoanTransactionsApiResource {
     @Produces({ MediaType.APPLICATION_JSON })
     @Operation(summary = "Retrieve Loan Transaction Template", description = "This is a convenience resource. It can be useful when building maintenance user interface screens for client applications. The template data returned consists of any or all of:\n"
             + "\n" + "Field Defaults\n" + "Allowed Value Lists\n\n" + "Example Requests:\n" + "\n"
-            + "loans/1/transactions/template?command=repayment" + "\n" + "loans/1/transactions/template?command=waiveinterest" + "\n"
-            + "loans/1/transactions/template?command=writeoff" + "\n" + "loans/1/transactions/template?command=close-rescheduled" + "\n"
-            + "loans/1/transactions/template?command=close" + "\n" + "loans/1/transactions/template?command=disburse" + "\n"
-            + "loans/1/transactions/template?command=disburseToSavings" + "\n" + "loans/1/transactions/template?command=recoverypayment"
-            + "\n" + "loans/1/transactions/template?command=prepayLoan" + "\n" + "loans/1/transactions/template?command=refundbycash" + "\n"
-            + "loans/1/transactions/template?command=refundbytransfer" + "\n" + "loans/1/transactions/template?command=foreclosure" + "\n"
+            + "loans/1/transactions/template?command=repayment" + "loans/1/transactions/template?command=merchantIssuedRefund"
+            + "loans/1/transactions/template?command=payoutRefund" + "loans/1/transactions/template?command=goodwillCredit" + "\n"
+            + "loans/1/transactions/template?command=waiveinterest" + "\n" + "loans/1/transactions/template?command=writeoff" + "\n"
+            + "loans/1/transactions/template?command=close-rescheduled" + "\n" + "loans/1/transactions/template?command=close" + "\n"
+            + "loans/1/transactions/template?command=disburse" + "\n" + "loans/1/transactions/template?command=disburseToSavings" + "\n"
+            + "loans/1/transactions/template?command=recoverypayment" + "\n" + "loans/1/transactions/template?command=prepayLoan" + "\n"
+            + "loans/1/transactions/template?command=refundbycash" + "\n" + "loans/1/transactions/template?command=refundbytransfer" + "\n"
+            + "loans/1/transactions/template?command=foreclosure" + "\n"
             + "loans/1/transactions/template?command=creditBalanceRefund (returned 'amount' field will have the overpaid value")
     @ApiResponses({
             @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.GetLoansLoanIdTransactionsTemplateResponse.class))) })
@@ -130,6 +133,18 @@ public class LoanTransactionsApiResource {
         LoanTransactionData transactionData = null;
         if (is(commandParam, "repayment")) {
             transactionData = this.loanReadPlatformService.retrieveLoanTransactionTemplate(loanId);
+        } else if (is(commandParam, "merchantIssuedRefund")) {
+            LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
+            transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.MERCHANT_ISSUED_REFUND,
+                    loanId, transactionDate);
+        } else if (is(commandParam, "payoutRefund")) {
+            LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
+            transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.PAYOUT_REFUND, loanId,
+                    transactionDate);
+        } else if (is(commandParam, "goodwillCredit")) {
+            LocalDate transactionDate = DateUtils.getLocalDateOfTenant();
+            transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.GOODWILL_CREDIT, loanId,
+                    transactionDate);
         } else if (is(commandParam, "waiveinterest")) {
             transactionData = this.loanReadPlatformService.retrieveWaiveInterestDetails(loanId);
         } else if (is(commandParam, "writeoff")) {
@@ -156,7 +171,8 @@ public class LoanTransactionsApiResource {
                 transactionDate = LocalDate.ofInstant(transactionDateParam.getDate("transactionDate", dateFormat, locale).toInstant(),
                         DateUtils.getDateTimeZoneOfTenant());
             }
-            transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(loanId, transactionDate);
+            transactionData = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanId,
+                    transactionDate);
         } else if (is(commandParam, "refundbycash")) {
             transactionData = this.loanReadPlatformService.retrieveRefundByCashTemplate(loanId);
         } else if (is(commandParam, "refundbytransfer")) {
@@ -209,15 +225,17 @@ public class LoanTransactionsApiResource {
     @Consumes({ MediaType.APPLICATION_JSON })
     @Produces({ MediaType.APPLICATION_JSON })
     @Operation(summary = "Significant Loan Transactions", description = "This API covers the major loan transaction functionality\n\n"
-            + "Example Requests:\n" + "\n" + "loans/1/transactions/template?command=repayment" + " | Make a Repayment | \n"
-            + "loans/1/transactions/template?command=waiveinterest" + " | Waive Interest | \n"
-            + "loans/1/transactions/template?command=writeoff" + " | Write-off Loan | \n"
-            + "loans/1/transactions/template?command=close-rescheduled" + " | Close Rescheduled Loan | \n"
-            + "loans/1/transactions/template?command=close" + " | Close Loan | \n" + "loans/1/transactions/template?command=undowriteoff"
-            + " | Undo Loan Write-off | \n" + "loans/1/transactions/template?command=recoverypayment" + " | Make Recovery Payment | \n"
-            + "loans/1/transactions/template?command=refundByCash" + " | Make a Refund of an Active Loan by Cash | \n"
-            + "loans/1/transactions/template?command=foreclosure" + " | Foreclosure of an Active Loan | \n"
-            + "loans/1/transactions/template?command=creditBalanceRefund" + " | Credit Balance Refund" + " |  \n")
+            + "Example Requests:\n" + "\n" + "loans/1/transactions?command=repayment" + " | Make a Repayment | \n"
+            + "loans/1/transactions?command=merchantIssuedRefund" + " | Merchant Issued Refund | \n"
+            + "loans/1/transactions?command=payoutRefund" + " | Payout Refund | \n" + "loans/1/transactions?command=goodwillCredit"
+            + " | Goodwil Credit | \n" + "loans/1/transactions?command=waiveinterest" + " | Waive Interest | \n"
+            + "loans/1/transactions?command=writeoff" + " | Write-off Loan | \n" + "loans/1/transactions?command=close-rescheduled"
+            + " | Close Rescheduled Loan | \n" + "loans/1/transactions?command=close" + " | Close Loan | \n"
+            + "loans/1/transactions?command=undowriteoff" + " | Undo Loan Write-off | \n" + "loans/1/transactions?command=recoverypayment"
+            + " | Make Recovery Payment | \n" + "loans/1/transactions?command=refundByCash"
+            + " | Make a Refund of an Active Loan by Cash | \n" + "loans/1/transactions?command=foreclosure"
+            + " | Foreclosure of an Active Loan | \n" + "loans/1/transactions?command=creditBalanceRefund" + " | Credit Balance Refund"
+            + " |  \n")
     @RequestBody(required = true, content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PostLoansLoanIdTransactionsRequest.class)))
     @ApiResponses({
             @ApiResponse(responseCode = "200", description = "OK", content = @Content(schema = @Schema(implementation = LoanTransactionsApiResourceSwagger.PostLoansLoanIdTransactionsResponse.class))) })
@@ -231,6 +249,15 @@ public class LoanTransactionsApiResource {
         if (is(commandParam, "repayment")) {
             final CommandWrapper commandRequest = builder.loanRepaymentTransaction(loanId).build();
             result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+        } else if (is(commandParam, "merchantIssuedRefund")) {
+            final CommandWrapper commandRequest = builder.loanMerchantIssuedRefundTransaction(loanId).build();
+            result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+        } else if (is(commandParam, "payoutRefund")) {
+            final CommandWrapper commandRequest = builder.loanPayoutRefundTransaction(loanId).build();
+            result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
+        } else if (is(commandParam, "goodwillCredit")) {
+            final CommandWrapper commandRequest = builder.loanGoodwillCreditTransaction(loanId).build();
+            result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
         } else if (is(commandParam, "waiveinterest")) {
             final CommandWrapper commandRequest = builder.waiveInterestPortionTransaction(loanId).build();
             result = this.commandsSourceWritePlatformService.logCommandSource(commandRequest);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionEnumData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionEnumData.java
index 1cf00e9e3..5d8928a75 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionEnumData.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanTransactionEnumData.java
@@ -31,6 +31,9 @@ public class LoanTransactionEnumData {
     private final boolean disbursement;
     private final boolean repaymentAtDisbursement;
     private final boolean repayment;
+    private final boolean merchantIssuedRefund;
+    private final boolean payoutRefund;
+    private final boolean goodwillCredit;
     private final boolean contra;
     private final boolean waiveInterest;
     private final boolean waiveCharges;
@@ -53,6 +56,9 @@ public class LoanTransactionEnumData {
         this.disbursement = Long.valueOf(1).equals(this.id);
         this.repaymentAtDisbursement = Long.valueOf(5).equals(this.id);
         this.repayment = Long.valueOf(2).equals(this.id);
+        this.merchantIssuedRefund = Long.valueOf(21).equals(this.id);
+        this.payoutRefund = Long.valueOf(22).equals(this.id);
+        this.goodwillCredit = Long.valueOf(23).equals(this.id);
         this.contra = Long.valueOf(3).equals(this.id);
         this.waiveInterest = Long.valueOf(4).equals(this.id);
         this.waiveCharges = Long.valueOf(9).equals(this.id);
@@ -88,7 +94,14 @@ public class LoanTransactionEnumData {
      * @return
      */
     public boolean isPaymentOrReceipt() {
-        if (isDisbursement() || isRepayment() || isRepaymentAtDisbursement() || isRecoveryRepayment()) {
+        if (isDisbursement() || isRepaymentType() || isRepaymentAtDisbursement() || isRecoveryRepayment()) {
+            return true;
+        }
+        return false;
+    }
+
+    public boolean isRepaymentType() {
+        if (isRepayment() || isMerchantIssuedRefund() || isPayoutRefund() || isGoodwillCredit()) {
             return true;
         }
         return false;
@@ -106,6 +119,18 @@ public class LoanTransactionEnumData {
         return this.repayment;
     }
 
+    public boolean isMerchantIssuedRefund() {
+        return this.merchantIssuedRefund;
+    }
+
+    public boolean isPayoutRefund() {
+        return this.payoutRefund;
+    }
+
+    public boolean isGoodwillCredit() {
+        return this.goodwillCredit;
+    }
+
     public boolean isWaiveInterest() {
         return this.waiveInterest;
     }
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 dbf7680f2..12a804bd8 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
@@ -3179,7 +3179,7 @@ public class Loan extends AbstractPersistableCustom {
             addLoanTransaction(loanTransaction);
         }
 
-        if (loanTransaction.isNotRepayment() && loanTransaction.isNotWaiver() && loanTransaction.isNotRecoveryRepayment()) {
+        if (loanTransaction.isNotRepaymentType() && loanTransaction.isNotWaiver() && loanTransaction.isNotRecoveryRepayment()) {
             final String errorMessage = "A transaction of type repayment or recovery repayment or waiver was expected but not received.";
             throw new InvalidLoanTransactionTypeException("transaction", "is.not.a.repayment.or.waiver.or.recovery.transaction",
                     errorMessage);
@@ -3502,7 +3502,7 @@ public class Loan extends AbstractPersistableCustom {
 
         final LocalDate currentTransactionDate = loanTransaction.getTransactionDate();
         for (final LoanTransaction previousTransaction : loanTransactions) {
-            if (previousTransaction.isRepayment() && previousTransaction.isNotReversed()) {
+            if (previousTransaction.isRepaymentType() && previousTransaction.isNotReversed()) {
                 if (currentTransactionDate.isBefore(previousTransaction.getTransactionDate())) {
                     isAfterLatRepayment = false;
                     break;
@@ -3543,7 +3543,7 @@ public class Loan extends AbstractPersistableCustom {
 
         LocalDate lastTransactionDate = null;
         for (final LoanTransaction transaction : this.loanTransactions) {
-            if (transaction.isRepayment() && transaction.isNonZero()) {
+            if (transaction.isRepaymentType() && transaction.isNonZero()) {
                 lastTransactionDate = transaction.getTransactionDate();
             }
         }
@@ -3615,7 +3615,7 @@ public class Loan extends AbstractPersistableCustom {
         validateActivityNotBeforeClientOrGroupTransferDate(LoanEvent.LOAN_REPAYMENT_OR_WAIVER,
                 transactionForAdjustment.getTransactionDate());
 
-        if (transactionForAdjustment.isNotRepayment() && transactionForAdjustment.isNotWaiver()
+        if (transactionForAdjustment.isNotRepaymentType() && transactionForAdjustment.isNotWaiver()
                 && transactionForAdjustment.isNotCreditBalanceRefund()) {
             final String errorMessage = "Only transactions of type repayment, waiver or credit balance refund can be adjusted.";
             throw new InvalidLoanTransactionTypeException("transaction",
@@ -3635,7 +3635,7 @@ public class Loan extends AbstractPersistableCustom {
             this.loanStatus = LoanStatus.ACTIVE.getValue();
         }
 
-        if (newTransactionDetail.isRepayment() || newTransactionDetail.isInterestWaiver()) {
+        if (newTransactionDetail.isRepaymentType() || newTransactionDetail.isInterestWaiver()) {
             changedTransactionDetail = handleRepaymentOrRecoveryOrWaiverTransaction(newTransactionDetail, loanLifecycleStateMachine,
                     transactionForAdjustment, scheduleGeneratorDTO, currentUser);
         }
@@ -4139,7 +4139,7 @@ public class Loan extends AbstractPersistableCustom {
         Money cumulativePaid = Money.zero(loanCurrency());
 
         for (final LoanTransaction repayment : this.loanTransactions) {
-            if (repayment.isRepayment() && !repayment.isReversed()) {
+            if (repayment.isRepaymentType() && !repayment.isReversed()) {
                 cumulativePaid = cumulativePaid.plus(repayment.getAmount(loanCurrency()));
             }
         }
@@ -4464,7 +4464,7 @@ public class Loan extends AbstractPersistableCustom {
                     && !transaction.getTransactionDate().isAfter(tillDate)) {
                 if (transaction.isAccrual()) {
                     receivableInterest = receivableInterest.plus(transaction.getInterestPortion(getCurrency()));
-                } else if (transaction.isRepayment() || transaction.isInterestWaiver()) {
+                } else if (transaction.isRepaymentType() || transaction.isInterestWaiver()) {
                     receivableInterest = receivableInterest.minus(transaction.getInterestPortion(getCurrency()));
                 }
             }
@@ -4953,7 +4953,7 @@ public class Loan extends AbstractPersistableCustom {
     public LocalDate getLastRepaymentDate() {
         LocalDate currentTransactionDate = getDisbursementDate();
         for (final LoanTransaction previousTransaction : this.loanTransactions) {
-            if (previousTransaction.isRepayment()) {
+            if (previousTransaction.isRepaymentType()) {
                 if (currentTransactionDate.isBefore(previousTransaction.getTransactionDate())) {
                     currentTransactionDate = previousTransaction.getTransactionDate();
                 }
@@ -5063,7 +5063,7 @@ public class Loan extends AbstractPersistableCustom {
             break;
             case LOAN_REPAYMENT_OR_WAIVER:
                 if (!isOpen()) {
-                    final String defaultUserMessage = "Loan Repayment or Waiver is not allowed. Loan Account is not active.";
+                    final String defaultUserMessage = "Loan Repayment (or its types) or Waiver is not allowed. Loan Account is not active.";
                     final ApiParameterError error = ApiParameterError
                             .generalError("error.msg.loan.repayment.or.waiver.account.is.not.active", defaultUserMessage);
                     dataValidationErrors.add(error);
@@ -6223,7 +6223,7 @@ public class Loan extends AbstractPersistableCustom {
 
         LocalDate lastTransactionDate = null;
         for (final LoanTransaction transaction : this.loanTransactions) {
-            if ((transaction.isRepayment() || transaction.isRefundForActiveLoan() || transaction.isCreditBalanceRefund())
+            if ((transaction.isRepaymentType() || transaction.isRefundForActiveLoan() || transaction.isCreditBalanceRefund())
                     && transaction.isNonZero() && transaction.isNotReversed()) {
                 lastTransactionDate = transaction.getTransactionDate();
             }
@@ -6268,7 +6268,7 @@ public class Loan extends AbstractPersistableCustom {
         Collections.reverse(loanTransactions);
         for (final LoanTransaction previousTransaction : loanTransactions) {
             if (lastTransactionDate.isBefore(previousTransaction.getTransactionDate())) {
-                if (previousTransaction.isRepayment() || previousTransaction.isWaiver() || previousTransaction.isChargePayment()) {
+                if (previousTransaction.isRepaymentType() || previousTransaction.isWaiver() || previousTransaction.isChargePayment()) {
                     throw new UndoLastTrancheDisbursementException(previousTransaction.getId());
                 }
             }
@@ -6602,7 +6602,7 @@ public class Loan extends AbstractPersistableCustom {
                     receivableInterest = receivableInterest.plus(transaction.getInterestPortion(currency));
                     receivableFee = receivableFee.plus(transaction.getFeeChargesPortion(currency));
                     receivablePenalty = receivablePenalty.plus(transaction.getPenaltyChargesPortion(currency));
-                } else if (transaction.isRepayment() || transaction.isChargePayment()) {
+                } else if (transaction.isRepaymentType() || transaction.isChargePayment()) {
                     receivableInterest = receivableInterest.minus(transaction.getInterestPortion(currency));
                     receivableFee = receivableFee.minus(transaction.getFeeChargesPortion(currency));
                     receivablePenalty = receivablePenalty.minus(transaction.getPenaltyChargesPortion(currency));
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java
index 29819a3e2..35cef98b4 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainService.java
@@ -28,9 +28,9 @@ import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
 
 public interface LoanAccountDomainService {
 
-    LoanTransaction makeRepayment(Loan loan, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
-            BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId, boolean isRecoveryRepayment,
-            boolean isAccountTransfer, HolidayDetailDTO holidatDetailDto, Boolean isHolidayValidationDone);
+    LoanTransaction makeRepayment(LoanTransactionType repaymentTransactionType, Loan loan, CommandProcessingResultBuilder builderResult,
+            LocalDate transactionDate, BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId,
+            boolean isRecoveryRepayment, boolean isAccountTransfer, HolidayDetailDTO holidatDetailDto, Boolean isHolidayValidationDone);
 
     LoanTransaction makeRefund(Long accountId, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
             BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId);
@@ -61,9 +61,10 @@ public interface LoanAccountDomainService {
      */
     void recalculateAccruals(Loan loan);
 
-    LoanTransaction makeRepayment(Loan loan, CommandProcessingResultBuilder builderResult, LocalDate transactionDate,
-            BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId, boolean isRecoveryRepayment,
-            boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto, Boolean isHolidayValidationDone, boolean isLoanToLoanTransfer);
+    LoanTransaction makeRepayment(LoanTransactionType repaymentTransactionType, Loan loan, CommandProcessingResultBuilder builderResult,
+            LocalDate transactionDate, BigDecimal transactionAmount, PaymentDetail paymentDetail, String noteText, String txnExternalId,
+            boolean isRecoveryRepayment, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto, Boolean isHolidayValidationDone,
+            boolean isLoanToLoanTransfer);
 
     void saveLoanWithDataIntegrityViolationChecks(Loan loan);
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
index cf6c62775..285b6f8d8 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java
@@ -146,12 +146,12 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
 
     @Transactional
     @Override
-    public LoanTransaction makeRepayment(final Loan loan, final CommandProcessingResultBuilder builderResult,
-            final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText,
-            final String txnExternalId, final boolean isRecoveryRepayment, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto,
-            Boolean isHolidayValidationDone) {
-        return makeRepayment(loan, builderResult, transactionDate, transactionAmount, paymentDetail, noteText, txnExternalId,
-                isRecoveryRepayment, isAccountTransfer, holidayDetailDto, isHolidayValidationDone, false);
+    public LoanTransaction makeRepayment(final LoanTransactionType repaymentTransactionType, final Loan loan,
+            final CommandProcessingResultBuilder builderResult, final LocalDate transactionDate, final BigDecimal transactionAmount,
+            final PaymentDetail paymentDetail, final String noteText, final String txnExternalId, final boolean isRecoveryRepayment,
+            boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto, Boolean isHolidayValidationDone) {
+        return makeRepayment(repaymentTransactionType, loan, builderResult, transactionDate, transactionAmount, paymentDetail, noteText,
+                txnExternalId, isRecoveryRepayment, isAccountTransfer, holidayDetailDto, isHolidayValidationDone, false);
     }
 
     @Transactional
@@ -171,13 +171,16 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
 
     @Transactional
     @Override
-    public LoanTransaction makeRepayment(final Loan loan, final CommandProcessingResultBuilder builderResult,
-            final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final String noteText,
-            final String txnExternalId, final boolean isRecoveryRepayment, boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto,
-            Boolean isHolidayValidationDone, final boolean isLoanToLoanTransfer) {
+    public LoanTransaction makeRepayment(final LoanTransactionType repaymentTransactionType, final Loan loan,
+            final CommandProcessingResultBuilder builderResult, final LocalDate transactionDate, final BigDecimal transactionAmount,
+            final PaymentDetail paymentDetail, final String noteText, final String txnExternalId, final boolean isRecoveryRepayment,
+            boolean isAccountTransfer, HolidayDetailDTO holidayDetailDto, Boolean isHolidayValidationDone,
+            final boolean isLoanToLoanTransfer) {
         AppUser currentUser = getAppUserIfPresent();
         checkClientOrGroupActive(loan);
-        this.businessEventNotifierService.notifyBusinessEventToBeExecuted(BusinessEvents.LOAN_MAKE_REPAYMENT,
+
+        BusinessEvents repaymentTypeEvent = getRepaymentTypeBusinessEvent(repaymentTransactionType, isRecoveryRepayment);
+        this.businessEventNotifierService.notifyBusinessEventToBeExecuted(repaymentTypeEvent,
                 constructEntityMap(BusinessEntity.LOAN, loan));
 
         // TODO: Is it required to validate transaction date with meeting dates
@@ -199,8 +202,8 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
             newRepaymentTransaction = LoanTransaction.recoveryRepayment(loan.getOffice(), repaymentAmount, paymentDetail, transactionDate,
                     txnExternalId, currentDateTime, currentUser);
         } else {
-            newRepaymentTransaction = LoanTransaction.repayment(loan.getOffice(), repaymentAmount, paymentDetail, transactionDate,
-                    txnExternalId, currentDateTime, currentUser);
+            newRepaymentTransaction = LoanTransaction.repaymentType(repaymentTransactionType, loan.getOffice(), repaymentAmount,
+                    paymentDetail, transactionDate, txnExternalId, currentDateTime, currentUser);
         }
 
         LocalDate recalculateFrom = null;
@@ -242,7 +245,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
 
         recalculateAccruals(loan);
 
-        this.businessEventNotifierService.notifyBusinessEventWasExecuted(BusinessEvents.LOAN_MAKE_REPAYMENT,
+        this.businessEventNotifierService.notifyBusinessEventWasExecuted(repaymentTypeEvent,
                 constructEntityMap(BusinessEntity.LOAN_TRANSACTION, newRepaymentTransaction));
 
         // disable all active standing orders linked to this loan if status
@@ -284,6 +287,22 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
         return newRepaymentTransaction;
     }
 
+    private BusinessEvents getRepaymentTypeBusinessEvent(LoanTransactionType repaymentTransactionType, boolean isRecoveryRepayment) {
+        BusinessEvents repaymentTypeEvent = null;
+        if (repaymentTransactionType.isRepayment()) {
+            repaymentTypeEvent = BusinessEvents.LOAN_MAKE_REPAYMENT;
+        } else if (repaymentTransactionType.isMerchantIssuedRefund()) {
+            repaymentTypeEvent = BusinessEvents.LOAN_MERCHANT_ISSUED_REFUND;
+        } else if (repaymentTransactionType.isPayoutRefund()) {
+            repaymentTypeEvent = BusinessEvents.LOAN_PAYOUT_REFUND;
+        } else if (repaymentTransactionType.isGoodwillCredit()) {
+            repaymentTypeEvent = BusinessEvents.LOAN_GOODWILL_CREDIT;
+        } else if (isRecoveryRepayment) {
+            repaymentTypeEvent = BusinessEvents.LOAN_RECOVERY_PAYMENT;
+        }
+        return repaymentTypeEvent;
+    }
+
     private void saveLoanTransactionWithDataIntegrityViolationChecks(LoanTransaction newRepaymentTransaction) {
         try {
             this.loanTransactionRepository.saveAndFlush(newRepaymentTransaction);
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 351566a2f..dd52ed73d 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
@@ -169,6 +169,13 @@ public class LoanTransaction extends AbstractPersistableCustom {
                 createdDate, appUser);
     }
 
+    public static LoanTransaction repaymentType(final LoanTransactionType repaymentType, final Office office, final Money amount,
+            final PaymentDetail paymentDetail, final LocalDate paymentDate, final String externalId, final LocalDateTime createdDate,
+            final AppUser appUser) {
+        return new LoanTransaction(null, office, repaymentType, paymentDetail, amount.getAmount(), paymentDate, externalId, createdDate,
+                appUser);
+    }
+
     public void setLoanTransactionToRepaymentScheduleMappings(final Integer installmentId, final BigDecimal chargePerInstallment) {
         for (LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping : this.loanTransactionToRepaymentScheduleMappings) {
             final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = loanTransactionToRepaymentScheduleMapping
@@ -524,16 +531,28 @@ public class LoanTransaction extends AbstractPersistableCustom {
         this.manuallyAdjustedOrReversed = true;
     }
 
-    public boolean isAnyTypeOfRepayment() {
-        return isRepayment() || isRepaymentAtDisbursement() || isRecoveryRepayment();
+    public boolean isRepaymentType() {
+        return isRepayment() || isMerchantIssuedRefund() || isPayoutRefund() || isGoodwillCredit();
     }
 
     public boolean isRepayment() {
         return LoanTransactionType.REPAYMENT.equals(getTypeOf()) && isNotReversed();
     }
 
-    public boolean isNotRepayment() {
-        return !isRepayment();
+    public boolean isMerchantIssuedRefund() {
+        return LoanTransactionType.MERCHANT_ISSUED_REFUND.equals(getTypeOf()) && isNotReversed();
+    }
+
+    public boolean isPayoutRefund() {
+        return LoanTransactionType.PAYOUT_REFUND.equals(getTypeOf()) && isNotReversed();
+    }
+
+    public boolean isGoodwillCredit() {
+        return LoanTransactionType.GOODWILL_CREDIT.equals(getTypeOf()) && isNotReversed();
+    }
+
+    public boolean isNotRepaymentType() {
+        return !isRepaymentType();
     }
 
     public boolean isIncomePosting() {
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 c38bda6be..e05d97262 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
@@ -52,7 +52,10 @@ public enum LoanTransactionType {
     CHARGE_PAYMENT(17, "loanTransactionType.chargePayment"), //
     REFUND_FOR_ACTIVE_LOAN(18, "loanTransactionType.refund"), //
     INCOME_POSTING(19, "loanTransactionType.incomePosting"), //
-    CREDIT_BALANCE_REFUND(20, "loanTransactionType.creditBalanceRefund");
+    CREDIT_BALANCE_REFUND(20, "loanTransactionType.creditBalanceRefund"), //
+    MERCHANT_ISSUED_REFUND(21, "loanTransactionType.merchantIssuedRefund"), //
+    PAYOUT_REFUND(22, "loanTransactionType.payoutRefund"), //
+    GOODWILL_CREDIT(23, "loanTransactionType.goodwillCredit");
 
     private final Integer value;
     private final String code;
@@ -135,6 +138,15 @@ public enum LoanTransactionType {
             case 20:
                 loanTransactionType = LoanTransactionType.CREDIT_BALANCE_REFUND;
             break;
+            case 21:
+                loanTransactionType = LoanTransactionType.MERCHANT_ISSUED_REFUND;
+            break;
+            case 22:
+                loanTransactionType = LoanTransactionType.PAYOUT_REFUND;
+            break;
+            case 23:
+                loanTransactionType = LoanTransactionType.GOODWILL_CREDIT;
+            break;
             default:
                 loanTransactionType = LoanTransactionType.INVALID;
             break;
@@ -154,6 +166,22 @@ public enum LoanTransactionType {
         return this.value.equals(LoanTransactionType.REPAYMENT.getValue());
     }
 
+    public boolean isMerchantIssuedRefund() {
+        return this.value.equals(LoanTransactionType.MERCHANT_ISSUED_REFUND.getValue());
+    }
+
+    public boolean isPayoutRefund() {
+        return this.value.equals(LoanTransactionType.PAYOUT_REFUND.getValue());
+    }
+
+    public boolean isGoodwillCredit() {
+        return this.value.equals(LoanTransactionType.GOODWILL_CREDIT.getValue());
+    }
+
+    public boolean isRepaymentType() {
+        return (isRepayment() || isMerchantIssuedRefund() || isPayoutRefund() || isGoodwillCredit());
+    }
+
     public boolean isRecoveryRepayment() {
         return this.value.equals(LoanTransactionType.RECOVERY_REPAYMENT.getValue());
     }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
index 8c95c2b2a..224cc7ca9 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java
@@ -152,7 +152,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
                 Collections.sort(installments, byDate);
             }
 
-            if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
+            if (loanTransaction.isRepaymentType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
                 // pass through for new transactions
                 if (loanTransaction.getId() == null) {
                     handleTransaction(loanTransaction, currency, installments, charges);
@@ -230,7 +230,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
             final List<LoanRepaymentScheduleInstallment> installments, final Set<LoanCharge> charges, final Money chargeAmountToProcess,
             final boolean isFeeCharge) {
         // to.
-        if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
+        if (loanTransaction.isRepaymentType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
             loanTransaction.resetDerivedComponents();
         }
         Money transactionAmountUnprocessed = processTransaction(loanTransaction, currency, installments, chargeAmountToProcess);
@@ -488,7 +488,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
         Money unProcessed = Money.zero(currency);
         for (final LoanTransaction loanTransaction : transactionsPostDisbursement) {
             Money amountToProcess = null;
-            if (loanTransaction.isRepayment() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
+            if (loanTransaction.isRepaymentType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
                 loanTransaction.resetDerivedComponents();
             }
             if (loanTransaction.isInterestWaiver()) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddAndDeleteLoanDisburseDetailsCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddAndDeleteLoanDisburseDetailsCommandHandler.java
index 214827055..07c161bdb 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddAndDeleteLoanDisburseDetailsCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddAndDeleteLoanDisburseDetailsCommandHandler.java
@@ -18,24 +18,20 @@
  */
 package org.apache.fineract.portfolio.loanaccount.handler;
 
+import lombok.RequiredArgsConstructor;
 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.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 @Service
+@RequiredArgsConstructor
 public class AddAndDeleteLoanDisburseDetailsCommandHandler implements NewCommandSourceHandler {
 
     private final LoanWritePlatformService writePlatformService;
 
-    @Autowired
-    public AddAndDeleteLoanDisburseDetailsCommandHandler(final LoanWritePlatformService writePlatformService) {
-        this.writePlatformService = writePlatformService;
-    }
-
     @Transactional
     @Override
     public CommandProcessingResult processCommand(final JsonCommand command) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddLoanChargeCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddLoanChargeCommandHandler.java
index b93d90c76..4c4987b4c 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddLoanChargeCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/AddLoanChargeCommandHandler.java
@@ -18,33 +18,27 @@
  */
 package org.apache.fineract.portfolio.loanaccount.handler;
 
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 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.infrastructure.core.exception.PlatformDataIntegrityException;
 import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.dao.DataIntegrityViolationException;
 import org.springframework.orm.jpa.JpaSystemException;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 @Service
+@RequiredArgsConstructor
+@Slf4j
 @CommandType(entity = "LOANCHARGE", action = "CREATE")
 public class AddLoanChargeCommandHandler implements NewCommandSourceHandler {
 
-    private static final Logger LOG = LoggerFactory.getLogger(AddLoanChargeCommandHandler.class);
-
     private final LoanWritePlatformService writePlatformService;
 
-    @Autowired
-    public AddLoanChargeCommandHandler(final LoanWritePlatformService writePlatformService) {
-        this.writePlatformService = writePlatformService;
-    }
-
     @Transactional
     @Override
     public CommandProcessingResult processCommand(final JsonCommand command) {
@@ -71,7 +65,7 @@ public class AddLoanChargeCommandHandler implements NewCommandSourceHandler {
     }
 
     private void logAsErrorUnexpectedDataIntegrityException(final Exception dve) {
-        LOG.error("Error occured.", dve);
+        log.error("Error occured.", dve);
     }
 
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/BulkUpdateLoanOfficerCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/BulkUpdateLoanOfficerCommandHandler.java
index 017b354d5..aac2d9dbc 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/BulkUpdateLoanOfficerCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/BulkUpdateLoanOfficerCommandHandler.java
@@ -18,26 +18,22 @@
  */
 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.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 @Service
+@RequiredArgsConstructor
 @CommandType(entity = "LOAN", action = "BULKREASSIGN")
 public class BulkUpdateLoanOfficerCommandHandler implements NewCommandSourceHandler {
 
     private final LoanWritePlatformService writePlatformService;
 
-    @Autowired
-    public BulkUpdateLoanOfficerCommandHandler(final LoanWritePlatformService writePlatformService) {
-        this.writePlatformService = writePlatformService;
-    }
-
     @Transactional
     @Override
     public CommandProcessingResult processCommand(final JsonCommand command) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanAsRescheduledCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanAsRescheduledCommandHandler.java
index af1ed6c15..d45b62f25 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanAsRescheduledCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanAsRescheduledCommandHandler.java
@@ -18,26 +18,22 @@
  */
 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.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 @Service
+@RequiredArgsConstructor
 @CommandType(entity = "LOAN", action = "CLOSEASRESCHEDULED")
 public class CloseLoanAsRescheduledCommandHandler implements NewCommandSourceHandler {
 
     private final LoanWritePlatformService writePlatformService;
 
-    @Autowired
-    public CloseLoanAsRescheduledCommandHandler(final LoanWritePlatformService writePlatformService) {
-        this.writePlatformService = writePlatformService;
-    }
-
     @Transactional
     @Override
     public CommandProcessingResult processCommand(final JsonCommand command) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanCommandHandler.java
index 2460bde85..c95416c65 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CloseLoanCommandHandler.java
@@ -18,26 +18,22 @@
  */
 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.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 @Service
+@RequiredArgsConstructor
 @CommandType(entity = "LOAN", action = "CLOSE")
 public class CloseLoanCommandHandler implements NewCommandSourceHandler {
 
     private final LoanWritePlatformService writePlatformService;
 
-    @Autowired
-    public CloseLoanCommandHandler(final LoanWritePlatformService writePlatformService) {
-        this.writePlatformService = writePlatformService;
-    }
-
     @Transactional
     @Override
     public CommandProcessingResult processCommand(final JsonCommand command) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CreditBalanceRefundCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CreditBalanceRefundCommandHandler.java
index 798290c51..4253ee231 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CreditBalanceRefundCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/CreditBalanceRefundCommandHandler.java
@@ -31,9 +31,9 @@ import org.springframework.orm.jpa.JpaSystemException;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
-@Slf4j
-@RequiredArgsConstructor
 @Service
+@RequiredArgsConstructor
+@Slf4j
 @CommandType(entity = "LOAN", action = "CREDITBALANCEREFUND")
 public class CreditBalanceRefundCommandHandler implements NewCommandSourceHandler {
 
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DeleteLoanChargeCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DeleteLoanChargeCommandHandler.java
index 98066bea5..b9b61e805 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DeleteLoanChargeCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DeleteLoanChargeCommandHandler.java
@@ -18,26 +18,22 @@
  */
 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.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 @Service
+@RequiredArgsConstructor
 @CommandType(entity = "LOANCHARGE", action = "DELETE")
 public class DeleteLoanChargeCommandHandler implements NewCommandSourceHandler {
 
     private final LoanWritePlatformService writePlatformService;
 
-    @Autowired
-    public DeleteLoanChargeCommandHandler(final LoanWritePlatformService writePlatformService) {
-        this.writePlatformService = writePlatformService;
-    }
-
     @Transactional
     @Override
     public CommandProcessingResult processCommand(final JsonCommand command) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanCommandHandler.java
index e6acaf692..a90183f79 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanCommandHandler.java
@@ -18,26 +18,22 @@
  */
 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.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 @Service
+@RequiredArgsConstructor
 @CommandType(entity = "LOAN", action = "DISBURSE")
 public class DisburseLoanCommandHandler implements NewCommandSourceHandler {
 
     private final LoanWritePlatformService writePlatformService;
 
-    @Autowired
-    public DisburseLoanCommandHandler(final LoanWritePlatformService writePlatformService) {
-        this.writePlatformService = writePlatformService;
-    }
-
     @Transactional
     @Override
     public CommandProcessingResult processCommand(final JsonCommand command) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanToSavingsCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanToSavingsCommandHandler.java
index 04fff7c9b..762b7c389 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanToSavingsCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DisburseLoanToSavingsCommandHandler.java
@@ -18,26 +18,22 @@
  */
 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.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 @Service
+@RequiredArgsConstructor
 @CommandType(entity = "LOAN", action = "DISBURSETOSAVINGS")
 public class DisburseLoanToSavingsCommandHandler implements NewCommandSourceHandler {
 
     private final LoanWritePlatformService writePlatformService;
 
-    @Autowired
-    public DisburseLoanToSavingsCommandHandler(final LoanWritePlatformService writePlatformService) {
-        this.writePlatformService = writePlatformService;
-    }
-
     @Transactional
     @Override
     public CommandProcessingResult processCommand(final JsonCommand command) {
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/ForeClosureCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/ForeClosureCommandHandler.java
index e249dc978..caf039903 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/ForeClosureCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/ForeClosureCommandHandler.java
@@ -18,25 +18,21 @@
  */
 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.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 @Service
+@RequiredArgsConstructor
 @CommandType(entity = "LOAN", action = "FORECLOSURE")
 public class ForeClosureCommandHandler implements NewCommandSourceHandler {
 
     private final LoanWritePlatformService writePlatformService;
 
-    @Autowired
-    public ForeClosureCommandHandler(final LoanWritePlatformService writePlatformService) {
-        this.writePlatformService = writePlatformService;
-    }
-
     @Override
     public CommandProcessingResult processCommand(final JsonCommand command) {
         return writePlatformService.forecloseLoan(command.getLoanId(), command);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanGoodwilCreditCommandHandler.java
similarity index 76%
copy from fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java
copy to fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanGoodwilCreditCommandHandler.java
index 4061bf6e2..81a696edf 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanGoodwilCreditCommandHandler.java
@@ -18,30 +18,28 @@
  */
 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.domain.LoanTransactionType;
 import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 @Service
-@CommandType(entity = "LOAN", action = "REPAYMENT")
-public class LoanRepaymentCommandHandler implements NewCommandSourceHandler {
+@RequiredArgsConstructor
+@CommandType(entity = "LOAN", action = "GOODWILLCREDIT")
+public class LoanGoodwilCreditCommandHandler implements NewCommandSourceHandler {
 
     private final LoanWritePlatformService writePlatformService;
 
-    @Autowired
-    public LoanRepaymentCommandHandler(final LoanWritePlatformService writePlatformService) {
-        this.writePlatformService = writePlatformService;
-    }
-
     @Transactional
     @Override
     public CommandProcessingResult processCommand(final JsonCommand command) {
         boolean isRecoveryRepayment = false;
-        return this.writePlatformService.makeLoanRepayment(command.getLoanId(), command, isRecoveryRepayment);
+        return this.writePlatformService.makeLoanRepayment(LoanTransactionType.GOODWILL_CREDIT, command.getLoanId(), command,
+                isRecoveryRepayment);
     }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanMerchantIssuedRefundCommandHandler.java
similarity index 76%
copy from fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java
copy to fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanMerchantIssuedRefundCommandHandler.java
index 4061bf6e2..433c479a7 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanMerchantIssuedRefundCommandHandler.java
@@ -18,30 +18,28 @@
  */
 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.domain.LoanTransactionType;
 import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 @Service
-@CommandType(entity = "LOAN", action = "REPAYMENT")
-public class LoanRepaymentCommandHandler implements NewCommandSourceHandler {
+@RequiredArgsConstructor
+@CommandType(entity = "LOAN", action = "MERCHANTISSUEDREFUND")
+public class LoanMerchantIssuedRefundCommandHandler implements NewCommandSourceHandler {
 
     private final LoanWritePlatformService writePlatformService;
 
-    @Autowired
-    public LoanRepaymentCommandHandler(final LoanWritePlatformService writePlatformService) {
-        this.writePlatformService = writePlatformService;
-    }
-
     @Transactional
     @Override
     public CommandProcessingResult processCommand(final JsonCommand command) {
         boolean isRecoveryRepayment = false;
-        return this.writePlatformService.makeLoanRepayment(command.getLoanId(), command, isRecoveryRepayment);
+        return this.writePlatformService.makeLoanRepayment(LoanTransactionType.MERCHANT_ISSUED_REFUND, command.getLoanId(), command,
+                isRecoveryRepayment);
     }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DeleteLoanChargeCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanPayoutRefundCommandHandler.java
similarity index 76%
copy from fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DeleteLoanChargeCommandHandler.java
copy to fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanPayoutRefundCommandHandler.java
index 98066bea5..0f48ad6d9 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/DeleteLoanChargeCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanPayoutRefundCommandHandler.java
@@ -18,30 +18,28 @@
  */
 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.domain.LoanTransactionType;
 import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 @Service
-@CommandType(entity = "LOANCHARGE", action = "DELETE")
-public class DeleteLoanChargeCommandHandler implements NewCommandSourceHandler {
+@RequiredArgsConstructor
+@CommandType(entity = "LOAN", action = "PAYOUTREFUND")
+public class LoanPayoutRefundCommandHandler implements NewCommandSourceHandler {
 
     private final LoanWritePlatformService writePlatformService;
 
-    @Autowired
-    public DeleteLoanChargeCommandHandler(final LoanWritePlatformService writePlatformService) {
-        this.writePlatformService = writePlatformService;
-    }
-
     @Transactional
     @Override
     public CommandProcessingResult processCommand(final JsonCommand command) {
-
-        return this.writePlatformService.deleteLoanCharge(command.getLoanId(), command.entityId(), command);
+        boolean isRecoveryRepayment = false;
+        return this.writePlatformService.makeLoanRepayment(LoanTransactionType.PAYOUT_REFUND, command.getLoanId(), command,
+                isRecoveryRepayment);
     }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRecoveryPaymentCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRecoveryPaymentCommandHandler.java
index 87aa86d73..18ada7cef 100755
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRecoveryPaymentCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRecoveryPaymentCommandHandler.java
@@ -22,6 +22,7 @@ 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.domain.LoanTransactionType;
 import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -40,6 +41,6 @@ public class LoanRecoveryPaymentCommandHandler implements NewCommandSourceHandle
     @Override
     public CommandProcessingResult processCommand(JsonCommand command) {
         final boolean isRecoveryRepayment = true;
-        return writePlatformService.makeLoanRepayment(command.getLoanId(), command, isRecoveryRepayment);
+        return writePlatformService.makeLoanRepayment(LoanTransactionType.REPAYMENT, command.getLoanId(), command, isRecoveryRepayment);
     }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java
index 4061bf6e2..3bbd62bf7 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/handler/LoanRepaymentCommandHandler.java
@@ -18,30 +18,28 @@
  */
 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.domain.LoanTransactionType;
 import org.apache.fineract.portfolio.loanaccount.service.LoanWritePlatformService;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 @Service
+@RequiredArgsConstructor
 @CommandType(entity = "LOAN", action = "REPAYMENT")
 public class LoanRepaymentCommandHandler implements NewCommandSourceHandler {
 
     private final LoanWritePlatformService writePlatformService;
 
-    @Autowired
-    public LoanRepaymentCommandHandler(final LoanWritePlatformService writePlatformService) {
-        this.writePlatformService = writePlatformService;
-    }
-
     @Transactional
     @Override
     public CommandProcessingResult processCommand(final JsonCommand command) {
         boolean isRecoveryRepayment = false;
-        return this.writePlatformService.makeLoanRepayment(command.getLoanId(), command, isRecoveryRepayment);
+        return this.writePlatformService.makeLoanRepayment(LoanTransactionType.REPAYMENT, command.getLoanId(), command,
+                isRecoveryRepayment);
     }
 }
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
index fe9776087..45abfa493 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanApplicationWritePlatformServiceJpaRepositoryImpl.java
@@ -112,6 +112,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanSummaryWrapper;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTopupDetails;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationDateException;
 import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeDeleted;
 import org.apache.fineract.portfolio.loanaccount.exception.LoanApplicationNotInSubmittedAndPendingApprovalStateCannotBeModified;
@@ -378,8 +379,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
                                         + " should be after last transaction date of loan to be closed "
                                         + lastUserTransactionOnLoanToClose);
                     }
-                    BigDecimal loanOutstanding = this.loanReadPlatformService
-                            .retrieveLoanPrePaymentTemplate(loanIdToClose, newLoanApplication.getDisbursementDate()).getAmount();
+                    BigDecimal loanOutstanding = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT,
+                            loanIdToClose, newLoanApplication.getDisbursementDate()).getAmount();
                     final BigDecimal firstDisbursalAmount = newLoanApplication.getFirstDisbursalAmount();
                     if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) {
                         throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
@@ -956,8 +957,8 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
                                             + " should be after last transaction date of loan to be closed "
                                             + lastUserTransactionOnLoanToClose);
                         }
-                        BigDecimal loanOutstanding = this.loanReadPlatformService
-                                .retrieveLoanPrePaymentTemplate(loanIdToClose, existingLoanApplication.getDisbursementDate()).getAmount();
+                        BigDecimal loanOutstanding = this.loanReadPlatformService.retrieveLoanPrePaymentTemplate(
+                                LoanTransactionType.REPAYMENT, loanIdToClose, existingLoanApplication.getDisbursementDate()).getAmount();
                         final BigDecimal firstDisbursalAmount = existingLoanApplication.getFirstDisbursalAmount();
                         if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) {
                             throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
@@ -1456,7 +1457,7 @@ public class LoanApplicationWritePlatformServiceJpaRepositoryImpl implements Loa
                                     + " should be after last transaction date of loan to be closed " + lastUserTransactionOnLoanToClose);
                 }
                 BigDecimal loanOutstanding = this.loanReadPlatformService
-                        .retrieveLoanPrePaymentTemplate(loanIdToClose, expectedDisbursementDate).getAmount();
+                        .retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanIdToClose, expectedDisbursementDate).getAmount();
                 final BigDecimal firstDisbursalAmount = loan.getFirstDisbursalAmount();
                 if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) {
                     throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
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 8618c33f0..3d81a76a1 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
@@ -38,6 +38,7 @@ import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
 import org.apache.fineract.portfolio.loanaccount.data.PaidInAdvanceData;
 import org.apache.fineract.portfolio.loanaccount.data.RepaymentScheduleRelatedLoanData;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 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;
@@ -114,7 +115,7 @@ public interface LoanReadPlatformService {
 
     List<Long> fetchLoansForInterestRecalculation(Integer pageSize, Long maxLoanIdInList, String officeHierarchy);
 
-    LoanTransactionData retrieveLoanPrePaymentTemplate(Long loanId, LocalDate onDate);
+    LoanTransactionData retrieveLoanPrePaymentTemplate(LoanTransactionType repaymentTransactionType, Long loanId, LocalDate onDate);
 
     Collection<LoanTransactionData> retrieveWaiverLoanTransactions(Long loanId);
 
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 cb3e177ca..bf126f58d 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
@@ -445,9 +445,11 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
     }
 
     @Override
-    public LoanTransactionData retrieveLoanPrePaymentTemplate(final Long loanId, LocalDate onDate) {
+    public LoanTransactionData retrieveLoanPrePaymentTemplate(final LoanTransactionType repaymentTransactionType, final Long loanId,
+            LocalDate onDate) {
 
         this.context.authenticatedUser();
+        this.loanUtilService.validateRepaymentTransactionType(repaymentTransactionType);
 
         final Loan loan = this.loanRepositoryWrapper.findOneWithNotFoundDetection(loanId, true);
         loan.setHelpers(null, null, loanRepaymentScheduleTransactionProcessorFactory);
@@ -461,7 +463,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService {
         final LocalDate recalculateFrom = null;
         final ScheduleGeneratorDTO scheduleGeneratorDTO = loanUtilService.buildScheduleGeneratorDTO(loan, recalculateFrom);
         final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment = loan.fetchPrepaymentDetail(scheduleGeneratorDTO, onDate);
-        final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(LoanTransactionType.REPAYMENT);
+        final LoanTransactionEnumData transactionType = LoanEnumerations.transactionType(repaymentTransactionType);
         final Collection<PaymentTypeData> paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
         final BigDecimal outstandingLoanBalance = loanRepaymentScheduleInstallment.getPrincipalOutstanding(currency).getAmount();
         final BigDecimal unrecognizedIncomePortion = null;
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java
index 0653a7218..8c810548f 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanUtilService.java
@@ -30,6 +30,7 @@ import java.util.Locale;
 import java.util.Set;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
+import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
 import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
 import org.apache.fineract.organisation.holiday.domain.Holiday;
 import org.apache.fineract.organisation.holiday.domain.HolidayRepository;
@@ -57,6 +58,7 @@ import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
 import org.apache.fineract.portfolio.loanaccount.data.ScheduleGeneratorDTO;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleGeneratorFactory;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -351,4 +353,12 @@ public class LoanUtilService {
         return disbursementDatas;
     }
 
+    public void validateRepaymentTransactionType(LoanTransactionType repaymentTransactionType) {
+        if (!repaymentTransactionType.isRepaymentType()) {
+            throw new PlatformServiceUnavailableException("error.msg.repaymentTransactionType.provided.not.a.repayment.type",
+                    "Loan :" + repaymentTransactionType.getCode() + " Repayment Transaction Type provided is not a Repayment Type",
+                    repaymentTransactionType.getCode());
+        }
+    }
+
 }
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 9c49ade84..83555bb24 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
@@ -32,6 +32,7 @@ import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulk
 import org.apache.fineract.portfolio.collectionsheet.command.CollectionSheetBulkRepaymentCommand;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType;
 import org.apache.fineract.portfolio.loanaccount.loanschedule.data.OverdueLoanScheduleData;
 
 public interface LoanWritePlatformService {
@@ -43,7 +44,8 @@ public interface LoanWritePlatformService {
 
     CommandProcessingResult undoLoanDisbursal(Long loanId, JsonCommand command);
 
-    CommandProcessingResult makeLoanRepayment(Long loanId, JsonCommand command, boolean isRecoveryRepayment);
+    CommandProcessingResult makeLoanRepayment(LoanTransactionType repaymentTransactionType, Long loanId, JsonCommand command,
+            boolean isRecoveryRepayment);
 
     Map<String, Object> makeLoanBulkRepayment(CollectionSheetBulkRepaymentCommand bulkRepaymentCommand);
 
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 512242dee..ee5772b7a 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
@@ -488,7 +488,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
                 }
 
                 BigDecimal loanOutstanding = this.loanReadPlatformService
-                        .retrieveLoanPrePaymentTemplate(loanIdToClose, actualDisbursementDate).getAmount();
+                        .retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanIdToClose, actualDisbursementDate).getAmount();
                 final BigDecimal firstDisbursalAmount = loan.getFirstDisbursalAmount();
                 if (loanOutstanding.compareTo(firstDisbursalAmount) > 0) {
                     throw new GeneralPlatformDomainRuleException("error.msg.loan.amount.less.than.outstanding.of.loan.to.be.closed",
@@ -886,7 +886,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
                 final LocalDate expectedDisbursementDate = command
                         .localDateValueOfParameterNamed(LoanApiConstants.disbursementDateParameterName);
                 BigDecimal loanOutstanding = this.loanReadPlatformService
-                        .retrieveLoanPrePaymentTemplate(loanIdToClose, expectedDisbursementDate).getAmount();
+                        .retrieveLoanPrePaymentTemplate(LoanTransactionType.REPAYMENT, loanIdToClose, expectedDisbursementDate).getAmount();
                 BigDecimal netDisbursalAmount = loan.getApprovedPrincipal().subtract(loanOutstanding);
                 loan.adjustNetDisbursalAmount(netDisbursalAmount);
             }
@@ -941,15 +941,17 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
         int j = 0;
         for (JsonElement element : repayments) {
             childCommand = JsonCommand.fromExistingCommand(command, element);
-            result = makeLoanRepayment(childLoanId[j++], childCommand, false);
+            result = makeLoanRepayment(LoanTransactionType.REPAYMENT, childLoanId[j++], childCommand, false);
         }
         return result;
     }
 
     @Transactional
     @Override
-    public CommandProcessingResult makeLoanRepayment(final Long loanId, final JsonCommand command, final boolean isRecoveryRepayment) {
+    public CommandProcessingResult makeLoanRepayment(final LoanTransactionType repaymentTransactionType, final Long loanId,
+            final JsonCommand command, final boolean isRecoveryRepayment) {
 
+        this.loanUtilService.validateRepaymentTransactionType(repaymentTransactionType);
         this.loanEventApiJsonValidator.validateNewRepaymentTransaction(command.json());
 
         final LocalDate transactionDate = command.localDateValueOfParameterNamed("transactionDate");
@@ -973,9 +975,9 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
         final HolidayDetailDTO holidayDetailDto = null;
         boolean isAccountTransfer = false;
         final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder();
-        LoanTransaction loanTransaction = this.loanAccountDomainService.makeRepayment(loan, commandProcessingResultBuilder, transactionDate,
-                transactionAmount, paymentDetail, noteText, txnExternalId, isRecoveryRepayment, isAccountTransfer, holidayDetailDto,
-                isHolidayValidationDone);
+        LoanTransaction loanTransaction = this.loanAccountDomainService.makeRepayment(repaymentTransactionType, loan,
+                commandProcessingResultBuilder, transactionDate, transactionAmount, paymentDetail, noteText, txnExternalId,
+                isRecoveryRepayment, isAccountTransfer, holidayDetailDto, isHolidayValidationDone);
 
         // Update loan transaction on repayment.
         if (AccountType.fromInt(loan.getLoanType()).isIndividualAccount()) {
@@ -1044,10 +1046,10 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
                     this.paymentDetailWritePlatformService.persistPaymentDetail(paymentDetail);
                 }
                 final CommandProcessingResultBuilder commandProcessingResultBuilder = new CommandProcessingResultBuilder();
-                LoanTransaction loanTransaction = this.loanAccountDomainService.makeRepayment(loan, commandProcessingResultBuilder,
-                        bulkRepaymentCommand.getTransactionDate(), singleLoanRepaymentCommand.getTransactionAmount(), paymentDetail,
-                        bulkRepaymentCommand.getNote(), null, isRecoveryRepayment, isAccountTransfer, holidayDetailDTO,
-                        isHolidayValidationDone);
+                LoanTransaction loanTransaction = this.loanAccountDomainService.makeRepayment(LoanTransactionType.REPAYMENT, loan,
+                        commandProcessingResultBuilder, bulkRepaymentCommand.getTransactionDate(),
+                        singleLoanRepaymentCommand.getTransactionAmount(), paymentDetail, bulkRepaymentCommand.getNote(), null,
+                        isRecoveryRepayment, isAccountTransfer, holidayDetailDTO, isHolidayValidationDone);
                 transactionIds.add(loanTransaction.getId());
             }
         }
@@ -1183,7 +1185,7 @@ public class LoanWritePlatformServiceJpaRepositoryImpl implements LoanWritePlatf
 
         this.loanAccountDomainService.recalculateAccruals(loan);
         Map<BusinessEntity, Object> entityMap = constructEntityMap(BusinessEntity.LOAN_ADJUSTED_TRANSACTION, transactionToAdjust);
-        if (newTransactionDetail.isRepayment() && newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) {
+        if (newTransactionDetail.isRepaymentType() && newTransactionDetail.isGreaterThanZero(loan.getPrincpal().getCurrency())) {
             entityMap.put(BusinessEntity.LOAN_TRANSACTION, newTransactionDetail);
         }
         this.businessEventNotifierService.notifyBusinessEventWasExecuted(BusinessEvents.LOAN_ADJUST_TRANSACTION, entityMap);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
index 56faedcba..0407304a2 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/service/LoanEnumerations.java
@@ -415,6 +415,18 @@ public final class LoanEnumerations {
                 optionData = new LoanTransactionEnumData(LoanTransactionType.CREDIT_BALANCE_REFUND.getValue().longValue(),
                         LoanTransactionType.CREDIT_BALANCE_REFUND.getCode(), "Credit Balance Refund");
             break;
+            case MERCHANT_ISSUED_REFUND:
+                optionData = new LoanTransactionEnumData(LoanTransactionType.MERCHANT_ISSUED_REFUND.getValue().longValue(),
+                        LoanTransactionType.MERCHANT_ISSUED_REFUND.getCode(), "Merchant Issued Refund");
+            break;
+            case PAYOUT_REFUND:
+                optionData = new LoanTransactionEnumData(LoanTransactionType.PAYOUT_REFUND.getValue().longValue(),
+                        LoanTransactionType.PAYOUT_REFUND.getCode(), "Payout Refund");
+            break;
+            case GOODWILL_CREDIT:
+                optionData = new LoanTransactionEnumData(LoanTransactionType.GOODWILL_CREDIT.getValue().longValue(),
+                        LoanTransactionType.GOODWILL_CREDIT.getCode(), "Goodwill Credit");
+            break;
         }
         return optionData;
     }
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 e871a7f94..0eed83638 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
@@ -31,4 +31,5 @@
     <include file="parts/0009_hold_reason_savings_account.xml" relativeToChangelogFile="true"/>
     <include file="parts/0010_lien_allowed_on_savings_account_products.xml" relativeToChangelogFile="true"/>
     <include file="parts/0011_add_credit_balance_refund_permission.xml" relativeToChangelogFile="true"/>
+    <include file="parts/0012_add_merchantissuedrefund_payoutrefund_goodwillcredit_permissions.xml" relativeToChangelogFile="true"/>
 </databaseChangeLog>
diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0012_add_merchantissuedrefund_payoutrefund_goodwillcredit_permissions.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0012_add_merchantissuedrefund_payoutrefund_goodwillcredit_permissions.xml
new file mode 100644
index 000000000..f8706a844
--- /dev/null
+++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0012_add_merchantissuedrefund_payoutrefund_goodwillcredit_permissions.xml
@@ -0,0 +1,46 @@
+<?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.1.xsd">
+    <changeSet author="fineract" id="1">
+        <insert tableName="m_permission">
+            <column name="grouping" value="transaction_loan" />
+            <column name="code" value="MERCHANTISSUEDREFUND_LOAN" />
+            <column name="entity_name" value="LOAN" />
+            <column name="action_name" value="MERCHANTISSUEDREFUND" />
+            <column name="can_maker_checker" valueBoolean="false" />
+        </insert>
+        <insert tableName="m_permission">
+            <column name="grouping" value="transaction_loan" />
+            <column name="code" value="PAYOUTREFUND_LOAN" />
+            <column name="entity_name" value="LOAN" />
+            <column name="action_name" value="PAYOUTREFUND" />
+            <column name="can_maker_checker" valueBoolean="false" />
+        </insert>
+        <insert tableName="m_permission">
+            <column name="grouping" value="transaction_loan" />
+            <column name="code" value="GOODWILLCREDIT_LOAN" />
+            <column name="entity_name" value="LOAN" />
+            <column name="action_name" value="GOODWILLCREDIT" />
+            <column name="can_maker_checker" valueBoolean="false" />
+        </insert>
+    </changeSet>
+</databaseChangeLog>
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java
similarity index 84%
rename from integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundIntegrationTest.java
rename to integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java
index 91959773e..61508badb 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundIntegrationTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java
@@ -45,9 +45,9 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 @SuppressWarnings({ "rawtypes", "unchecked" })
-public class ClientLoanCreditBalanceRefundIntegrationTest {
+public class ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest {
 
-    private static final Logger LOG = LoggerFactory.getLogger(ClientLoanCreditBalanceRefundIntegrationTest.class);
+    private static final Logger LOG = LoggerFactory.getLogger(ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.class);
 
     private ResponseSpecification responseSpec;
     private RequestSpecification requestSpec;
@@ -56,11 +56,15 @@ public class ClientLoanCreditBalanceRefundIntegrationTest {
     private JournalEntryHelper journalEntryHelper;
     private AccountHelper accountHelper;
     private Integer disbursedLoanID;
+    private static final String CASH_BASED = "2";
     private static final String ACCRUAL_PERIODIC = "3";
     private Account assetAccount;
     private Account incomeAccount;
     private Account expenseAccount;
     private Account overpaymentAccount;
+    private static final String MERCHANT_ISSUED_REFUND = "merchantIssuedRefund";
+    private static final String PAYOUT_REFUND = "payoutRefund";
+    private static final String GOODWILL_CREDIT = "goodwillCredit";
 
     @BeforeEach
     public void setup() {
@@ -76,12 +80,13 @@ public class ClientLoanCreditBalanceRefundIntegrationTest {
         this.expenseAccount = this.accountHelper.createExpenseAccount();
         this.overpaymentAccount = this.accountHelper.createLiabilityAccount();
         this.journalEntryHelper = new JournalEntryHelper(this.requestSpec, this.responseSpec);
+    }
 
+    private void disburseLoanOfAccountingRule(final String accountingType) {
         final String principal = "12000.00";
         final String submitApproveDisburseDate = "01 January 2022";
         this.disbursedLoanID = fromStartToDisburseLoan(submitApproveDisburseDate, principal, ACCRUAL_PERIODIC, assetAccount, incomeAccount,
                 expenseAccount, overpaymentAccount);
-
     }
 
     private Integer createLoanProduct(final String principal, final boolean multiDisburseLoan, final String accountingRule,
@@ -162,6 +167,7 @@ public class ClientLoanCreditBalanceRefundIntegrationTest {
 
     @Test
     public void creditBalanceRefundCanOnlyBeAppliedWhereLoanStatusIsOverpaidTest() {
+        disburseLoanOfAccountingRule(ACCRUAL_PERIODIC);
         HashMap loanStatusHashMap = makeRepayment("06 January 2022", 2000.00f); // not full payment
         LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
 
@@ -174,14 +180,11 @@ public class ClientLoanCreditBalanceRefundIntegrationTest {
         assertEquals("error.msg.loan.credit.balance.refund.account.is.not.overpaid",
                 cbrErrors.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE));
 
-        // ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec,
-        // this.responseSpec, loanID);
-        // final int loanScheduleLineCount = loanSchedule.size();
-
     }
 
     @Test
     public void cantRefundMoreThanOverpaidTest() {
+        disburseLoanOfAccountingRule(ACCRUAL_PERIODIC);
         HashMap loanStatusHashMap = makeRepayment("06 January 2022", 20000.00f); // overpayment
         LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap);
 
@@ -204,6 +207,7 @@ public class ClientLoanCreditBalanceRefundIntegrationTest {
 
     @Test
     public void fullRefundChangesStatusToClosedObligationMetTest() {
+        disburseLoanOfAccountingRule(ACCRUAL_PERIODIC);
         HashMap loanStatusHashMap = makeRepayment("06 January 2022", 20000.00f); // overpayment
         LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap);
 
@@ -229,6 +233,7 @@ public class ClientLoanCreditBalanceRefundIntegrationTest {
 
     @Test
     public void partialRefundKeepsOverpaidStatusTest() {
+        disburseLoanOfAccountingRule(ACCRUAL_PERIODIC);
         HashMap loanStatusHashMap = makeRepayment("06 January 2022", 20000.00f); // overpayment
         LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap);
 
@@ -246,6 +251,7 @@ public class ClientLoanCreditBalanceRefundIntegrationTest {
     @Test
     public void newCreditBalanceRefundSavesExternalIdTest() {
 
+        disburseLoanOfAccountingRule(ACCRUAL_PERIODIC);
         HashMap loanStatusHashMap = makeRepayment("06 January 2022", 20000.00f); // overpayment
         LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap);
 
@@ -265,6 +271,7 @@ public class ClientLoanCreditBalanceRefundIntegrationTest {
     @Test
     public void newCreditBalanceRefundFindsDuplicateExternalIdTest() {
 
+        disburseLoanOfAccountingRule(ACCRUAL_PERIODIC);
         HashMap loanStatusHashMap = makeRepayment("06 January 2022", 20000.00f); // overpayment
         LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap);
 
@@ -287,6 +294,7 @@ public class ClientLoanCreditBalanceRefundIntegrationTest {
     @Test
     public void newCreditBalanceRefundCreatesCorrectJournalEntriesForPeriodicAccrualsTest() {
 
+        disburseLoanOfAccountingRule(ACCRUAL_PERIODIC);
         HashMap loanStatusHashMap = makeRepayment("06 January 2022", 20000.00f); // overpayment
         LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap);
 
@@ -304,4 +312,45 @@ public class ClientLoanCreditBalanceRefundIntegrationTest {
 
     }
 
+    @Test
+    public void newCreditBalanceRefundCreatesCorrectJournalEntriesForCashAccrualsTest() {
+
+        disburseLoanOfAccountingRule(CASH_BASED);
+        HashMap loanStatusHashMap = makeRepayment("08 January 2022", 20000.00f); // overpayment
+        LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap);
+
+        final Float refund = 1000.00f; // partial refund
+        final String creditBalanceRefundDate = "09 January 2022";
+        final String externalId = null;
+        final Integer resourceId = (Integer) loanTransactionHelper.creditBalanceRefund(creditBalanceRefundDate, refund, externalId,
+                disbursedLoanID, "resourceId");
+        Assertions.assertNotNull(resourceId);
+
+        this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, creditBalanceRefundDate,
+                new JournalEntry(refund, JournalEntry.TransactionType.DEBIT));
+        this.journalEntryHelper.checkJournalEntryForLiabilityAccount(overpaymentAccount, creditBalanceRefundDate,
+                new JournalEntry(refund, JournalEntry.TransactionType.CREDIT));
+
+    }
+
+    @Test
+    public void repaymentTransactionTypeMatchesTest() {
+        disburseLoanOfAccountingRule(ACCRUAL_PERIODIC);
+        verifyRepaymentTransactionTypeMatches(MERCHANT_ISSUED_REFUND);
+        verifyRepaymentTransactionTypeMatches(PAYOUT_REFUND);
+        verifyRepaymentTransactionTypeMatches(GOODWILL_CREDIT);
+
+    }
+
+    private void verifyRepaymentTransactionTypeMatches(final String repaymentTransactionType) {
+        HashMap loanStatusHashMap = this.loanTransactionHelper.makeRepaymentTypePayment(repaymentTransactionType, "06 January 2022",
+                200.00f, this.disbursedLoanID);
+        Integer newTransactionId = (Integer) loanStatusHashMap.get("resourceId");
+        loanStatusHashMap = this.loanTransactionHelper.getLoanTransactionDetails(this.disbursedLoanID, newTransactionId);
+
+        HashMap typeMap = (HashMap) loanStatusHashMap.get("type");
+        Boolean isTypeCorrect = (Boolean) typeMap.get(repaymentTransactionType);
+        Assertions.assertTrue(Boolean.TRUE.equals(isTypeCorrect), "Not " + repaymentTransactionType);
+    }
+
 }
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 50942ac27..85527944d 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
@@ -339,6 +339,12 @@ public class LoanTransactionHelper {
                 getCreditBalanceRefundBodyAsJSON(date, amountToBePaid, externalId), jsonAttributeToGetback);
     }
 
+    public HashMap makeRepaymentTypePayment(final String repaymentTypeCommand, final String date, final Float amountToBePaid,
+            final Integer loanID) {
+        return (HashMap) performLoanTransaction(createLoanTransactionURL(repaymentTypeCommand, loanID),
+                getRepaymentBodyAsJSON(date, amountToBePaid), "");
+    }
+
     public HashMap makeRepayment(final String date, final Float amountToBePaid, final Integer loanID) {
         return (HashMap) performLoanTransaction(createLoanTransactionURL(MAKE_REPAYMENT_COMMAND, loanID),
                 getRepaymentBodyAsJSON(date, amountToBePaid), "");