Skip to content

Latest commit

 

History

14 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LoanOriginationApi

CI

A Java 17 and Spring Boot 3 API that evaluates a loan application with deterministic rules, produces an indicative annual rate and amortization schedule, and stores the input and decision in an H2 file database.

This is a portfolio service, not a lending system. It has no authentication, bureau integration, affordability verification, fraud detection, regulatory workflow, or production-grade data controls. Do not use its score or price to make real credit decisions.

Run

./mvnw spring-boot:run

On Windows PowerShell:

.\mvnw.cmd spring-boot:run

Run the verification suite:

./mvnw test

The service listens on http://localhost:8080. H2 persists to ./data/loan-origination.mv.db, so decisions survive restarts on the same machine.

API

Method Path Purpose
POST /api/loans/apply Validate, score, price, schedule, and persist an application
GET /api/loans/{applicationId} Retrieve a persisted decision and regenerate its deterministic schedule
POST /api/loans/{applicationId}/decide Resolve a MANUAL_REVIEW application to APPROVED or REJECTED
GET /api/loans/health Return service health

Apply:

curl -X POST http://localhost:8080/api/loans/apply \
  -H "Content-Type: application/json" \
  -d '{
    "applicantName": "Ada Lovelace",
    "monthlyIncome": 40000,
    "monthlyDebt": 3000,
    "requestedAmount": 60000,
    "termMonths": 24,
    "latePaymentsLast12m": 0
  }'

PowerShell uses curl.exe, avoiding the curl alias:

curl.exe -X POST http://localhost:8080/api/loans/apply -H "Content-Type: application/json" -d '{\"applicantName\":\"Ada Lovelace\",\"monthlyIncome\":40000,\"monthlyDebt\":3000,\"requestedAmount\":60000,\"termMonths\":24,\"latePaymentsLast12m\":0}'

Resolve a review after replacing the placeholder with the returned identifier:

curl -X POST http://localhost:8080/api/loans/LN-EXAMPLE/decide \
  -H "Content-Type: application/json" \
  -d '{"decision":"APPROVED"}'

Decision and rate rules

Every application begins at 650 points. The scoring service then applies the following fixed adjustments:

Rule Adjustment
Debt-to-income at or below 20% +80
Debt-to-income above 20% through 40% +20
Debt-to-income above 40% -90
Requested amount at or below three monthly incomes +30
Requested amount above six monthly incomes -60
No late payments in the last 12 months +40
One or two late payments -30
Three or more late payments -100
Term longer than 60 months -20

Scores are constrained to 300 through 850. Scores of 700 or more are APPROVED; 580 through 699 are MANUAL_REVIEW; lower scores are REJECTED.

The annual percentage rate is a score band, not a quote or a regulatory APR calculation:

Score Annual rate
760–850 8%
700–759 12%
660–699 18%
580–659 28%
Below 580 Rejected, no rate

For non-rejected applications, monthly payments use the annuity formula P × r × (1 + r)^n / ((1 + r)^n - 1), where r is the annual rate divided by 12. Monetary values use RoundingMode.HALF_UP at two decimal places. Each schedule row satisfies payment = principal + interest; the final period uses the remaining principal, so total principal equals the requested amount within 0.01 and the final balance is zero. A manual-review result is indicative until it is resolved.

Auditability and security

The response and application snapshot include a loan-score-v1 score breakdown. Each applied rule has a stable rule ID, its score delta, and the input values used to evaluate it. The snapshot also has createdAt and, once resolved, decidedAt.

The database stores append-only APPLICATION_SCORED and MANUAL_DECIDED audit events with their timestamps and resulting decision. The decision snapshot remains the current-state record. Manual resolution is transactional and uses optimistic locking, so an already-resolved or concurrent decision returns a conflict instead of silently replacing the prior result.

Requests use Bean Validation. API responses include Cache-Control: no-store, Content-Security-Policy: default-src 'none', Referrer-Policy: no-referrer, X-Content-Type-Options: nosniff, and X-Frame-Options: DENY. These headers do not replace authentication, TLS, authorization, rate limiting, encryption, retention controls, or a security review.

Documentation

Related portfolio projects

License

MIT

About

Loan origination API in Java: rule-based credit score, risk-priced rate, amortization schedule, H2 persistence.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages