<?php
/**
 * @package    JBusinessDirectory
 *
 * @author     CMSJunkie http://www.cmsjunkie.com
 * @copyright  Copyright (C) 2007 - 2021 CMSJunkie. All rights reserved.
 * @license    https://www.gnu.org/licenses/agpl-3.0.en.html
 */

defined('_JEXEC') or die('Restricted access');

class Paypal extends IPaymentProcessor {
	public $type;
	public $name;

	private $paypal_email;
	private $mode;
	private $paymentUrlTest = 'https://www.sandbox.paypal.com/cgi-bin/webscr';
	private $paymentUrl = 'https://www.paypal.com/cgi-bin/webscr';

	private $notifyUrl;
	private $returnUrl;
	private $cancelUrl;

	private $currencyCode;
	private $amount;
	private $itemNumber;
	private $itemName;

	public $recurring = false;

	public function initialize($data) {
		$this->type         = $data->type;
		$this->name         = $data->name;
		$this->mode         = $data->mode;
		if ($this->mode=='test') {
			$this->paypal_email = $data->test_paypal_email;
		} else {
			$this->paypal_email = $data->paypal_email;
		}

		$this->log = Logger::getInstance();
	}

	public function getPaymentGatewayUrl() {
		if ($this->mode == "test") {
			return $this->paymentUrlTest;
		} else {
			return $this->paymentUrl;
		}
	}

	public function getPaypalIpnUrl() {
	  	if ($this->mode == "test") {
			return 'https://ipnpb.sandbox.paypal.com/cgi-bin/webscr';
		} else {
			return 'https://ipnpb.paypal.com/cgi-bin/webscr';
		}
	}

	public function getPaymentProcessorHtml($data = null) {
		$html = "<ul id=\"payment_form_$this->type\" style=\"display:none\" class=\"form-list\">
		<li>
		    " . JText::_('LNG_PROCESSOR_PAYPAL_INFO', true) . "
		    </li>
		</ul>";

		return $html;
	}

	public function getHtmlFields() {
		$html = '';
		$html .= sprintf('<input type="hidden" name="cmd" id="cmd" value="_xclick"/>');
		$html .= sprintf('<input type="hidden" name="charset" id="charset" value="utf-8">');
		$html .= sprintf('<input type="hidden" name="item_name" id="item_name" value="%s">', $this->itemName);
		$html .= sprintf('<input type="hidden" name="item_number" id="item_number" value="%s">', $this->itemNumber);
		$html .= sprintf('<input type="hidden" name="no_shipping" id="no_shipping" value="1">');
		$html .= sprintf('<input type="hidden" name="business" id="business" value="%s">', $this->paypal_email);
		$html .= sprintf('<input type="hidden" name="cbt" id="cbt" value="%s">', JText::_('LNG_FINALIZE_PAYMENT', true));

		$html .= sprintf('<input type="hidden" name="notify_url" id="notify_url" value="%s">', $this->notifyUrl);
		$html .= sprintf('<input type="hidden" name="return" id="return" value="%s">', $this->returnUrl);
		$html .= sprintf('<input type="hidden" name="cancel_return" id="cancel_return" value="%s">', $this->cancelUrl);

		$html .= sprintf('<input type="hidden" name="amount" value="%.2f" />', $this->amount);
		$html .= sprintf('<input type="hidden" name="currency_code" value="%s" />', $this->currencyCode);
		$html .= sprintf('<input type="hidden" name="custom" value="%s" />', $this->itemNumber);

		return $html;
	}

	public function processTransaction($data, $controller = "payment") {
		$this->returnUrl = JBusinessUtil::getWebsiteURL().JRoute::_("index.php?option=com_jbusinessdirectory&task=$controller.processResponse&processor=paypal&orderId=$data->id", false, 0);
		$this->notifyUrl = JBusinessUtil::getWebsiteURL().JRoute::_("index.php?option=com_jbusinessdirectory&task=$controller.processAutomaticResponse&processor=paypal", false, 0);
		$this->cancelUrl = JBusinessUtil::getWebsiteURL().JRoute::_("index.php?option=com_jbusinessdirectory&task=$controller.processCancelResponse", false, 0);
		
		$this->amount       = $data->amount;
		$this->itemName     = $data->service . " " . $data->description;
		$this->itemNumber   = $data->id;
		$this->currencyCode = $data->currency;

		$result                 = new stdClass();
		$result->transaction_id = 0;
		$result->amount         = $data->amount;
		$result->payment_date   = date("Y-m-d");
		$result->response_code  = 0;
		$result->order_id       = $data->id;
		$result->currency       = $data->currency;
		$result->processor_type = $this->type;
		$result->status         = PAYMENT_REDIRECT;
		$result->payment_status = PAYMENT_STATUS_PENDING;

		return $result;
	}

	public function processResponse($data) {
		$result = new stdClass();

		if (isset($data['item_number'])) {
			$result->transaction_id   = $data["txn_id"];
			$result->amount           = $data["mc_gross"];
			$result->transactionTime  = date("Y-m-d", strtotime($data["payment_date"]));
			$result->response_code    = $data["payment_status"];
			$result->response_message = "";
			$result->order_id         = $data["item_number"];
		} else {
			$result->order_id = $data["orderId"];
		}

		$result->currency       = $data["mc_currency"];
		$result->processor_type = $this->type;
		$result->payment_method = "paypal";

		if (isset($data['item_number']) && $this->sendAck()) {
			if (strcmp($data["payment_status"], "Completed") == 0) {
				$result->status         = PAYMENT_SUCCESS;
				$result->payment_status = PAYMENT_STATUS_PAID;
			} elseif (
				strcmp($data["payment_status"], "Declined") == 0 ||
				strcmp($data["payment_status"], "Expired") == 0 ||
				strcmp($data["payment_status"], "Failed") == 0) {
				$result->status         = PAYMENT_ERROR;
				$result->payment_status = PAYMENT_STATUS_FAILURE;
			} elseif (
				strcmp($data["payment_status"], "Pending") == 0) {
				$result->status         = PAYMENT_WAITING;
				$result->payment_status = PAYMENT_STATUS_WAITING;
			}
		} else {
			$result->status         = PAYMENT_ERROR;
			$result->payment_status = PAYMENT_STATUS_FAILURE;
		}

		if (!isset($data['item_number'])) {
			$result->status         = PAYMENT_WAITING;
			$result->payment_status = PAYMENT_STATUS_PENDING;
		}

		return $result;
	}

	public function sendAck() {

		$loop = true;
		$maxLoops = 15;
		$index = 0;
		
		while($loop || $index < $maxLoops){

			// Reading POSTed data directly from $_POST causes serialization issues with array data in the POST.
			// Instead, read raw POST data from the input stream.
			$raw_post_data  = file_get_contents('php://input');
			$raw_post_array = explode('&', $raw_post_data);

			$this->log->LogDebug(serialize($raw_post_data));
			$this->log->LogDebug(serialize($raw_post_array));

			$myPost         = array();
			foreach ($raw_post_array as $keyval) {
				$keyval = explode('=', $keyval);
				if (count($keyval) == 2) {
					$myPost[$keyval[0]] = urldecode($keyval[1]);
				}
			}
			// read the IPN message sent from PayPal and prepend 'cmd=_notify-validate'
			$req = 'cmd=_notify-validate';
			if (function_exists('get_magic_quotes_gpc')) {
				$get_magic_quotes_exists = true;
			}
			foreach ($myPost as $key => $value) {
				if ($get_magic_quotes_exists == true && get_magic_quotes_gpc() == 1) {
					$value = urlencode(stripslashes($value));
				} else {
					$value = urlencode($value);
				}
				$req .= "&$key=$value";
			}

			$this->log->LogDebug("Paypal IPN request");
			$this->log->LogDebug(serialize($req));

			// POST IPN data back to PayPal to validate
			$ch = curl_init($this->getPaypalIpnUrl());
			curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
			curl_setopt($ch, CURLOPT_POST, 1);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
			curl_setopt($ch, CURLOPT_POSTFIELDS, $req);
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 1);
			curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
			curl_setopt($ch, CURLOPT_FORBID_REUSE, 1);
			curl_setopt($ch, CURLOPT_HTTPHEADER, array('Connection: Close'));

			if (!($res = curl_exec($ch))) {
				$this->log->LogDebug("Got " . curl_error($ch) . " when processing IPN data");
				curl_close($ch);
				exit;
			}
			curl_close($ch);

			$this->log->LogDebug("Paypal IPN result");
			$this->log->LogDebug(serialize($res));

			if (strcmp($res, "VERIFIED") == 0) {
				return true;
			} elseif (strcmp($res, "INVALID") == 0) {
				return false;
			}

			$index++;
		}

		return null;
	}

	public function getPaymentDetails($paymentDetails) {
		return JText::_('LNG_PROCESSOR_PAYPAL', true);
	}
}
