Development of a Mortgage Calculator on 1C-Bitrix
A mortgage calculator is one of the most technically rich financial tools on a website. Unlike simple "area × price" multiplication, it involves annuity payment formulas, down payment accounting, insurance, and differentiated vs. annuity repayment types. At the same time, the user expects an instant answer — any delay is perceived as a site error.
The math: annuity payment
An annuity payment is the same amount every month. The formula:
P = S × r / (1 - (1 + r)^(-n))
Where:
-
P— monthly payment -
S— loan amount (property price minus down payment) -
r— monthly interest rate (annual ÷ 12 ÷ 100) -
n— number of months
Implementation in PHP:
namespace MyProject\Services;
class MortgageCalculator
{
public static function calculate(
float $propertyPrice,
float $downPayment,
float $annualRate,
int $termYears
): array {
$loanAmount = $propertyPrice - $downPayment;
$termMonths = $termYears * 12;
$monthlyRate = $annualRate / 12 / 100;
if ($monthlyRate == 0) {
// Zero-interest installment (rare but happens)
$monthlyPayment = $loanAmount / $termMonths;
} else {
$monthlyPayment = $loanAmount
* $monthlyRate
* pow(1 + $monthlyRate, $termMonths)
/ (pow(1 + $monthlyRate, $termMonths) - 1);
}
$totalPayment = $monthlyPayment * $termMonths;
$totalInterest = $totalPayment - $loanAmount;
$overpaymentPct = ($totalInterest / $loanAmount) * 100;
return [
'loan_amount' => round($loanAmount, 2),
'monthly_payment' => round($monthlyPayment, 2),
'total_payment' => round($totalPayment, 2),
'total_interest' => round($totalInterest, 2),
'overpayment_pct' => round($overpaymentPct, 1),
'down_payment_pct' => round(($downPayment / $propertyPrice) * 100, 1),
];
}
public static function buildPaymentSchedule(
float $loanAmount,
float $annualRate,
int $termMonths
): array {
$schedule = [];
$monthlyRate = $annualRate / 12 / 100;
$monthlyPayment = $loanAmount
* $monthlyRate
* pow(1 + $monthlyRate, $termMonths)
/ (pow(1 + $monthlyRate, $termMonths) - 1);
$balance = $loanAmount;
for ($month = 1; $month <= $termMonths; $month++) {
$interestPayment = $balance * $monthlyRate;
$principalPayment = $monthlyPayment - $interestPayment;
$balance -= $principalPayment;
$schedule[] = [
'month' => $month,
'payment' => round($monthlyPayment, 2),
'principal' => round($principalPayment, 2),
'interest' => round($interestPayment, 2),
'balance' => round(max($balance, 0), 2),
];
}
return $schedule;
}
}
Payment schedule: table and chart
A full repayment schedule is a competitive advantage. The user can see how the principal debt decreases and how much goes toward interest in different periods. Chart.js or ApexCharts are used for visualization — lightweight libraries without unnecessary dependencies.
// Chart data comes from the server via AJAX
async function updateChart(params) {
const response = await fetch('/ajax/mortgage/schedule/', {
method: 'POST',
body: new URLSearchParams({ ...params, sessid: BX.bitrix_sessid() }),
});
const data = await response.json();
if (!data.success) return;
// Group by year for the chart
const years = [];
const principalByYear = [];
const interestByYear = [];
data.schedule.forEach((month, i) => {
const yearIndex = Math.floor(i / 12);
if (!years[yearIndex]) {
years[yearIndex] = `Year ${yearIndex + 1}`;
principalByYear[yearIndex] = 0;
interestByYear[yearIndex] = 0;
}
principalByYear[yearIndex] += month.principal;
interestByYear[yearIndex] += month.interest;
});
renderStackedBarChart(years, principalByYear, interestByYear);
}
Insurance accounting
Banks require insurance. The calculator must show the real cost:
- Property insurance — 0.1–0.5% of property value per year
- Life and health insurance — 0.3–1.5% of the loan balance (optional, but reduces the rate)
- Title insurance — relevant for the secondary market
$annualInsurance = $loanAmount * ($insuranceRate / 100);
$monthlyInsurance = $annualInsurance / 12;
$totalMonthlyPayment = $monthlyPayment + $monthlyInsurance;
Bank comparison
For aggregators and developer websites — a comparison table of offers from several banks. Data is stored in an HL block and updated by a manager:
| Bank | Rate | Down payment | Term |
|---|---|---|---|
| Bank A | 8.5% | from 15% | up to 30 years |
| Bank B | 7.9% | from 20% | up to 25 years |
| Bank C | 9.1% | from 10% | up to 30 years |
Integration with the request form
The calculation result is passed to the request form as hidden fields — the manager can see what the client was considering:
document.getElementById('submit-btn').addEventListener('click', () => {
document.getElementById('hidden-loan-amount').value = currentResult.loan_amount;
document.getElementById('hidden-monthly-pay').value = currentResult.monthly_payment;
document.getElementById('hidden-annual-rate').value = currentParams.annualRate;
document.getElementById('hidden-term-years').value = currentParams.termYears;
});
Timelines
| Task | Timeline |
|---|---|
| Basic calculator (annuity, main fields, request form) | 5–8 days |
| Calculator with payment schedule, chart, insurance accounting | 2–3 weeks |
| Full mortgage aggregator with multiple banks, saved calculations | 4–6 weeks |
A mortgage calculator works when the math is correct and the UX is not intimidating. The client should get an answer within 2–3 seconds of interacting with the form — that is the criterion for a successful tool.







