Payment Processing

Comprehensive guide to processing payments in LMS including payment methods, hierarchies, auto-pay management, and failed payment handling.

Payment Processing

📘

Complete Payment Management

The Loan Management System provides comprehensive payment processing capabilities including multiple payment methods, flexible payment hierarchies, auto-pay functionality, and robust failed payment handling.

Payment processing is a core function of LMS, enabling you to accept payments through various methods, allocate funds according to different hierarchies, and manage recurring payments automatically.


Payment Processing Overview

LMS supports multiple payment methods and allocation strategies to meet diverse business needs:

Payment Methods

MethodDescriptionAuto-InitiateUse Case
CashIn-person cash paymentNoBranch payments, walk-in customers
CheckPaper check paymentNoMailed payments, manual processing
ACHAutomated Clearing House bank transferYesAuto-pay, recurring payments
Debit CardDebit card paymentYesOnline payments, customer portal

Payment Flow

%%{init: {'theme': 'base', 'themeVariables': {'background': '#ffffff', 'mainBkg': '#ffffff', 'clusterBkg': '#ffffff', 'clusterBorder': '#2563eb', 'titleColor': '#000000', 'primaryColor': '#2563eb', 'primaryTextColor': '#ffffff', 'lineColor': '#374151'}, 'flowchart': {'padding': 20, 'nodeSpacing': 25, 'rankSpacing': 35}}}%%
flowchart LR
    subgraph MainContainer[💳 Payment Processing Flow]
        direction LR
        A[💳 Payment] --> B[✅ Validate] --> C[💰 Allocate] --> D[📊 Record]
        
        style A fill:#2563eb,stroke:#1e40af,color:#fff
        style B fill:#6b7280,stroke:#4b5563,color:#fff
        style C fill:#059669,stroke:#047857,color:#fff
        style D fill:#2563eb,stroke:#1e40af,color:#fff
    end

Flow Steps:

  • Payment - Payment received via any method
  • Validate - Validate payment date and amount
  • Allocate - Apply payment according to hierarchy
  • Record - Record transaction and update balances

Payment Hierarchies

Payment hierarchies determine how funds are allocated across principal, interest, and fees:

HierarchyDescriptionAllocation Order
SystemDefault allocationFees → Interest → Principal
ScheduleApply to scheduled paymentPer schedule allocation
Principal OnlyAll to principal100% Principal
PayoffClear all balancesAll outstanding amounts
CustomUser-defined allocationCustom percentages

Allocation Flow

%%{init: {'theme': 'base', 'themeVariables': {'background': '#ffffff', 'mainBkg': '#ffffff', 'clusterBkg': '#ffffff', 'clusterBorder': '#2563eb', 'titleColor': '#000000', 'primaryColor': '#2563eb', 'primaryTextColor': '#ffffff', 'lineColor': '#374151'}, 'flowchart': {'padding': 20, 'nodeSpacing': 25, 'rankSpacing': 35}}}%%
flowchart LR
    subgraph MainContainer[💰 Payment Allocation]
        direction LR
        A[💰 Payment] --> B{⚖️ Hierarchy} --> C[📊 Allocate]
        B -->|System| D[Fees → Interest → Principal]
        B -->|Schedule| E[Per Schedule]
        B -->|Principal| F[100% Principal]
        B -->|Payoff| G[All Outstanding]
        D --> C
        E --> C
        F --> C
        G --> C
        
        style A fill:#2563eb,stroke:#1e40af,color:#fff
        style B fill:#6b7280,stroke:#4b5563,color:#fff
        style C fill:#059669,stroke:#047857,color:#fff
        style D fill:#2563eb,stroke:#1e40af,color:#fff
        style E fill:#2563eb,stroke:#1e40af,color:#fff
        style F fill:#2563eb,stroke:#1e40af,color:#fff
        style G fill:#2563eb,stroke:#1e40af,color:#fff
    end

Make Payment API

The Make Payment API allows you to process payments for any loan using various payment methods and hierarchies.

Endpoint

POST /v1/payment-processor/loans/{loanNumber}/payments

Request Parameters

ParameterTypeRequiredDescription
paymentInstrumentstringYesCash, Check, ACH, DebitCard
paymentTypestringYesSystem, Schedule, PrincipalOnly, Payoff, Custom
paymentDatedateYesDate payment is received
effectiveDatedateYesDate payment is applied
paymentAmountnumberYesPayment amount
bankAccountIdstringConditionalRequired for ACH payments
checkNumberstringConditionalRequired for Check payments
userRemarksstringNoPayment notes or comments
recastingOptionstringNoReducePayment or KeepPaymentSame

Payment Validation Rules

RuleDescription
Payment DateMust be ≥ Funded Date
Effective DateMust be ≥ Funded Date AND ≤ Payment Date
Debit CardEffective Date ≥ max(Funded Date, Today + Processing Days)

Recasting Options

When a payment exceeds the scheduled amount, you can choose how to handle the excess:

OptionDescriptionImpact
ReducePaymentLower monthly payment, same termPayment amount reduced, term unchanged
KeepPaymentSameSame payment, shorter termPayment amount unchanged, term reduced

Make Manual Payment (Cash)

curl -X POST 'https://api.demo.lms.lendfoundry.com/v1/payment-processor/loans/LN-2024-001234/payments' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "paymentInstrument": "Cash",
    "paymentType": "Schedule",
    "paymentDate": "2024-02-01",
    "effectiveDate": "2024-02-01",
    "paymentAmount": 500.00,
    "userRemarks": "Monthly payment - cash",
    "recastingOption": "KeepPaymentSame"
  }'
const makeCashPayment = async (loanNumber, amount, paymentDate) => {
  const url = `https://api.demo.lms.lendfoundry.com/v1/payment-processor/loans/${loanNumber}/payments`;
  
  const payload = {
    paymentInstrument: 'Cash',
    paymentType: 'Schedule',
    paymentDate: paymentDate,
    effectiveDate: paymentDate,
    paymentAmount: amount,
    userRemarks: 'Monthly payment - cash',
    recastingOption: 'KeepPaymentSame'
  };
  
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_TOKEN',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
  });
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return await response.json();
};

const result = await makeCashPayment('LN-2024-001234', 500.00, '2024-02-01');
console.log(`Payment ID: ${result.paymentId}`);
import requests

def make_cash_payment(loan_number, amount, payment_date):
    url = f"https://api.demo.lms.lendfoundry.com/v1/payment-processor/loans/{loan_number}/payments"
    headers = {
        "Authorization": "Bearer YOUR_TOKEN",
        "Content-Type": "application/json"
    }
    
    payload = {
        "paymentInstrument": "Cash",
        "paymentType": "Schedule",
        "paymentDate": payment_date,
        "effectiveDate": payment_date,
        "paymentAmount": amount,
        "userRemarks": "Monthly payment - cash",
        "recastingOption": "KeepPaymentSame"
    }
    
    response = requests.post(url, json=payload, headers=headers)
    response.raise_for_status()
    return response.json()

result = make_cash_payment("LN-2024-001234", 500.00, "2024-02-01")
print(f"Payment ID: {result['paymentId']}")
public static String makeCashPayment(String loanNumber, double amount, String paymentDate) throws Exception {
    String url = BASE_URL + "/payment-processor/loans/" + loanNumber + "/payments";
    
    Map<String, Object> payload = new HashMap<>();
    payload.put("paymentInstrument", "Cash");
    payload.put("paymentType", "Schedule");
    payload.put("paymentDate", paymentDate);
    payload.put("effectiveDate", paymentDate);
    payload.put("paymentAmount", amount);
    payload.put("userRemarks", "Monthly payment - cash");
    payload.put("recastingOption", "KeepPaymentSame");
    
    String requestBody = mapper.writeValueAsString(payload);
    
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(url))
        .header("Authorization", "Bearer YOUR_TOKEN")
        .header("Content-Type", "application/json")
        .POST(BodyPublishers.ofString(requestBody))
        .build();
    
    HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
    return response.body();
}

Make ACH Payment

curl -X POST 'https://api.demo.lms.lendfoundry.com/v1/payment-processor/loans/LN-2024-001234/payments' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "paymentInstrument": "ACH",
    "paymentType": "System",
    "paymentDate": "2024-02-01",
    "effectiveDate": "2024-02-01",
    "paymentAmount": 750.00,
    "bankAccountId": "BA-12345",
    "userRemarks": "ACH payment"
  }'
const makeACHPayment = async (loanNumber, amount, bankAccountId, paymentDate) => {
  const url = `https://api.demo.lms.lendfoundry.com/v1/payment-processor/loans/${loanNumber}/payments`;
  
  const payload = {
    paymentInstrument: 'ACH',
    paymentType: 'System',
    paymentDate: paymentDate,
    effectiveDate: paymentDate,
    paymentAmount: amount,
    bankAccountId: bankAccountId,
    userRemarks: 'ACH payment'
  };
  
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_TOKEN',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
  });
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return await response.json();
};

const result = await makeACHPayment('LN-2024-001234', 750.00, 'BA-12345', '2024-02-01');
console.log(`Payment ID: ${result.paymentId}`);
import requests

def make_ach_payment(loan_number, amount, bank_account_id, payment_date):
    url = f"https://api.demo.lms.lendfoundry.com/v1/payment-processor/loans/{loan_number}/payments"
    headers = {
        "Authorization": "Bearer YOUR_TOKEN",
        "Content-Type": "application/json"
    }
    
    payload = {
        "paymentInstrument": "ACH",
        "paymentType": "System",
        "paymentDate": payment_date,
        "effectiveDate": payment_date,
        "paymentAmount": amount,
        "bankAccountId": bank_account_id,
        "userRemarks": "ACH payment"
    }
    
    response = requests.post(url, json=payload, headers=headers)
    response.raise_for_status()
    return response.json()

result = make_ach_payment("LN-2024-001234", 750.00, "BA-12345", "2024-02-01")
print(f"Payment ID: {result['paymentId']}")
public static String makeACHPayment(String loanNumber, double amount, String bankAccountId, String paymentDate) throws Exception {
    String url = BASE_URL + "/payment-processor/loans/" + loanNumber + "/payments";
    
    Map<String, Object> payload = new HashMap<>();
    payload.put("paymentInstrument", "ACH");
    payload.put("paymentType", "System");
    payload.put("paymentDate", paymentDate);
    payload.put("effectiveDate", paymentDate);
    payload.put("paymentAmount", amount);
    payload.put("bankAccountId", bankAccountId);
    payload.put("userRemarks", "ACH payment");
    
    String requestBody = mapper.writeValueAsString(payload);
    
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(url))
        .header("Authorization", "Bearer YOUR_TOKEN")
        .header("Content-Type", "application/json")
        .POST(BodyPublishers.ofString(requestBody))
        .build();
    
    HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
    return response.body();
}

Make Payoff Payment

curl -X POST 'https://api.demo.lms.lendfoundry.com/v1/payment-processor/loans/LN-2024-001234/payments' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "paymentInstrument": "Check",
    "paymentType": "Payoff",
    "paymentDate": "2024-02-15",
    "effectiveDate": "2024-02-15",
    "paymentAmount": 46400.00,
    "checkNumber": "1234",
    "userRemarks": "Payoff payment"
  }'
const makePayoffPayment = async (loanNumber, amount, checkNumber, paymentDate) => {
  const url = `https://api.demo.lms.lendfoundry.com/v1/payment-processor/loans/${loanNumber}/payments`;
  
  const payload = {
    paymentInstrument: 'Check',
    paymentType: 'Payoff',
    paymentDate: paymentDate,
    effectiveDate: paymentDate,
    paymentAmount: amount,
    checkNumber: checkNumber,
    userRemarks: 'Payoff payment'
  };
  
  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_TOKEN',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
  });
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return await response.json();
};

const result = await makePayoffPayment('LN-2024-001234', 46400.00, '1234', '2024-02-15');
console.log(`Payment ID: ${result.paymentId}`);
import requests

def make_payoff_payment(loan_number, amount, check_number, payment_date):
    url = f"https://api.demo.lms.lendfoundry.com/v1/payment-processor/loans/{loan_number}/payments"
    headers = {
        "Authorization": "Bearer YOUR_TOKEN",
        "Content-Type": "application/json"
    }
    
    payload = {
        "paymentInstrument": "Check",
        "paymentType": "Payoff",
        "paymentDate": payment_date,
        "effectiveDate": payment_date,
        "paymentAmount": amount,
        "checkNumber": check_number,
        "userRemarks": "Payoff payment"
    }
    
    response = requests.post(url, json=payload, headers=headers)
    response.raise_for_status()
    return response.json()

result = make_payoff_payment("LN-2024-001234", 46400.00, "1234", "2024-02-15")
print(f"Payment ID: {result['paymentId']}")
public static String makePayoffPayment(String loanNumber, double amount, String checkNumber, String paymentDate) throws Exception {
    String url = BASE_URL + "/payment-processor/loans/" + loanNumber + "/payments";
    
    Map<String, Object> payload = new HashMap<>();
    payload.put("paymentInstrument", "Check");
    payload.put("paymentType", "Payoff");
    payload.put("paymentDate", paymentDate);
    payload.put("effectiveDate", paymentDate);
    payload.put("paymentAmount", amount);
    payload.put("checkNumber", checkNumber);
    payload.put("userRemarks", "Payoff payment");
    
    String requestBody = mapper.writeValueAsString(payload);
    
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(url))
        .header("Authorization", "Bearer YOUR_TOKEN")
        .header("Content-Type", "application/json")
        .POST(BodyPublishers.ofString(requestBody))
        .build();
    
    HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
    return response.body();
}

Payment Response Example

{
  "paymentId": "PAY-2024-005678",
  "loanId": "LN-2024-001234",
  "status": "Success",
  "paymentDate": "2024-02-01",
  "effectiveDate": "2024-02-01",
  "paymentAmount": 500.00,
  "paymentInstrument": "Cash",
  "allocationDetails": {
    "principal": 350.00,
    "interest": 125.00,
    "fees": 25.00
  },
  "newBalance": {
    "principalOutstanding": 44650.00,
    "interestOutstanding": 0.00,
    "feesOutstanding": 0.00,
    "totalOutstanding": 44650.00
  },
  "message": "Payment recorded successfully"
}

Auto-Pay Management

Auto-pay enables automatic payment processing on scheduled dates using ACH or Debit Card.

Auto-Pay Flow

%%{init: {'theme': 'base', 'themeVariables': {'background': '#ffffff', 'mainBkg': '#ffffff', 'clusterBkg': '#ffffff', 'clusterBorder': '#2563eb', 'titleColor': '#000000', 'primaryColor': '#2563eb', 'primaryTextColor': '#ffffff', 'lineColor': '#374151'}, 'flowchart': {'padding': 20, 'nodeSpacing': 25, 'rankSpacing': 35}}}%%
flowchart LR
    subgraph MainContainer[⚙️ Auto-Pay Management]
        direction LR
        A[⚙️ Setup] --> B[📅 Schedule] --> C[💳 Process] --> D{✅ Success?}
        D -->|Yes| E[✅ Complete]
        D -->|No| F[🔄 Retry]
        F --> C
        
        style A fill:#2563eb,stroke:#1e40af,color:#fff
        style B fill:#2563eb,stroke:#1e40af,color:#fff
        style C fill:#059669,stroke:#047857,color:#fff
        style D fill:#6b7280,stroke:#4b5563,color:#fff
        style E fill:#059669,stroke:#047857,color:#fff
        style F fill:#d97706,stroke:#b45309,color:#fff
    end

Pause Auto-Pay

curl -X PUT 'https://api.demo.lms.lendfoundry.com/v1/loan-management/loans/LN-2024-001234/autopay/pause' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "effectiveDate": "2024-02-01",
    "reason": "Customer request - temporary hardship"
  }'
const pauseAutoPay = async (loanNumber, effectiveDate, reason) => {
  const url = `https://api.demo.lms.lendfoundry.com/v1/loan-management/loans/${loanNumber}/autopay/pause`;
  
  const payload = {
    effectiveDate: effectiveDate,
    reason: reason
  };
  
  const response = await fetch(url, {
    method: 'PUT',
    headers: {
      'Authorization': 'Bearer YOUR_TOKEN',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
  });
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return await response.json();
};

await pauseAutoPay('LN-2024-001234', '2024-02-01', 'Customer request - temporary hardship');
import requests

def pause_autopay(loan_number, effective_date, reason):
    url = f"https://api.demo.lms.lendfoundry.com/v1/loan-management/loans/{loan_number}/autopay/pause"
    headers = {
        "Authorization": "Bearer YOUR_TOKEN",
        "Content-Type": "application/json"
    }
    
    payload = {
        "effectiveDate": effective_date,
        "reason": reason
    }
    
    response = requests.put(url, json=payload, headers=headers)
    response.raise_for_status()
    return response.json()

pause_autopay("LN-2024-001234", "2024-02-01", "Customer request - temporary hardship")
public static String pauseAutoPay(String loanNumber, String effectiveDate, String reason) throws Exception {
    String url = BASE_URL + "/loan-management/loans/" + loanNumber + "/autopay/pause";
    
    Map<String, Object> payload = new HashMap<>();
    payload.put("effectiveDate", effectiveDate);
    payload.put("reason", reason);
    
    String requestBody = mapper.writeValueAsString(payload);
    
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(url))
        .header("Authorization", "Bearer YOUR_TOKEN")
        .header("Content-Type", "application/json")
        .PUT(BodyPublishers.ofString(requestBody))
        .build();
    
    HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
    return response.body();
}

Resume Auto-Pay

curl -X PUT 'https://api.demo.lms.lendfoundry.com/v1/loan-management/loans/LN-2024-001234/autopay/resume' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "effectiveDate": "2024-03-01"
  }'
const resumeAutoPay = async (loanNumber, effectiveDate) => {
  const url = `https://api.demo.lms.lendfoundry.com/v1/loan-management/loans/${loanNumber}/autopay/resume`;
  
  const payload = {
    effectiveDate: effectiveDate
  };
  
  const response = await fetch(url, {
    method: 'PUT',
    headers: {
      'Authorization': 'Bearer YOUR_TOKEN',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(payload)
  });
  
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
  return await response.json();
};

await resumeAutoPay('LN-2024-001234', '2024-03-01');
import requests

def resume_autopay(loan_number, effective_date):
    url = f"https://api.demo.lms.lendfoundry.com/v1/loan-management/loans/{loan_number}/autopay/resume"
    headers = {
        "Authorization": "Bearer YOUR_TOKEN",
        "Content-Type": "application/json"
    }
    
    payload = {
        "effectiveDate": effective_date
    }
    
    response = requests.put(url, json=payload, headers=headers)
    response.raise_for_status()
    return response.json()

resume_autopay("LN-2024-001234", "2024-03-01")
public static String resumeAutoPay(String loanNumber, String effectiveDate) throws Exception {
    String url = BASE_URL + "/loan-management/loans/" + loanNumber + "/autopay/resume";
    
    Map<String, Object> payload = new HashMap<>();
    payload.put("effectiveDate", effectiveDate);
    
    String requestBody = mapper.writeValueAsString(payload);
    
    HttpRequest request = HttpRequest.newBuilder()
        .uri(URI.create(url))
        .header("Authorization", "Bearer YOUR_TOKEN")
        .header("Content-Type", "application/json")
        .PUT(BodyPublishers.ofString(requestBody))
        .build();
    
    HttpResponse<String> response = client.send(request, BodyHandlers.ofString());
    return response.body();
}

Failed Payment Handling

When auto-pay payments fail, the system handles retries and recovery:

Failure Reasons

ReasonDescriptionAction
NSFNon-sufficient fundsRetry after grace period
Invalid AccountAccount closed or invalidContact customer
Stop PaymentCustomer stopped paymentManual review
Processing ErrorSystem processing errorRetry immediately

Failed Payment Flow

%%{init: {'theme': 'base', 'themeVariables': {'background': '#ffffff', 'mainBkg': '#ffffff', 'clusterBkg': '#ffffff', 'clusterBorder': '#2563eb', 'titleColor': '#000000', 'primaryColor': '#2563eb', 'primaryTextColor': '#ffffff', 'lineColor': '#374151'}, 'flowchart': {'padding': 20, 'nodeSpacing': 25, 'rankSpacing': 35}}}%%
flowchart LR
    subgraph MainContainer[❌ Failed Payment Handling]
        direction LR
        A[💳 Payment] --> B{❌ Failed?}
        B -->|Yes| C[📋 Reason] --> D{🔄 Retry?}
        D -->|Yes| E[⏱️ Wait] --> F[🔄 Retry]
        D -->|No| G[📞 Manual]
        F --> B
        B -->|No| H[✅ Success]
        
        style A fill:#2563eb,stroke:#1e40af,color:#fff
        style B fill:#6b7280,stroke:#4b5563,color:#fff
        style C fill:#dc2626,stroke:#b91c1c,color:#fff
        style D fill:#6b7280,stroke:#4b5563,color:#fff
        style E fill:#d97706,stroke:#b45309,color:#fff
        style F fill:#d97706,stroke:#b45309,color:#fff
        style G fill:#dc2626,stroke:#b91c1c,color:#fff
        style H fill:#059669,stroke:#047857,color:#fff
    end

Screenshots

📷

Screenshot: Payment Processing Form

This screenshot shows the payment processing interface in LMS with payment method selection, amount entry, and allocation options.

Payment Processing Form
📷

Screenshot: Payment History View

This screenshot shows the payment history interface displaying all processed payments, their status, allocation, and transaction details.

Payment History View

Next Steps

ResourceDescription
Loan OnboardingLearn how to onboard loans from LOS
Line of Credit OperationsUnderstand LOC operations and draw management
API ReferenceFull API documentation

Ready for LOC Operations?

Head to the Line of Credit Guide to learn about LOC draw management and operations.