Design and overhaul of Account Plans screen

* Basically working now except for VAT calculation
* Includes pricing grid, basic total calculation
* Revamped layout
* Implemented getting the service ID in BillingController.php
This commit is contained in:
Albert Santoni 2014-06-25 18:58:54 -04:00
parent 6f1727d8f3
commit d92e5197e1
10 changed files with 393 additions and 51 deletions

View file

@ -14,6 +14,7 @@ require_once "DateHelper.php";
require_once "OsPath.php"; require_once "OsPath.php";
require_once "Database.php"; require_once "Database.php";
require_once "Timezone.php"; require_once "Timezone.php";
require_once "Auth.php";
require_once __DIR__.'/forms/helpers/ValidationTypes.php'; require_once __DIR__.'/forms/helpers/ValidationTypes.php';
require_once __DIR__.'/controllers/plugins/RabbitMqPlugin.php'; require_once __DIR__.'/controllers/plugins/RabbitMqPlugin.php';
require_once __DIR__.'/controllers/plugins/Maintenance.php'; require_once __DIR__.'/controllers/plugins/Maintenance.php';

View file

@ -64,3 +64,9 @@ define('UI_BLOCK_SESSNAME', 'BLOCK');*/
define('SOUNDCLOUD_NOT_UPLOADED_YET' , -1); define('SOUNDCLOUD_NOT_UPLOADED_YET' , -1);
define('SOUNDCLOUD_PROGRESS' , -2); define('SOUNDCLOUD_PROGRESS' , -2);
define('SOUNDCLOUD_ERROR' , -3); define('SOUNDCLOUD_ERROR' , -3);
//WHMCS integration
define("WHMCS_API_URL", "https://account.sourcefabric.com/includes/api.php");
define("SUBDOMAIN_WHMCS_CUSTOM_FIELD_NAME", "Choose your domain");

View file

@ -12,6 +12,7 @@ class BillingController extends Zend_Controller_Action {
$CC_CONFIG = Config::getConfig(); $CC_CONFIG = Config::getConfig();
$baseUrl = Application_Common_OsPath::getBaseDir(); $baseUrl = Application_Common_OsPath::getBaseDir();
$this->view->headLink()->appendStylesheet($baseUrl.'css/billing.css?'.$CC_CONFIG['airtime_version']); $this->view->headLink()->appendStylesheet($baseUrl.'css/billing.css?'.$CC_CONFIG['airtime_version']);
BillingController::ensureClientIdIsValid();
$request = $this->getRequest(); $request = $this->getRequest();
$form = new Application_Form_BillingUpgradeDowngrade(); $form = new Application_Form_BillingUpgradeDowngrade();
@ -35,7 +36,7 @@ class BillingController extends Zend_Controller_Action {
$postfields["action"] = "upgradeproduct"; $postfields["action"] = "upgradeproduct";
$postfields["clientid"] = Application_Model_Preference::GetClientId(); $postfields["clientid"] = Application_Model_Preference::GetClientId();
$postfields["serviceid"] = self::getClientServiceId(); $postfields["serviceid"] = self::getClientInstanceId();
$postfields["type"] = "product"; $postfields["type"] = "product";
$postfields["newproductid"] = $formData["newproductid"]; $postfields["newproductid"] = $formData["newproductid"];
$postfields["newproductbillingcycle"] = $formData["newproductbillingcycle"]; $postfields["newproductbillingcycle"] = $formData["newproductbillingcycle"];
@ -175,6 +176,7 @@ class BillingController extends Zend_Controller_Action {
{ {
$request = $this->getRequest(); $request = $this->getRequest();
$form = new Application_Form_BillingClient(); $form = new Application_Form_BillingClient();
BillingController::ensureClientIdIsValid();
if ($request->isPost()) { if ($request->isPost()) {
$formData = $request->getPost(); $formData = $request->getPost();
if ($form->isValid($formData)) { if ($form->isValid($formData)) {
@ -218,6 +220,7 @@ class BillingController extends Zend_Controller_Action {
public function invoicesAction() public function invoicesAction()
{ {
BillingController::ensureClientIdIsValid();
$credentials = self::getAPICredentials(); $credentials = self::getAPICredentials();
$postfields = array(); $postfields = array();
@ -237,6 +240,7 @@ class BillingController extends Zend_Controller_Action {
public function invoiceAction() public function invoiceAction()
{ {
BillingController::ensureClientIdIsValid();
$request = $this->getRequest(); $request = $this->getRequest();
$invoice_id = $request->getParam('invoiceid'); $invoice_id = $request->getParam('invoiceid');
self::viewInvoice($invoice_id); self::viewInvoice($invoice_id);
@ -259,10 +263,52 @@ class BillingController extends Zend_Controller_Action {
$result = self::makeRequest($credentials["url"], $query_string); $result = self::makeRequest($credentials["url"], $query_string);
Logging::info($result); Logging::info($result);
if ($_SERVER['SERVER_NAME'] == "airtime.localhost") {
return "1384";
}
//This code must run on airtime.pro for it to work... it's trying to match
//the server's hostname with the client subdomain.
foreach ($result["products"] as $product)
{
if (strpos($product[0]["groupname"], "Airtime") === FALSE)
{
//Ignore non-Airtime products
continue;
}
else
{
if ($product[0]["status"] === "Active") {
$airtimeProduct = $product[0];
$subdomain = '';
foreach ($airtimeProduct['customfields']['customfield'] as $customField)
{
if ($customField['name'] === SUBDOMAIN_WHMCS_CUSTOM_FIELD_NAME)
{
$subdomain = $customField['value'];
if (($subdomain . ".airtime.pro") === $_SERVER['SERVER_NAME'])
{
return $airtimeProduct['id'];
}
}
}
}
}
}
throw new Exception("Unable to match subdomain to a service ID");
} }
public static function getProducts() public static function getProducts()
{ {
//Making this static to cache the products during a single HTTP request.
//This saves us roundtrips to WHMCS if getProducts() is called multiple times.
static $result = array();
if (!empty($result))
{
return $result["products"]["product"];
}
$credentials = self::getAPICredentials(); $credentials = self::getAPICredentials();
$postfields = array(); $postfields = array();
@ -280,6 +326,21 @@ class BillingController extends Zend_Controller_Action {
return $result["products"]["product"]; return $result["products"]["product"];
} }
public static function getProductPricesAndTypes()
{
$products = BillingController::getProducts();
foreach ($products as $k => $p) {
$productPrices[$p["name"]] = array(
"monthly" => $p["pricing"]["USD"]["monthly"],
"annually" => $p["pricing"]["USD"]["annually"]
);
$productTypes[$p["pid"]] = $p["name"] . " ($" . $productPrices[$p['name']]['monthly'] . "/mo)";
}
return array($productPrices, $productTypes);
}
public static function getClientDetails() public static function getClientDetails()
{ {
try { try {
@ -325,4 +386,12 @@ class BillingController extends Zend_Controller_Action {
Logging::info($e->getMessage()); Logging::info($e->getMessage());
} }
} }
private static function ensureClientIdIsValid()
{
if (Application_Model_Preference::GetClientId() == null)
{
throw new Exception("Invalid client ID: " . Application_Model_Preference::GetClientId());
}
}
} }

View file

@ -1,8 +1,5 @@
<?php <?php
define("WHMCS_API_URL", "https://account.sourcefabric.com/includes/api.php");
define("SUBDOMAIN_WHMCS_CUSTOM_FIELD_NAME", "Choose your domain");
class WhmcsLoginController extends Zend_Controller_Action class WhmcsLoginController extends Zend_Controller_Action
{ {

View file

@ -34,7 +34,7 @@ class Application_Form_BillingClient extends Zend_Form
$companyname->setLabel(_('Company Name:')) $companyname->setLabel(_('Company Name:'))
->setValue($client["companyname"]) ->setValue($client["companyname"])
->setAttrib('class', 'input_text') ->setAttrib('class', 'input_text')
->setRequired(true) ->setRequired(false)
->addValidator($notEmptyValidator) ->addValidator($notEmptyValidator)
->addFilter('StringTrim'); ->addFilter('StringTrim');
$this->addElement($companyname); $this->addElement($companyname);
@ -85,7 +85,7 @@ class Application_Form_BillingClient extends Zend_Form
$this->addElement($state); $this->addElement($state);
$postcode = new Zend_Form_Element_Text('postcode'); $postcode = new Zend_Form_Element_Text('postcode');
$postcode->setLabel(_('Zip Code:')) $postcode->setLabel(_('Zip Code / Postal Code:'))
->setValue($client["postcode"]) ->setValue($client["postcode"])
->setAttrib('class', 'input_text') ->setAttrib('class', 'input_text')
->setRequired(true) ->setRequired(true)

View file

@ -5,15 +5,7 @@ class Application_Form_BillingUpgradeDowngrade extends Zend_Form
{ {
$productPrices = array(); $productPrices = array();
$productTypes = array(); $productTypes = array();
$products = BillingController::getProducts(); list($productPrices, $productTypes) = BillingController::getProductPricesAndTypes();
foreach ($products as $k => $p) {
$productPrices[$p["name"]] = array(
"monthly" => $p["pricing"]["USD"]["monthly"],
"annualy" => $p["pricing"]["USD"]["annually"]
);
$productTypes[$p["pid"]] = $p["name"];
}
//$currentPlanType = ucfirst(Application_Model_Preference::GetPlanLevel()); //$currentPlanType = ucfirst(Application_Model_Preference::GetPlanLevel());
$currentPlanType = "Hobbyist"; $currentPlanType = "Hobbyist";
@ -30,7 +22,7 @@ class Application_Form_BillingUpgradeDowngrade extends Zend_Form
$billingcycle = new Zend_Form_Element_Radio('newproductbillingcycle'); $billingcycle = new Zend_Form_Element_Radio('newproductbillingcycle');
$billingcycle->setLabel(_('Billing cycle:')) $billingcycle->setLabel(_('Billing cycle:'))
->setMultiOptions(array('monthly' => 'monthly', 'annually' => 'annually')) ->setMultiOptions(array('monthly' => 'Monthly', 'annually' => 'Annually'))
->setRequired(true) ->setRequired(true)
->setValue('monthly'); ->setValue('monthly');
$this->addElement($billingcycle); $this->addElement($billingcycle);
@ -50,6 +42,8 @@ class Application_Form_BillingUpgradeDowngrade extends Zend_Form
$this->addElement($submit);*/ $this->addElement($submit);*/
$client = new Application_Form_BillingClient(); $client = new Application_Form_BillingClient();
$client->removeElement("password2");
$client->removeElement("password2verify");
$this->addSubForm($client, 'billing_client_info'); $this->addSubForm($client, 'billing_client_info');
} }
} }

View file

@ -1,4 +1,5 @@
<div class="ui-widget ui-widget-content block-shadow clearfix padded-strong"> <div class="ui-widget ui-widget-content block-shadow clearfix padded-strong">
<H2>Account Details</H2>
<?php if (isset($this->errorMessage)) {?> <?php if (isset($this->errorMessage)) {?>
<div class="errors"><?php echo $this->errorMessage ?></div> <div class="errors"><?php echo $this->errorMessage ?></div>
<?php } else if (isset($this->successMessage)) {?> <?php } else if (isset($this->successMessage)) {?>

View file

@ -1,19 +1,162 @@
<?php <?php
$form = $this->form; $form = $this->form;
$form->setAttrib('id', 'upgrade-downgrade'); $form->setAttrib('id', 'upgrade-downgrade');
?> ?>
<div class="ui-widget ui-widget-content block-shadow clearfix padded-strong"> <script type="text/javascript">
<H2>Account Plans</H2> <?php echo("var products = " . json_encode(BillingController::getProducts()) . ";");
?>
//Disable annual billing for hobbyist plan
function validatePlan()
{
if ($("#newproductid-25").is(":checked")) {
$("#newproductbillingcycle-annually").prop("disabled", "true");
$("label[for='newproductbillingcycle-annually']").addClass("disabled");
$("#newproductbillingcycle-monthly").prop("checked", "true");
} else {
$("#newproductbillingcycle-annually").removeProp("disabled");
$("label[for='newproductbillingcycle-annually']").removeClass("disabled");
}
}
function recalculateTotal()
{
console.log(products);
var newProductId = $("input[type='radio'][name='newproductid']:checked");
if (newProductId.length > 0) {
newProductId = newProductId.val();
} else {
return;
}
var newProduct = null;
for (var i = 0; i < products.length; i++)
{
if (products[i].pid == newProductId) {
newProduct = products[i];
break;
}
}
var total = "0";
if ($("#newproductbillingcycle-monthly").is(":checked")) {
total = "$" + newProduct.pricing["USD"]["monthly"] + " per month";
} else if ($("#newproductbillingcycle-annually").is(":checked")) {
total = "$" + newProduct.pricing["USD"]["annually"] + " per year";
}
$("#total").text(total);
}
$(document).ready(function() {
recalculateTotal();
$("input[name='newproductid']").change(function() {
validatePlan();
recalculateTotal();
});
$("input[name='newproductbillingcycle']").change(function() {
recalculateTotal();
});
$("#hobbyist_grid_price").text("$" + products[0].pricing["USD"]["monthly"] + " / month");
$("#starter_grid_price").text("$" + products[1].pricing["USD"]["monthly"] + " / month");
$("#plus_grid_price").text("$" + products[2].pricing["USD"]["monthly"] + " / month");
$("#premium_grid_price").text("$" + products[3].pricing["USD"]["monthly"] + " / month");
});
</script>
<div class="ui-widget ui-widget-content block-shadow clearfix padded-strong billing-panel">
<H2><?=_("Account Plans")?></H2>
<H3><?=_("Upgrade today and get more listeners and storage space!")?></H3>
<div class="pricing-grid"> <div class="pricing-grid">
pricing grid here <table>
<tr>
<th>Hobbyist</th>
<th>Starter</th>
<th>Plus</th>
<th>Premium</th>
</tr>
<tr>
<td>1 Stream
</td>
<td>2 Streams
</td>
<td>2 Streams
</td>
<td>3 Streams
</td>
</tr>
<tr>
<td>64kbps Stream Quality
</td>
<td>64kbps and 128kbps Stream Quality
</td>
<td>64kbps and 196kbps Stream Quality
</td>
<td>64kbps, 128kbps, and 196kbps Stream Quality
</td>
</tr>
<tr>
<td>5 Listeners
</td>
<td>40 Listeners per stream
</td>
<td>100 Listeners per stream
</td>
<td>500 Listeners per stream
</td>
</tr>
<tr>
<td>2GB Storage
</td>
<td>5GB Storage
</td>
<td>30GB Storage
</td>
<td>150GB Storage
</td>
</tr>
<tr>
<td>Ticket, Email, Forum Support
</td>
<td>Live Chat, Ticket, Email, Forum Support
</td>
<td>Live Chat, Ticket, Email, Forum Support
</td>
<td>Live Chat, Ticket, Email, Forum Support
</td>
</tr>
<tr>
<td>
</td>
<td>Save 15% if paid annually
</td>
<td>Save 15% if paid annually
</td>
<td>Save 15% if paid annually
</td>
</tr>
<tr class="price">
<td id="hobbyist_grid_price">
</td>
<td id="starter_grid_price">
</td>
<td id="plus_grid_price">
</td>
<td id="premium_grid_price">
</td>
</tr>
</table>
</div> </div>
<?php if (isset($this->errorMessage)) {?> <!--
<div class="errors"><?php echo $this->errorMessage ?></div> <p> <a target="_blank" href="https://www.airtime.pro/pricing"><?=_("View Plans")?></a> (Opens in a new window)</p>
<?php }?> -->
<div>Plan Level: <?php echo Application_Model_Preference::GetPlanLevel();?></div> <p id="current_plan"><b>Current Plan:</b> <?php echo Application_Model_Preference::GetPlanLevel();?>
<?php //echo $form ?> </p>
<h3>Choose a plan:</h3>
<form id="<?php echo $form->getId(); ?>" method="<?php echo $form->getMethod() ?>" action="<?php echo <form id="<?php echo $form->getId(); ?>" method="<?php echo $form->getMethod() ?>" action="<?php echo
$form->getAction()?>" enctype="<?php echo $form->getEncType();?>"> $form->getAction()?>" enctype="<?php echo $form->getEncType();?>">
@ -23,36 +166,51 @@ pricing grid here
<div id="billingcycle"> <div id="billingcycle">
<?php echo $form->newproductbillingcycle ?> <?php echo $form->newproductbillingcycle ?>
</div> </div>
<div id="paymentmethod"> <div id="billingcycle_disclaimer">
<?php echo $form->paymentmethod ?> Save 15% on annual plans (Hobbyist plan excluded).
</div>
<div class="clearfix"></div>
<div id="total_box"><b>Total:</b><br><span id="total"></span></div>
<div id="vat_disclaimer">
Plus VAT if you are an EU resident without a valid VAT number.
</div> </div>
<div class="clearfix"></div> <h3>Enter your payment details:</h3>
<?php if (isset($this->errorMessage)) {?>
<div class="errors"><?php echo $this->errorMessage ?></div>
<?php }?>
<?php //echo $form ?>
<?php $billingForm = $form->getSubform("billing_client_info") ?> <?php $billingForm = $form->getSubform("billing_client_info") ?>
<div> <div class="billing_col1">
<?=$billingForm->firstname?> <?=$billingForm->firstname?>
</div> </div>
<div> <div class="billing_col2">
<?=$billingForm->lastname?> <?=$billingForm->lastname?>
</div> </div>
<div> <div class="clearfix"></div>
<div class="billing_col1">
<?=$billingForm->companyname?> <?=$billingForm->companyname?>
</div> </div>
<div> <div class="billing_col2">
<?=$billingForm->email?> <?=$billingForm->email?>
</div> </div>
<div> <div class="clearfix"></div>
<div class="billing_col1">
<?=$billingForm->address1?> <?=$billingForm->address1?>
</div> </div>
<div> <div class="billing_col2">
<?=$billingForm->address2?> <?=$billingForm->address2?>
</div> </div>
<div> <div class="clearfix"></div>
<div class="billing_col1">
<?=$billingForm->city?> <?=$billingForm->city?>
</div> </div>
<div> <div class="billing_col2">
<?=$billingForm->state?> <?=$billingForm->state?>
</div> </div>
<div class="clearfix"></div>
<div> <div>
<?=$billingForm->postcode?> <?=$billingForm->postcode?>
</div> </div>
@ -68,19 +226,30 @@ pricing grid here
<div> <div>
<?=$billingForm->securityqans?> <?=$billingForm->securityqans?>
</div> </div>
<div style="float:right; width: 200px;"><p>VAT will be added to your invoice if you are an EU resident without a valid VAT number.</p>
</div>
<div> <div>
<?=$billingForm->getElement("7"); ?> <?=$billingForm->getElement("7"); ?>
</div> </div>
<div class="clearfix"></div>
<div> <div>
<?=$billingForm->getElement("71"); ?> <?php //$billingForm->getElement("71"); ?>
<div class="billing_checkbox">
<?=$billingForm->getElement("71")->renderViewHelper(); ?>
</div> </div>
<div> <?=$billingForm->getElement("71")->renderLabel(); ?>
<?=$billingForm->password2?>
</div> </div>
<div> <div class="clearfix"></div>
<?=$billingForm->password2verify?>
<div style="float:right; width: 200px;"><p>After submitting your order, you will be redirected to an invoice with payment buttons.</p>
</div> </div>
<input type="submit" val="Submit"> <div id="paymentmethod">
<?php echo $form->paymentmethod ?>
</div>
<div class="clearfix"></div>
<input type="submit" value="Submit Order"></input>
<div class="clearfix"></div>
</form> </form>
<br><br><br><br>
</div> </div>

View file

@ -6,7 +6,7 @@
<div class="trial-box-calendar-gray"><?php echo _("days") ?></div> <div class="trial-box-calendar-gray"><?php echo _("days") ?></div>
</div> </div>
<div class="trial-box-button"> <div class="trial-box-button">
<a title="<?php echo _("Purchase your copy of Airtime")?> href="https://account.sourcefabric.com/clientarea.php" target="_blank"><?php echo _("My Account") ?></a> <a title="<?php echo _("Purchase your copy of Airtime")?> href="/billing/upgrade" target="_blank"><?php echo _("My Account") ?></a>
</div> </div>
</div> </div>
<?php }?> <?php }?>

View file

@ -1,5 +1,28 @@
@CHARSET "UTF-8"; @CHARSET "UTF-8";
.billing-panel
{
width: 400px;
margin: 0 auto;
margin-bottom: 30px;
}
#upgrade-downgrade
{
border: 0px solid #000;
margin: 0 auto;
color: #000;
}
#upgrade-downgrade label
{
color: rgb(28,28,28);
}
#upgrade-downgrade label.disabled
{
color: rgb(108,108,108);
}
#upgrade-downgrade dl #upgrade-downgrade dl
{ {
width: 300px; width: 300px;
@ -15,19 +38,101 @@
margin-bottom: 10px; margin-bottom: 10px;
} }
.pricing-grid table
{
border-spacing: 0px;
border-collapse: separate;
border: 1px solid #777;
width: 600px;
margin-left: -100px;
/*background-color: #555;*/
table-layout: fixed;
margin-top: 20px;
margin-bottom: 20px;
box-shadow: 0px 5px 5px rgba(0,0,0,0.5);
}
.pricing-grid td, .pricing-grid th
{
border-bottom: 1px solid #999;
border-right: 1px solid #bbb;
background-color: #ccc;
padding: 10px;
}
.pricing-grid th
{
border-top-left-radius: 5px;
border-top-right-radius: 5px;
border: 0px;
}
.pricing-grid tr.price td
{
text-align: right;
background-color: #ddd;
font-weight: bold;
}
#current_plan
{
text-align: center;
}
#plantype #plantype
{ {
float: left; float: left;
} }
#billingcycle #billingcycle
{ {
float: left; float: left;
margin-left: 30px;
}
#billingcycle_disclaimer
{
float: left;
margin-left: 30px;
width: 200px;
}
#vat_disclaimer
{
text-align: right;
font-size: 0.9em;
margin-bottom: 30px;
}
#total_box
{
text-align: right;
margin-top: 30px;
margin-bottom: 10px;
border: 1px solid #777;
background: #ccc;
padding: 5px;
} }
#paymentmethod #paymentmethod
{ {
float: left; }
.billing_col1, .billing_col2
{
float: left;
margin-right: 10px;
}
.billing_checkbox
{
float: left;
margin-right: 5px;
margin-bottom: 10px;
}
#upgrade-downgrade input[type=submit]
{
float: right;
} }