<?php
/**
 * @package     CMGiftCard
 * @subpackage  plg_cmgiftcard_authorizenetah
 * @copyright   Copyright (C) 2016-2024 CMExension
 * @license     GNU General Public License version 2 or later
 */

defined('_JEXEC') or die;

require_once JPATH_ROOT . '/administrator/components/com_cmgiftcard/helpers/plugin/cmgiftcard.php';
require_once __DIR__ . '/sdk/autoload.php';

use Joomla\CMS\Session\Session;
use Joomla\CMS\Uri\Uri;
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use net\authorize\api\contract\v1 as AnetAPI;
use net\authorize\api\controller as AnetController;

/**
 * Authorize.Net payment method for CMGiftCard.
 *
 * @package     CMGiftCard
 * @subpackage  plg_cmgiftcard_authorizenetah
 * @since       1.3.0
 */
class PlgCMGiftCardAuthorizeNetAH extends CMGiftCardPlugin
{
	/**
	 * Constructor
	 *
	 * @param   object  &$subject  The object to observe
	 * @param   array   $config    An optional associative array of configuration settings.
	 *                             Recognized key values include 'name', 'group', 'params', 'language'
	 *                             (this list is not meant to be comprehensive).
	 *
	 * @since   1.3.0
	 */
	public function __construct(&$subject, $config = array())
	{
		$this->gatewayId = 'authorizenetah';
		$this->gatewayName = 'PLG_CMGIFTCARD_AUTHORIZENETAH_NAME';

		parent::__construct($subject, $config);
	}

	/**
	 * Method to get payment form.
	 *
	 * @param   array  $data  Billing data.
	 *
	 * @return  array
	 *
	 * @since   1.3.0
	 */
	public function onCMGCGetPaymentForm($data)
	{
		$gatewayId = isset($data['billing']['payment_method']) ? $data['billing']['payment_method'] : '';

		if ($gatewayId != $this->gatewayId)
			return false;

		$loginId = $this->params->get('login_id');
		$transactionKey = $this->params->get('transaction_key');
		$isLive = $this->params->get('mode', 1);

		$siteUrl = str_replace(Uri::root(true), '', substr(Uri::root(), 0, -1));
		$returnUrl = $siteUrl . CMGiftCardHelper::prepareRoute('index.php?option=com_cmgiftcard&view=checkout') . '?layout=success';
		$cancelUrl = $siteUrl . CMGiftCardHelper::prepareRoute('index.php?option=com_cmgiftcard&view=checkout') . '?layout=cancel';
		$notifyUrl = Uri::root() . 'index.php?option=com_cmgiftcard&task=callback&gateway=' . $this->gatewayId;

		$description = $this->params->get('description', '');
		$description = str_replace('{order_number}', $data['billing']['order_number'], $description);

		$amount = $data['total'];
		$firstName = isset($data['billing']['first_name']) ? $data['billing']['first_name'] : '';
		$lastName = isset($data['billing']['last_name']) ? $data['billing']['last_name'] : '';
		$address = isset($data['billing']['address_1']) ? $data['billing']['address_1'] : '';
		$state = isset($data['billing']['state'])? $data['billing']['state'] : '';
		$zip = isset($data['billing']['postal_code']) ? $data['billing']['postal_code'] : '';
		$city = isset($data['billing']['city']) ? $data['billing']['city'] : '';
		$country = isset($data['billing']['country']) ? $data['billing']['country'] : '';
		$orderNumber = $data['billing']['order_number'];
		$url = ($isLive) ? 'https://accept.authorize.net/payment/payment' : 'https://test.authorize.net/payment/payment';

		// Set order number to session to validate later.
		Factory::getSession()->set('order_number', $orderNumber, 'CMGiftCard');

		/* Create a merchantAuthenticationType object with authentication details
		retrieved from the constants file */
		$merchantAuthentication = new AnetAPI\MerchantAuthenticationType();
		$merchantAuthentication->setName($loginId);
		$merchantAuthentication->setTransactionKey($transactionKey);

		// Create order information
		$order = new AnetAPI\OrderType();
		$order->setInvoiceNumber($orderNumber);
		$order->setDescription($description);

		// Set the customer's Bill To address
		$customerAddress = new AnetAPI\CustomerAddressType();
		$customerAddress->setFirstName($firstName);
		$customerAddress->setLastName($lastName);
		$customerAddress->setAddress($address);
		$customerAddress->setCity($city);
		$customerAddress->setState($state);
		$customerAddress->setZip($zip);
		$customerAddress->setCountry($country);

		// Create a transaction
		$transactionRequestType = new AnetAPI\TransactionRequestType();
		$transactionRequestType->setTransactionType("authCaptureTransaction");
		$transactionRequestType->setAmount($amount);
		$transactionRequestType->setOrder($order);
		$transactionRequestType->setBillTo($customerAddress);

		// Set Hosted Form options
		$setting1 = new AnetAPI\SettingType();
		$setting1->setSettingName("hostedPaymentButtonOptions");
		$setting1->setSettingValue("{\"text\": \"Pay\"}");

		$setting2 = new AnetAPI\SettingType();
		$setting2->setSettingName("hostedPaymentOrderOptions");
		$setting2->setSettingValue("{\"show\": false}");

		$hostedPaymentReturnOptions = [
			'showReceipt' => false
		];

		$setting3 = new AnetAPI\SettingType();
		$setting3->setSettingName("hostedPaymentReturnOptions");
		$setting3->setSettingValue(json_encode($hostedPaymentReturnOptions));

		$hostedPaymentIFrameCommunicatorUrl = [
			'url' => Uri::root() . 'plugins/cmgiftcard/' . $this->gatewayId . '/IFrameCommunicator.html'
		];

		$setting4 = new AnetAPI\SettingType();
		$setting4->setSettingName("hostedPaymentIFrameCommunicatorUrl");
		$setting4->setSettingValue(json_encode($hostedPaymentIFrameCommunicatorUrl));

		$hostedPaymentCustomerOptions = [
			'showEmail' => true,
			'requiredEmail' => true,
		];

		$setting5 = new AnetAPI\SettingType();
		$setting5->setSettingName("hostedPaymentCustomerOptions");
		$setting5->setSettingValue(json_encode($hostedPaymentCustomerOptions));

		$hostedPaymentShippingAddressOptions = [
			'show' => true,
			'required' => true,
		];

		$setting6 = new AnetAPI\SettingType();
		$setting6->setSettingName("hostedPaymentShippingAddressOptions");
		$setting6->setSettingValue(json_encode($hostedPaymentShippingAddressOptions));

		$hostedPaymentBillingAddressOptions = [
			'show' => true,
			'required' => true,
		];

		$setting7 = new AnetAPI\SettingType();
		$setting7->setSettingName("hostedPaymentBillingAddressOptions");
		$setting7->setSettingValue(json_encode($hostedPaymentBillingAddressOptions));

		// Build transaction request
		$request = new AnetAPI\GetHostedPaymentPageRequest();
		$request->setMerchantAuthentication($merchantAuthentication);
		$request->setTransactionRequest($transactionRequestType);
		$request->addToHostedPaymentSettings($setting1);
		$request->addToHostedPaymentSettings($setting2);
		$request->addToHostedPaymentSettings($setting3);
		$request->addToHostedPaymentSettings($setting4);
		$request->addToHostedPaymentSettings($setting5);
		$request->addToHostedPaymentSettings($setting6);
		$request->addToHostedPaymentSettings($setting7);

		// Execute request
		$controller = new AnetController\GetHostedPaymentPageController($request);

		if ($isLive)
			$response = $controller->executeWithApiResponse(\net\authorize\api\constants\ANetEnvironment::PRODUCTION);
		else
			$response = $controller->executeWithApiResponse(\net\authorize\api\constants\ANetEnvironment::SANDBOX);

		$token = '';
		$apiError = '';

		if (($response != null) && ($response->getMessages()->getResultCode() == "Ok"))
		{
			$token = $response->getToken();
		}
		else
		{
			$errorMessages = $response->getMessages()->getMessage();
			$apiError = Text::sprintf('PLG_CMGIFTCARD_AUTHORIZENETAH_API_ERROR', $errorMessages[0]->getCode() . ' ' . $errorMessages[0]->getText());
		}

		@ob_start();
		include dirname(__FILE__) . '/form.php';
		$html = @ob_get_clean();

		return $html;
	}

	/**
	 * Method to get validate transaction.
	 *
	 * @return  boolean
	 *
	 * @since   1.3.0
	 */
	public function onCMGCValidateCallback()
	{
		$app = Factory::getApplication();
		$jinput = $app->input;
		$gatewayId = $jinput->get('gateway', '');
		$loginId = $this->params->get('login_id');
		$transactionKey = $this->params->get('transaction_key');
		$isLive = $this->params->get('mode', 1);

		$db = Factory::getDbo();
		$query = $db->getQuery(true);

		$result = array(
			'validation'	=> false,
			'gateway_id'	=> $this->gatewayId,
			'order_id'		=> ''
		);

		if ($gatewayId != $this->gatewayId)
		{
			return $result;
		}

		$session = Factory::getSession();
		$isValid = true;
		$logData = [];

		if (!Session::checkToken('get'))
		{
			$logData['failure_reason'] = 'Invalid token';
			$isValid = false;
		}

		if ($isValid)
		{
			$transactionId = (int) $jinput->get('transaction_id');

			if ($transactionId >= 0)
			{
				$logData['transaction_id'] = $transactionId;
			}
			else
			{
				$logData['failure_reason'] = 'Transaction ID not found';
				$isValid = false;
			}
		}

		if ($isValid)
		{
			$orderNumber = $session->get('order_number', '', 'CMGiftCard');

			if (!empty($orderNumber))
			{
				$logData['order_number'] = $orderNumber;
			}
			else
			{
				$reason = 'Empty order number';
				$isValid = false;
			}
		}

		if ($isValid)
		{
			// Check if order exists.
			$query->select('*')
				->from($db->qn('#__cmgiftcard_orders'))
				->where($db->qn('order_number') . ' = ' . $db->q($orderNumber));
			$db->setQuery($query);
			$order = $db->loadObject();

			if (isset($order->id))
			{
				$logData['order_id'] = $order->id;
			}
			else
			{
				$isValid = false;
				$reason = "Order number $orderNumber didn't exist";
			}
		}

		if ($isValid)
		{
			$query->clear()
				->select('*')
				->from($db->qn('#__cmgiftcard_cards'))
				->where($db->qn('order_id') . ' = ' . $db->quote($order->id));
			$db->setQuery($query);
			$cards = $db->loadObjectList();

			if (empty($cards))
			{
				$isValid = false;
				$reason = "No gift cards found";
			}
		}

		if ($isValid)
		{
			// If sandbox is enabled, the transaction is considered as completed
			// because transaction in sandbox always has 0 ID.
			if (!$isLive)
			{
				$result['valid'] = $isValid;
				$result['transaction_id'] = $transactionId;
				$result['order_id'] = $order->id;

				return $result;
			}

			$merchantAuthentication = new AnetAPI\MerchantAuthenticationType();
			$merchantAuthentication->setName($loginId);
			$merchantAuthentication->setTransactionKey($transactionKey);

			$request = new AnetAPI\GetTransactionDetailsRequest();
			$request->setMerchantAuthentication($merchantAuthentication);
			$request->setTransId($transactionId);

			$controller = new AnetController\GetTransactionDetailsController($request);

			if ($isLive)
				$response = $controller->executeWithApiResponse(\net\authorize\api\constants\ANetEnvironment::PRODUCTION);
			else
				$response = $controller->executeWithApiResponse(\net\authorize\api\constants\ANetEnvironment::SANDBOX);

			if (($response != null) && ($response->getMessages()->getResultCode() == "Ok"))
			{
				$transaction = $response->getTransaction();
				$responseCode = $transaction->getResponseCode();
				$amount = $transaction->getAuthAmount();

				switch ($responseCode)
				{
					case 1:
						break;
					case 2:
						$isValid = false;
						$reason = "Transaction was declined";
						break;
					case 3:
						$isValid = false;
						$reason = "Transaction had error";
						break;
					case 4:
						$isValid = false;
						$reason = "Transaction was held for review";
						break;
					default:
						$isValid = false;
						$reason = "Unknown transaction response code: $responseCode";
				}
			}
			else
			{
				$isValid = false;
				$errorMessages = $response->getMessages()->getMessage();
				$reason = $errorMessages[0]->getCode() . "  " .$errorMessages[0]->getText();
			}
		}

		if ($isValid)
		{
			$orderAmount = 0;

			foreach ($cards as $card)
			{
				$orderAmount += $card->amount;
			}

			$isValid = ($orderAmount - $amount) < 0.01;

			if (!$isValid)
			{
				$reason = "Invalid amount, expected $orderAmount, got $amount";
			}
		}

		$session->clear('order_number', 'CMGiftCard');

		$result['valid'] = $isValid;

		if ($isValid)
		{
			$result['transaction_id'] = $transactionId;
			$result['order_id'] = $order->id;
		}
		elseif (!$isValid && !empty($reason))
		{
			$logData['failure_reason'] = $reason;
		}

		$this->logNotification($logData, $isValid);

		return $result;
	}
}
