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/platon/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/sale/handlers/paysystem/platon/handler.php
<?php

namespace Sale\Handlers\PaySystem;

use Bitrix\Main;
use Bitrix\Main\Entity\Result;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\Request;
use Bitrix\Main\Web\HttpClient;
use Bitrix\Main\Web\Json;
use Bitrix\Sale\Payment;
use Bitrix\Sale\PaymentCollection;
use Bitrix\Sale\PaySystem;
use Bitrix\Sale\PaySystem\Logger;
use Bitrix\Sale\PaySystem\ServiceResult;
use Bitrix\Sale\PriceMaths;

class PlatonHandler extends PaySystem\ServiceHandler implements PaySystem\IRefund
{
	private const PAYMENT_METHOD_CODE = 'CC';
	private const REFUND_ACTION = 'CREDITVOID';

	private const ANALYTICS_TAG = 'api_bitrix24ua';

	/*
	 * order: payment id
	 * ext1: the name of the handler
	 * ext2: pay system service ID
	 * ext3: analytics tag
	 */
	private const CALLBACK_ORDER_PARAM = 'order';
	private const CALLBACK_EXT1_PARAM = 'ext1';
	private const CALLBACK_EXT2_PARAM = 'ext2';

	private const TRANSACTION_STATUS_SALE = 'SALE';
	private const TRANSACTION_STATUS_REFUND = 'REFUND';

	private const REFUND_STATUS_ACCEPTED = 'ACCEPTED';
	private const REFUND_STATUS_ERROR = 'ERROR';

	private const PS_MODE_BANK_CARD = 'bank_card';
	private const PS_MODE_GOOGLE_PAY = 'google_pay';
	private const PS_MODE_APPLE_PAY = 'apple_pay';
	private const PS_MODE_PRIVAT24 = 'privat24';

	/**
	 * @inheritDoc
	 */
	public function initiatePay(Payment $payment, Request $request = null)
	{
		$result = new PaySystem\ServiceResult();

		$params = [
			'CURRENCY' => $payment->getField('CURRENCY'),
			'SUM' => PriceMaths::roundPrecision($payment->getSum()),
			'FORM_ACTION_URL' => $this->getUrl($payment, "formActionUrl"),
			'FORM_DATA' => $this->getFormData($payment),
		];
		$this->setExtraParams($params);

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

		return $result;
	}

	/**
	 * forms the data that will be sent with a POST request via a form
	 * @param Payment $payment
	 * @return array
	 */
	private function getFormData(Payment $payment): array
	{
		$apiKey = $this->getBusinessValue($payment, 'PLATON_API_KEY');
		$paymentMethodCode = self::PAYMENT_METHOD_CODE;
		$paymentData = $this->getPaymentData($payment);
		$encodedPaymentData = $this->encodePaymentData($paymentData);
		$successUrl = $this->getSuccessUrl($payment);
		$password = $this->getBusinessValue($payment, 'PLATON_PASSWORD');

		$sign = $this->getPaymentFormSignature(
			$apiKey,
			$paymentMethodCode,
			$encodedPaymentData,
			$successUrl,
			$password
		);

		$paymentNumber = $payment->getField('ACCOUNT_NUMBER');
		$paySystemId = $this->service->getField('ID');

		$formData = [
			'KEY' => $apiKey,
			'PAYMENT' => $paymentMethodCode,
			'DATA' => $encodedPaymentData,
			'URL' => $successUrl,
			'REQ_TOKEN' => 'N',
			'SIGN' => $sign,
			'ORDER' => $paymentNumber,
			'EXT_1' => 'PLATON',
			'EXT_2' => $paySystemId,
			'EXT_3' => self::ANALYTICS_TAG,
		];

		$email = $this->getUserEmailValue($payment);
		if ($email)
		{
			$formData['EMAIL'] = $email;
		}

		return $formData;
	}

	/**
	 * encodes the payment data as per the documentation
	 * @param array $paymentData
	 * @return string
	 */
	private function encodePaymentData(array $paymentData): string
	{
		return base64_encode(Json::encode($paymentData, JSON_UNESCAPED_UNICODE));
	}

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

	/**
	 * @param Payment $payment
	 * @return array
	 */
	private function getPaymentData(Payment $payment)
	{
		$formattedPaymentSum = $this->getFormattedPaymentSum($payment);
		$paymentDescription = $this->getPaymentDescription($payment);

		return [
			'amount' => $formattedPaymentSum,
			'currency' => $payment->getField("CURRENCY"),
			'description' => $paymentDescription,
			'recurring' => 'N',
		];
	}

	/**
	 * @param Payment $payment
	 * @return string
	 */
	private function getFormattedPaymentSum(Payment $payment)
	{
		$paymentSum = PriceMaths::roundPrecision($payment->getSum());
		return $this->formatPaymentSum($paymentSum);
	}

	/**
	 * @param $paymentSum
	 * @return string
	 */
	private function formatPaymentSum($paymentSum): string
	{
		return number_format($paymentSum, 2, '.', '');
	}

	/**
	 * returns either the link from a handler's settings or the order confirmation page
	 * @param Payment $payment
	 * @return string
	 */
	private function getSuccessUrl(Payment $payment): string
	{
		return $this->getBusinessValue($payment, 'PLATON_SUCCESS_URL') ?: $this->service->getContext()->getUrl();
	}

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

		$descriptionTemplate = $this->getBusinessValue($payment, 'PLATON_PAYMENT_DESCRIPTION');
		$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() : '',
			],
			$descriptionTemplate
		);

		return $description;
	}

	/**
	 * calculates the control signature for the request
	 * @param string $apiKey
	 * @param string $paymentMethodCode
	 * @param string $encodedPaymentData
	 * @param string $successUrl
	 * @param string $password
	 * @return string
	 */
	private function getPaymentFormSignature(
		string $apiKey,
		string $paymentMethodCode,
		string $encodedPaymentData,
		string $successUrl,
		string $password
	): string
	{
		return md5(
			mb_strtoupper(
				strrev($apiKey)
				. strrev($paymentMethodCode)
				. strrev($encodedPaymentData)
				. strrev($successUrl)
				. strrev($password)
			)
		);
	}

	/**
	 * @inheritDoc
	 */
	public function getCurrencyList()
	{
		return ['UAH'];
	}

	/**
	 * @inheritDoc
	 */
	public function processRequest(Payment $payment, Request $request)
	{
		$result = new PaySystem\ServiceResult();
		Logger::addDebugInfo(__CLASS__ . ': request payload: ' . Json::encode($request->getValues(), JSON_UNESCAPED_UNICODE));

		$signatureCheckResult = $this->checkCallbackSignature($payment, $request);
		if ($signatureCheckResult->isSuccess())
		{
			$transactionId = $request->get('id');
			$transactionStatus = $request->get('status');

			if ($transactionId && $transactionStatus === self::TRANSACTION_STATUS_SALE)
			{
				$description = Loc::getMessage('SALE_HPS_PLATON_TRANSACTION_DESCRIPTION', [
					'#ID#' => $request->get('id'),
					'#PAYMENT_NUMBER#' => $request->get('order'),
				]);
				$requestSum = $request->get('amount');
				$paymentFields = [
					'PS_INVOICE_ID' => $transactionId,
					'PS_STATUS_CODE' => $transactionStatus,
					'PS_STATUS_DESCRIPTION' => $description,
					'PS_SUM' => $requestSum,
					'PS_CURRENCY' => $request->get('currency'),
					'PS_RESPONSE_DATE' => new Main\Type\DateTime(),
					'PS_STATUS' => 'N',
					'PS_CARD_NUMBER' => $request->get('card'),
				];

				if ($this->checkPaymentSum($payment, $requestSum))
				{
					$paymentFields['PS_STATUS'] = 'Y';

					$result->setOperationType(PaySystem\ServiceResult::MONEY_COMING);
					$result->setPsData($paymentFields);
				}
				else
				{
					$errorMessage = Loc::getMessage('SALE_HPS_PLATON_SUM_MISMATCH');
					$paymentFields['PS_STATUS_DESCRIPTION'] .= $description . ' ' . $errorMessage;
					$result->addError(new Main\Error($errorMessage));
				}
			}
			elseif ($transactionStatus === self::TRANSACTION_STATUS_REFUND)
			{
				$oldDescription = $payment->getField('PS_STATUS_DESCRIPTION');
				$newDescription = str_replace(
					' ' . Loc::getMessage('SALE_HPS_PLATON_REFUND_IN_PROCESS'),
					'',
					$oldDescription
				);
				$payment->setField('PS_STATUS_DESCRIPTION', $newDescription);
				$result->setOperationType(PaySystem\ServiceResult::MONEY_LEAVING);
			}
			else
			{
				$errorMessage = $request->get('error_message');
				if (!isset($errorMessage))
				{
					$errorMessage = Loc::getMessage('SALE_HPS_PLATON_REQUEST_ERROR');
				}
				$result->addError(new Main\Error($errorMessage));
			}
		}
		else
		{
			$result->addError(new Main\Error(Loc::getMessage('SALE_HPS_PLATON_SIGNATURE_MISMATCH')));
		}

		return $result;
	}

	/**
	 * checks the callback signature to see if the callback is genuine
	 * @param Request $request
	 * @return ServiceResult
	 */
	private function checkCallbackSignature(Payment $payment, Request $request): PaySystem\ServiceResult
	{
		$result = new ServiceResult();

		$callbackSignature = $request->get('sign');

		$email = $this->getUserEmailValue($payment);

		$password = $this->getBusinessValue($payment, 'PLATON_PASSWORD');
		$order = $payment->getField('ACCOUNT_NUMBER');
		$card = $request->get('card');
		$localSignature = $this->getCallbackSignature($email, $password, $order, $card);

		Logger::addDebugInfo(__CLASS__ . ": local signature: $localSignature, callback signature: $callbackSignature");
		if ($callbackSignature !== $localSignature)
		{
			$result->addError(new Main\Error('signature mismatch'));
		}

		return $result;
	}

	/**
	 * checks if the actual sum of the payment is equal to the sum paid by the customer
	 * @param Payment $payment
	 * @param $requestSum
	 * @return bool
	 */
	private function checkPaymentSum(Payment $payment, $requestSum): bool
	{
		$roundedRequestSum = PriceMaths::roundPrecision($requestSum);
		$roundedPaymentSum = PriceMaths::roundPrecision($payment->getSum());
		Logger::addDebugInfo(__CLASS__ . ": request sum: $roundedRequestSum, payment sum: $roundedPaymentSum");

		return $roundedRequestSum === $roundedPaymentSum;
	}

	/**
	 * @inheritDoc
	 */
	public static function getIndicativeFields()
	{
		return [
			self::CALLBACK_ORDER_PARAM,
			self::CALLBACK_EXT1_PARAM,
			self::CALLBACK_EXT2_PARAM,
		];
	}

	/**
	 * @inheritDoc
	 */
	protected static function isMyResponseExtended(Request $request, $paySystemId)
	{
		return (int)$request->get(self::CALLBACK_EXT2_PARAM) === (int)$paySystemId;
	}

	/**
	 * @inheritDoc
	 */
	public function getPaymentIdFromRequest(Request $request)
	{
		return $request->get('order');
	}

	/**
	 * @inheritDoc
	 */
	public function refund(Payment $payment, $refundableSum)
	{
		$result = new PaySystem\ServiceResult();

		$transactionId = $payment->getField('PS_INVOICE_ID');
		$cardNumber = $payment->getField('PS_CARD_NUMBER');

		if ($cardNumber)
		{
			$formattedPaymentSum = $this->getFormattedPaymentSum($payment);
			$apiKey = $this->getBusinessValue($payment, 'PLATON_API_KEY');
			$email = $this->getUserEmailValue($payment);

			$password = $this->getBusinessValue($payment, 'PLATON_PASSWORD');

			$signature = $this->getCallbackSignature($email, $password, $transactionId, $cardNumber);

			$fields = [
				'action' => self::REFUND_ACTION,
				'client_key' => $apiKey,
				'trans_id' => $transactionId,
				'amount' => $formattedPaymentSum,
				'hash' => $signature,
			];

			$responseResult = $this->send($this->getUrl($payment, "requestUrl"), $fields);
			if (!$responseResult->isSuccess())
			{
				$result->addErrors($responseResult->getErrors());
				return $result;
			}

			$responseData = $responseResult->getData();

			Logger::addDebugInfo(__CLASS__ . ': refund payload: ' . Json::encode($responseData, JSON_UNESCAPED_UNICODE));
			switch ($responseData['result'])
			{
				case self::REFUND_STATUS_ACCEPTED:
					$newDescription = $payment->getField('PS_STATUS_DESCRIPTION') . ' ' . Loc::getMessage('SALE_HPS_PLATON_REFUND_IN_PROCESS');
					$payment->setField('PS_STATUS_DESCRIPTION', $newDescription);
					$result->setData($responseData);
					break;
				case self::REFUND_STATUS_ERROR:
					$result->addError(new Main\Error(Loc::getMessage('SALE_HPS_PLATON_RESPONSE_ERROR', [
						'#PS_RESPONSE#' => $responseData['error_message'],
					])));
					break;
				default:
					$result->addError(new Main\Error(Loc::getMessage('SALE_HPS_PLATON_REFUND_ERROR')));
			}
		}
		else
		{
			$result->addError(new Main\Error(Loc::getMessage('SALE_HPS_PLATON_ERROR_CARD_NOT_FOUND')));
		}

		return $result;
	}

	/**
	 * sends a request to the specified url
	 * @param string $url
	 * @param array $params
	 * @return Result
	 */
	private function send(string $url, array $params): Result
	{
		$result = new Result();

		$httpClient = new HttpClient();
		$response = $httpClient->post($url, $params);
		if ($response === false)
		{
			$errors = $httpClient->getError();
			foreach ($errors as $code =>$message)
			{
				$result->addError(new Main\Error($message, $code));
			}
		}
		else
		{
			$responseData = $this->decode($response);
			$result->setData($responseData);
		}

		return $result;
	}

	/**
	 * @param string $email
	 * @param string $password
	 * @param string $transactionId
	 * @param string $cardNumber
	 * @return string
	 */
	private function getCallbackSignature(string $email, string $password, string $transactionId, string $cardNumber): string
	{
		return md5(
			mb_strtoupper(
				strrev($email)
				. $password
				. $transactionId
				. strrev(
					mb_substr($cardNumber, 0, 6)
					. mb_substr($cardNumber, -4)
				)
			)
		);
	}

	/**
	 * @param Payment $payment
	 * @return string
	 */
	private function getUserEmailValue(Payment $payment): string
	{
		$email = '';
		$emailProperty = $payment->getOrder()->getPropertyCollection()->getUserEmail();
		if ($emailProperty)
		{
			$email = $emailProperty->getValue();
		}

		return $email ?? '';
	}

	protected function getUrlList()
	{
		return [
			'formActionUrl' => 'https://secure.platononline.com/payment/auth',
			'requestUrl' => 'https://secure.platononline.com/post-unq/',
		];
	}

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

Youez - 2016 - github.com/yon3zu
LinuXploit