public function &getTCPDF() { // Load PDF signing certificates if (!class_exists('AkeebasubsHelperCparams')) { require_once JPATH_ADMINISTRATOR . '/components/com_akeebasubs/helpers/cparams.php'; } $certificateFile = AkeebasubsHelperCparams::getParam('invoice_certificatefile', 'certificate.cer'); $secretKeyFile = AkeebasubsHelperCparams::getParam('invoice_secretkeyfile', 'secret.cer'); $secretKeyPass = AkeebasubsHelperCparams::getParam('invoice_secretkeypass', ''); $extraCertFile = AkeebasubsHelperCparams::getParam('invoice_extracert', 'extra.cer'); $certificate = ''; $secretkey = ''; $extracerts = ''; $path = JPATH_ADMINISTRATOR . '/components/com_akeebasubs/assets/tcpdf/certificates/'; if (JFile::exists($path . $certificateFile)) { $certificate = JFile::read($path . $certificateFile); } if (!empty($certificate)) { if (JFile::exists($path . $secretKeyFile)) { $secretkey = JFile::read($path . $secretKeyFile); } if (empty($secretkey)) { $secretkey = $certificate; } if (JFile::exists($path . $extraCertFile)) { $extracerts = JFile::read($path . $extraCertFile); } if (empty($extracerts)) { $extracerts = ''; } } // Set up TCPDF $jreg = JFactory::getConfig(); $tmpdir = $jreg->get('tmp_path'); $tmpdir = rtrim($tmpdir, '/' . DIRECTORY_SEPARATOR) . '/'; $siteName = $jreg->get('sitename'); $baseurl = JURI::base(); $baseurl = rtrim($baseurl, '/'); define('K_TCPDF_EXTERNAL_CONFIG', 1); define('K_PATH_MAIN', JPATH_BASE . '/'); define('K_PATH_URL', $baseurl); define('K_PATH_FONTS', JPATH_ROOT . '/media/com_akeebasubs/tcpdf/fonts/'); define('K_PATH_CACHE', $tmpdir); define('K_PATH_URL_CACHE', $tmpdir); define('K_PATH_IMAGES', JPATH_ROOT . '/media/com_akeebasubs/tcpdf/images/'); define('K_BLANK_IMAGE', K_PATH_IMAGES . '_blank.png'); define('PDF_PAGE_FORMAT', 'A4'); define('PDF_PAGE_ORIENTATION', 'P'); define('PDF_CREATOR', 'Akeeba Subscriptions'); define('PDF_AUTHOR', $siteName); define('PDF_UNIT', 'mm'); define('PDF_MARGIN_HEADER', 5); define('PDF_MARGIN_FOOTER', 10); define('PDF_MARGIN_TOP', 27); define('PDF_MARGIN_BOTTOM', 25); define('PDF_MARGIN_LEFT', 15); define('PDF_MARGIN_RIGHT', 15); define('PDF_FONT_NAME_MAIN', 'dejavusans'); define('PDF_FONT_SIZE_MAIN', 8); define('PDF_FONT_NAME_DATA', 'dejavusans'); define('PDF_FONT_SIZE_DATA', 8); define('PDF_FONT_MONOSPACED', 'dejavusansmono'); define('PDF_IMAGE_SCALE_RATIO', 1.25); define('HEAD_MAGNIFICATION', 1.1); define('K_CELL_HEIGHT_RATIO', 1.25); define('K_TITLE_MAGNIFICATION', 1.3); define('K_SMALL_RATIO', 2 / 3); define('K_THAI_TOPCHARS', true); define('K_TCPDF_CALLS_IN_HTML', false); require_once JPATH_ADMINISTRATOR . '/components/com_akeebasubs/assets/tcpdf/tcpdf.php'; $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); $pdf->SetCreator(PDF_CREATOR); $pdf->SetAuthor(PDF_AUTHOR); $pdf->SetTitle('Invoice'); $pdf->SetSubject('Invoice'); $pdf->setHeaderFont(array(PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN)); $pdf->setFooterFont(array(PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA)); $pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED); $pdf->SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT); $pdf->SetHeaderMargin(PDF_MARGIN_HEADER); $pdf->SetFooterMargin(PDF_MARGIN_FOOTER); $pdf->SetAutoPageBreak(TRUE, PDF_MARGIN_BOTTOM); $pdf->setImageScale(PDF_IMAGE_SCALE_RATIO); $pdf->setHeaderFont(array('dejavusans', '', 8, '', false)); $pdf->setFooterFont(array('dejavusans', '', 8, '', false)); $pdf->SetFont('dejavusans', '', 8, '', false); if (!empty($certificate)) { $pdf->setSignature($certificate, $secretkey, $secretKeyPass, $extracerts); } return $pdf; }
public static function getURL($filename) { // Get the base site URL $url = JURI::base(); $url = rtrim($url, '/'); // Take into account relative URL for administrator list($isCLI, $isAdmin) = F0FDispatcher::isCliAdmin(); if ($isAdmin) { $url .= '/..'; } if (!class_exists('AkeebasubsHelperCparams')) { require_once JPATH_ADMINISTRATOR . '/components/com_akeebasubs/helpers/cparams.php'; } $imagePath = trim(AkeebasubsHelperCparams::getParam('imagedir', 'images/'), '/'); if (version_compare(JVERSION, '3.0', 'le')) { // Joomla! 2.5 : Pretty much straightforward return $url . '/' . $imagePath . '/' . $filename; } else { // Joomla! 3.0+ : Where the heck is the image? $testJ25 = JPATH_SITE . '/' . $imagePath . '/' . $filename; $testJ30 = JPATH_SITE . '/' . $filename; if (file_exists($testJ30)) { return $url . '/' . $filename; } else { return $url . '/' . $imagePath . '/' . $filename; } } }
public function cron($cachable = false) { // Makes sure SiteGround's SuperCache doesn't cache the CRON view JResponse::setHeader('X-Cache-Control', 'False', true); require_once F0FTemplateUtils::parsePath('admin://components/com_akeebasubs/helpers/cparams.php', true); $configuredSecret = AkeebasubsHelperCparams::getParam('secret', ''); if (empty($configuredSecret)) { header('HTTP/1.1 503 Service unavailable due to configuration'); JFactory::getApplication()->close(); } $secret = $this->input->get('secret', null, 'raw'); if ($secret != $configuredSecret) { header('HTTP/1.1 403 Forbidden'); JFactory::getApplication()->close(); } $command = $this->input->get('command', null, 'raw'); $command = trim(strtolower($command)); if (empty($command)) { header('HTTP/1.1 501 Not implemented'); JFactory::getApplication()->close(); } F0FPlatform::getInstance()->importPlugin('system'); F0FPlatform::getInstance()->runPlugins('onAkeebasubsCronTask', array($command, array('time_limit' => 10))); echo "{$command} OK"; JFactory::getApplication()->close(); }
public static function date($date, $format = null) { JLoader::import('joomla.utilities.date'); $jDate = new JDate($date); if (empty($format)) { if (!class_exists('AkeebasubsHelperCparams')) { require_once JPATH_ADMINISTRATOR . '/components/com_akeebasubs/helpers/cparams.php'; } $format = AkeebasubsHelperCparams::getParam('dateformat', 'Y-m-d H:i'); $format = str_replace('%', '', $format); } return $jDate->format($format, true); }
public static function setParam($key, $value) { if (!is_object(self::$params)) { JLoader::import('joomla.application.component.helper'); self::$params = JComponentHelper::getParams('com_akeebasubs'); } self::$params->set($key, $value); $db = JFactory::getDBO(); $data = self::$params->toString(); $sql = $db->getQuery(true)->update($db->qn('#__extensions'))->set($db->qn('params') . ' = ' . $db->q($data))->where($db->qn('element') . ' = ' . $db->q('com_akeebasubs'))->where($db->qn('type') . ' = ' . $db->q('component')); $db->setQuery($sql); $db->execute(); }
protected function onRead($tpl = null) { JRequest::setVar('hidemainmenu', true); $model = $this->getModel(); $this->item = $model->getItem(); $this->dnt = $this->do_not_track(); // Get component parameters and pass them to the view require_once JPATH_ADMINISTRATOR . '/components/com_akeebasubs/helpers/cparams.php'; $cparams = (object) array('currencypos' => AkeebasubsHelperCparams::getParam('currencypos', 'before'), 'stepsbar' => AkeebasubsHelperCparams::getParam('stepsbar', 1), 'allowlogin' => AkeebasubsHelperCparams::getParam('allowlogin', 1), 'currencysymbol' => AkeebasubsHelperCparams::getParam('currencysymbol', '€'), 'personalinfo' => AkeebasubsHelperCparams::getParam('personalinfo', 1), 'showdiscountfield' => AkeebasubsHelperCparams::getParam('showdiscountfield', 1), 'showtaxfield' => AkeebasubsHelperCparams::getParam('showtaxfield', 1), 'showregularfield' => AkeebasubsHelperCparams::getParam('showregularfield', 1), 'showcouponfield' => AkeebasubsHelperCparams::getParam('showcouponfield', 1), 'hidelonepaymentoption' => AkeebasubsHelperCparams::getParam('hidelonepaymentoption', 1), 'reqcoupon' => AkeebasubsHelperCparams::getParam('reqcoupon', 0)); $this->cparams = $cparams; $this->apply_validation = JFactory::getSession()->get('apply_validation.' . $this->item->akeebasubs_level_id, 0, 'com_akeebasubs') ? 'true' : 'false'; // Makes sure SiteGround's SuperCache doesn't cache the subscription page JResponse::setHeader('X-Cache-Control', 'False', true); }
/** * Returns the payment form to be submitted by the user's browser. The form must have an ID of * "paymentForm" and a visible submit button. * * @param string $paymentmethod * @param JUser $user * @param AkeebasubsTableLevel $level * @param AkeebasubsTableSubscription $subscription * @return string */ public function onAKPaymentNew($paymentmethod, $user, $level, $subscription) { if ($paymentmethod != $this->ppName) { return false; } $doc = JFactory::getDocument(); $doc->addScriptDeclaration("\nvar PAYMILL_PUBLIC_KEY = '" . $this->getPublicKey() . "';\n"); $doc->addScript("https://bridge.paymill.de/"); $callbackUrl = JURI::base() . 'index.php?option=com_akeebasubs&view=callback&paymentmethod=paymill&sid=' . $subscription->akeebasubs_subscription_id; $data = (object) array('url' => $callbackUrl, 'amount' => (int) ($subscription->gross_amount * 100), 'currency' => strtoupper(AkeebasubsHelperCparams::getParam('currency', 'EUR')), 'description' => $level->title . ' #' . $subscription->akeebasubs_subscription_id, 'carholder' => $user->name); @ob_start(); include dirname(__FILE__) . '/paymill/form.php'; $html = @ob_get_clean(); return $html; }
/** * Returns the payment form to be submitted by the user's browser. The form must have an ID of * "paymentForm" and a visible submit button. * * @param string $paymentmethod * @param JUser $user * @param AkeebasubsTableLevel $level * @param AkeebasubsTableSubscription $subscription * @return string */ public function onAKPaymentNew($paymentmethod, $user, $level, $subscription) { if ($paymentmethod != $this->ppName) { return false; } $doc = JFactory::getDocument(); $doc->addScriptDeclaration("\n\t\t\tStripe.setPublishableKey('" . $this->getPublicKey() . "');\n\t\t\t"); $doc->addScript("https://js.stripe.com/v2/"); $doc->addScriptDeclaration("\n\t\t\t\takeeba.jQuery(function(\$){\n\t\t\t\t\tvar stripeResponseHandler = function(status, response) {\n\t\t\t\t\t\t\$('.control-group').removeClass('error');\n\t\t\t\t\t\tif (response.error) {\n\t\t\t\t\t\t\tif(response.error.code == 'incorrect_number') {\n\t\t\t\t\t\t\t\t\$('#control-group-card-number').addClass('error');\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_INCORRECT_NUMBER') . "\");\n\t\t\t\t\t\t\t}else if(response.error.code == 'invalid_number') {\n\t\t\t\t\t\t\t\t\$('#control-group-card-number').addClass('error');\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_INVALID_NUMBER') . "\");\n\t\t\t\t\t\t\t}else if(response.error.code == 'invalid_expiry_month') {\n\t\t\t\t\t\t\t\t\$('#control-group-card-expiry').addClass('error');\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_INVALID_EXP_MONTH') . "\");\n\t\t\t\t\t\t\t}else if(response.error.code == 'invalid_expiry_year') {\n\t\t\t\t\t\t\t\t\$('#control-group-card-expiry').addClass('error');\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_INVALID_EXP_YEAR') . "\");\n\t\t\t\t\t\t\t}else if(response.error.code == 'invalid_cvc') {\n\t\t\t\t\t\t\t\t\$('#control-group-card-cvc').addClass('error');\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_INVALID_CVC') . "\");\n\t\t\t\t\t\t\t}else if(response.error.code == 'expired_card') {\n\t\t\t\t\t\t\t\t\$('#control-group-card-expiry').addClass('error');\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_EXPIRED_CARD') . "\");\n\t\t\t\t\t\t\t}else if(response.error.code == 'incorrect_cvc') {\n\t\t\t\t\t\t\t\t\$('#control-group-card-cvc').addClass('error');\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_INCORRECT_CVC') . "\");\n\t\t\t\t\t\t\t}else if(response.error.code == 'card_declined') {\n\t\t\t\t\t\t\t\t\$('#control-group-card-number').addClass('error');\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_CARD_DECLINED') . "\");\n\t\t\t\t\t\t\t}else if(response.error.code == 'missing') {\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_MISSING') . "\");\n\t\t\t\t\t\t\t}else if(response.error.code == 'processing_error') {\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_PROCESSING_ERROR') . "\");\n\t\t\t\t\t\t\t}else if(status == 401) {\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_UNAUTHORIZED') . "\");\n\t\t\t\t\t\t\t}else if(status == 402) {\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_REQUEST_FAILED') . "\");\n\t\t\t\t\t\t\t}else if(status == 404) {\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_NOT_FOUND') . "\");\n\t\t\t\t\t\t\t}else if(status >= 500) {\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_SERVER_ERROR') . "\");\n\t\t\t\t\t\t\t}else {\n\t\t\t\t\t\t\t\t\$('#payment-errors').text(\"" . JText::_('PLG_AKPAYMENT_STRIPE_FORM_UNKNOWN_ERROR') . "\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\$('#payment-errors').show();\n\t\t\t\t\t\t\t\$('#payment-button').removeAttr('disabled');\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\$('#payment-errors').hide();\n\t\t\t\t\t\t\tvar token = response.id;\n\t\t\t\t\t\t\t\$('#token').val(token);\n\t\t\t\t\t\t\t\$('#payment-form').submit();\n\t\t\t\t\t\t}\n\t\t\t\t\t};\n\n\t\t\t\t\t\$('#payment-form').submit(function(e){\n\t\t\t\t\t\tvar token = \$('#token').val();\n\t\t\t\t\t\tif(!!token) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}else{\n\t\t\t\t\t\t\t\$('#payment-button').attr('disabled', 'disabled');\n\t\t\t\t\t\t\tStripe.createToken({\n\t\t\t\t\t\t\t\tnumber:\$('#card-number').val(),\n\t\t\t\t\t\t\t\texp_month:\$('#card-expiry-month').val(),\n\t\t\t\t\t\t\t\texp_year:\$('#card-expiry-year').val(),\n\t\t\t\t\t\t\t\tcvc:\$('#card-cvc').val()\n\t\t\t\t\t\t\t}, stripeResponseHandler);\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t});\n\t\t\t"); $callbackUrl = JURI::base() . 'index.php?option=com_akeebasubs&view=callback&paymentmethod=stripe&sid=' . $subscription->akeebasubs_subscription_id; $data = (object) array('url' => $callbackUrl, 'amount' => (int) ($subscription->gross_amount * 100), 'currency' => strtolower(AkeebasubsHelperCparams::getParam('currency', 'usd')), 'description' => $level->title . ' #' . $subscription->akeebasubs_subscription_id, 'cardholder' => $user->name); @ob_start(); include dirname(__FILE__) . '/stripe/form.php'; $html = @ob_get_clean(); return $html; }
/** * Get the rendering of this field type for a repeatable (grid) display, * e.g. in a view listing many item (typically a "browse" task) * * @since 2.0 * * @return string The field HTML */ public function getRepeatable() { include_once F0FTemplateUtils::parsePath('admin://components/com_akeebasubs/helpers/cparams.php', true); // Initialise $class = $this->id; $typeField = 'type'; $classValue = 'akeebasubs-coupon-discount-value'; $classPercent = 'akeebasubs-coupon-discount-percent'; $classLastPercent = 'akeebasubs-coupon-discount-lastpercent'; // Get field parameters if ($this->element['class']) { $class = (string) $this->element['class']; } if ($this->element['type_field']) { $typeField = (string) $this->element['type_field']; } if ($this->element['class_value']) { $classValue = (string) $this->element['class_value']; } if ($this->element['class_percent']) { $classPercent = (string) $this->element['class_percent']; } if ($this->element['class_lastpercent']) { $classLastPercent = (string) $this->element['class_lastpercent']; } $type = $this->item->{$typeField}; // Start the HTML output $extraClass = $type == 'value' ? $classValue : $classPercent; $extraClass .= $type == 'lastpercent' ? ' ' . $classLastPercent : ''; $html = '<span class="' . $class . ' ' . $extraClass . '">'; // Case 1: Value discount if ($type == 'value') { if (AkeebasubsHelperCparams::getParam('currencypos', 'before') == 'before') { $html .= AkeebasubsHelperCparams::getParam('currencysymbol', '€'); } $html .= ' ' . sprintf('%02.02f', (double) $this->value) . ' '; if (AkeebasubsHelperCparams::getParam('currencypos', 'before') == 'after') { $html .= AkeebasubsHelperCparams::getParam('currencysymbol', '€'); } } else { $html .= sprintf('%2.2f', (double) $this->value) . ' %'; } // End the HTML output $html .= '</span>'; return $html; }
/** * Returns the payment form to be submitted by the user's browser. The form must have an ID of * "paymentForm" and a visible submit button. * * @param string $paymentmethod * @param JUser $user * @param AkeebasubsTableLevel $level * @param AkeebasubsTableSubscription $subscription * @return string */ public function onAKPaymentNew($paymentmethod, $user, $level, $subscription) { if ($paymentmethod != $this->ppName) { return false; } $slug = F0FModel::getTmpInstance('Levels', 'AkeebasubsModel')->setId($subscription->akeebasubs_level_id)->getItem()->slug; $rootURL = rtrim(JURI::base(), '/'); $subpathURL = JURI::base(true); if (!empty($subpathURL) && $subpathURL != '/') { $rootURL = substr($rootURL, 0, -1 * strlen($subpathURL)); } $data = (object) array('url' => $this->params->get('checkout') == 'single' ? 'https://www.2checkout.com/checkout/spurchase' : 'https://www.2checkout.com/checkout/purchase', 'sid' => $this->params->get('sid', ''), 'x_receipt_link_url' => $rootURL . str_replace('&', '&', JRoute::_('index.php?option=com_akeebasubs&view=message&slug=' . $slug . '&layout=order&subid=' . $subscription->akeebasubs_subscription_id)), 'params' => $this->params, 'name' => $user->name, 'email' => $user->email, 'currency' => strtoupper(AkeebasubsHelperCparams::getParam('currency', 'EUR')), 'recurring' => $level->recurring ? $subscription->recurring_amount >= 0.01 ? 2 : 1 : 0); if ($data->recurring >= 1) { $ppDuration = $this->_toPPDuration($level->duration); $data->t3 = $ppDuration->unit; $data->p3 = $ppDuration->value; } $kuser = F0FModel::getTmpInstance('Users', 'AkeebasubsModel')->user_id($user->id)->getFirstItem(); @ob_start(); include dirname(__FILE__) . '/2conew/form.php'; $html = @ob_get_clean(); return $html; }
/** * Get the rendering of this field type for a repeatable (grid) display, * e.g. in a view listing many item (typically a "browse" task) * * @since 2.0 * * @return string The field HTML */ public function getRepeatable() { include_once F0FTemplateUtils::parsePath('admin://components/com_akeebasubs/helpers/cparams.php', true); // Initialise $class = $this->id; // Get field parameters if ($this->element['class']) { $class = (string) $this->element['class']; } // Start the HTML output $html = '<span class="' . $class . '">'; // First line: regular price if (AkeebasubsHelperCparams::getParam('currencypos', 'before') == 'before') { $html .= AkeebasubsHelperCparams::getParam('currencysymbol', '€'); } $html .= ' ' . sprintf('%02.02f', (double) $this->value) . ' '; if (AkeebasubsHelperCparams::getParam('currencypos', 'before') == 'after') { $html .= AkeebasubsHelperCparams::getParam('currencysymbol', '€'); } // Second line: sign-up fee if (property_exists($this->item, 'signupfee') && $this->item->signupfee >= 0.01) { $html .= '<br /><span class="small">( '; $html .= JText::_('COM_AKEEBASUBS_LEVEL_FIELD_SIGNUPFEE_LIST'); if (AkeebasubsHelperCparams::getParam('currencypos', 'before') == 'before') { $html .= ' ' . AkeebasubsHelperCparams::getParam('currencysymbol', '€'); } $html .= sprintf('%02.02f', (double) $this->item->signupfee); if (AkeebasubsHelperCparams::getParam('currencypos', 'before') == 'after') { $html .= AkeebasubsHelperCparams::getParam('currencysymbol', '€'); } $html .= ' )</span>'; } // End the HTML output $html .= '</span>'; return $html; }
public function onAKPaymentCallback($paymentmethod, $data) { JLoader::import('joomla.utilities.date'); // Check if we're supposed to handle this if ($paymentmethod != $this->ppName) { return false; } // Check IPN data for validity (i.e. protect against fraud attempt) $isValid = $this->isValidIPN($data); if (!$isValid) { $data['akeebasubs_failure_reason'] = 'PayPal reports transaction as invalid'; } // Check txn_type; we only accept web_accept transactions with this plugin if ($isValid) { // This is required to process some IPNs, such as Reversed and Canceled_Reversal if (!array_key_exists('txn_type', $data)) { $data['txn_type'] = 'workaround_to_missing_txn_type'; } $validTypes = array('workaround_to_missing_txn_type', 'web_accept', 'recurring_payment', 'subscr_payment'); $isValid = in_array($data['txn_type'], $validTypes); if (!$isValid) { $data['akeebasubs_failure_reason'] = "Transaction type " . $data['txn_type'] . " can't be processed by this payment plugin."; } else { $recurring = !in_array($data['txn_type'], array('web_accept', 'workaround_to_missing_txn_type')); } } // Load the relevant subscription row if ($isValid) { $id = array_key_exists('custom', $data) ? (int) $data['custom'] : -1; $subscription = null; if ($id > 0) { $subscription = F0FModel::getTmpInstance('Subscriptions', 'AkeebasubsModel')->setId($id)->getItem(); if ($subscription->akeebasubs_subscription_id <= 0 || $subscription->akeebasubs_subscription_id != $id) { $subscription = null; $isValid = false; } } else { $isValid = false; } if (!$isValid) { $data['akeebasubs_failure_reason'] = 'The referenced subscription ID ("custom" field) is invalid'; } } // Check that receiver_email / receiver_id is what the site owner has configured if ($isValid) { $receiver_email = $data['receiver_email']; $receiver_id = $data['receiver_id']; $valid_id = $this->getMerchantID(); $isValid = $receiver_email == $valid_id || strtolower($receiver_email) == strtolower($receiver_email) || $receiver_id == $valid_id || strtolower($receiver_id) == strtolower($receiver_id); if (!$isValid) { $data['akeebasubs_failure_reason'] = 'Merchant ID does not match receiver_email or receiver_id'; } } // Check that mc_gross is correct $isPartialRefund = false; if ($isValid && !is_null($subscription)) { $mc_gross = floatval($data['mc_gross']); // @todo On recurring subscriptions recalculate the net, tax and gross price by removing the signup fee if ($recurring && $subscription->recurring_amount >= 0.01) { $gross = $subscription->recurring_amount; } else { $gross = $subscription->gross_amount; } if ($mc_gross > 0) { // A positive value means "payment". The prices MUST match! // Important: NEVER, EVER compare two floating point values for equality. $isValid = $gross - $mc_gross < 0.01; } else { $isPartialRefund = false; $temp_mc_gross = -1 * $mc_gross; $isPartialRefund = $gross - $temp_mc_gross > 0.01; } if (!$isValid) { $data['akeebasubs_failure_reason'] = 'Paid amount does not match the subscription amount'; } } // Check that txn_id has not been previously processed if ($isValid && !is_null($subscription) && !$isPartialRefund) { if ($subscription->processor_key == $data['txn_id']) { if ($subscription->state == 'C') { if (strtolower($data['payment_status']) != 'refunded') { $isValid = false; $data['akeebasubs_failure_reason'] = "I will not process the same txn_id twice"; } } } } // Check that mc_currency is correct if ($isValid && !is_null($subscription)) { $mc_currency = strtoupper($data['mc_currency']); $currency = strtoupper(AkeebasubsHelperCparams::getParam('currency', 'EUR')); if ($mc_currency != $currency) { $isValid = false; $data['akeebasubs_failure_reason'] = "Invalid currency; expected {$currency}, got {$mc_currency}"; } } // Log the IPN data $this->logIPN($data, $isValid); // Fraud attempt? Do nothing more! if (!$isValid) { return false; } // Check the payment_status switch ($data['payment_status']) { case 'Canceled_Reversal': case 'Completed': $newStatus = 'C'; break; case 'Created': case 'Pending': case 'Processed': $newStatus = 'P'; break; case 'Denied': case 'Expired': case 'Failed': case 'Refunded': case 'Reversed': case 'Voided': default: // Partial refunds can only by issued by the merchant. In that case, // we don't want the subscription to be cancelled. We have to let the // merchant adjust its parameters if needed. if ($isPartialRefund) { $newStatus = 'C'; } else { $newStatus = 'X'; } break; } // Update subscription status (this also automatically calls the plugins) $updates = array('akeebasubs_subscription_id' => $id, 'processor_key' => $data['txn_id'], 'state' => $newStatus, 'enabled' => 0); // On recurring payments also store the subscription ID if (array_key_exists('subscr_id', $data)) { $subscr_id = $data['subscr_id']; $params = $subscription->params; if (!is_array($params)) { $params = json_decode($params, true); } if (is_null($params) || empty($params)) { $params = array(); } $params['recurring_id'] = $subscr_id; $updates['params'] = $params; } JLoader::import('joomla.utilities.date'); if ($newStatus == 'C') { $this->fixDates($subscription, $updates); } // In the case of a successful recurring payment, fetch the old subscription's data if ($recurring && $newStatus == 'C' && $subscription->state == 'C') { // Fix the starting date if the payment was accepted after the subscription's start date. This // works around the case where someone pays by e-Check on January 1st and the check is cleared // on January 5th. He'd lose those 4 days without this trick. Or, worse, if it was a one-day pass // the user would have paid us and we'd never given him a subscription! $regex = '/^\\d{1,4}(\\/|-)\\d{1,2}(\\/|-)\\d{2,4}[[:space:]]{0,}(\\d{1,2}:\\d{1,2}(:\\d{1,2}){0,1}){0,1}$/'; if (!preg_match($regex, $subscription->publish_up)) { $subscription->publish_up = '2001-01-01'; } if (!preg_match($regex, $subscription->publish_down)) { $subscription->publish_down = '2038-01-01'; } $jNow = new JDate(); $jStart = new JDate($subscription->publish_up); $jEnd = new JDate($subscription->publish_down); $now = $jNow->toUnix(); $start = $jStart->toUnix(); $end = $jEnd->toUnix(); // Create a new record for the old subscription $oldData = $subscription->getData(); $oldData['akeebasubs_subscription_id'] = 0; $oldData['publish_down'] = $jNow->toSql(); $oldData['enabled'] = 0; $oldData['contact_flag'] = 3; $oldData['notes'] = "Automatically renewed subscription on " . $jNow->toSql(); // Calculate new start/end time for the subscription $allSubs = F0FModel::getTmpInstance('Subscriptions', 'AkeebasubsModel')->paystate('C')->level($subscription->akeebasubs_level_id)->user_id($subscription->user_id); $max_expire = 0; if (count($allSubs)) { foreach ($allSubs as $aSub) { $jExpire = new JDate($aSub->publish_down); $expire = $jExpire->toUnix(); if ($expire > $max_expire) { $max_expire = $expire; } } } $duration = $end - $start; $start = max($now, $max_expire); $end = $start + $duration; $jStart = new JDate($start); $jEnd = new JDate($end); $updates['publish_up'] = $jStart->toSql(); $updates['publish_down'] = $jEnd->toSql(); // Save the record for the old subscription $table = F0FModel::getTmpInstance('Subscriptions', 'AkeebasubsModel')->getTable(); $table->reset(); $table->bind($oldData); $table->store(); } elseif ($recurring && $newStatus != 'C') { // Recurring payment, but payment_status is not Completed. We have // stop right now and not save the changes. Otherwise the status of // the subscription will become P or X and the recurring payment // code above will not run when PayPal sends us a new IPN with the // status set to Completed. return; } // Save the changes $subscription->save($updates); // Run the onAKAfterPaymentCallback events JLoader::import('joomla.plugin.helper'); JPluginHelper::importPlugin('akeebasubs'); $app = JFactory::getApplication(); $jResponse = $app->triggerEvent('onAKAfterPaymentCallback', array($subscription)); return true; }
/** * Drop down list of payment methods (active payment plugins) */ public static function paymentmethods($name = 'paymentmethod', $selected = '', $attribs = array()) { // Get the list of payment plugins $plugins = F0FModel::getTmpInstance('Subscribes', 'AkeebasubsModel')->getPaymentPlugins(); // Load the component parameters helper if (!class_exists('AkeebasubsHelperCparams')) { require_once JPATH_ADMINISTRATOR . '/components/com_akeebasubs/helpers/cparams.php'; } // Initialise parameters $level_id = isset($attribs['level_id']) ? $attribs['level_id'] : 0; $always_dropdown = isset($attribs['always_dropdown']) ? 1 : 0; $default_option = isset($attribs['default_option']) ? 1 : 0; // Per-level payment option filtering if ($level_id > 0) { // Load the subscription level $level = F0FModel::getTmpInstance('Levels', 'AkeebasubsModel')->getItem($level_id); $payment_plugins = $level->payment_plugins; if (!empty($payment_plugins)) { $payment_plugins = explode(',', $payment_plugins); $temp = array(); foreach ($plugins as $plugin) { if (in_array($plugin->name, $payment_plugins)) { $temp[] = $plugin; } } if (!empty($temp)) { $plugins = $temp; } } } $returnRawList = false; if (isset($attribs['return_raw_list'])) { $returnRawList = $attribs['return_raw_list']; unset($attribs['return_raw_list']); } if ($returnRawList) { return $plugins; } // Determine how to render the payment method (drop-down or radio box) if (AkeebasubsHelperCparams::getParam('useppimages', 1) > 0 && !$always_dropdown) { // Show images instead of a drop-down $options = array(); foreach ($plugins as $plugin) { if (!isset($plugin->image)) { $plugin->image = ''; } else { $plugin->image = trim($plugin->image); } if (empty($plugin->image)) { $plugin->image = rtrim(JURI::base(), '/') . '/media/com_akeebasubs/images/frontend/credit_card_logos.gif'; } $innerHTML = '<img border="0" src="' . $plugin->image . '" /> '; if (AkeebasubsHelperCparams::getParam('useppimages', 1) == 2) { $innerHTML .= '<span class="pull-left">' . $plugin->title . '</span>'; } $options[] = array('value' => $plugin->name, 'label' => $innerHTML); // In case we don't have a default selection, select the first item on the list if (empty($selected)) { $selected = $plugin->name; } } $html = '<span class="akeebasubs-paymentmethod-images">'; if (!empty($options)) { foreach ($options as $o) { $html .= '<label class="radio input-xxlarge"><input type="radio" name="' . $name . '" id="' . $name . $o['value'] . '" value="' . $o['value'] . '" '; if ($o['value'] == $selected) { $html .= 'checked="checked"'; } $html .= '/>' . $o['label'] . '</label>'; } } //$html .= self::genericradiolist($options, $name, $attribs, $selected, $name); $html .= '</span>'; return $html; } else { // Show drop-down $options = array(); if ($default_option) { $options[] = JHTML::_('select.option', '', JText::_('COM_AKEEBASUBS_LEVEL_FIELD_PAYMENT_PLUGINS_UNSELECT')); $selected = explode(',', $selected); } foreach ($plugins as $plugin) { $options[] = JHTML::_('select.option', $plugin->name, $plugin->title); } return self::genericlist($options, $name, $attribs, $selected, $name); } }
// Protect from unauthorized access defined('_JEXEC') or die; $this->loadHelper('cparams'); $this->loadHelper('modules'); $this->loadHelper('format'); $this->loadHelper('message'); // Translate message $message = AkeebasubsHelperMessage::processLanguage($this->item->ordertext); // Parse merge tags $message = AkeebasubsHelperMessage::processSubscriptionTags($message, $this->subscription); // Process content plugins $message = JHTML::_('content.prepare', $message); ?> <?php if (AkeebasubsHelperCparams::getParam('stepsbar', 1) && $this->subscription->prediscount_amount > 0.01) { echo $this->loadAnyTemplate('level/steps', array('step' => 'done')); } ?> <h1 class="componentheading"> <?php echo $this->escape(JText::_('COM_AKEEBASUBS_MESSAGE_THANKYOU')); ?> </h1> <?php echo $message; ?> <div class="akeebasubs-goback">
/** * Automatically unblock a user whose subscription is paid (status = C) and * enabled, if he's not already enabled. */ private function userUnblock() { // Make sure the payment is complete if ($this->state != 'C') { return; } // Make sure the subscription is enabled if (!$this->enabled) { return; } // Paid and enabled subscription; enable the user if he's not already enabled $user = JFactory::getUser($this->user_id); if ($user->block) { // Check the confirmfree component parameter and subscription level's price // If it's a free subscription do not activate the user. if (!class_exists('AkeebasubsHelperCparams')) { require_once JPATH_ADMINISTRATOR . '/components/com_akeebasubs/helpers/cparams.php'; } $confirmfree = AkeebasubsHelperCparams::getParam('confirmfree', 0); if ($confirmfree) { $level = F0FModel::getTmpInstance('Levels', 'AkeebasubsModel')->getItem($this->akeebasubs_level_id); if ($level->price < 0.01) { // Do not activate free subscription return; } } $updates = array('block' => 0, 'activation' => ''); $user->bind($updates); $user->save($updates); } }
/** * Creates new tax rules based on the user preferences */ public function createTaxRules() { // Get the state variables $params = $this->getStateVars(); // Should I proceed? if ($params->novatcalc) { // User opted out from VAT configuration return; } // Is this an EU country? $euCountries = AkeebasubsHelperEuVATInfo::getEUVATCountries(); $inEU = AkeebasubsHelperEuVATInfo::isEUVATCountry($params->country); // Store the country where the business is based (needed for proper invoicing) AkeebasubsHelperCparams::setParam('invoice_country', $params->country); // Prototype for tax rules $data = array('akeebasubs_level_id' => $params->akeebasubs_level_id, 'country' => '', 'state' => '', 'city' => '', 'vies' => 0, 'taxrate' => 0, 'enabled' => 1, 'ordering' => 0); $ordering = 0; if (!$inEU && !$params->viesreg) { // Non-EU business, without an EU VAT ID // A. All countries, with or without VIES registration: taxrate% $data['taxrate'] = $params->taxrate; $data['ordering'] = ++$ordering; F0FModel::getTmpInstance('Taxrules', 'AkeebasubsModel')->setId(0)->save($data); $data['vies'] = 1; $data['ordering'] = ++$ordering; F0FModel::getTmpInstance('Taxrules', 'AkeebasubsModel')->setId(0)->save($data); } elseif ($params->viesreg) { // EU VIES-registered business, or non-EU business with an EU VAT ID // A. All countries, with or without VIES registration, 0% $data['ordering'] = ++$ordering; F0FModel::getTmpInstance('Taxrules', 'AkeebasubsModel')->setId(0)->save($data); $data['vies'] = 1; $data['ordering'] = ++$ordering; F0FModel::getTmpInstance('Taxrules', 'AkeebasubsModel')->setId(0)->save($data); // B.1. All countries with the same VAT number prefix as mine, with or without VIES registration, taxrate% $data['taxrate'] = $params->taxrate; $myVATNrPrefix = AkeebasubsHelperEuVATInfo::getEUVATPrefix($params->country); foreach ($euCountries as $country) { $theirVATNrPrefix = AkeebasubsHelperEuVATInfo::getEUVATPrefix($country); if ($theirVATNrPrefix != $myVATNrPrefix) { continue; } $data['country'] = $country; $data['vies'] = 0; $data['ordering'] = ++$ordering; F0FModel::getTmpInstance('Taxrules', 'AkeebasubsModel')->setId(0)->save($data); $data['vies'] = 1; $data['ordering'] = ++$ordering; F0FModel::getTmpInstance('Taxrules', 'AkeebasubsModel')->setId(0)->save($data); } // C. All other EU countries, without VIES registration, taxrate% (and with VIES: 0%) foreach ($euCountries as $country) { $theirVATNrPrefix = AkeebasubsHelperEuVATInfo::getEUVATPrefix($country); if ($theirVATNrPrefix == $myVATNrPrefix) { continue; } // New VAT MOSS rules (post-2015): Each country gets its own VAT rate $data['taxrate'] = AkeebasubsHelperEuVATInfo::getEUVATRate($country); $data['vies'] = 0; $data['country'] = $country; $data['ordering'] = ++$ordering; F0FModel::getTmpInstance('Taxrules', 'AkeebasubsModel')->setId(0)->save($data); } } else { // EU non-VIES-registered business // A. All countries, with or without VIES registration, 0% $data['ordering'] = ++$ordering; F0FModel::getTmpInstance('Taxrules', 'AkeebasubsModel')->setId(0)->save($data); $data['vies'] = 1; $data['ordering'] = ++$ordering; F0FModel::getTmpInstance('Taxrules', 'AkeebasubsModel')->setId(0)->save($data); // B. All EU countries, with or without VIES registration, taxrate% foreach ($euCountries as $country) { // New VAT MOSS rules (post-2015): Each country gets its own VAT rate $data['taxrate'] = AkeebasubsHelperEuVATInfo::getEUVATRate($country); $data['country'] = $country; $data['vies'] = 0; $data['ordering'] = ++$ordering; F0FModel::getTmpInstance('Taxrules', 'AkeebasubsModel')->setId(0)->save($data); $data['vies'] = 1; $data['ordering'] = ++$ordering; F0FModel::getTmpInstance('Taxrules', 'AkeebasubsModel')->setId(0)->save($data); } } }
<legend><?php echo JText::_('COM_AKEEBASUBS_LEVEL_PERSUBFIELDS'); ?> </legend> <?php echo $subfieldsHTML; } } ?> </fieldset> </div> <?php $aks_validate_url = JURI::base() . 'index.php'; $aks_personal_info = AkeebasubsHelperCparams::getParam('personalinfo', 1) ? 'true' : 'false'; $aks_noneuvat = AkeebasubsHelperCparams::getParam('noneuvat', 0) ? 'true' : 'false'; $script = <<<JS ;// This comment is intentionally put here to prevent badly written plugins from causing a Javascript error // due to missing trailing semicolon and/or newline in their code. // Akeeba Subscriptions --- START >> >> >> var akeebasubs_validate_url = "{$aks_validate_url}"; var akeebasubs_valid_form = false; var akeebasubs_personalinfo = {$aks_personal_info}; var akeebasubs_noneuvat = {$aks_noneuvat}; // Akeeba Subscriptions --- END << << << JS; JFactory::getDocument()->addScriptDeclaration($script);
<div class="form-actions"> <button class="btn btn-primary btn-large" id="update_userinfo" type="submit"><?php echo JText::_('COM_AKEEBASUBS_USERINFO_BUTTON_UPDATE_USER'); ?> </button> </div> </form> <?php echo AkeebasubsHelperModules::loadposition('akeebasubscriptionsuserinfofooter'); ?> </div> <?php $aks_personal_info = AkeebasubsHelperCparams::getParam('personalinfo', 1); $aks_msg_error_overall = JText::_('COM_AKEEBASUBS_LEVEL_ERR_JSVALIDATIONOVERALL', true); $script = <<<ENDSCRIPT // Akeeba Subscriptions --- START >> >> >> (function(\$) { \t\$(document).ready(function(){ \t\t// Commented out until we can resolve some strange validation errors for some users \t\t// \$('#signupForm').submit(onSignupFormSubmit); \t\tvalidatePassword(); \t\tvalidateName(); \t\tvalidateEmail(); \t\tif({$aks_personal_info} != 0) { \t\t\tvalidateAddress(); \t\t} \t\tif({$aks_personal_info} == 1) {
/** * Updates the user info based on the state data * * @param bool $allowNewUser When true, we can create a new user. False, only update an existing user's data. * * @return boolean */ public function updateUserInfo($allowNewUser = true, $level = null) { $state = $this->getStateVariables(); $user = JFactory::getUser(); $user = $this->getState('user', $user); if ($user->id == 0 && !$allowNewUser) { // New user creation is not allowed. Sorry. return false; } if ($user->id == 0) { // Check for an existing, blocked, unactivated user with the same // username or email address. $user1 = F0FModel::getTmpInstance('Jusers', 'AkeebasubsModel')->username($state->username)->block(1)->getFirstItem(); $user2 = F0FModel::getTmpInstance('Jusers', 'AkeebasubsModel')->email($state->email)->block(1)->getFirstItem(); $id1 = $user1->id; $id2 = $user2->id; // Do we have a match? if ($id1 || $id2) { if ($id1 == $id2) { // Username and email match with the blocked user; reuse that // user, please. $user = JFactory::getUser($user1->id); } elseif ($id1 && $id2) { // We have both the same username and same email, but in two // different users. In order to avoid confusion we will remove // user 2 and change user 1's email into the email address provided // Remove the last subscription for $user2 (it will be an unpaid one) $submodel = F0FModel::getTmpInstance('Subscriptions', 'AkeebasubsModel'); $substodelete = $submodel->user_id($id2)->getList(); if (!empty($substodelete)) { foreach ($substodelete as $subtodelete) { $subtable = $submodel->getTable(); $subtable->delete($subtodelete->akeebasubs_subscription_id); } } // Remove $user2 and set $user to $user1 so that it gets updated $user2->delete($id2); $user = JFactory::getUser($user1->id); $user->email = $state->email; $user->save(true); } elseif (!$id1 && $id2) { // We have a user with the same email, but the wrong username. // Use this user (the username is updated later on) $user = JFactory::getUser($user2->id); } elseif ($id1 && !$id2) { // We have a user with the same username, but the wrong email. // Use this user (the email is updated later on) $user = JFactory::getUser($user1->id); } } } if (is_null($user->id) || $user->id == 0) { // CREATE A NEW USER $params = array('name' => $state->name, 'username' => $state->username, 'email' => $state->email, 'password' => $state->password, 'password2' => $state->password2); $user = new JUser(0); JLoader::import('joomla.application.component.helper'); $usersConfig = JComponentHelper::getParams('com_users'); $newUsertype = $usersConfig->get('new_usertype'); // get the New User Group from com_users' settings if (empty($newUsertype)) { $newUsertype = 2; } $params['groups'] = array($newUsertype); $params['sendEmail'] = 0; // Set the user's default language to whatever the site's current language is $params['params'] = array('language' => JFactory::getConfig()->get('language')); // We always block the user, so that only a successful payment or // clicking on the email link activates his account. This is to // prevent spam registrations when the subscription form is abused. JLoader::import('joomla.user.helper'); $params['block'] = 1; $randomString = JUserHelper::genRandomPassword(); $hash = JApplication::getHash($randomString); $params['activation'] = $hash; $userIsSaved = false; $user->bind($params); $userIsSaved = $user->save(); } else { // UPDATE EXISTING USER // Remove unpaid subscriptions on the same level for this user $unpaidSubs = F0FModel::getTmpInstance('Subscriptions', 'AkeebasubsModel')->user_id($user->id)->paystate('N', 'X')->getItemList(); if (!empty($unpaidSubs)) { foreach ($unpaidSubs as $unpaidSub) { $table = F0FModel::getTmpInstance('Subscriptions', 'AkeebasubsModel')->getTable(); $table->delete($unpaidSub->akeebasubs_subscription_id); } } // Update existing user's details $userRecord = F0FModel::getTmpInstance('Jusers', 'AkeebasubsModel')->setId($user->id)->getItem(); $updates = array('name' => $state->name, 'email' => $state->email); if (!empty($state->password) && $state->password == $state->password2) { JLoader::import('joomla.user.helper'); $salt = JUserHelper::genRandomPassword(32); $pass = JUserHelper::getCryptedPassword($state->password, $salt); $updates['password'] = $pass . ':' . $salt; } if (!empty($state->username)) { $updates['username'] = $state->username; } $userIsSaved = $userRecord->save($updates); } // Send activation email for free subscriptions if confirmfree is enabled if ($user->block && $level->price < 0.01) { if (!class_exists('AkeebasubsHelperCparams')) { require_once JPATH_ADMINISTRATOR . '/components/com_akeebasubs/helpers/cparams.php'; } $confirmfree = AkeebasubsHelperCparams::getParam('confirmfree', 0); if ($confirmfree) { // Send the activation email if (!isset($params)) { $params = array(); } $this->sendActivationEmail($user, $params); } } if (!$userIsSaved) { JError::raiseWarning('', JText::_($user->getError())); // ...raise a Warning return false; } else { $this->setState('user', $user); } return $userIsSaved; }
?> </h4> </td> </tr> <?php foreach ($this->items as $subscription) { ?> <?php if (!in_array($subscription->akeebasubs_subscription_id, $this->sortTable[$area])) { continue; } $m = 1 - $m; $email = trim($subscription->email); $email = strtolower($email); $rowClass = $subscription->enabled ? '' : 'expired'; $canRenew = AkeebasubsHelperCparams::getParam('showrenew', 1) ? true : false; $level = $this->allLevels[$subscription->akeebasubs_level_id]; if ($level->only_once) { $canRenew = false; } elseif (!$level->enabled) { $canRenew = false; } $jPublishUp = new JDate($subscription->publish_up); ?> <tr class="row<?php echo $m; ?> <?php echo $rowClass; ?> ">
private function IPNCallback($data) { JLoader::import('joomla.utilities.date'); // Check IPN data for validity (i.e. protect against fraud attempt) $isValid = $this->isValidIPN($data); if (!$isValid) { $data['akeebasubs_failure_reason'] = 'PayPal reports transaction as invalid'; } // Check txn_type; we only accept web_accept transactions with this plugin if ($isValid) { // This is required to process some IPNs, such as Reversed and Canceled_Reversal if (!array_key_exists('txn_type', $data)) { $data['txn_type'] = 'workaround_to_missing_txn_type'; } $validTypes = array('workaround_to_missing_txn_type', 'web_accept', 'recurring_payment', 'subscr_payment', 'express_checkout'); $isValid = in_array($data['txn_type'], $validTypes); if (!$isValid) { $data['akeebasubs_failure_reason'] = "Transaction type " . $data['txn_type'] . " can't be processed by this payment plugin."; } else { $recurring = $data['txn_type'] == 'recurring_payment'; } } // Load the relevant subscription row if ($isValid) { $id = $recurring ? $data['rp_invoice_id'] : $data['invoice']; $subscription = null; if ($id > 0) { $subscription = F0FModel::getTmpInstance('Subscriptions', 'AkeebasubsModel')->setId($id)->getItem(); if ($subscription->akeebasubs_subscription_id <= 0 || $subscription->akeebasubs_subscription_id != $id) { $subscription = null; $isValid = false; } } else { $isValid = false; } if (!$isValid) { $data['akeebasubs_failure_reason'] = 'The referenced subscription ID ("custom" field) is invalid'; } } // Check that mc_gross is correct $isPartialRefund = false; if ($isValid && !is_null($subscription)) { $mc_gross = floatval($data['mc_gross']); $gross = $subscription->gross_amount; if ($mc_gross > 0) { // A positive value means "payment". The prices MUST match! // Important: NEVER, EVER compare two floating point values for equality. $isValid = $gross - $mc_gross < 0.01; } else { $isPartialRefund = false; $temp_mc_gross = -1 * $mc_gross; $isPartialRefund = $gross - $temp_mc_gross > 0.01; } if (!$isValid) { $data['akeebasubs_failure_reason'] = 'Paid amount does not match the subscription amount'; } } // Check that txn_id has not been previously processed if ($isValid && !is_null($subscription) && !$isPartialRefund) { if ($subscription->processor_key == $data['txn_id']) { if ($subscription->state == 'C') { $isValid = false; $data['akeebasubs_failure_reason'] = "I will not process the same txn_id twice"; } } } // Check that mc_currency is correct if ($isValid && !is_null($subscription)) { $mc_currency = strtoupper($data['mc_currency']); $currency = strtoupper(AkeebasubsHelperCparams::getParam('currency', 'EUR')); if ($mc_currency != $currency) { $isValid = false; $data['akeebasubs_failure_reason'] = "Invalid currency; expected {$currency}, got {$mc_currency}"; } } // Log the IPN data $this->logIPN($data, $isValid); // Fraud attempt? Do nothing more! if (!$isValid) { return false; } // Check the payment_status switch ($data['payment_status']) { case 'Canceled_Reversal': case 'Completed': $newStatus = 'C'; break; case 'Created': case 'Pending': case 'Processed': $newStatus = 'P'; break; case 'Denied': case 'Expired': case 'Failed': case 'Refunded': case 'Reversed': case 'Voided': default: // Partial refunds can only by issued by the merchant. In that case, // we don't want the subscription to be cancelled. We have to let the // merchant adjust its parameters if needed. if ($isPartialRefund) { $newStatus = 'C'; } else { $newStatus = 'X'; } break; } // Update subscription status (this also automatically calls the plugins) $updates = array('akeebasubs_subscription_id' => $id, 'processor_key' => $data['txn_id'], 'state' => $newStatus, 'enabled' => 0); JLoader::import('joomla.utilities.date'); if ($newStatus == 'C') { $this->fixDates($subscription, $updates); } // In the case of a successful recurring payment, fetch the old subscription's data if ($recurring && $newStatus == 'C' && $subscription->state == 'C') { $jNow = new JDate(); $jStart = new JDate($subscription->publish_up); $jEnd = new JDate($subscription->publish_down); $now = $jNow->toUnix(); $start = $jStart->toUnix(); $end = $jEnd->toUnix(); // Create a new record for the old subscription $oldData = $subscription->getData(); $oldData['akeebasubs_subscription_id'] = 0; $oldData['publish_down'] = $jNow->toSql(); $oldData['enabled'] = 0; $oldData['contact_flag'] = 3; $oldData['notes'] = "Automatically renewed subscription on " . $jNow->toSql(); // Calculate new start/end time for the subscription $allSubs = F0FModel::getTmpInstance('Subscriptions', 'AkeebasubsModel')->paystate('C')->level($subscription->akeebasubs_level_id)->user_id($subscription->user_id); $max_expire = 0; if (count($allSubs)) { foreach ($allSubs as $aSub) { $jExpire = new JDate($aSub->publish_down); $expire = $jExpire->toUnix(); if ($expire > $max_expire) { $max_expire = $expire; } } } $duration = $end - $start; $start = max($now, $max_expire); $end = $start + $duration; $jStart = new JDate($start); $jEnd = new JDate($end); $updates['publish_up'] = $jStart->toSql(); $updates['publish_down'] = $jEnd->toSql(); // Save the record for the old subscription $table = F0FModel::getTmpInstance('Subscriptions', 'AkeebasubsModel')->getTable(); $table->reset(); $table->bind($oldData); $table->store(); } // Save the changes $subscription->save($updates); // Run the onAKAfterPaymentCallback events JLoader::import('joomla.plugin.helper'); JPluginHelper::importPlugin('akeebasubs'); $app = JFactory::getApplication(); $jResponse = $app->triggerEvent('onAKAfterPaymentCallback', array($subscription)); return true; }
" class="title"> <strong><?php echo htmlspecialchars($subscription->username, ENT_COMPAT, 'UTF-8'); ?> </strong> <br/><?php echo htmlspecialchars($subscription->name, ENT_COMPAT, 'UTF-8'); ?> </a> </td> <td> <?php echo sprintf('%2.2f', (double) $subscription->net_amount); ?> <?php echo AkeebasubsHelperCparams::getParam('currencysymbol', '€'); ?> </td> <td> <?php echo AkeebasubsHelperFormat::date($subscription->created_on, 'Y-m-d H:i'); ?> </td> </tr> <?php } ?> <?php } else { ?> <tr>
public function onAKPaymentCallback($paymentmethod, $data) { JLoader::import('joomla.utilities.date'); // Check if we're supposed to handle this if ($paymentmethod != $this->ppName) { return false; } // Check IPN data for validity (i.e. protect against fraud attempt) $isValid = $this->isValidIPN($data); if (!$isValid) { $data['akeebasubs_failure_reason'] = 'MD5 hash does not validate'; } // Load the relevant subscription row if ($isValid) { $id = array_key_exists('transaction_id', $data) ? (int) $data['custom'] : -1; $subscription = null; if ($id > 0) { $subscription = F0FModel::getTmpInstance('Subscriptions', 'AkeebasubsModel')->setId($id)->getItem(); if ($subscription->akeebasubs_subscription_id <= 0 || $subscription->akeebasubs_subscription_id != $id) { $subscription = null; $isValid = false; } } else { $isValid = false; } if (!$isValid) { $data['akeebasubs_failure_reason'] = 'The referenced subscription ID ("transaction_id" field) is invalid'; } } // Check that receiver is what the site owner has configured if ($isValid) { $receiver_email = $data['pay_to_email']; $valid_id = $this->params->get('merchant', ''); $isValid = $receiver_email == $valid_id || strtolower($receiver_email) == strtolower($receiver_email); if (!$isValid) { $data['akeebasubs_failure_reason'] = 'Merchant ID does not match pay_to_email'; } } // Check that amount is correct $isPartialRefund = false; if ($isValid && !is_null($subscription)) { $mc_gross = floatval($data['amount']); $gross = $subscription->gross_amount; if ($mc_gross > 0) { // A positive value means "payment". The prices MUST match! // Important: NEVER, EVER compare two floating point values for equality. $isValid = $gross - $mc_gross < 0.01; } else { $isPartialRefund = false; $temp_mc_gross = -1 * $mc_gross; $isPartialRefund = $gross - $temp_mc_gross > 0.01; } if (!$isValid) { $data['akeebasubs_failure_reason'] = 'Paid amount does not match the subscription amount'; } } // Check that mb_transaction_id has not been previously processed if ($isValid && !is_null($subscription) && !$isPartialRefund) { if ($subscription->processor_key == $data['mb_transaction_id']) { if ($subscription->state == 'C') { $isValid = false; $data['akeebasubs_failure_reason'] = "I will not process the same mb_transaction_id twice"; } } } // Check that currency is correct if ($isValid && !is_null($subscription)) { $mc_currency = strtoupper($data['currency']); $currency = strtoupper(AkeebasubsHelperCparams::getParam('currency', 'EUR')); if ($mc_currency != $currency) { $isValid = false; $data['akeebasubs_failure_reason'] = "Invalid currency; expected {$currency}, got {$mc_currency}"; } } // Log the IPN data $this->logIPN($data, $isValid); // Fraud attempt? Do nothing more! if (!$isValid) { return false; } // Check the payment_status switch ($data['status']) { case '2': $newStatus = 'C'; break; case '0': $newStatus = 'P'; break; case '-3': if ($isPartialRefund) { $newStatus = 'C'; } else { $newStatus = 'X'; } break; case '-1': case '-2': default: $newStatus = 'X'; break; } // Update subscription status (this also automatically calls the plugins) $updates = array('akeebasubs_subscription_id' => $id, 'processor_key' => $data['txn_id'], 'state' => $newStatus, 'enabled' => 0); JLoader::import('joomla.utilities.date'); if ($newStatus == 'C') { $this->fixDates($subscription, $updates); } $subscription->save($updates); // Run the onAKAfterPaymentCallback events JLoader::import('joomla.plugin.helper'); JPluginHelper::importPlugin('akeebasubs'); $app = JFactory::getApplication(); $jResponse = $app->triggerEvent('onAKAfterPaymentCallback', array($subscription)); return true; }
<?php /** * @package AkeebaSubs * @copyright Copyright (c)2010-2015 Nicholas K. Dionysopoulos * @license GNU General Public License version 3, or later */ defined('_JEXEC') or die; AkeebaStrapper::addJSfile('media://com_akeebasubs/js/autosubmit.js?' . AKEEBASUBS_VERSIONHASH); $this->loadHelper('cparams'); $this->loadHelper('modules'); $this->loadHelper('format'); if (AkeebasubsHelperCparams::getParam('stepsbar', 1)) { echo $this->loadAnyTemplate('level/steps', array('step' => 'payment')); } ?> <?php echo $this->form;
/** * Pre-processes the message text in $text, replacing merge tags with those * fetched based on subscription $sub * * @param string $text The message to process * @param AkeebasubsTableSubscription $sub A subscription object * * @return string The processed string */ public static function processSubscriptionTags($text, $sub, $extras = array()) { // Get the user object for this subscription $user = JFactory::getUser($sub->user_id); // Get the extra user parameters object for the subscription $kuser = F0FModel::getTmpInstance('Users', 'AkeebasubsModel')->user_id($sub->user_id)->getFirstItem(); // Get the subscription level $level = F0FModel::getTmpInstance('Levels', 'AkeebasubsModel')->getItem($sub->akeebasubs_level_id); // Merge the user objects $userdata = array_merge((array) $user, (array) $kuser->getData()); // Create and replace merge tags for subscriptions. Format [SUB:KEYNAME] if ($sub instanceof AkeebasubsTableSubscription) { $subData = (array) $sub->getData(); } else { $subData = (array) $sub; } foreach ($subData as $k => $v) { if (is_array($v) || is_object($v)) { continue; } if (substr($k, 0, 1) == '_') { continue; } if ($k == 'akeebasubs_subscription_id') { $k = 'id'; } $tag = '[SUB:' . strtoupper($k) . ']'; if (in_array($k, array('net_amount', 'gross_amount', 'tax_amount', 'prediscount_amount', 'discount_amount', 'affiliate_comission'))) { $v = sprintf('%.2f', $v); } $text = str_replace($tag, $v, $text); } // Create and replace merge tags for the subscription level. Format [LEVEL:KEYNAME] $levelData = (array) $level->getData(); foreach ($levelData as $k => $v) { if (is_array($v) || is_object($v)) { continue; } if (substr($k, 0, 1) == '_') { continue; } if ($k == 'akeebasubs_level_id') { $k = 'id'; } $tag = '[LEVEL:' . strtoupper($k) . ']'; $text = str_replace($tag, $v, $text); } // Create and replace merge tags for custom per-subscription data. Format [SUBCUSTOM:KEYNAME] if (array_key_exists('params', $subData)) { if (is_string($subData['params'])) { $custom = json_decode($subData['params'], true); } elseif (is_array($subData['params'])) { $custom = $subData['params']; } elseif (is_object($subData['params'])) { $custom = (array) $subData['params']; } else { $custom = array(); } // Extra check for subcustom params: if you save a subscription form the backend, // custom fields are inside an array named subcustom if (is_array($custom) && isset($custom['subcustom'])) { $custom = $custom['subcustom']; } if (!empty($custom)) { foreach ($custom as $k => $v) { if (is_object($v)) { continue; } if (substr($k, 0, 1) == '_') { continue; } $tag = '[SUBCUSTOM:' . strtoupper($k) . ']'; if (is_array($v)) { continue; } $text = str_replace($tag, $v, $text); } } } // Create and replace merge tags for user data. Format [USER:KEYNAME] foreach ($userdata as $k => $v) { if (is_object($v) || is_array($v)) { continue; } if (substr($k, 0, 1) == '_') { continue; } if ($k == 'akeebasubs_subscription_id') { $k = 'id'; } $tag = '[USER:'******']'; $text = str_replace($tag, $v, $text); } // Create and replace merge tags for custom fields data. Format [CUSTOM:KEYNAME] if (array_key_exists('params', $userdata)) { if (is_string($userdata['params'])) { $custom = json_decode($userdata['params']); } elseif (is_array($userdata['params'])) { $custom = $userdata['params']; } elseif (is_object($userdata['params'])) { $custom = (array) $userdata['params']; } else { $custom = array(); } if (!empty($custom)) { foreach ($custom as $k => $v) { if (substr($k, 0, 1) == '_') { continue; } $tag = '[CUSTOM:' . strtoupper($k) . ']'; if (is_array($v)) { $v = implode(', ', $v); } $text = str_replace($tag, $v, $text); } } } // Extra variables replacement // -- Coupon code $couponcode = ''; if ($sub->akeebasubs_coupon_id) { $couponData = F0FModel::getTmpInstance('Coupons', 'AkeebasubsModel')->savestate(0)->getItem($sub->akeebasubs_coupon_id); $couponcode = $couponData->coupon; } // -- Get the site name $config = JFactory::getConfig(); if (version_compare(JVERSION, '3.0', 'ge')) { $sitename = $config->get('sitename'); } else { $sitename = $config->getValue('config.sitename'); } // -- First/last name $fullname = $user->name; $nameParts = explode(' ', $fullname, 2); $firstname = array_shift($nameParts); $lastname = !empty($nameParts) ? array_shift($nameParts) : ''; // -- Get the subscription level $level = F0FModel::getTmpInstance('Levels', 'AkeebasubsModel')->setId($sub->akeebasubs_level_id)->getItem(); // -- Site URL list($isCli, $isAdmin) = F0FDispatcher::isCliAdmin(); if ($isCli) { JLoader::import('joomla.application.component.helper'); $baseURL = JComponentHelper::getParams('com_akeebasubs')->get('siteurl', 'http://www.example.com'); $temp = str_replace('http://', '', $baseURL); $temp = str_replace('https://', '', $temp); $parts = explode($temp, '/', 2); $subpathURL = count($parts) > 1 ? $parts[1] : ''; } else { $baseURL = JURI::base(); $subpathURL = JURI::base(true); } $baseURL = str_replace('/administrator', '', $baseURL); $subpathURL = str_replace('/administrator', '', $subpathURL); // -- My Subscriptions URL if ($isAdmin || $isCli) { $url = 'index.php?option=com_akeebasubs&view=subscriptions&layout=default'; } else { $url = str_replace('&', '&', JRoute::_('index.php?option=com_akeebasubs&view=subscriptions&layout=default')); } $url = ltrim($url, '/'); $subpathURL = ltrim($subpathURL, '/'); if (substr($url, 0, strlen($subpathURL) + 1) == "{$subpathURL}/") { $url = substr($url, strlen($subpathURL) + 1); } $mysubsurl = rtrim($baseURL, '/') . '/' . ltrim($url, '/'); $currency = ''; if (!class_exists('AkeebasubsHelperCparams')) { @(include_once JPATH_ADMINISTRATOR . '/components/com_akeebasubs/helpers/cparams.php'); } if (class_exists('AkeebasubsHelperCparams')) { $currency = AkeebasubsHelperCparams::getParam('currencysymbol', '€'); } // Dates JLoader::import('joomla.utilities.date'); $jFrom = new JDate($sub->publish_up); $jTo = new JDate($sub->publish_down); // Download ID $dlid = md5($user->id . $user->username . $user->password); // User's state, human readable $formatted_state = ''; $state = $kuser->state; if (!empty($state)) { if (!class_exists('AkeebasubsHelperSelect')) { require_once JPATH_ADMINISTRATOR . '/components/com_akeebasubs/helpers/select.php'; } $formatted_state = AkeebasubsHelperSelect::formatState($state); } // User's country, human readable $formatted_country = ''; $country = $kuser->country; if (!empty($country)) { if (!class_exists('AkeebasubsHelperSelect')) { require_once JPATH_ADMINISTRATOR . '/components/com_akeebasubs/helpers/select.php'; } $formatted_country = AkeebasubsHelperSelect::formatCountry($country); } // -- The actual replacement $extras = array_merge(array("\\n" => "\n", '[SITENAME]' => $sitename, '[SITEURL]' => $baseURL, '[FULLNAME]' => $fullname, '[FIRSTNAME]' => $firstname, '[LASTNAME]' => $lastname, '[USERNAME]' => $user->username, '[USEREMAIL]' => $user->email, '[LEVEL]' => $level->title, '[ENABLED]' => JText::_('COM_AKEEBASUBS_SUBSCRIPTION_COMMON_' . ($sub->enabled ? 'ENABLED' : 'DISABLED')), '[PAYSTATE]' => JText::_('COM_AKEEBASUBS_SUBSCRIPTION_STATE_' . $sub->state), '[PUBLISH_UP]' => $jFrom->format(JText::_('DATE_FORMAT_LC2'), true), '[PUBLISH_UP_EU]' => $jFrom->format('d/m/Y H:i:s', true), '[PUBLISH_UP_USA]' => $jFrom->format('m/d/Y h:i:s a', true), '[PUBLISH_UP_JAPAN]' => $jFrom->format('Y/m/d H:i:s', true), '[PUBLISH_DOWN]' => $jTo->format(JText::_('DATE_FORMAT_LC2'), true), '[PUBLISH_DOWN_EU]' => $jTo->format('d/m/Y H:i:s', true), '[PUBLISH_DOWN_USA]' => $jTo->format('m/d/Y h:i:s a', true), '[PUBLISH_DOWN_JAPAN]' => $jTo->format('Y/m/d H:i:s', true), '[MYSUBSURL]' => $mysubsurl, '[URL]' => $mysubsurl, '[CURRENCY]' => $currency, '[$]' => $currency, '[DLID]' => $dlid, '[COUPONCODE]' => $couponcode, '[USER:STATE_FORMATTED]' => $formatted_state, '[USER:COUNTRY_FORMATTED]' => $formatted_country, '[NAME]' => $firstname, '[STATE]' => JText::_('COM_AKEEBASUBS_SUBSCRIPTION_STATE_' . $sub->state), '[FROM]' => $jFrom->format(JText::_('DATE_FORMAT_LC2'), true), '[TO]' => $jTo->format(JText::_('DATE_FORMAT_LC2'), true)), $extras); foreach ($extras as $key => $value) { $text = str_replace($key, $value, $text); } return $text; }
/** * Sanitizes the VAT number and checks if it's valid for a specific country. * Ref: http://ec.europa.eu/taxation_customs/vies/faq.html#item_8 * * @param string $country Country code * @param string $vatnumber VAT number to check * * @return array The VAT number and the validity check */ private function _checkVATFormat($country, $vatnumber) { $ret = (object) array('prefix' => $country, 'vatnumber' => $vatnumber, 'valid' => true); $vatnumber = strtoupper($vatnumber); // All uppercase $vatnumber = preg_replace('/[^A-Z0-9]/', '', $vatnumber); // Remove spaces, dots and stuff $vat_country_prefix = $country; // Remove the country prefix, if it exists if ($vat_country_prefix == 'GR') { $vat_country_prefix = 'EL'; } if (substr($vatnumber, 0, strlen($vat_country_prefix)) == $vat_country_prefix) { $vatnumber = substr($vatnumber, 2); } $ret->prefix = $vat_country_prefix; $ret->vatnumber = $vatnumber; switch ($ret->prefix) { case 'AT': // AUSTRIA // VAT number is called: MWST. // Format: U + 8 numbers if (strlen($vatnumber) != 9) { $ret->valid = false; } if ($ret->valid) { if (substr($vatnumber, 0, 1) != 'U') { $ret->valid = false; } } if ($ret->valid) { $rest = substr($vatnumber, 1); if (preg_replace('/[0-9]/', '', $rest) != '') { $ret->valid = false; } } break; case 'BG': // BULGARIA // Format: 9 or 10 digits if (strlen($vatnumber) != 10 && strlen($vatnumber) != 9) { $ret->valid = false; } if ($ret->valid) { if (preg_replace('/[0-9]/', '', $vatnumber) != '') { $ret->valid = false; } } break; case 'CY': // CYPRUS // Format: 8 digits and a trailing letter if (strlen($vatnumber) != 9) { $ret->valid = false; } if ($ret->valid) { $check = substr($vatnumber, -1); if (preg_replace('/[0-9]/', '', $check) == '') { $ret->valid = false; } } if ($ret->valid) { $check = substr($vatnumber, 0, -1); if (preg_replace('/[0-9]/', '', $check) != '') { $ret->valid = false; } } break; case 'CZ': // CZECH REPUBLIC // Format: 8, 9 or 10 digits $len = strlen($vatnumber); if (!in_array($len, array(8, 9, 10))) { $ret->valid = false; } if ($ret->valid) { if (preg_replace('/[0-9]/', '', $vatnumber) != '') { $ret->valid = false; } } break; case 'BE': // BELGIUM // VAT number is called: BYW. // Format: 9 digits if (strlen($vatnumber) == 10 && substr($vatnumber, 0, 1) == '0') { if (preg_replace('/[0-9]/', '', $vatnumber) != '') { $ret->valid = false; } break; } break; case 'DE': // GERMANY // VAT number is called: MWST. // Format: 9 digits // GERMANY // VAT number is called: MWST. // Format: 9 digits case 'GR': case 'EL': // GREECE // VAT number is called: ΑΦΜ. // Format: 9 digits // GREECE // VAT number is called: ΑΦΜ. // Format: 9 digits case 'PT': // PORTUGAL // VAT number is called: IVA. // Format: 9 digits // PORTUGAL // VAT number is called: IVA. // Format: 9 digits case 'EE': // ESTONIA // Format: 9 digits if (strlen($vatnumber) != 9) { $ret->valid = false; } if ($ret->valid) { if (preg_replace('/[0-9]/', '', $vatnumber) != '') { $ret->valid = false; } } break; case 'DK': // DENMARK // VAT number is called: MOMS. // Format: 8 digits // DENMARK // VAT number is called: MOMS. // Format: 8 digits case 'FI': // FINLAND // VAT number is called: ALV. // Format: 8 digits // FINLAND // VAT number is called: ALV. // Format: 8 digits case 'LU': // LUXEMBURG // VAT number is called: TVA. // Format: 8 digits // LUXEMBURG // VAT number is called: TVA. // Format: 8 digits case 'HU': // HUNGARY // Format: 8 digits // HUNGARY // Format: 8 digits case 'MT': // MALTA // Format: 8 digits // MALTA // Format: 8 digits case 'SI': // SLOVENIA // Format: 8 digits if (strlen($vatnumber) != 8) { $ret->valid = false; } if ($ret->valid) { if (preg_replace('/[0-9]/', '', $vatnumber) != '') { $ret->valid = false; } } break; case 'FR': // FRANCE // VAT number is called: TVA. // Format: 11 digits; or 10 digits and a letter; or 9 digits and two letters // Eg: 12345678901 or X2345678901 or 1X345678901 or XX345678901 if (strlen($vatnumber) != 11) { $ret->valid = false; } if ($ret->valid) { // Letters O and I are forbidden if (strstr($vatnumber, 'O')) { $ret->valid = false; } if (strstr($vatnumber, 'I')) { $ret->valid = false; } } if ($ret->valid) { $valid = false; // Case I: no letters if (preg_replace('/[0-9]/', '', $vatnumber) == '') { $valid = true; } // Case II: first character is letter, rest is numbers if (!$valid) { if (preg_replace('/[0-9]/', '', substr($vatnumber, 1)) == '') { $valid = true; } } // Case III: second character is letter, rest is numbers if (!$valid) { $check = substr($vatnumber, 0, 1) . substr($vatnumber, 2); if (preg_replace('/[0-9]/', '', $check) == '') { $valid = true; } } // Case IV: first two characters are letters, rest is numbers if (!$valid) { $check = substr($vatnumber, 2); if (preg_replace('/[0-9]/', '', $check) == '') { $valid = true; } } $ret->valid = $valid; } break; case 'IE': // IRELAND // VAT number is called: VAT. // Format: seven digits and a letter; or six digits and two letters // Eg: 1234567X or 1X34567X if (strlen($vatnumber) != 8) { $ret->valid = false; } if ($ret->valid) { // The last position must be a letter $check = substr($vatnumber, -1); if (preg_replace('/[0-9]/', '', $check) == '') { $ret->valid = false; } } if ($ret->valid) { // Skip the second position (it's a number or letter, who cares), check the rest $check = substr($vatnumber, 0, 1) . substr($vatnumber, 2, -1); if (preg_replace('/[0-9]/', '', $check) != '') { $ret->valid = false; } } break; case 'IT': // ITALY // VAT number is called: IVA. // Format: 11 digits if (strlen($vatnumber) != 11) { $ret->valid = false; } if ($ret->valid) { if (preg_replace('/[0-9]/', '', $vatnumber) != '') { $ret->valid = false; } } break; case 'LT': // LITHUANIA // Format: 9 or 12 digits if (strlen($vatnumber) != 9 && strlen($vatnumber) != 12) { $ret->valid = false; } if ($ret->valid) { if (preg_replace('/[0-9]/', '', $vatnumber) != '') { $ret->valid = false; } } break; case 'LV': // LATVIA // Format: 11 digits if (strlen($vatnumber) != 11) { $ret->valid = false; } if ($ret->valid) { if (preg_replace('/[0-9]/', '', $vatnumber) != '') { $ret->valid = false; } } break; case 'PL': // POLAND // Format: 10 digits // POLAND // Format: 10 digits case 'SK': // SLOVAKIA // Format: 10 digits if (strlen($vatnumber) != 10) { $ret->valid = false; } if ($ret->valid) { if (preg_replace('/[0-9]/', '', $vatnumber) != '') { $ret->valid = false; } } break; case 'RO': // ROMANIA // Format: 2 to 10 digits $len = strlen($vatnumber); if ($len < 2 || $len > 10) { $ret->valid = false; } if ($ret->valid) { if (preg_replace('/[0-9]/', '', $vatnumber) != '') { $ret->valid = false; } } break; case 'NL': // NETHERLANDS // VAT number is called: BTW. // Format: 12 characters long, first 9 characters are numbers, last three characters are B01 to B99 if (strlen($vatnumber) != 12) { $ret->valid = false; } if ($ret->valid) { if (substr($vatnumber, 9, 1) != 'B') { $ret->valid = false; } } if ($ret->valid) { $check = substr($vatnumber, 0, 9) . substr($vatnumber, 11); if (preg_replace('/[0-9]/', '', $check) == '') { $valid = true; } } break; case 'ES': // SPAIN // VAT number is called: IVA. // Format: Eight digits and one letter; or seven digits and two letters // E.g.: X12345678 or 12345678X or X1234567X if (strlen($vatnumber) != 9) { $ret->valid = false; } if ($ret->valid) { // If first is number last must be letter $check = substr($vatnumber, 0, 1); if (preg_replace('/[0-9]/', '', $check) == '') { $check = substr($vatnumber, 0); if (preg_replace('/[0-9]/', '', $check) == '') { $ret->valid = false; } } } if ($ret->valid) { // If first is not a number, the last can be anything; just check the middle $check = substr($vatnumber, 1, -1); if (preg_replace('/[0-9]/', '', $check) != '') { $ret->valid = false; } } break; case 'SE': // SWEDEN // VAT number is called: MOMS. // Format: Twelve digits, last two must be 01 if (strlen($vatnumber) != 12) { $ret->valid = false; } if ($ret->valid) { if (substr($vatnumber, -2) != '01') { $ret->valid = false; } } if ($ret->valid) { if (preg_replace('/[0-9]/', '', $vatnumber) != '') { $ret->valid = false; } } break; case 'GB': // UNITED KINGDOM // VAT number is called: VAT. // Format: Nine or twelve digits; or 5 characters (alphanumeric) if (strlen($vatnumber) == 5) { break; } if (strlen($vatnumber) != 9 && strlen($vatnumber) != 12) { $ret->valid = false; } if ($ret->valid) { if (preg_replace('/[0-9]/', '', $vatnumber) != '') { $ret->valid = false; } } break; case 'HR': // CROATIA // VAT number is called: PDV. // Format: 11 digits if (strlen($vatnumber) != 11) { $ret->valid = false; } if ($ret->valid) { if (preg_replace('/[0-9]/', '', $vatnumber) != '') { $ret->valid = false; } } break; default: $allowNonEUVAT = AkeebasubsHelperCparams::getParam('noneuvat', 0); $ret->valid = $allowNonEUVAT ? true : false; break; } return $ret; }