PHP Functions to Calculate Interest Payments

I built these to help calculate how much of my monthly mortgage and car payments were actually going towards the principal and how much was interest.

This first function actually does the monthly calculations. There are a number of variables that you can set via the $args array. See the inline comments for more details there.

One that I'll point out is $args['type']. This should be set to either "daily" or "monthly" based on how your interest is calculated. If your loan is compounded daily, you should also set $args['compounded'] to "daily", otherwise you can leave it as FALSE.

1.   function calculate_payment($args) {
2.   	if (!is_array($args)) { return false; }
3.   	$type = $args['type']; // 'daily' or 'monthly'
4.   	if (array_key_exists('compounded',$args)) { $compounded = $args['compounded']; } else { $compounded = false; } // can be 'daily' or false
5.   	$apr = $args['apr'];   // percentage or decimal
6.   	if ($apr > 1) { $apr = $apr / 100; } // (convert percentage to decimal if necessary)
7.   	if (array_key_exists('balance',$args)) { $balance = $args['balance']; } // dollar amount
8.   	else { $balance = $args['principal']; } // dollar amount
9.   	if (array_key_exists('escrow',$args)) { $escrow = $args['escrow']; } // dollar amount
10.  	else { $escrow = 0; }
11.  	$monthly_payment = $args['monthly_payment']; // dollar amount
12.  	$payment_time = $args['payment_time']; // unix timestamp
13.  	if (array_key_exists('start_time',$args)) { $start_time = $args['start_time']; } else { $start_time = strtotime('-1 month',$payment_time); }
14.  	if (array_key_exists('interest_overrides',$args) && is_array($args['interest_overrides'])) { $interest_overrides = $args['interest_overrides']; } else { $interest_overrides = array(); }
15.  
16.  	if ($type == 'monthly') {
17.  		$mpr = $apr / 12;
18.  		$monthly_interest = $balance * $mpr;
19.  		$days = date('t',$start_time);
20.  	} elseif ($type == 'daily') {
21.  		$dpr = $apr / 365;
22.  		if ($compounded == 'daily') {
23.  			$current_time = $start_time;
24.  			$monthly_interest = 0;
25.  			while ($current_time < $payment_time) {
26.  				$daily_interest = $balance * $dpr;
27.  				$monthly_interest = $monthly_interest + $daily_interest;
28.  				$current_time = strtotime('+1 day',$current_time);
29.  			}
30.  		} else {
31.  			$days = round(($payment_time - $start_time) / (60*60*24));
32.  			$monthly_interest = $balance * $days * $dpr;
33.  		}
34.  	} else { return false; }
35.  
36.  	//check for overrides
37.  	if (array_key_exists($payment_time,$interest_overrides)) {
38.  		$monthly_interest = $interest_overrides[$payment_time];
39.  	}
40.  
41.  	$monthly_interest = round($monthly_interest,2);
42.  	$monthly_principal = $monthly_payment - $escrow - $monthly_interest; //principal paid this month
43.  	$balance = $balance - $monthly_principal;
44.  	return array(
45.  	  'payment_date'=>date('Y-m-d',$payment_time),
46.  	  'days'=>$days,
47.  	  'payment'=>$monthly_payment,
48.  	  'escrow'=>$escrow,
49.  	  'principal'=>$monthly_principal,
50.  	  'interest'=>$monthly_interest,
51.  	  'balance'=>$balance,
52.  	);
53.  }

Loans payments often aren't on the first of the month. They wait until the first Monday, skipping weekends and holidays. That affects the amount of interest spent in each cycle (if your interest is calculated daily), so determining your payment dates is important. This function lets you determine your payment dates after you provide some specifics for your loan.

1.   function payment_times($args) {
2.   	if (array_key_exists('holidays',$args)) { $holidays = $args['holidays']; } else { $holidays = array(); }
3.   	$m = 0;
4.   	$times = array();
5.   	$time = $args['loan_origination'];
6.   	if (array_key_exists('payment_times',$args) && is_array($args['payment_times'])) {
7.   		foreach ($args['payment_times'] as $time) {
8.   			$m++;
9.   			$times[$m] = $time;
10.  		}
11.  	}
12.  	while ($m < $args['months']) {
13.  		$m++;
14.  		$time = strtotime('+1 month',$time); //move forward a month
15.  		$time = mktime(12,0,0,date('n',$time),1,date('Y',$time)); //first of the month
16.  		if (date('w',$time) == 6) { $time = strtotime('+2 days',$time); } //Saturday to Monday
17.  		elseif (date('w',$time) == 0) { $time = strtotime('+1 day',$time); } //Sunday to Monday
18.  
19.  		if (in_array($time,$holidays)) { $time = strtotime('+1 day',$time); }
20.  		
21.  		$times[] = $time;
22.  		$last_time = $time;
23.  	}
24.  	return $times;
25.  }

Finally, this function displays all the data that you've calculated. It also calls the other two functions as needed, so this is the only one that you'll actually have to call from your code:

1.   function loan_table($loan_info) {
2.   	$date_format = 'M j, Y'; //this can be changed
3.   
4.   	$loan_info['payment_times'] = payment_times($loan_info);
5.   	$totals = array('payment'=>0,'principal'=>0,'interest'=>0,'escrow'=>0);
6.   	$table = '<table class="standard">';
7.   	$table .= '<thead><tr>';
8.   		$table .= '<td>Payment<br />Date</td>';
9.   		$table .= '<td>Days</td>';
10.  		$table .= '<td>Payment</td>';
11.  		$table .= '<td>Principal</td>';
12.  		$table .= '<td>Interest</td>';
13.  		$table .= '<td>Escrow</td>';
14.  		$table .= '<td>Remaining<br />Balance</td>';
15.  	$table .= '</tr></thead>';
16.  
17.  	$table .= '<tr>';
18.  		$table .= '<td>'.date($date_format,$loan_info['loan_origination']).'</td>';
19.  		$table .= '<td>&nbsp;</td>';
20.  		$table .= '<td>&nbsp;</td>';
21.  		$table .= '<td>&nbsp;</td>';
22.  		$table .= '<td>&nbsp;</td>';
23.  		$table .= '<td>&nbsp;</td>';
24.  		$table .= '<td>$'.number_format($loan_info['principal'],2).'</td>';
25.  	$table .= '</tr>';
26.  
27.  	$last_time = $loan_info['loan_origination'];
28.  	foreach ($loan_info['payment_times'] as $time) {
29.  		if ($loan_info['balance'] < 0) { break; }
30.  		$args = $loan_info;
31.  		$args['start_time'] = $last_time;
32.  		$args['payment_time'] = $time;
33.  		$payment = calculate_payment($args);
34.  		$table .= '<tr>';
35.  		$table .= '<td>'.date($date_format,$time).'</td>';
36.  		$table .= '<td>'.$payment['days'].'</td>';
37.  		$table .= '<td>$'.number_format($payment['payment'],2).'</td>';
38.  		$table .= '<td>$'.number_format($payment['principal'],2).'</td>';
39.  		$table .= '<td>$'.number_format($payment['interest'],2).'</td>';
40.  		$table .= '<td>$'.number_format($payment['escrow'],2).'</td>';
41.  		$table .= '<td>$'.number_format($payment['balance'],2).'</td>';
42.  		$table .= '</tr>';
43.  		$loan_info['balance'] = round($payment['balance'],2);
44.  		$last_time = $time;
45.  		foreach ($totals as $key => $value) {
46.  			$totals[$key] = $value + $payment[$key];
47.  		}
48.  	}
49.  	$table .= '<tfoot><tr>';
50.  		$table .= '<td colspan="2">Totals</td>';
51.  		$table .= '<td>$'.number_format($totals['payment'],2).'</td>';
52.  		$table .= '<td>$'.number_format($totals['principal'],2).'</td>';
53.  		$table .= '<td>$'.number_format($totals['interest'],2).'</td>';
54.  		$table .= '<td>$'.number_format($totals['escrow'],2).'</td>';
55.  		$table .= '<td>&nbsp;</td>';
56.  	$table .= '</tr></tfoot>';
57.  	$table .= '</table>';
58.  	return $table;
59.  }

Seeing the code in action always helps, so here's an example of how all 3 functions work together. First the code you'd use:

1.   $example = array(
2.     'type'=>'daily',
3.     'principal'=>17000.00,
4.     'apr'=>3.9,
5.     'monthly_payment'=>300.00,
6.     'months'=>60,
7.     'loan_origination'=>mktime(12,0,0,9,17,2012),
8.     'payment_times'=>array(
9.   	mktime(12,0,0,10,18,2012),
10.  	mktime(12,0,0,12,1,2012)
11.    ),
12.    'holidays'=>array(
13.  	mktime(12,0,0,1,1,2013),
14.    ),
15.    'interest_overrides'=>array(
16.    )
17.  );
18.  echo loan_table($example);

And, here's the table that would produce:

Payment
Date
DaysPaymentPrincipalInterestEscrowRemaining
Balance
Sep 17, 2012     $17,000.00
Oct 18, 201231$300.00$243.69$56.31$0.00$16,756.31
Dec 1, 201244$300.00$221.22$78.78$0.00$16,535.09
Jan 2, 201332$300.00$243.46$56.54$0.00$16,291.63
Feb 1, 201330$300.00$247.78$52.22$0.00$16,043.85
Mar 1, 201328$300.00$252.00$48.00$0.00$15,791.85
Apr 1, 201331$300.00$247.69$52.31$0.00$15,544.16
May 1, 201330$300.00$250.17$49.83$0.00$15,293.99
Jun 3, 201333$300.00$246.07$53.93$0.00$15,047.92
Jul 1, 201328$300.00$254.98$45.02$0.00$14,792.94
Aug 1, 201331$300.00$251.00$49.00$0.00$14,541.94
Sep 2, 201332$300.00$250.28$49.72$0.00$14,291.66
Oct 1, 201329$300.00$255.72$44.28$0.00$14,035.94
Nov 1, 201331$300.00$253.51$46.49$0.00$13,782.43
Dec 2, 201331$300.00$254.35$45.65$0.00$13,528.08
Jan 1, 201430$300.00$256.64$43.36$0.00$13,271.44
Feb 3, 201433$300.00$253.20$46.80$0.00$13,018.24
Mar 3, 201428$300.00$261.05$38.95$0.00$12,757.19
Apr 1, 201429$300.00$260.47$39.53$0.00$12,496.72
May 1, 201430$300.00$259.94$40.06$0.00$12,236.78
Jun 2, 201432$300.00$258.16$41.84$0.00$11,978.62
Jul 1, 201429$300.00$262.88$37.12$0.00$11,715.74
Aug 1, 201431$300.00$261.19$38.81$0.00$11,454.55
Sep 1, 201431$300.00$262.06$37.94$0.00$11,192.49
Oct 1, 201430$300.00$264.12$35.88$0.00$10,928.37
Nov 3, 201433$300.00$261.47$38.53$0.00$10,666.90
Dec 1, 201428$300.00$268.09$31.91$0.00$10,398.81
Jan 1, 201531$300.00$265.56$34.44$0.00$10,133.25
Feb 2, 201532$300.00$265.35$34.65$0.00$9,867.90
Mar 2, 201528$300.00$270.48$29.52$0.00$9,597.42
Apr 1, 201530$300.00$269.24$30.76$0.00$9,328.18
May 1, 201530$300.00$270.10$29.90$0.00$9,058.08
Jun 1, 201531$300.00$270.00$30.00$0.00$8,788.08
Jul 1, 201530$300.00$271.83$28.17$0.00$8,516.25
Aug 3, 201533$300.00$269.97$30.03$0.00$8,246.28
Sep 1, 201529$300.00$274.45$25.55$0.00$7,971.83
Oct 1, 201530$300.00$274.45$25.55$0.00$7,697.38
Nov 2, 201532$300.00$273.68$26.32$0.00$7,423.70
Dec 1, 201529$300.00$277.00$23.00$0.00$7,146.70
Jan 1, 201631$300.00$276.33$23.67$0.00$6,870.37
Feb 1, 201631$300.00$277.24$22.76$0.00$6,593.13
Mar 1, 201629$300.00$279.57$20.43$0.00$6,313.56
Apr 1, 201631$300.00$279.09$20.91$0.00$6,034.47
May 2, 201631$300.00$280.01$19.99$0.00$5,754.46
Jun 1, 201630$300.00$281.55$18.45$0.00$5,472.91
Jul 1, 201630$300.00$282.46$17.54$0.00$5,190.45
Aug 1, 201631$300.00$282.81$17.19$0.00$4,907.64
Sep 1, 201631$300.00$283.74$16.26$0.00$4,623.90
Oct 3, 201632$300.00$284.19$15.81$0.00$4,339.71
Nov 1, 201629$300.00$286.55$13.45$0.00$4,053.16
Dec 1, 201630$300.00$287.01$12.99$0.00$3,766.15
Jan 2, 201732$300.00$287.12$12.88$0.00$3,479.03
Feb 1, 201730$300.00$288.85$11.15$0.00$3,190.18
Mar 1, 201728$300.00$290.46$9.54$0.00$2,899.72
Apr 3, 201733$300.00$289.78$10.22$0.00$2,609.94
May 1, 201728$300.00$292.19$7.81$0.00$2,317.75
Jun 1, 201731$300.00$292.32$7.68$0.00$2,025.43
Jul 3, 201732$300.00$293.07$6.93$0.00$1,732.36
Aug 1, 201729$300.00$294.63$5.37$0.00$1,437.73
Sep 1, 201731$300.00$295.24$4.76$0.00$1,142.49
Oct 2, 201731$300.00$296.22$3.78$0.00$846.27
Totals$18,000.00$16,153.73$1,846.27$0.00 

This is just a fictional example that includes a balloon payment at the end. You'd ovbiously want to plug in your own numbers instead.


Comments

Loading…

This post was published on May 10th, 2014 by Robert James Reese in PHP. Before using any of the code or other content in this post, you must read and agree to our terms of use.