If you’ve ever poked around in your Blesta billing system, you might have noticed something that’s not so obvious to your customers: paying for a longer billing term saves money. Not only does it reduce your processor fees, it also means extra cash upfront for your business, which can then be passed on as discounts for loyal customers.
Trying to set this up you might've noticed that your clients might not exactly spot that there is even a discount being offered on these longer terms as Blesta doesn't do anything to show that it is a cheaper price overall.
Luckily, the fix is quite simple, you'll simply have to edit the order form template that you're using. To do so, navigate to the order plugin in your Blesta files located in the plugins/order
folder (for example: /var/www/blesta/plugins/order
). Inside of this folder you'll notice a few more, namely the one we're interested in: views
. Navigate to views
and then templates
. Here you might see something familiar ajax
, standard
, wizard
- If you recall from configuring your order forms, these are the order form templates you're able to pick between.
This is what we'll need to edit, though we advise that you make a copy of the original, and once you're done another modified copy. This is due to Blesta overwriting your files on updates, so you'll need a backed-up copy to restore from, or you'll have to re-edit the files again. Note that if the core template files are modified, we advise re-editing the new files from Blesta as they might have updates or new features.
Now, onto the actual modifications - Here are the exact files that will need to be edited:
The edits that you need to make are about the same in each one of these files, so simply follow these instructions:
At the start of each file add the following PHP code:<?php
function getSmallestTerm($pricing) {
$smallest_length = null;
$smallest = $pricing[0] ?? null;
foreach ($pricing as $price) {
if ($price->period == 'onetime') {
continue;
}
$length = match ($price->period) {
'day' => 1 * $price->term,
'week' => 7 * $price->term,
'month' => 31 * $price->term,
'year' => 365 * $price->term,
default => PHP_INT_MAX
};
if ($smallest_length == null || $length < $smallest_length) {
$smallest_length = $length;
$smallest = $price;
}
}
return $smallest;
}
function calculateDiscount($smallest, $current) {
$smallest_length = match ($smallest->period) {
'day' => 1 * $smallest->term,
'week' => 7 * $smallest->term,
'month' => 31 * $smallest->term,
'year' => 365 * $smallest->term,
default => PHP_INT_MAX
};
$current_length = match ($current->period) {
'day' => 1 * $current->term,
'week' => 7 * $current->term,
'month' => 31 * $current->term,
'year' => 365 * $current->term,
default => PHP_INT_MAX
};
if ($smallest_length <= 0) return 0;
if ($current_length <= 0) return 0;
$smallest_daily_rate = $smallest->price / $smallest_length;
$current_daily_rate = $current->price / $current_length;
if ($smallest_daily_rate <= 0) return 0;
$discount = $smallest_daily_rate - $current_daily_rate;
$percentage_discount = ($discount / $smallest_daily_rate) * 100;
return $percentage_discount;
}
function getDiscountText($smallest, $current){
$discount = calculateDiscount($smallest, $current);
if ($discount > 0) {
return ' (' . round($discount) . '% Discount)';
}
}
?>
Note that at the end of you'll see how the `getDiscountText` function returns a static string, if your website is not in English you can of course change that to your language or add multi-language support. For the purposes of this quick demonstration, we have that static string.
2. Locate the following section of code, note that there can be multiple per file:<?php
$prices = [];
$option_attributes = [];
foreach ($package->pricing as $price) {
if ((isset($price->period) ? $price->period : null) == 'onetime') {
$period = (isset($periods[$price->period]) ? $periods[$price->period] : null);
} else {
$term = (isset($price->term) ? $this->Html->safe($price->term) : null);
$period = $this->Html->concat(' ', $term, ($term == 1 ? (isset($periods[$price->period]) ? $periods[$price->period] : null) : (isset($periods[$price->period . '_plural']) ? $periods[$price->period . '_plural'] : null)));
}
$display_price = $this->CurrencyFormat->format($price->price, $price->currency, ['decimals' => $price->precision]);
// Set the pricing text to include or not include renewal pricing
$prices[$price->id] = ((isset($price->period) ? $price->period : null) == 'onetime' || ((isset($price->price) ? $price->price : null) == (isset($price->price_renews) ? $price->price_renews : null))
? $this->_('Main.index.package_price', true, $period, $display_price)
: $this->_('Main.index.package_price_recurring', true, $period, $display_price, $this->CurrencyFormat->format((isset($price->price_renews) ? $price->price_renews : null), (isset($price->currency) ? $price->currency : null), ['decimals' => $price->precision]))
);
$option_attributes[$price->id] = ['data-period' => $this->_('Main.index.package_price', true, $period, null), 'data-display-price' => $display_price];
}
$this->Form->fieldSelect('pricing_id', $prices, null, array_merge(['class' => 'form-control'], empty($prices) ? ['disabled' => 'disabled'] : []), $option_attributes);
?>
3. For each of the sections of code found in step 2 repeat the next steps.
4. Before the foreach($package→pricing as $price)
add the following:$smallest_term = getSmallestTerm($package->pricing);
Now that area of code should look something like this:<?php
$prices = [];
$option_attributes = [];
$smallest_term = getSmallestTerm($package->pricing);
foreach ($package->pricing as $price) {
if ((isset($price->period) ? $price->period : null) == 'onetime') {
5. After ….package_price', true, $period, $display_price)
add the following:. getDiscountText($smallest_term, $price)
An example of this change would be from this:$prices[$price->id] = ((isset($price->period) ? $price->period : null) == 'onetime' || ((isset($price->price) ? $price->price : null) == (isset($price->price_renews) ? $price->price_renews : null))
? $this->_('Main.index.package_price', true, $period, $display_price)
: $this->_('Main.index.package_price_recurring', true, $period, $display_price, $this->CurrencyFormat->format((isset($price->price_renews) ? $price->price_renews : null), (isset($price->currency) ? $price->currency : null), ['decimals' => $price->precision]))
);
To this:$prices[$price->id] = ((isset($price->period) ? $price->period : null) == 'onetime' || ((isset($price->price) ? $price->price : null) == (isset($price->price_renews) ? $price->price_renews : null))
? $this->_('Main.index.package_price', true, $period, $display_price) . getDiscountText($smallest_term, $price)
: $this->_('Main.index.package_price_recurring', true, $period, $display_price, $this->CurrencyFormat->format((isset($price->price_renews) ? $price->price_renews : null), (isset($price->currency) ? $price->currency : null), ['decimals' => $price->precision]))
);
And that's it done! A quick and simple quality of life improvement and hopefully some money saved on processor fees!
Co-Owner of Blesta Club LTD and main developer. Owner of Code Cats LTD and ZZP in The Netherlands.
Part owner of Blesta Club Ltd, Owner of TekLan Hosting and a few other companies.