403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/sale/handlers/paysystem/bepaid/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/sale/handlers/paysystem/bepaid/handler.php
<?php
namespace Sale\Handlers\PaySystem;

use Bitrix\Main,
	Bitrix\Main\Web\HttpClient,
	Bitrix\Main\Localization\Loc,
	Bitrix\Sale,
	Bitrix\Sale\PaySystem,
	Bitrix\Main\Request,
	Bitrix\Sale\Payment,
	Bitrix\Sale\PaySystem\ServiceResult,
	Bitrix\Sale\PaymentCollection,
	Bitrix\Sale\PriceMaths;

Loc::loadMessages(__FILE__);

/**
 * Class BePaidHandler
 * @package Sale\Handlers\PaySystem
 */
class BePaidHandler extends PaySystem\ServiceHandler implements
	PaySystem\IRefund,
	PaySystem\Domain\Verification\IVerificationable
{
	private const MODE_CHECKOUT = 'checkout';
	private const MODE_WIDGET = 'widget';

	private const CHECKOUT_API_URL = 'https://checkout.bepaid.by';
	private const GATEWAY_API_URL = 'https://gateway.bepaid.by';
	private const API_VERSION = '2.1';

	private const TRACKING_ID_DELIMITER = '#';

	private const STATUS_SUCCESSFUL_CODE = 'successful';
	private const STATUS_ERROR_CODE = 'error';

	private const SEND_METHOD_HTTP_POST = "POST";
	private const SEND_METHOD_HTTP_GET = "GET";

	/**
	 * @return array
	 */
	public static function getHandlerModeList(): array
	{
		return PaySystem\Manager::getHandlerDescription('bePaid')['HANDLER_MODE_LIST'];
	}

	/**
	 * @param Payment $payment
	 * @param Request|null $request
	 * @return ServiceResult
	 */
	public function initiatePay(Payment $payment, Request $request = null): ServiceResult
	{
		$result = new ServiceResult();

		$createPaymentTokenResult = $this->createPaymentToken($payment);
		if (!$createPaymentTokenResult->isSuccess())
		{
			$result->addErrors($createPaymentTokenResult->getErrors());
			return $result;
		}

		$createPaymentTokenData = $createPaymentTokenResult->getData();
		if (!empty($createPaymentTokenData['checkout']['token']))
		{
			$result->setPsData(['PS_INVOICE_ID' => $createPaymentTokenData['checkout']['token']]);
		}

		if ($this->isCheckoutMode())
		{
			$result->setPaymentUrl($createPaymentTokenData['checkout']['redirect_url']);
		}

		$this->setExtraParams($this->getTemplateParams($payment, $createPaymentTokenData));

		$showTemplateResult = $this->showTemplate($payment, $this->getTemplateName());
		if ($showTemplateResult->isSuccess())
		{
			$result->setTemplate($showTemplateResult->getTemplate());
		}
		else
		{
			$result->addErrors($showTemplateResult->getErrors());
		}

		return $result;
	}

	/**
	 * @return string
	 */
	private function getTemplateName(): string
	{
		return (string)$this->service->getField('PS_MODE');
	}

	/**
	 * @param Payment $payment
	 * @param array $paymentTokenData
	 * @return array
	 */
	private function getTemplateParams(Payment $payment, array $paymentTokenData): array
	{
		$params = [
			'sum' => (string)(PriceMaths::roundPrecision($payment->getSum())),
			'currency' => $payment->getField('CURRENCY'),
		];

		if ($this->isWidgetMode())
		{
			$params['checkout_url'] = self::CHECKOUT_API_URL;
			$params['token'] = $paymentTokenData['checkout']['token'];
			$params['checkout'] = [
				'iframe' => true,
				'test' => $this->isTestMode($payment),
				'transaction_type' => 'payment',
				'order' => [
					'amount' => (string)(PriceMaths::roundPrecision($payment->getSum()) * 100),
					'currency' => $payment->getField('CURRENCY'),
					'description' => $this->getPaymentDescription($payment),
					'tracking_id' => $payment->getId().self::TRACKING_ID_DELIMITER.$this->service->getField('ID'),
					'additional_data' => self::getAdditionalData(),
				],
				'settings' => [
					'success_url' => $this->getSuccessUrl($payment),
					'decline_url' => $this->getDeclineUrl($payment),
					'notification_url' => $this->getBusinessValue($payment, 'BEPAID_NOTIFICATION_URL'),
					'language' => LANGUAGE_ID,
				],
			];
		}
		else
		{
			$params['url'] = $paymentTokenData['checkout']['redirect_url'];
		}

		return $params;
	}

	/**
	 * @param Payment $payment
	 * @return ServiceResult
	 */
	private function createPaymentToken(Payment $payment): ServiceResult
	{
		$result = new ServiceResult();

		$url = $this->getUrl($payment, 'getPaymentToken');
		$params = [
			'checkout' => [
				'version' => self::API_VERSION,
				'test' => $this->isTestMode($payment),
				'transaction_type' => 'payment',
				'order' => [
					'amount' => (string)(PriceMaths::roundPrecision($payment->getSum()) * 100),
					'currency' => $payment->getField('CURRENCY'),
					'description' => $this->getPaymentDescription($payment),
					'tracking_id' => $payment->getId().self::TRACKING_ID_DELIMITER.$this->service->getField('ID'),
					'additional_data' => self::getAdditionalData(),
				],
				'settings' => [
					'success_url' => $this->getSuccessUrl($payment),
					'decline_url' => $this->getDeclineUrl($payment),
					'fail_url' => $this->getFailUrl($payment),
					'cancel_url' => $this->getCancelUrl($payment),
					'notification_url' => $this->getBusinessValue($payment, 'BEPAID_NOTIFICATION_URL'),
					'language' => LANGUAGE_ID,
				],
			],
		];
		$headers = $this->getHeaders($payment);

		$sendResult = $this->send(self::SEND_METHOD_HTTP_POST, $url, $params, $headers);
		if ($sendResult->isSuccess())
		{
			$paymentTokenData = $sendResult->getData();
			$verifyResponseResult = $this->verifyResponse($paymentTokenData);
			if ($verifyResponseResult->isSuccess())
			{
				$result->setData($paymentTokenData);
			}
			else
			{
				$result->addErrors($verifyResponseResult->getErrors());
			}
		}
		else
		{
			$result->addErrors($sendResult->getErrors());
		}

		return $result;
	}

	/**
	 * @param Payment $payment
	 * @return ServiceResult
	 */
	private function getBePaidPayment(Payment $payment): ServiceResult
	{
		$result = new ServiceResult();

		$url = $this->getUrl($payment, 'getPaymentStatus');
		$headers = $this->getHeaders($payment);

		$sendResult = $this->send(self::SEND_METHOD_HTTP_GET, $url, [], $headers);
		if ($sendResult->isSuccess())
		{
			$paymentData = $sendResult->getData();
			$verifyResponseResult = $this->verifyResponse($paymentData);
			if ($verifyResponseResult->isSuccess())
			{
				$result->setData($paymentData);
			}
			else
			{
				$result->addErrors($verifyResponseResult->getErrors());
			}
		}
		else
		{
			$result->addErrors($sendResult->getErrors());
		}

		return $result;
	}

	/**
	 * @param Payment $payment
	 * @param $refundableSum
	 * @return ServiceResult
	 */
	public function refund(Payment $payment, $refundableSum): ServiceResult
	{
		$result = new ServiceResult();

		$bePaidPaymentResult = $this->getBePaidPayment($payment);
		if (!$bePaidPaymentResult->isSuccess())
		{
			$result->addErrors($bePaidPaymentResult->getErrors());
			return $result;
		}

		$bePaidPaymentData = $bePaidPaymentResult->getData();

		$url = $this->getUrl($payment, 'refund');
		$params = [
			'request' => [
				'parent_uid' => $bePaidPaymentData['checkout']['gateway_response']['payment']['uid'],
				'amount' => (string)(PriceMaths::roundPrecision($refundableSum) * 100),
				'reason' => $payment->getField('PAY_RETURN_COMMENT') ?: Loc::getMessage('SALE_HPS_BEPAID_REFUND_REASON'),
			],
		];
		$headers = $this->getHeaders($payment);

		$sendResult = $this->send(self::SEND_METHOD_HTTP_POST, $url, $params, $headers);
		if (!$sendResult->isSuccess())
		{
			$result->addErrors($sendResult->getErrors());
			return $result;
		}

		$refundData = $sendResult->getData();
		$verifyResponseResult = $this->verifyResponse($refundData);
		if ($verifyResponseResult->isSuccess())
		{
			if ($refundData['transaction']['status'] === static::STATUS_SUCCESSFUL_CODE
				&& PriceMaths::roundPrecision($refundData['transaction']['amount'] / 100) === PriceMaths::roundPrecision($refundableSum)
			)
			{
				$result->setOperationType(PaySystem\ServiceResult::MONEY_LEAVING);
			}
		}
		else
		{
			$result->addErrors($verifyResponseResult->getErrors());
		}

		return $result;
	}

	/**
	 * @param string $method
	 * @param string $url
	 * @param array $params
	 * @param array $headers
	 * @return ServiceResult
	 */
	private function send(string $method, string $url, array $params = [], array $headers = []): ServiceResult
	{
		$result = new ServiceResult();

		$httpClient = new HttpClient();
		foreach ($headers as $name => $value)
		{
			$httpClient->setHeader($name, $value);
		}

		if ($method === self::SEND_METHOD_HTTP_GET)
		{
			$response = $httpClient->get($url);
		}
		else
		{
			$postData = null;
			if ($params)
			{
				$postData = static::encode($params);
			}

			PaySystem\Logger::addDebugInfo(__CLASS__.': request data: '.$postData);

			$response = $httpClient->post($url, $postData);
		}

		if ($response === false)
		{
			$errors = $httpClient->getError();
			foreach ($errors as $code => $message)
			{
				$result->addError(PaySystem\Error::create($message, $code));
			}

			return $result;
		}

		PaySystem\Logger::addDebugInfo(__CLASS__.': response data: '.$response);

		$response = static::decode($response);
		if ($response)
		{
			$result->setData($response);
		}
		else
		{
			$result->addError(PaySystem\Error::create(Loc::getMessage('SALE_HPS_BEPAID_RESPONSE_DECODE_ERROR')));
		}

		return $result;
	}

	/**
	 * @param array $response
	 * @return ServiceResult
	 */
	private function verifyResponse(array $response): ServiceResult
	{
		$result = new ServiceResult();

		if (!empty($response['errors']))
		{
			$result->addError(PaySystem\Error::create($response['message']));
		}
		elseif (!empty($response['response']['errors']))
		{
			$result->addError(PaySystem\Error::create($response['response']['message']));
		}
		elseif (!empty($response['response']['status']) && $response['response']['status'] === self::STATUS_ERROR_CODE)
		{
			$result->addError(PaySystem\Error::create($response['response']['message']));
		}
		elseif (!empty($response['checkout']['status']) && $response['checkout']['status'] === self::STATUS_ERROR_CODE)
		{
			$result->addError(PaySystem\Error::create($response['checkout']['message']));
		}

		return $result;
	}

	/**
	 * @return array|string[]
	 */
	public function getCurrencyList(): array
	{
		return ['BYN', 'USD', 'EUR', 'RUB'];
	}

	/**
	 * @param Payment $payment
	 * @param Request $request
	 * @return ServiceResult
	 */
	public function processRequest(Payment $payment, Request $request): ServiceResult
	{
		$result = new ServiceResult();

		$inputStream = static::readFromStream();
		$data = static::decode($inputStream);
		$transaction = $data['transaction'];

		$bePaidPaymentResult = $this->getBePaidPayment($payment);
		if ($bePaidPaymentResult->isSuccess())
		{
			$bePaidPaymentData = $bePaidPaymentResult->getData();
			if ($bePaidPaymentData['checkout']['status'] === self::STATUS_SUCCESSFUL_CODE)
			{
				$description = Loc::getMessage('SALE_HPS_BEPAID_TRANSACTION', [
					'#ID#' => $transaction['uid'],
				]);
				$fields = [
					'PS_STATUS_CODE' => $transaction['status'],
					'PS_STATUS_DESCRIPTION' => $description,
					'PS_SUM' => $transaction['amount'] / 100,
					'PS_STATUS' => 'N',
					'PS_CURRENCY' => $transaction['currency'],
					'PS_RESPONSE_DATE' => new Main\Type\DateTime()
				];

				if ($this->isSumCorrect($payment, $transaction['amount'] / 100))
				{
					$fields['PS_STATUS'] = 'Y';

					PaySystem\Logger::addDebugInfo(
						__CLASS__.': PS_CHANGE_STATUS_PAY='.$this->getBusinessValue($payment, 'PS_CHANGE_STATUS_PAY')
					);

					if ($this->getBusinessValue($payment, 'PS_CHANGE_STATUS_PAY') === 'Y')
					{
						$result->setOperationType(PaySystem\ServiceResult::MONEY_COMING);
					}
				}
				else
				{
					$error = Loc::getMessage('SALE_HPS_BEPAID_ERROR_SUM');
					$fields['PS_STATUS_DESCRIPTION'] .= '. '.$error;
					$result->addError(PaySystem\Error::create($error));
				}

				$result->setPsData($fields);
			}
		}
		else
		{
			$result->addErrors($bePaidPaymentResult->getErrors());
		}

		return $result;
	}

	/**
	 * @param Payment $payment
	 * @param $sum
	 * @return bool
	 */
	private function isSumCorrect(Payment $payment, $sum): bool
	{
		PaySystem\Logger::addDebugInfo(
			__CLASS__.': bePaidSum='.PriceMaths::roundPrecision($sum)."; paymentSum=".PriceMaths::roundPrecision($payment->getSum())
		);

		return PriceMaths::roundPrecision($sum) === PriceMaths::roundPrecision($payment->getSum());
	}

	/**
	 * @param Request $request
	 * @param int $paySystemId
	 * @return bool
	 */
	public static function isMyResponse(Request $request, $paySystemId): bool
	{
		$inputStream = static::readFromStream();
		if ($inputStream)
		{
			$data = static::decode($inputStream);
			if ($data === false)
			{
				return false;
			}

			if (isset($data['transaction']['tracking_id']))
			{
				[, $trackingPaySystemId] = explode(self::TRACKING_ID_DELIMITER, $data['transaction']['tracking_id']);
				return (int)$trackingPaySystemId === (int)$paySystemId;
			}
		}

		return false;
	}

	/**
	 * @param Request $request
	 * @return bool|int|mixed
	 */
	public function getPaymentIdFromRequest(Request $request)
	{
		$inputStream = static::readFromStream();
		if ($inputStream)
		{
			$data = static::decode($inputStream);
			if (isset($data['transaction']['tracking_id']))
			{
				[$trackingPaymentId] = explode(self::TRACKING_ID_DELIMITER, $data['transaction']['tracking_id']);
				return (int)$trackingPaymentId;
			}
		}

		return false;
	}

	/**
	 * @param Payment $payment
	 * @return mixed
	 */
	private function getPaymentDescription(Payment $payment)
	{
		/** @var PaymentCollection $collection */
		$collection = $payment->getCollection();
		$order = $collection->getOrder();
		$userEmail = $order->getPropertyCollection()->getUserEmail();

		$description =  str_replace(
			[
				'#PAYMENT_NUMBER#',
				'#ORDER_NUMBER#',
				'#PAYMENT_ID#',
				'#ORDER_ID#',
				'#USER_EMAIL#'
			],
			[
				$payment->getField('ACCOUNT_NUMBER'),
				$order->getField('ACCOUNT_NUMBER'),
				$payment->getId(),
				$order->getId(),
				($userEmail) ? $userEmail->getValue() : ''
			],
			$this->getBusinessValue($payment, 'BEPAID_PAYMENT_DESCRIPTION')
		);

		return $description;
	}

	/**
	 * @param Payment $payment
	 * @return mixed|string
	 */
	private function getSuccessUrl(Payment $payment)
	{
		return $this->getBusinessValue($payment, 'BEPAID_SUCCESS_URL') ?: $this->service->getContext()->getUrl();
	}

	/**
	 * @param Payment $payment
	 * @return mixed|string
	 */
	private function getDeclineUrl(Payment $payment)
	{
		return $this->getBusinessValue($payment, 'BEPAID_DECLINE_URL') ?: $this->service->getContext()->getUrl();
	}

	/**
	 * @param Payment $payment
	 * @return mixed|string
	 */
	private function getFailUrl(Payment $payment)
	{
		return $this->getBusinessValue($payment, 'BEPAID_FAIL_URL') ?: $this->service->getContext()->getUrl();
	}

	/**
	 * @param Payment $payment
	 * @return mixed|string
	 */
	private function getCancelUrl(Payment $payment)
	{
		return $this->getBusinessValue($payment, 'BEPAID_CANCEL_URL') ?: $this->service->getContext()->getUrl();
	}

	/**
	 * @param Payment $payment
	 * @return array
	 */
	private function getHeaders(Payment $payment): array
	{
		$headers = [
			'Content-Type' => 'application/json',
			'Accept' => 'application/json',
			'Authorization' => 'Basic '.$this->getBasicAuthString($payment),
			'RequestID' => $this->getIdempotenceKey(),
		];

		if ($this->isWidgetMode())
		{
			$headers['X-API-Version'] = 2;
		}

		return $headers;
	}

	/**
	 * @param Payment $payment
	 * @return string
	 */
	private function getBasicAuthString(Payment $payment): string
	{
		return base64_encode(
			$this->getBusinessValue($payment, 'BEPAID_ID')
			. ':'
			. $this->getBusinessValue($payment, 'BEPAID_SECRET_KEY')
		);
	}

	/**
	 * @return bool
	 */
	private function isWidgetMode(): bool
	{
		return $this->service->getField('PS_MODE') === self::MODE_WIDGET;
	}

	/**
	 * @return bool
	 */
	private function isCheckoutMode(): bool
	{
		return $this->service->getField('PS_MODE') === self::MODE_CHECKOUT;
	}

	/**
	 * @return string
	 */
	private function getIdempotenceKey(): string
	{
		return sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
			mt_rand(0, 0xffff), mt_rand(0, 0xffff),
			mt_rand(0, 0xffff),
			mt_rand(0, 0x0fff) | 0x4000,
			mt_rand(0, 0x3fff) | 0x8000,
			mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
		);
	}

	/**
	 * @param Payment $payment
	 * @param string $action
	 * @return string
	 */
	protected function getUrl(Payment $payment = null, $action): string
	{
		$url = parent::getUrl($payment, $action);
		if ($payment !== null && $action === 'getPaymentStatus')
		{
			$url = str_replace('#payment_token#', $payment->getField('PS_INVOICE_ID'), $url);
		}

		return $url;
	}

	/**
	 * @return array
	 */
	protected function getUrlList(): array
	{
		return [
			'getPaymentToken' => self::CHECKOUT_API_URL.'/ctp/api/checkouts',
			'getPaymentStatus' => self::CHECKOUT_API_URL.'/ctp/api/checkouts/#payment_token#',
			'refund' => self::GATEWAY_API_URL.'/transactions/refunds',
		];
	}

	/**
	 * @param Payment $payment
	 * @return bool
	 */
	protected function isTestMode(Payment $payment = null): bool
	{
		return ($this->getBusinessValue($payment, 'PS_IS_TEST') === 'Y');
	}

	/**
	 * @return bool|string
	 */
	private static function readFromStream()
	{
		return file_get_contents('php://input');
	}

	/**
	 * @param array $data
	 * @return mixed
	 */
	private static function encode(array $data)
	{
		return Main\Web\Json::encode($data, JSON_UNESCAPED_UNICODE);
	}

	/**
	 * @param string $data
	 * @return mixed
	 */
	private static function decode($data)
	{
		try
		{
			return Main\Web\Json::decode($data);
		}
		catch (Main\ArgumentException $exception)
		{
			return false;
		}
	}

	private static function getAdditionalData(): array
	{
		$additionalData = [
			'platform_data' => self::getPlatformData(),
		];

		$integrationData = self::getIntegrationData();
		if ($integrationData)
		{
			$additionalData['integration_data'] = $integrationData;
		}

		return $additionalData;
	}

	private static function getPlatformData(): string
	{
		if (Main\ModuleManager::isModuleInstalled('bitrix24'))
		{
			$platformType = 'Bitrix24';
		}
		elseif (Main\ModuleManager::isModuleInstalled('intranet'))
		{
			$platformType = 'Self-hosted';
		}
		else
		{
			$platformType = 'Bitrix Site Manager';
		}

		return $platformType;
	}

	private static function getIntegrationData(): ?string
	{
		$version = self::getSaleModuleVersion();
		if (!$version)
		{
			return null;
		}

		return 'bePaid system module v' . $version;
	}

	private static function getSaleModuleVersion(): ?string
	{
		$modulePath = getLocalPath('modules/sale/install/version.php');
		if ($modulePath === false)
		{
			return null;
		}

		$arModuleVersion = array();
		include $_SERVER['DOCUMENT_ROOT'].$modulePath;
		return (isset($arModuleVersion['VERSION']) ? (string)$arModuleVersion['VERSION'] : null);
	}

	public static function getModeList(): array
	{
		return array_keys(self::getHandlerModeList());
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit