<?php
/**
 * @package        RSForm! Pro
 * @copyright  (c) 2007 - 2016 RSJoomla!
 * @link           https://www.rsjoomla.com
 * @license        GNU General Public License http://www.gnu.org/licenses/gpl-3.0.en.html
 */

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\Utilities\IpHelper;
use Joomla\CMS\Http\HttpFactory;

/**
 * Class RSFPPayFast
 */
class RSFPPayFast
{
	private $merchantId;
	private $merchantKey;
	private $passPhrase;

	public $args = array();
	public $code;
	public $submissionId;
	public $host = '%s.payfast.co.za';

	public function __construct()
	{
		$this->host = sprintf($this->host, RSFormProHelper::getConfig('payfast.test') ? 'sandbox' : 'www');
		$this->merchantId = trim(RSFormProHelper::getConfig('payfast.merchantid'));
		$this->merchantKey = trim(RSFormProHelper::getConfig('payfast.merchantkey'));
		$this->passPhrase = trim(RSFormProHelper::getConfig('payfast.passphrase'));

		$this->args = array(
			'merchant_id' => $this->merchantId,
			'merchant_key' => $this->merchantKey,
			'return_url' => '',
			'cancel_url' => '',
			'notify_url' => '',
			// Buyer details
			'name_first' => '',
			'name_last' => '',
			'email_address' => '',
			// Transaction details
			'm_payment_id' => '',
			'amount' => '',
			'item_name' => '',
			'item_description' => '',
			'custom_int1' => '',
			'custom_str1' => ''
		);
	}

	public static function getInstance()
	{
		static $inst;

		if (!$inst)
		{
			$inst = new RSFPPayFast;
		}

		return $inst;
	}
	
	public function redirect()
	{
		$data = $this->args;

		if (empty($data['m_payment_id']))
		{
			$data['m_payment_id'] = $this->code;
		}

		if (empty($data['custom_int1']))
		{
			$data['custom_int1'] = $this->submissionId;
		}

		if (empty($data['custom_str1']))
		{
			$data['custom_str1'] = $this->code;
		}

		// Create parameter string
		$pfOutput = '';
		foreach ($data as $key => $val)
		{
			if (!empty($val))
			{
				$pfOutput .= $key .'='. urlencode(trim($val)) .'&';
			}
		}

		// Remove last ampersand
		$getString = substr($pfOutput, 0, -1);

		// Add the passphrase if there is one set on the account
		if ($this->passPhrase)
		{
			$getString .= '&passphrase='. urlencode($this->passPhrase);
		}

		$data['signature'] = md5($getString);
		
		Factory::getApplication()->redirect('https://' . $this->host . '/eng/process?' . http_build_query($data));
	}

	public function itn()
	{
		// Posted variables from ITN
		$pfData = $_POST;

		// Strip any slashes in data
		foreach ($pfData as $key => $val)
		{
			$pfData[$key] = stripslashes($val);
		}

		// Construct variables
		$pfParamString = '';
		foreach ($pfData as $key => $val)
		{
			if ($key != 'signature')
			{
				$pfParamString .= $key .'='. urlencode($val) .'&';
			}
		}

		// Remove the last '&' from the parameter string
		$pfParamString = substr( $pfParamString, 0, -1 );
		$pfTempParamString = $pfParamString;

		// Passphrase stored in website database
		$passPhrase = RSFormProHelper::getConfig('payfast.passphrase');

		if ($passPhrase)
		{
			$pfTempParamString .= '&passphrase=' . urlencode($passPhrase);
		}

		$signature = md5($pfTempParamString);

		if (empty($pfData['signature']))
		{
			throw new Exception('No signature found in ITN data.');
		}

		if ($signature != $pfData['signature'])
		{
			throw new Exception(sprintf('Wrong signature. Received %s, expected %s', $pfData['signature'], $signature));
		}

		// Variable initialization
		$validHosts = array(
			'www.payfast.co.za',
			'sandbox.payfast.co.za',
			'w1w.payfast.co.za',
			'w2w.payfast.co.za',
		);

		$validIps = array();

		foreach ($validHosts as $pfHostname)
		{
			$ips = gethostbynamel($pfHostname);
			if ($ips !== false)
			{
				$validIps = array_merge($validIps, $ips);
			}
		}

		// Remove duplicates
		$validIps = array_unique($validIps);

		$ip = IpHelper::getIp();

		if (!in_array($ip, $validIps))
		{
			throw new Exception(sprintf('Source IP %s not valid.', $ip));
		}

		// Let's get the submission
		$input  = Factory::getApplication()->input;
		$db     = Factory::getDbo();
		$query  = $db->getQuery(true);
		$code 	= $input->getCmd('code');
		$formId = $input->getInt('formId');

		$query->select($db->qn('SubmissionId'))
			->from($db->qn('#__rsform_submissions', 's'))
			->where($db->qn('s.FormId') . ' = ' . $db->q($formId))
			->where('MD5(CONCAT(' . $db->qn('s.SubmissionId') . ',' . $db->qn('s.DateSubmitted') . ')) = ' . $db->q($code));
		$db->setQuery($query);

		$SubmissionId = $db->loadResult();

		if (empty($SubmissionId))
		{
			throw new Exception('SubmissionId is missing from request (custom_int1).');
		}

		$query->clear();
		$query->select($db->qn('FieldValue'))
			->from($db->qn('#__rsform_submission_values'))
			->where($db->qn('SubmissionId') . '=' . $db->q($SubmissionId))
			->where($db->qn('FieldName') . '=' . $db->q('rsfp_Total'));
		$db->setQuery($query);
		$cartTotal = $db->loadResult(); // This amount needs to be sourced from your application
		if (floatval($cartTotal) > floatval($pfData['amount_gross']))
		{
			throw new Exception(sprintf('Amounts mismatch. Expected %s, received %s', $cartTotal, $pfData['amount_gross']));
		}

		$http = HttpFactory::getHttp();
		$response = $http->post('https://' . $this->host . '/eng/query/validate', $pfParamString, array(), 5);

		if (trim($response->body) !== 'VALID')
		{
			throw new Exception(sprintf('Expected VALID, got %s', $response->body));
		}

		if ($pfData ['payment_status'] == 'COMPLETE')
		{
			// If complete, update your application
			$query->clear();
			$query->update($db->qn('#__rsform_submission_values'))
				->set($db->qn('FieldValue') . ' = ' . $db->q(1))
				->where($db->qn('FieldName') . ' = ' . $db->q('_STATUS'))
				->where($db->qn('SubmissionId') . ' = ' . $db->q($SubmissionId));
			$db->setQuery($query);
			$db->execute();

			if (!empty($pfData['pf_payment_id']))
			{
				$query->clear();
				$query->update($db->qn('#__rsform_submission_values'))
					->set($db->qn('FieldValue') . ' = ' . $db->q($pfData['pf_payment_id']))
					->where($db->qn('FieldName') . ' = ' . $db->q('_TRANSACTION_ID'))
					->where($db->qn('SubmissionId') . ' = ' . $db->q($SubmissionId));
				$db->setQuery($query);
				$db->execute();
			}

			Factory::getApplication()->triggerEvent('onRsformAfterConfirmPayment', array($SubmissionId));
		}
	}
}