<?php
use Joomla\CMS\Language\Multilanguage;
use Joomla\CMS\Factory;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Table\Table;
/**
 * @version        4.3
 * @package        Joomla
 * @subpackage     Joom Donation
 * @author         Tuan Pham Ngoc
 * @copyright      Copyright (C) 2009 - 2023 Ossolution Team
 * @license        GNU/GPL, see LICENSE.php
 */
defined('_JEXEC') or die;

class os_paypal extends OSFPayment
{
	/**
	 * Constructor functions, init some parameter
	 *
	 * @param JRegistry $params
	 * @param array     $config
	 */
	public function __construct($params, $config = array())
	{
		parent::__construct($params, $config);
		$this->mode = $params->get('paypal_mode');
		if ($this->mode)
		{
			$this->url = 'https://www.paypal.com/cgi-bin/webscr';
		}
		else
		{
			$this->url = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
		}
		$this->setParameter('business', $params->get('paypal_id'));
		$this->setParameter('rm', 2);
		$payment_type = $params->get('payment_type', 0);
		$this->setParameter('cmd', '_donations');
		$this->setParameter('no_shipping', 1);
		$this->setParameter('no_note', 1);
		$locale = $params->get('paypal_locale','');

		if ($locale == '')
		{
			if (Multilanguage::isEnabled())
			{
				$locale = Factory::getLanguage()->getTag();
				$locale = str_replace("-","_",$locale);
			}
			else
			{
				$locale = 'en_US';
			}
		}
		
		$this->setParameter('lc', $locale);
		$this->setParameter('charset', 'utf-8');
	}


	/**
	 * Check to see whether this payment gateway support recurring payment
	 *
	 */
	public function getEnableRecurring()
	{
		return 1;
	}

	/**
	 * Process Payment
	 *
	 * @param object $row
	 * @param array  $data
	 */
	public function processPayment($row, $data)
	{
		$db = Factory::getDbo();
		$Itemid  = Factory::getApplication()->input->getInt('Itemid', 0);
		$siteUrl = Uri::base();
		$tag   = Factory::getLanguage()->getTag();
		$query = $db->getQuery(true);
	    $query->select('`sef`')
		->from('#__languages')
		->where('published = 1')
		->where('lang_code=' . $db->quote($tag));
	    $db->setQuery($query, 0, 1);
	    $langLink = '&lang=' . $db->loadResult();
		// Use Campaign PayPal account if it is setup in the campaign
		if ($row->campaign_id)
		{
			$query = $db->getQuery(true);
			$query->select('paypal_id')
				->from('#__jd_campaigns')
				->where('id=' . $row->campaign_id);
			$db->setQuery($query);
			if ($paypalId = $db->loadResult())
			{
				$this->setParameter('business', $paypalId);
			}
		}

		//checking currency
        $availableCurrenciesArr = array('AUD', 'CAD', 'EUR', 'GBP', 'JPY', 'USD', 'NZD', 'CHF', 'HKD', 'SGD', 'SEK', 'DKK', 'PLN', 'NOK', 'HUF', 'CZK', 'ILS', 'MXN', 'BRL', 'MYR', 'PHP', 'TWD', 'THB', 'TRY', 'RUB');
        if (!in_array($data['currency'], $availableCurrenciesArr))
        {
            //convert to USD
            //$exchange = DonationHelper::get_conversion($data['currency'], 'USD');
            //$data['gateway_amount'] = $data['gateway_amount']*$exchange;
            //$data['currency'] = 'USD';
            //store into database
            //$db->setQuery("Update #__jd_donors set currency_code = '".$data['currency']."',amount_converted = '".$data['gateway_amount']."' where id = '$row->id'");
            //$db->execute();
        }else{
            //$db->setQuery("Update #__jd_donors set currency_code = '".$data['currency']."',amount_converted = '".$data['gateway_amount']."' where id = '$row->id'");
            //$db->execute();
        }

		if ($row->state)
        {
            $state   = DonationHelper::getStateCode($row->country, $row->state);
        }
        else
        {
            $state = '';
        }

		$this->setParameter('currency_code', $data['currency']);
		$this->setParameter('item_name', $data['item_name']);
		$this->setParameter('amount', $data['gateway_amount']);
		$this->setParameter('custom', $row->id);
		$this->setParameter('return', Uri::getInstance()->toString(array('scheme', 'user', 'pass', 'host')) . Route::_(DonationHelperRoute::getDonationCompleteRoute($row->id, $row->campaign_id, $Itemid), false));

		$this->setParameter('cancel_return', $siteUrl . 'index.php?option=com_jdonation&view=cancel' . ($row->campaign_id > 0 ? '&campaign_id=' . $row->campaign_id : '') . '&Itemid=' . $Itemid);
		$this->setParameter('notify_url', $siteUrl . 'index.php?option=com_jdonation&task=payment_confirm&payment_method=os_paypal'.$langLink);
		$this->setParameter('address1', $row->address);
		$this->setParameter('address2', $row->address2);
		$this->setParameter('city', $row->city);
		$this->setParameter('country', $data['country']);
		$this->setParameter('first_name', $row->first_name);
		$this->setParameter('last_name', $row->last_name);
		$this->setParameter('state', $state);
		$this->setParameter('zip', $row->zip);
		$this->setParameter('email', $row->email);
		//$this->setParameter('campaign_id',$row->campaign_id);
		$this->setParameter('language',$row->language);
		$this->renderRedirectForm();
	}

	/**
	 * Process recurring payment
	 *
	 * @param object $row
	 * @param array  $data
	 */
	public function processRecurringPayment($row, $data)
	{
		$Itemid  = Factory::getApplication()->input->getInt('Itemid', 0);
		$siteUrl = Uri::base();

		// Use Campaign PayPal account if it is setup in the campaign
		if ($row->campaign_id)
		{
			$db    = Factory::getDbo();
			$query = $db->getQuery(true);
			$query->select('paypal_id')
				->from('#__jd_campaigns')
				->where('id=' . $row->campaign_id);
			$db->setQuery($query);
			if ($paypalId = $db->loadResult())
			{
				$this->setParameter('business', $paypalId);
			}
		}

		$this->setParameter('item_name', $data['item_name']);
		$this->setParameter('currency_code', $data['currency']);
		$this->setParameter('custom', $row->id);
		$this->setParameter('return', Uri::getInstance()->toString(array('scheme', 'user', 'pass', 'host')) . Route::_(DonationHelperRoute::getDonationCompleteRoute($row->id, $row->campaign_id, $Itemid), false));
		$this->setParameter('cancel_return', $siteUrl . 'index.php?option=com_jdonation&view=cancel' . ($row->campaign_id > 0 ? '&campaign_id=' . $row->campaign_id : '') . '&Itemid=' . $Itemid);
		$this->setParameter('notify_url', $siteUrl . 'index.php?option=com_jdonation&task=recurring_donation_confirm&payment_method=os_paypal');
		$this->setParameter('cmd', '_xclick-subscriptions');
		$this->setParameter('src', 1);
		$this->setParameter('sra', 1);
		$this->setParameter('a3', round($data['gateway_amount'], 2));
		$this->setParameter('address1', $row->address);
		$this->setParameter('address2', $row->address2);
		$this->setParameter('city', $row->city);
		$this->setParameter('email', $row->email);
		$this->setParameter('country', $data['country']);
		$this->setParameter('first_name', $row->first_name);
		$this->setParameter('last_name', $row->last_name);
		$this->setParameter('state', $row->state);
		$this->setParameter('zip', $row->zip);
		switch ($row->r_frequency)
		{
			case 'd':
				$p3 = 1;
				$t3 = 'D';
				break;
			case 'w' :
				$p3 = 1;
				$t3 = 'W';
				break;
			case 'b' :
				$p3 = 2;
				$t3 = 'W';
				break;
			case 'm' :
				$p3 = 1;
				$t3 = 'M';
				break;
			case 'q' :
				$p3 = 3;
				$t3 = 'M';
				break;
			case 's' :
				$p3 = 6;
				$t3 = 'M';
				break;
			case 'a' :
				$p3 = 1;
				$t3 = 'Y';
				break;
		}
		$this->setParameter('p3', $p3);
		$this->setParameter('t3', $t3);
		$this->setParameter('lc', 'US');
		if ($row->r_times > 1)
		{
			$this->setParameter('srt', $row->r_times);
		}
		$this->renderRedirectForm();
	}

	/**
	 * Verify payment
	 *
	 * @return bool
	 */
	public function verifyPayment()
	{
		$db = Factory::getDbo();
		$ret = $this->validate();
		if ($ret)
		{
			$id            = $this->notificationData['custom'];
			$transactionId = $this->notificationData['txn_id'];
			$amount        = $this->notificationData['mc_gross'];
			$currency      = $this->notificationData['mc_currency'];
			$paymentFee    = $this->notificationData['mc_fee'];
			$receiverEmail = $this->notificationData['receiver_email'];
			$receiverId    = $this->notificationData['receiver_id'];

			if ($amount < 0)
			{
				return false;
			}
			$row = Table::getInstance('jdonation', 'Table');
			$row->load($id);
			if (!$row->id)
			{
				return false;
			}
			/*
			$paypalId = $params->get('paypal_id');
			if ($row->campaign_id)
			{
				$db    = JFactory::getDbo();
				$query = $db->getQuery(true);
				$query->select('paypal_id')
					->from('#__jd_campaigns')
					->where('id=' . $row->campaign_id);
				$db->setQuery($query);
				$campaign_paypalId = $db->loadResult();
				if($campaign_paypalId != ""){
					$paypalId = $campaign_paypalId;
				}
			}
			if ($receiverEmail != $payPalId && $receiverEmail != $receiverId)
			{
				return false;
			}
			*/

			//verify amount and currency
            $org_currency       = $row->currency_code;
            $amount_converted   = $row->amount;
            if((strtoupper($currency) == "") || (strtoupper($currency) != strtoupper($org_currency))){
                return false;
            }

            if(floatval($amount) < $amount_converted){
                return false;
            }

			if ($row->published == 0)
			{
				//$row->payment_fee = $paymentFee;
				$this->onPaymentSuccess($row, $transactionId);
			}
		}
	}

	/**
	 * Verify recurring payment
	 *
	 */
	public function verifyRecurringPayment()
	{

		$ret = $this->validate();
		if ($ret)
		{
			$id            = $this->notificationData['custom'];
			$transactionId = $this->notificationData['txn_id'];
			$amount        = $this->notificationData['mc_gross'];
			$paymentFee    = $this->notificationData['mc_fee'];
			$txnType       = $this->notificationData['txn_type'];
			$subscrId      = $this->notificationData['subscr_id'];
			if ($amount < 0)
			{
				return false;
			}
			$row = Table::getInstance('jdonation', 'Table');
			switch ($txnType)
			{
				case 'subscr_signup' :
					$row->load($id);

					if (!$row->id)
					{
						return false;
					}

					if ($row->published == 0)
					{
						$row->subscr_id = $subscrId;
						if($transactionId == "")
						{
							$transactionId = $subscrId;
						}
						$this->onPaymentSuccess($row, $transactionId);
					}
					break;
				case 'subscr_payment' :
					$row->load($id);
					if (!$row->id)
					{
						return false;
					}
					$row->payment_made = $row->payment_made + 1;
					$row->amount       = $amount;
					//$row->payment_fee  = $paymentFee;
					$row->store();

					//Create a new one time donation record and send emails when a recurring payment happens
					if ($row->payment_made > 1)
					{
						$row                = clone $row;
						$row->id            = 0;
						$row->donation_type = 'I';
						$row->created_date  = gmdate('Y-m-d H:i:s');
						$row->invoice_number = (int) $row->invoice_number + 1;
						$this->onPaymentSuccess($row, $transactionId);
					}
					break;
                case 'subscr_cancel':
                    DonationHelper::cancelRecurringDonation($id);
                    break;
			}
		}
	}

	/**
	 * Get list of supported currencies
	 *
	 * @return array
	 */
	public function getSupportedCurrencies()
	{
		return array(
			'AUD',
			'BRL',
			'CAD',
			'CZK',
			'DKK',
			'EUR',
			'HKD',
			'HUF',
			'ILS',
			'JPY',
			'MYR',
			'MXN',
			'NOK',
			'NZD',
			'PHP',
			'PLN',
			'GBP',
			'RUB',
			'SGD',
			'SEK',
			'CHF',
			'TWD',
			'THB',
			'TRY',
			'USD'
		);
	}

	/**
	 * Validate the post data from paypal to our server
	 *
	 * @return string
	 */
	protected function validate()
	{
		if (function_exists('curl_init'))
		{
			return $this->validateIPN();
		}
		$this->notificationData = $_POST;

		$hostname = $this->mode ? 'www.paypal.com' : 'www.sandbox.paypal.com';
		$url      = 'ssl://' . $hostname;
		$port     = 443;
		$req      = 'cmd=_notify-validate';

		foreach ($_POST as $key => $value)
		{
			$value = urlencode(stripslashes($value));
			$req .= "&$key=$value";
		}

		$header = '';
		$header .= "POST /cgi-bin/webscr HTTP/1.1\r\n";
		$header .= "Host: $hostname:$port\r\n";
		$header .= "Content-Type: application/x-www-form-urlencoded\r\n";
		$header .= "Content-Length: " . strlen($req) . "\r\n";
		$header .= "User-Agent: Events Booking\r\n";
		$header .= "Connection: Close\r\n\r\n";

		$errNum   = '';
		$errStr   = '';
		$response = '';
		$fp       = fsockopen($url, $port, $errNum, $errStr, 30);

		if (!$fp)
		{
			$response = 'Could not open SSL connection to ' . $hostname . ':' . $port;
			$this->logGatewayData($response);

			return false;
		}

		fputs($fp, $header . $req);
		while (!feof($fp))
		{
			$response .= fgets($fp, 1024);
		}
		fclose($fp);


		$this->logGatewayData($response);

		if (!$this->mode || stristr($response, "VERIFIED"))
		{
			return true;
		}

		return false;
	}
	/**
	 * Validate PayPal IPN using PayPal library
	 *
	 * @return bool
	 */
	protected function validateIPN()
	{
		JLoader::register('PaypalIPN', JPATH_ROOT . '/components/com_jdonation/payments/paypal/PayPalIPN.php');

		$ipn = new PaypalIPN;
		// Use sandbox URL if test mode is configured
		if (!$this->mode)
		{
			$ipn->useSandbox();
		}
		// Disable use custom certs
		$ipn->usePHPCerts();

		$this->notificationData = $_POST;
		try
		{
			$valid = $ipn->verifyIPN();
			$this->logGatewayData($ipn->getResponse());
			if (!$this->mode || $valid)
			{
				return true;
			}
			return false;
		}
		catch (Exception $e)
		{
			$this->logGatewayData($e->getMessage());
			return false;
		}
	}

    /**
     * Cancel recurring subscription
     *
     * @param $row
     *
     * @return bool
     * @throws Exception
     *
     * @since 1.0
     */
    public function cancelDonation($row)
    {
        list($apiUrl, $apiUser, $apiPassword, $apiSignature) = $this->getNvpApiParameters();

        if (!$apiUser || !$apiPassword || !$apiSignature)
        {
            Factory::getApplication()->enqueueMessage('Cancel Recurring Subscription is not supported for the payment method you are using', 'error');

            return false;
        }

        $curl = curl_init();
        curl_setopt($curl, CURLOPT_VERBOSE, 1);
        curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($curl, CURLOPT_POST, 1);
        curl_setopt($curl, CURLOPT_URL, $apiUrl);
        curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query([
            'USER'      => $apiUser,
            'PWD'       => $apiPassword,
            'SIGNATURE' => $apiSignature,
            'VERSION'   => '108',
            'METHOD'    => 'ManageRecurringPaymentsProfileStatus',
            'PROFILEID' => $row->subscr_id,
            'ACTION'    => 'Cancel',
        ]));

        $response = curl_exec($curl);
        curl_close($curl);

        $nvp = $this->deformatNVP($response);

        if ($nvp['ACK'] == 'Success')
        {
            return true;
        }
        else
        {
            Factory::getApplication()->enqueueMessage($nvp['L_LONGMESSAGE0'], 'error');

            return false;
        }
    }

    /**
     * Get NvpApi Parameters
     *
     * @return array
     */
    private function getNvpApiParameters()
    {
        if ($this->mode)
        {
            $apiUrl       = 'https://api-3t.paypal.com/nvp';
            $apiUser      = $this->params->get('paypal_api_user');
            $apiPassword  = $this->params->get('paypal_api_password');
            $apiSignature = $this->params->get('paypal_api_signature');
        }
        else
        {
            $apiUrl       = 'https://api-3t.sandbox.paypal.com/nvp';
            $apiUser      = $this->params->get('paypal_api_user_sandbox');
            $apiPassword  = $this->params->get('paypal_api_password_sandbox');
            $apiSignature = $this->params->get('paypal_api_signature_sandbox');
        }

        return [$apiUrl, $apiUser, $apiPassword, $apiSignature];
    }

    /**
     * Extract response from PayPal into array
     *
     * @param $response
     *
     * @return array
     */
    private function deformatNVP($response)
    {
        $nvp = [];

        parse_str(urldecode($response), $nvp);

        return $nvp;
    }

    /**
     * Method to check if API Credentials is entered into the payment plugin parameters
     */
    private function isApiCredentialsEntered()
    {
        list($apiUrl, $apiUser, $apiPassword, $apiSignature) = $this->getNvpApiParameters();

        return $apiUser && $apiPassword && $apiSignature;
    }

    /**
     * Method to check if cancel recurring subscription is supported
     *
     * @return bool
     */
    public function supportCancelRecurringSubscription()
    {
        return $this->isApiCredentialsEntered();
    }
}
