-
Notifications
You must be signed in to change notification settings - Fork 0
/
uc_payment_plan.module
630 lines (579 loc) · 23.2 KB
/
uc_payment_plan.module
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
<?php
/*******************************************************************************
* Drupal Hooks
******************************************************************************/
/**
* Implements hook_menu().
*/
function uc_payment_plan_menu() {
$items['authnet/test-silent-post'] = array(
'page callback' => 'drupal_get_form',
'page arguments' => array('uc_payment_plan_test_silent_post'),
'access arguments' => array('administer store'),
'title' => 'Generate a test response from Authorize.Net',
'type' => MENU_CALLBACK,
);
$items['cart/payment-plan'] = array(
'title' => 'Update it',
'page callback' => 'uc_payment_plan_ajax_update',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Implements hook_theme().
*/
function uc_payment_plan_theme() {
return array(
'uc_payment_plan_checkout_form' => array(
'arguments' => array('element' => NULL),
),
);
}
/*******************************************************************************
* Ubercart Hooks
******************************************************************************/
/**
* Implementation of hook_payment_gateway().
*/
function uc_payment_plan_payment_gateway() {
$gateways[] = array(
'id' => 'payment_plan',
'title' => t('Payment Plan'),
'settings' => 'uc_payment_plan_settings_form',
'credit' => 'uc_payment_plan_charge',
'credit_txn_types' => array(UC_CREDIT_AUTH_ONLY, UC_CREDIT_AUTH_CAPTURE),
);
return $gateways;
}
/**
* Implementation of hook_checkout_pane().
*/
function uc_payment_plan_checkout_pane() {
// If payment plan is not available, don't setup a checkout pane.
if (!uc_payment_plan_available()) {
return array();
}
$panes[] = array(
'id' => 'payment_plan',
'callback' => 'uc_checkout_pane_payment_plan',
'title' => t('Payment plan'),
'weight' => 7,
'process' => TRUE,
'collapsible' => TRUE,
);
return $panes;
}
/**
* Implementation of hook_line_item().
*/
function uc_payment_plan_line_item() {
$items[] = array(
'id' => 'payment_plan_fee',
'title' => t('Payment plan fee'),
'calculated' => TRUE,
'stored' => TRUE,
'callback' => 'uc_line_item_payment_plan_fee',
);
return $items;
}
/**
* Implementation of hook_order().
*/
function uc_payment_plan_order($op, &$arg1, $arg2) {
// An order just got saved.
if ($op == 'save') {
// Generate processing fee line item.
$line_items = uc_line_item_payment_plan_fee('load', $arg1);
// If order has any line items.
if (is_array($arg1->line_items)) {
// Loop through the existing order line items.
foreach ($arg1->line_items as $key => $existing_line) {
// If we find a processing fee line item.
if ($existing_line['type'] == 'payment_plan_fee') {
// Update it.
if (!empty($line_items) and $existing_line['type'] == $line_items[0]['id']) {
uc_order_update_line_item($existing_line['line_item_id'], $line_items[0]['title'], $line_items[0]['amount']);
$arg1->line_items[$key][] = $line_items[0];
unset($line_items[0]);
}
// Delete it.
else {
uc_order_delete_line_item($existing_line['line_item_id']);
unset($arg1->line_items[$key]);
}
break;
}
}
}
// Save processing fee line item (if it exists).
if (!empty($line_items[0])) {
uc_order_line_item_add($arg1->order_id, $line_items[0]['id'], $line_items[0]['title'], $line_items[0]['amount'], $line_items[0]['weight']);
$new_line['type'] = $line_items[0]['id'];
$arg1->line_items[] = $line_items[0];
}
}
}
/**
* Implementation of hook_uc_arb_payment().
*/
function uc_payment_plan_uc_arb_payment($response) {
// Lookup order
$order = uc_order_load($response['x_invoice_num']);
// If payment was successful, add entry to payment log
if ($response['x_response_code'] == 1) {
$comment = t('Authorize.Net ARB payment #!payment_num', array('!payment_num' => $response['x_subscription_paynum']));
uc_payment_enter($order->order_id, $order->payment_method, $response['x_amount'], 0, $response, $comment);
}
// Add comment to order log
$args = array(
'!comment' => $comment,
'!amount' => uc_price($response['x_amount'], array()),
'!accepted' => $response['x_response_code'] == 1 ? 'accepted' : 'not accepted',
'@post' => print_r($response, TRUE),
);
uc_order_comment_save($order->order_id, 0, t('<strong>Payment Plan:</strong> !comment of !amount !accepted.<pre>@post</pre>', $args), 'admin');
}
/*******************************************************************************
* Callbacks
******************************************************************************/
/**
* Callback to show settings form on payment gateways page.
*/
function uc_payment_plan_settings_form() {
$form['uc_payment_plan_interval'] = array(
'#type' => 'fieldset',
'#title' => t('Installment Interval'),
);
$form['uc_payment_plan_interval']['uc_payment_plan_length'] = array(
'#type' => 'textfield',
'#title' => t('Length'),
'#size' => 4,
'#default_value' => variable_get('uc_payment_plan_length', 1),
'#prefix' => '<div style="float: left; margin-right: 1em">',
'#suffix' => '</div>',
);
$form['uc_payment_plan_interval']['uc_payment_plan_unit'] = array(
'#type' => 'select',
'#title' => 'Unit',
'#options' => array('days' => 'days', 'months' => 'months'),
'#default_value' => variable_get('uc_payment_plan_unit', 'months'),
'#prefix' => '<div style="float: left">',
'#suffix' => '</div>',
);
$form['uc_payment_plan_fee'] = array(
'#type' => 'textfield',
'#title' => t('Installment Processing Fee'),
'#description' => t('A fee incurred per installment.'),
'#default_value' => variable_get('uc_payment_plan_fee', 0),
'#size' => 10,
'#field_prefix' => variable_get('uc_sign_after_amount', FALSE) ? '' : variable_get('uc_currency_sign', '$'),
'#field_suffix' => variable_get('uc_sign_after_amount', FALSE) ? variable_get('uc_currency_sign', '$') : '',
);
$form['uc_payment_plan_instructions'] = array(
'#type' => 'textarea',
'#title' => 'Instructions',
'#description' => 'These instructions appear on the checkout form above the payment plan options.<br />Available variables: !length, !unit, !fee, !cart_total.',
'#default_value' => variable_get('uc_payment_plan_instructions', 'You may make full payment now or spread your cost over several installments. However, there is an additional processing fee of !fee added per installment. Your credit card will be charged today and automatically every !length !unit until all payments are completed. (The amounts below do not yet include taxes and shipping.)'),
);
$products = array();
$result = db_query("SELECT nid, title FROM {node} WHERE type = 'product' ORDER BY title");
while ($row = db_fetch_object($result)) {
$products[$row->nid] = $row->title;
}
$form['uc_payment_plan_products'] = array(
'#type' => 'select',
'#title' => 'Payment Plan Products',
'#description' => 'Select the products which must be in the cart to enable payment plan purchasing.',
'#multiple' => true,
'#options' => $products,
'#default_value' => variable_get('uc_payment_plan_products', array()),
);
return $form;
}
/**
* Handles the payment plan checkout pane.
*/
function uc_checkout_pane_payment_plan($op, &$arg1, $arg2) {
switch ($op) {
// Checkout page (pane)
case 'view':
// Add JS and CSS.
drupal_add_js(drupal_get_path('module', 'uc_payment_plan') . '/js/uc_payment_plan_form.js');
drupal_add_js(array('ucPaymentPlan' => array(
'callback' => url('cart/payment-plan'),
'data' => uc_payment_plan_option_data($arg1->order_total),
)), 'setting');
drupal_add_css(drupal_get_path('module', 'uc_payment_plan') . '/js/uc_payment_plan_form.css');
// Instructions.
$description = '';
$instructions = variable_get('uc_payment_plan_instructions', 'You may make full payment now or spread your cost over several installments. However, there is an additional processing fee of !fee added per installment. Your credit card will be charged today and automatically every !length !unit until all payments are completed. (The amounts below do not yet include taxes and shipping.)');
if (!empty($instructions)) {
$context = array('location' => 'checkout-view-payment-plan-instructions');
$replacements = array(
'!length' => variable_get('uc_payment_plan_length', 1),
'!unit' => variable_get('uc_payment_plan_unit', 'months'),
'!fee' => uc_price(array('price' => variable_get('uc_payment_plan_fee', 0), 'qty' => 1), $context),
'!cart_total' => $arg1->order_total,
);
$description = '<div id="payment-plan-instructions">'.t($instructions, $replacements).'</div>';
}
// Payment plan options. Table is rendered in them function, but data is
// injected via Javascript.
$contents['payment_plan_option'] = array(
'#type' => 'radios',
'#options' => array(
100 => t('Full Payment'),
50 => t('50% Down Payment'),
25 => t('25% Down Payment'),
),
'#default_value' => !empty($arg1->data['payment_plan_option']) ? $arg1->data['payment_plan_option'] : 100,
'#required' => TRUE,
'#theme' => 'uc_payment_plan_checkout_form',
'#order' => $arg1,
);
return array('description' => $description, 'contents' => $contents);
// After checkout form is submitted
case 'process':
// Credit card must be chosen, and something other than full payment.
if ($_POST['panes']['payment']['payment_method'] == 'credit' and $arg2['payment_plan_option'] != 100) {
// Store payment plan option in order data.
$arg1->data['payment_plan_option'] = $arg2['payment_plan_option'];
}
return TRUE;
// Show payment plan details on checkout review page (if plan exists).
case 'review':
if (!isset($arg1->data['payment_plan_option'])) {
return;
}
$details = uc_payment_plan_calculate_option($arg1->data['payment_plan_option'], $arg1->order_total, FALSE);
$length = variable_get('uc_payment_plan_length', 1);
$unit = variable_get('uc_payment_plan_unit', 'months');
$context = array('location' => 'checkout-review-line-item');
return array(
array('title' => "Today's Payment", 'data' => uc_price($details['down_payment'], $context)),
array('title' => 'Installments', 'data' => $details['num_installments'].' x '.uc_price($details['installment'], $context)),
array('title' => 'Interval', 'data' => $length.' '.($length > 1 ? $unit : substr($unit, 0, -1))),
);
}
}
/**
* Callback for payment plan fee line item.
*/
function uc_line_item_payment_plan_fee($op, $arg1) {
if ($op == 'load') {
$lines = array();
if (isset($arg1->data['payment_plan_option'])) {
$details = uc_payment_plan_calculate_option($arg1->data['payment_plan_option'], $arg1->order_total);
if ($details['total_fees'] > 0) {
$lines[] = array(
'id' => 'payment_plan_fee',
'title' => t('Payment plan fee'),
'amount' => $details['total_fees'],
'weight' => 6,
);
}
}
return $lines;
}
}
/**
* Updates payment plan table via ajax.
*/
function uc_payment_plan_ajax_update() {
$order = unserialize($_POST['order']);
$order_total = uc_order_get_total($order);
echo json_encode(uc_payment_plan_option_data($order_total));
exit;
}
/**
* Callback which determines which way to process the payment.
*/
function uc_payment_plan_charge($order_id, $amount, $data) {
global $user;
$order = uc_order_load($order_id);
// If this is a 100% payment, don't create a subscription, but just hand it
// off to the Authorize.Net module for a single charge.
if (empty($order->data['payment_plan_option'])) {
return uc_authorizenet_charge($order_id, $amount, $data);
}
// Otherwise, we'll use Authorize.Net's ARB to create a subscription with an
// initial "trial" payment. Previous versions of this module used the handy
// uc_authorizenet_arb_create() function; however, in recent versions, it's
// become closely tied to uc_recurring. So, we lift some of the code from
// that function.
// Get payment plan details (without fees re-calculated).
$details = uc_payment_plan_calculate_option($order->data['payment_plan_option'], $amount, FALSE);
// Get the country data for the billing information.
$billing_country = uc_get_country_data(array('country_id' => $order->billing_country));
// Build the data array for the request.
$data = array(
'refId' => substr($order->order_id . '-' . time(), 0, 20),
'subscription' => array(
'name' => substr(t('Order @order_id', array('@order_id' => $order->order_id)), 0, 50),
'paymentSchedule' => array(
'interval' => array(
'length' => variable_get('uc_payment_plan_length', 1),
'unit' => variable_get('uc_payment_plan_unit', 'months'),
),
'startDate' => date('Y-m-d'),
'totalOccurrences' => $details['num_payments'],
'trialOccurrences' => 1,
),
'amount' => $details['installment'],
'trialAmount' => $details['down_payment'],
'payment' => array(), // Data inserted below based on payment method.
'order' => array(
'invoiceNumber' => substr($order->order_id, 0, 20),
'description' => substr(t('Order @order_id', array('@order_id' => $order->order_id)), 0, 255),
),
'customer' => array(
'id' => substr($order->uid, 0, 20),
'email' => substr($order->primary_email, 0, 255),
'phoneNumber' => substr($order->billing_phone, 0, 25),
),
'billTo' => array(
'firstName' => substr($order->billing_first_name, 0, 50),
'lastName' => substr($order->billing_last_name, 0, 50),
'company' => substr($order->billing_company, 0, 50),
'address' => substr($order->billing_street1, 0, 60),
'city' => substr($order->billing_city, 0, 40),
'state' => substr(uc_get_zone_code($order->billing_zone), 0, 2),
'zip' => substr($order->billing_postal_code, 0, 20),
'country' => !$billing_country ? '' : $billing_country[0]['country_iso_code_2'],
),
),
);
// Add shipping information if it's available.
if (!empty($order->delivery_first_name)) {
$delivery_country = uc_get_country_data(array('country_id' => $order->delivery_country));
$data['subscription']['shipTo'] = array(
'firstName' => substr($order->delivery_first_name, 0, 50),
'lastName' => substr($order->delivery_last_name, 0, 50),
'company' => substr($order->delivery_company, 0, 50),
'address' => substr($order->delivery_street1, 0, 60),
'city' => substr($order->delivery_city, 0, 40),
'state' => substr(uc_get_zone_code($order->delivery_zone), 0, 2),
'zip' => substr($order->delivery_postal_code, 0, 20),
'country' => !$delivery_country ? '' : $delivery_country[0]['country_iso_code_2'],
);
}
// Add the payment information to the data array based on the payment method.
if ($order->payment_method == 'credit') {
if ($order->payment_details['cc_exp_month'] < 10) {
$order->payment_details['cc_exp_month'] = '0' . $order->payment_details['cc_exp_month'];
}
$data['subscription']['payment'] = array(
'creditCard' => array(
'cardNumber' => $order->payment_details['cc_number'],
'expirationDate' => $order->payment_details['cc_exp_year'] . '-' . $order->payment_details['cc_exp_month'],
),
);
}
// Build the XML string.
$xml = _uc_authorizenet_xml_api_wrapper('ARBCreateSubscriptionRequest', _uc_authorizenet_array_to_xml($data));
// Send the request off to the server and get the response.
$response = uc_authorizenet_xml_api(variable_get('uc_authnet_arb_mode', 'disabled'), $xml);
// Fail if the response is empty or FALSE.
if (!$response) {
uc_order_comment_save($order_id, $user->uid, t('<strong>Payment Plan:</strong> Could not setup the Authorize.Net ARB subscription for some reason.'), 'admin');
return array(
'success' => FALSE,
'message' => t('Your order could not be completed because there was a problem setting up your payment plan. Please contact the site administrator. We are sorry for the inconvenience.'),
);
}
// Parse the response into a data array.
$data = _uc_authorizenet_arb_parse_response($response);
// Response failed.
if ($data['resultCode'] == 'Error') {
uc_order_comment_save($order_id, $user->uid, t('<strong>Payment Plan:</strong> Could not setup the Authorize.Net ARB subscription.<br>@error - @text', array('@error' => $data['code'], '@text' => $data['text'])), 'admin');
return array(
'success' => FALSE,
'message' => t('Your order could not be completed because there was a problem setting up your payment plan. Please contact the site administrator. We are sorry for the inconvenience.'),
);
}
// Otherwise, subscription was setup successfully; we don't log payment,
// though, since we will wait for Authorize.Net to do that for us.
else {
uc_order_comment_save($order_id, $user->uid, t('<strong>Payment Plan:</strong> Subscription ID @subscription_id setup.', array('@subscription_id' => $data['subscriptionId'])), 'admin');
return array(
'success' => TRUE,
'log_payment' => FALSE,
);
}
}
/**
* Tests silent post.
*/
function uc_payment_plan_test_silent_post() {
$hash_value = '';
$data = array(
'x_trans_id' => 2147490176,
'x_amount' => number_format(100.00, 2),
);
$data['x_MD5_Hash'] = strtoupper(md5($hash_value.$data['x_trans_id'].$data['x_amount']));
foreach ($data as $key => $value) {
$form[$key] = array('#type' => 'hidden', '#value' => $value);
}
$form['x_invoice_num'] = array(
'#type' => 'textfield',
'#title' => 'Order Number',
'#default_value' => 51,
'#size' => 10,
);
$form['x_subscription_id'] = array(
'#type' => 'textfield',
'#title' => 'Subscription ID',
'#default_value' => 542632,
'#size' => 10,
);
$form['x_subscription_paynum'] = array(
'#type' => 'textfield',
'#title' => 'Subscription Payment Number',
'#default_value' => 1,
'#size' => 10,
);
$form['x_response_code'] = array(
'#type' => 'textfield',
'#title' => 'Response Code',
'#default_value' => 1,
'#size' => 10,
);
$form['#action'] = url('authnet/silent-post');
$form['submit'] = array('#type' => 'submit', '#value' => 'POST');
return $form;
}
/*******************************************************************************
* Themes
******************************************************************************/
/**
* Themes the payment plan radio buttons.
*/
function theme_uc_payment_plan_checkout_form($element) {
$fees = variable_get('uc_payment_plan_fee', 0);
// Table header.
$header = array(
array('data' => t('Payment Plan'), 'class' => 'plan'),
t('Down Payment'),
'',
t('Installments'),
'',
t('Total Cost'),
);
if ($fees) {
$header[] = array('data' => t('Fees'), 'class' => 'fees');
}
// Table rows.
$context = array('location' => 'checkout-view-payment-plan-table');
foreach (element_children($element) as $key) {
$row = array(
array('data' => theme('radio', $element[$key]), 'class' => 'plan'),
array('data' => '', 'class' => 'down-payment'),
'+',
array('data' => '', 'class' => 'installments'),
'=',
array('data' => '', 'class' => 'payment-plan-total'),
);
if ($fees) {
$row[] = array('data' => '', 'class' => 'fees');
}
$rows[] = array('data' => $row, 'class' => "option{$element[$key]['#return_value']}");
}
return theme('table', $header, $rows, array('id' => 'payment-plan-table', 'class' => 'cart-review'));
}
/*******************************************************************************
* Utilities
******************************************************************************/
/**
* Checks several conditions do determine availability of payment plan.
*/
function uc_payment_plan_available() {
// Payment plan must be enabled in payment gateway settings.
if (!variable_get('uc_pg_payment_plan_enabled', FALSE)) {
return FALSE;
}
// Credit payment method must be available.
else if (!variable_get('uc_payment_method_credit_checkout', FALSE)) {
return FALSE;
}
// Credit payment method's gateway must be payment plan.
else if (variable_get('uc_payment_credit_gateway', 'none') != 'payment_plan') {
return FALSE;
}
// Authorize.Net must be enabled.
else if (!variable_get('uc_pg_authorizenet_enabled', FALSE)) {
return FALSE;
}
// ARB must not be disabled.
else if (variable_get('uc_authnet_arb_mode', 'disabled') == 'disabled') {
return FALSE;
}
// One of the required products must be in cart.
$required = variable_get('uc_payment_plan_products', array());
if (!empty($required)) {
$in_cart = FALSE;
foreach (uc_cart_get_contents() as $product) {
if (in_array($product->nid, $required)) {
$in_cart = TRUE;
break;
}
}
if (!$in_cart) {
return FALSE;
}
}
// All checks pass!
return TRUE;
}
/**
* Defines the payment plan options.
*/
function uc_payment_plan_options($key = NULL) {
$options = array(
100 => t('Full Payment'),
50 => t('50% Down Payment'),
25 => t('25% Down Payment'),
);
return $key ? $options[$key] : $options;
}
/**
* Returns array of rendered payment plan data for given total.
*/
function uc_payment_plan_option_data($order_total) {
$data = array();
$fees = variable_get('uc_payment_plan_fee', 0);
$context = array('location' => 'checkout-view-payment-plan-table');
foreach (array(100, 50, 25) as $option) {
$details = uc_payment_plan_calculate_option($option, $order_total, $fees);
$data[$option]['down_payment'] = uc_price(array('price' => $details['down_payment'], 'qty' => 1), $context);
$data[$option]['installments'] = uc_price(array('price' => $details['installment'], 'qty' => 1), $context);
if ($details['installment'] > 0) {
$data[$option]['installments'] = "{$details['num_installments']} x {$data[$option]['installments']}";
}
$data[$option]['payment_plan_total'] = uc_price(array('price' => $details['payment_plan_total'], 'qty' => 1), $context);
if ($fees) {
$data[$option]['fees'] = uc_price(array('price' => $details['total_fees'], 'qty' => 1), $context);
}
}
return $data;
}
/**
* Calculates payment plan details.
*/
function uc_payment_plan_calculate_option($option, $original_total, $fees = TRUE) {
if ($option == 100) {
$num_payments = $num_installments = $installment = $total_fees = 0;
$payment_plan_total = $down_payment = $original_total;
}
else {
$num_payments = 100 / $option;
$fee = $fees ? variable_get('uc_payment_plan_fee', 0) : 0;
$total_fees = $fee * $num_payments;
$payment_plan_total = $original_total + $total_fees;
$num_installments = $num_payments - 1;
$installment = floor($payment_plan_total / $num_payments * 100) / 100;
$down_payment = $payment_plan_total - ($installment * $num_installments);
}
return compact('total_fees', 'option', 'payment_plan_total', 'num_payments', 'num_installments', 'installment', 'down_payment');
}