diff --git a/airtime_mvc/application/Bootstrap.php b/airtime_mvc/application/Bootstrap.php index 2307b8061..8b4ea73f8 100644 --- a/airtime_mvc/application/Bootstrap.php +++ b/airtime_mvc/application/Bootstrap.php @@ -14,6 +14,7 @@ require_once "DateHelper.php"; require_once "OsPath.php"; require_once "Database.php"; require_once "Timezone.php"; +require_once "Auth.php"; require_once __DIR__.'/forms/helpers/ValidationTypes.php'; require_once __DIR__.'/controllers/plugins/RabbitMqPlugin.php'; require_once __DIR__.'/controllers/plugins/Maintenance.php'; diff --git a/airtime_mvc/application/configs/constants.php b/airtime_mvc/application/configs/constants.php index 9e2563d0f..69ce78d19 100644 --- a/airtime_mvc/application/configs/constants.php +++ b/airtime_mvc/application/configs/constants.php @@ -64,3 +64,9 @@ define('UI_BLOCK_SESSNAME', 'BLOCK');*/ define('SOUNDCLOUD_NOT_UPLOADED_YET' , -1); define('SOUNDCLOUD_PROGRESS' , -2); 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"); + diff --git a/airtime_mvc/application/controllers/BillingController.php b/airtime_mvc/application/controllers/BillingController.php index 3747b2ee7..e8aeea0f5 100644 --- a/airtime_mvc/application/controllers/BillingController.php +++ b/airtime_mvc/application/controllers/BillingController.php @@ -9,10 +9,23 @@ class BillingController extends Zend_Controller_Action { public function upgradeAction() { + $CC_CONFIG = Config::getConfig(); + $baseUrl = Application_Common_OsPath::getBaseDir(); + $this->view->headLink()->appendStylesheet($baseUrl.'css/billing.css?'.$CC_CONFIG['airtime_version']); + BillingController::ensureClientIdIsValid(); + $request = $this->getRequest(); $form = new Application_Form_BillingUpgradeDowngrade(); if ($request->isPost()) { - //$formData = $form->getValues(); + + /* + * TODO: determine if VAT shoould be charged on the invoice or not. + * We'll need to check if a VAT number was supplied in the form and if so, + * validate it somehow. We'll also need to make sure the country given is + * in the EU + */ + $apply_vat = false; + $formData = $request->getPost(); if ($form->isValid($formData)) { $credentials = self::getAPICredentials(); @@ -21,11 +34,9 @@ class BillingController extends Zend_Controller_Action { $postfields["username"] = $credentials["username"]; $postfields["password"] = md5($credentials["password"]); $postfields["action"] = "upgradeproduct"; - //$postfields["clientid"] = Application_Model_Preference::GetClientId(); - $postfields["clientid"] = 1846; - //TODO: do not hardcode - //$postfields["serviceid"] = self::getClientInstanceId(); - $postfields["serviceid"] = "1678"; + $postfields["clientid"] = Application_Model_Preference::GetClientId(); + + $postfields["serviceid"] = self::getClientInstanceId(); $postfields["type"] = "product"; $postfields["newproductid"] = $formData["newproductid"]; $postfields["newproductbillingcycle"] = $formData["newproductbillingcycle"]; @@ -36,12 +47,14 @@ class BillingController extends Zend_Controller_Action { foreach ($postfields AS $k=>$v) $upgrade_query_string .= "$k=".urlencode($v)."&"; //update client info + $clientfields = array(); $clientfields["username"] = $credentials["username"]; $clientfields["password"] = md5($credentials["password"]); - $clientfields["action"] = "updateclient"; - //$clientfields["clientid"] = Application_Model_Preference::GetClientId(); - $clientfields["clientid"] = 1846; + $clientfields["action"] = "updateclient"; + $clientfields["clientid"] = Application_Model_Preference::GetClientId(); + $clientfields["customfields"] = base64_encode(serialize($formData["customfields"])); + unset($formData["customfields"]); $clientfields["responsetype"] = "json"; unset($formData["newproductid"]); unset($formData["newproductbillingcycle"]); @@ -59,9 +72,17 @@ class BillingController extends Zend_Controller_Action { $this->setErrorMessage(); $this->view->form = $form; } else { - //$result = $this->makeRequest($credentials["url"], $upgrade_query_string); - //self::viewInvoice($result["invoiceid"]); - self::viewInvoice(5108); + $result = $this->makeRequest($credentials["url"], $upgrade_query_string); + if ($result["result"] == "error") { + Logging::info($_SERVER['HTTP_HOST']." - Account upgrade failed. - ".$result["message"]); + $this->setErrorMessage(); + $this->view->form = $form; + } else { + if ($apply_vat) { + $this->addVatToInvoice($result["invoiceid"]); + } + self::viewInvoice($result["invoiceid"]); + } } } else { $this->view->form = $form; @@ -71,6 +92,47 @@ class BillingController extends Zend_Controller_Action { } } + private function addVatToInvoice($invoice_id) + { + $credentials = self::getAPICredentials(); + + //First we need to get the invoice details: sub total, and total + //so we can calcuate the amount of VAT to add + $invoicefields = array(); + $invoicefields["username"] = $credentials["username"]; + $invoicefields["password"] = md5($credentials["password"]); + $invoicefields["action"] = "getinvoice"; + $invoicefields["invoiceid"] = $invoice_id; + $invoicefields["responsetype"] = "json"; + + $invoice_query_string = ""; + foreach ($invoicefields as $k=>$v) $invoice_query_string .= "$k=".urlencode($v)."&"; + + //TODO: error checking + $result = $this->makeRequest($credentials["url"], $invoice_query_string); + + $vat_rate = 19.00; + $vat_amount = $result["subtotal"] * ($vat_rate/100); + $invoice_total = $result["total"] + $vat_amount; + + //Second, update the invoice with the VAT amount and updated total + $postfields = array(); + $postfields["username"] = $credentials["username"]; + $postfields["password"] = md5($credentials["password"]); + $postfields["action"] = "updateinvoice"; + $postfields["invoiceid"] = $invoice_id; + $postfields["tax"] = "$vat_amount"; + $postfields["taxrate"] = "$vat_rate"; + $postfields["total"] = "$invoice_total"; + $postfields["responsetype"] = "json"; + + $query_string = ""; + foreach ($postfields as $k=>$v) $query_string .= "$k=".urlencode($v)."&"; + + //TODO: error checking + $result = $this->makeRequest($credentials["url"], $query_string); + } + private function setErrorMessage($msg=null) { if (!is_null($msg)) { @@ -114,6 +176,7 @@ class BillingController extends Zend_Controller_Action { { $request = $this->getRequest(); $form = new Application_Form_BillingClient(); + BillingController::ensureClientIdIsValid(); if ($request->isPost()) { $formData = $request->getPost(); if ($form->isValid($formData)) { @@ -124,8 +187,11 @@ class BillingController extends Zend_Controller_Action { $postfields["username"] = $credentials["username"]; $postfields["password"] = md5($credentials["password"]); $postfields["action"] = "updateclient"; - //$postfields["clientid"] = Application_Model_Preference::GetClientId(); - $postfields["clientid"] = 1846; + + $postfields["customfields"] = base64_encode(serialize($formData["customfields"])); + unset($formData["customfields"]); + + $postfields["clientid"] = Application_Model_Preference::GetClientId(); $postfields["responsetype"] = "json"; $postfields = array_merge($postfields, $formData); unset($postfields["password2verify"]); @@ -154,6 +220,7 @@ class BillingController extends Zend_Controller_Action { public function invoicesAction() { + BillingController::ensureClientIdIsValid(); $credentials = self::getAPICredentials(); $postfields = array(); @@ -161,18 +228,19 @@ class BillingController extends Zend_Controller_Action { $postfields["password"] = md5($credentials["password"]); $postfields["action"] = "getinvoices"; $postfields["responsetype"] = "json"; - $postfields["userid"] = 1846; - //$postfields["clientid"] = Application_Model_Preference::GetClientId(); + $postfields["clientid"] = Application_Model_Preference::GetClientId(); $query_string = ""; foreach ($postfields AS $k=>$v) $query_string .= "$k=".urlencode($v)."&"; $result = self::makeRequest($credentials["url"], $query_string); + $this->view->invoices = $result["invoices"]["invoice"]; } public function invoiceAction() { + BillingController::ensureClientIdIsValid(); $request = $this->getRequest(); $invoice_id = $request->getParam('invoiceid'); self::viewInvoice($invoice_id); @@ -188,18 +256,59 @@ class BillingController extends Zend_Controller_Action { $postfields["password"] = md5($credentials["password"]); $postfields["action"] = "getclientsproducts"; $postfields["responsetype"] = "json"; - $postfields["clientid"] = 1846; - //$postfields["clientid"] = Application_Model_Preference::GetClientId(); + $postfields["clientid"] = Application_Model_Preference::GetClientId(); $query_string = ""; foreach ($postfields AS $k=>$v) $query_string .= "$k=".urlencode($v)."&"; $result = self::makeRequest($credentials["url"], $query_string); 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() { + //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(); $postfields = array(); @@ -216,6 +325,21 @@ class BillingController extends Zend_Controller_Action { $result = self::makeRequest($credentials["url"], $query_string); 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() { @@ -227,8 +351,7 @@ class BillingController extends Zend_Controller_Action { $postfields["password"] = md5($credentials["password"]); $postfields["action"] = "getclientsdetails"; $postfields["stats"] = true; - //$postfields["clientid"] = Application_Model_Preference::GetClientId(); - $postfields["clientid"] = 1846; + $postfields["clientid"] = Application_Model_Preference::GetClientId(); $postfields["responsetype"] = "json"; $query_string = ""; @@ -263,4 +386,12 @@ class BillingController extends Zend_Controller_Action { Logging::info($e->getMessage()); } } + + private static function ensureClientIdIsValid() + { + if (Application_Model_Preference::GetClientId() == null) + { + throw new Exception("Invalid client ID: " . Application_Model_Preference::GetClientId()); + } + } } \ No newline at end of file diff --git a/airtime_mvc/application/controllers/WhmcsLoginController.php b/airtime_mvc/application/controllers/WhmcsLoginController.php index 0f0e327de..2f78ce5ed 100644 --- a/airtime_mvc/application/controllers/WhmcsLoginController.php +++ b/airtime_mvc/application/controllers/WhmcsLoginController.php @@ -1,8 +1,5 @@ setDecorators(array( array('ViewScript', array('viewScript' => 'form/billing-purchase.phtml'))));*/ $client = BillingController::getClientDetails(); @@ -34,7 +34,7 @@ class Application_Form_BillingClient extends Zend_Form $companyname->setLabel(_('Company Name:')) ->setValue($client["companyname"]) ->setAttrib('class', 'input_text') - ->setRequired(true) + ->setRequired(false) ->addValidator($notEmptyValidator) ->addFilter('StringTrim'); $this->addElement($companyname); @@ -85,7 +85,7 @@ class Application_Form_BillingClient extends Zend_Form $this->addElement($state); $postcode = new Zend_Form_Element_Text('postcode'); - $postcode->setLabel(_('Zip Code:')) + $postcode->setLabel(_('Zip Code / Postal Code:')) ->setValue($client["postcode"]) ->setAttrib('class', 'input_text') ->setRequired(true) @@ -140,15 +140,16 @@ class Application_Form_BillingClient extends Zend_Form $this->addElement($securityqans); foreach ($client["customfields"] as $field) { - if ($field["id"] == 7) { + if ($field["id"] == "7") { $vatvalue = $field["value"]; - } elseif ($field["id"] == 71) { + } elseif ($field["id"] == "71") { $subscribevalue = $field["value"]; } } - $vat = new Zend_Form_Element_Text('customfield7'); + $vat = new Zend_Form_Element_Text("7"); $vat->setLabel(_('VAT/Tax ID (EU only)')) + ->setBelongsTo('customfields') ->setValue($vatvalue) ->setAttrib('class', 'input_text') //->setRequired(true) @@ -156,9 +157,10 @@ class Application_Form_BillingClient extends Zend_Form ->addFilter('StringTrim'); $this->addElement($vat); - $subscribe = new Zend_Form_Element_Checkbox('customfield71'); + $subscribe = new Zend_Form_Element_Checkbox('71'); $subscribe->setLabel(_('Subscribe to Sourcefabric newsletter')) ->setValue($subscribevalue) + ->setBelongsTo('customfields') ->setAttrib('class', 'input_text') ->setRequired(true) ->addValidator($notEmptyValidator) diff --git a/airtime_mvc/application/forms/BillingUpgradeDowngrade.php b/airtime_mvc/application/forms/BillingUpgradeDowngrade.php index 8a7d8a084..b89136689 100644 --- a/airtime_mvc/application/forms/BillingUpgradeDowngrade.php +++ b/airtime_mvc/application/forms/BillingUpgradeDowngrade.php @@ -4,37 +4,32 @@ class Application_Form_BillingUpgradeDowngrade extends Zend_Form public function init() { $productPrices = array(); - $productTypes = array(); - $products = BillingController::getProducts(); - - foreach ($products as $k => $p) { - $productPrices[$p["name"]] = array( - "monthly" => $p["pricing"]["USD"]["monthly"], - "annualy" => $p["pricing"]["USD"]["annually"] - ); - $productTypes[$p["pid"]] = $p["name"]; - } - + $productTypes = array(); + list($productPrices, $productTypes) = BillingController::getProductPricesAndTypes(); + //$currentPlanType = ucfirst(Application_Model_Preference::GetPlanLevel()); $currentPlanType = "Hobbyist"; if (($key = array_search($currentPlanType, $productTypes)) !== false) { - unset($productTypes[$key]); + //unset($productTypes[$key]); } $pid = new Zend_Form_Element_Radio('newproductid'); $pid->setLabel(_('Plan type:')) ->setMultiOptions($productTypes) + ->setRequired(true) ->setValue(26); - $this->addElement($pid); - + $this->addElement($pid); + $billingcycle = new Zend_Form_Element_Radio('newproductbillingcycle'); $billingcycle->setLabel(_('Billing cycle:')) - ->setMultiOptions(array('monthly' => 'monthly', 'annually' => 'annually')) + ->setMultiOptions(array('monthly' => 'Monthly', 'annually' => 'Annually')) + ->setRequired(true) ->setValue('monthly'); $this->addElement($billingcycle); $paymentmethod = new Zend_Form_Element_Radio('paymentmethod'); $paymentmethod->setLabel(_('Payment method:')) + ->setRequired(true) ->setMultiOptions(array( 'paypal' => _('PayPal'), 'tco' => _('Credit Card via 2Checkout'))) @@ -47,6 +42,8 @@ class Application_Form_BillingUpgradeDowngrade extends Zend_Form $this->addElement($submit);*/ $client = new Application_Form_BillingClient(); + $client->removeElement("password2"); + $client->removeElement("password2verify"); $this->addSubForm($client, 'billing_client_info'); } } diff --git a/airtime_mvc/application/views/scripts/billing/client.phtml b/airtime_mvc/application/views/scripts/billing/client.phtml index 980288c81..a38b8f685 100644 --- a/airtime_mvc/application/views/scripts/billing/client.phtml +++ b/airtime_mvc/application/views/scripts/billing/client.phtml @@ -1,4 +1,5 @@
+

Account Details

errorMessage)) {?>
errorMessage ?>
successMessage)) {?> diff --git a/airtime_mvc/application/views/scripts/billing/upgrade.phtml b/airtime_mvc/application/views/scripts/billing/upgrade.phtml index 6d40d3b0d..8a482ea80 100644 --- a/airtime_mvc/application/views/scripts/billing/upgrade.phtml +++ b/airtime_mvc/application/views/scripts/billing/upgrade.phtml @@ -1,8 +1,255 @@ -
-errorMessage)) {?> -
errorMessage ?>
- -
Plan Level:
-form ?> -



+form; + $form->setAttrib('id', 'upgrade-downgrade'); + +?> + + +
+

+

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
HobbyistStarterPlusPremium
1 Stream + 2 Streams + 2 Streams + 3 Streams +
64kbps Stream Quality + 64kbps and 128kbps Stream Quality + 64kbps and 196kbps Stream Quality + 64kbps, 128kbps, and 196kbps Stream Quality +
5 Listeners + 40 Listeners per stream + 100 Listeners per stream + 500 Listeners per stream +
2GB Storage + 5GB Storage + 30GB Storage + 150GB Storage +
Ticket, Email, Forum Support + Live Chat, Ticket, Email, Forum Support + Live Chat, Ticket, Email, Forum Support + Live Chat, Ticket, Email, Forum Support +
+ Save 15% if paid annually + Save 15% if paid annually + Save 15% if paid annually +
+ + + +
+
+ +

Current Plan: +

+ +

Choose a plan:

+
+ +
+ newproductid ?> +
+
+ newproductbillingcycle ?> +
+
+ Save 15% on annual plans (Hobbyist plan excluded). +
+
+
Total:
+ +
+ Plus VAT if you are an EU resident without a valid VAT number. +
+ +

Enter your payment details:

+ errorMessage)) {?> +
errorMessage ?>
+ + + + getSubform("billing_client_info") ?> +
+ firstname?> +
+
+ lastname?> +
+
+
+ companyname?> +
+
+ email?> +
+
+
+ address1?> +
+
+ address2?> +
+
+
+ city?> +
+
+ state?> +
+
+
+ postcode?> +
+
+ country?> +
+
+ phonenumber?> +
+
+ securityqid?> +
+
+ securityqans?> +
+

VAT will be added to your invoice if you are an EU resident without a valid VAT number.

+
+
+ getElement("7"); ?> +
+
+ +
+ getElement("71"); ?> +
+ getElement("71")->renderViewHelper(); ?> +
+ getElement("71")->renderLabel(); ?> +
+
+ +

After submitting your order, you will be redirected to an invoice with payment buttons.

+
+
+ paymentmethod ?> +
+ +
+ +
+
\ No newline at end of file diff --git a/airtime_mvc/application/views/scripts/partialviews/trialBox.phtml b/airtime_mvc/application/views/scripts/partialviews/trialBox.phtml index 301beaf75..dca68dca4 100644 --- a/airtime_mvc/application/views/scripts/partialviews/trialBox.phtml +++ b/airtime_mvc/application/views/scripts/partialviews/trialBox.phtml @@ -6,7 +6,7 @@
- + " href="/billing/upgrade" target="_blank">
diff --git a/airtime_mvc/public/css/billing.css b/airtime_mvc/public/css/billing.css new file mode 100644 index 000000000..c34e8ad49 --- /dev/null +++ b/airtime_mvc/public/css/billing.css @@ -0,0 +1,138 @@ +@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 +{ + width: 300px; +} + +#upgrade-downgrade dt, #upgrade-downgrade dd +{ + margin: 0px; +} + +#upgrade-downgrade dd +{ + 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 +{ + float: left; +} + +#billingcycle +{ + 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 +{ +} + +.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; +} \ No newline at end of file