%PDF- %PDF-
| Direktori : /home/bitrix/www/bitrix/modules/sale/handlers/paysystem/yandex/ |
| Current File : //home/bitrix/www/bitrix/modules/sale/handlers/paysystem/yandex/handler.php |
<?php
namespace Sale\Handlers\PaySystem;
use Bitrix\Main\Error;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\Request;
use Bitrix\Main\Text\Encoding;
use Bitrix\Main\Type\DateTime;
use Bitrix\Main\Web\HttpClient;
use Bitrix\Sale\BusinessValue;
use Bitrix\Sale\PaySystem;
use Bitrix\Sale\Payment;
Loc::loadMessages(__FILE__);
/**
* Class YandexHandler
* @package Sale\Handlers\PaySystem
*/
class YandexHandler
extends PaySystem\ServiceHandler
implements PaySystem\IRefundExtended, PaySystem\IHold
{
/**
* @param Payment $payment
* @param Request|null $request
* @return PaySystem\ServiceResult
*/
public function initiatePay(Payment $payment, Request $request = null)
{
$params = array(
'URL' => $this->getUrl($payment, 'pay'),
'PS_MODE' => $this->service->getField('PS_MODE'),
'BX_PAYSYSTEM_CODE' => $this->service->getField('ID'),
);
$this->setExtraParams($params);
return $this->showTemplate($payment, "template");
}
/**
* @return array
*/
public static function getIndicativeFields()
{
return array('BX_HANDLER' => 'YANDEX');
}
/**
* @param Request $request
* @param $paySystemId
* @return bool
*/
static protected function isMyResponseExtended(Request $request, $paySystemId)
{
$id = $request->get('BX_PAYSYSTEM_CODE');
return (int)$id === (int)$paySystemId;
}
/**
* @param Payment $payment
* @param int $refundableSum
* @return PaySystem\ServiceResult
*/
public function refund(Payment $payment, $refundableSum)
{
$result = new PaySystem\ServiceResult();
$error = '';
$requestDT = date('c');
$currency = $this->isTestMode($payment) ? 10643 : 643;
$cause = Loc::getMessage('SALE_HPS_YANDEX_CUSTOMER_REJECTION');
$shopId = $this->getBusinessValue($payment, 'YANDEX_SHOP_ID');
$request = '
<returnPaymentRequest
clientOrderId=\''.$payment->getId().'\'
requestDT=\''.$requestDT.'\'
invoiceId=\''.$payment->getField('PS_INVOICE_ID').'\'
shopId=\''.$shopId.'\'
amount=\''.number_format($refundableSum, 2, '.', '').'\'
currency=\''.$currency.'\'
cause=\''.Encoding::convertEncoding($cause, LANG_CHARSET, 'UTF-8').'\'
/>';
$url = $this->getUrl($payment, 'return');
$signResult = $this->signRequest($payment, $request);
if ($signResult->isSuccess())
{
$data = $signResult->getData();
$pkcs7 = $data['PKCS7'];
$CertPem = PaySystem\YandexCert::getValue('CERT', $shopId);
$PkeyPem = PaySystem\YandexCert::getValue('PKEY', $shopId);
$cert = self::createTmpFile($CertPem);
$pkey = self::createTmpFile($PkeyPem);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_ENCODING, "");
curl_setopt($ch, CURLOPT_USERAGENT, "1C-Bitrix");
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 120);
curl_setopt($ch, CURLOPT_TIMEOUT, 120);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $pkcs7);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSLCERT, $cert);
curl_setopt($ch, CURLOPT_SSLKEY, $pkey);
$content = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
PaySystem\Logger::addDebugInfo('Yandex: returnPaymentResponse: '.$content);
if ($content !== false)
{
$element = $this->parseXmlResponse('returnPaymentResponse', $content);
$status = (int)$element->getAttribute('status');
if ($status == 0)
{
$result->setOperationType(PaySystem\ServiceResult::MONEY_LEAVING);
}
else
{
$error .= Loc::getMessage('SALE_HPS_YANDEX_REFUND_ERROR').' '.Loc::getMessage('SALE_HPS_YANDEX_REFUND_ERROR_INFO', array('#STATUS#' => $status, '#ERROR#' => $element->getAttribute('error')));
}
}
else
{
$error .= Loc::getMessage('SALE_HPS_YANDEX_REFUND_CONNECTION_ERROR', array('#URL#' => $url, '#ERROR#' => $curlError, '#CODE#' => $httpCode));
}
}
else
{
$error .= implode("\n", $signResult->getErrorMessages());
}
if ($error !== '')
{
$result->addError(new Error($error));
$error = 'Yandex: returnPaymentRequest: '.join('\n', $result->getErrorMessages());
PaySystem\Logger::addError($error);
}
return $result;
}
/**
* @param Payment $payment
* @param Request $request
* @return bool
* @throws \Bitrix\Main\ArgumentNullException
* @throws \Bitrix\Main\ArgumentOutOfRangeException
* @throws \Bitrix\Main\ObjectException
*/
private function isCorrectHash(Payment $payment, Request $request)
{
$hash = md5(
implode(";", array(
$request->get('action'),
$request->get('orderSumAmount'),
$request->get('orderSumCurrencyPaycash'),
$request->get('orderSumBankPaycash'),
$this->getBusinessValue($payment, 'YANDEX_SHOP_ID'),
$request->get('invoiceId'),
$this->getBusinessValue($payment, 'PAYMENT_BUYER_ID'),
$this->getBusinessValue($payment, 'YANDEX_SHOP_KEY')
)
)
);
PaySystem\Logger::addDebugInfo(
'Yandex: calculatedHash='.ToUpper($hash)."; yandexHash=".ToUpper($request->get('md5'))
);
return ToUpper($hash) === ToUpper($request->get('md5'));
}
/**
* @param Payment $payment
* @param Request $request
* @return bool
* @throws \Bitrix\Main\ArgumentNullException
* @throws \Bitrix\Main\ArgumentOutOfRangeException
* @throws \Bitrix\Main\ObjectException
*/
private function isCorrectSum(Payment $payment, Request $request)
{
$sum = $request->get('orderSumAmount');
$paymentSum = $this->getBusinessValue($payment, 'PAYMENT_SHOULD_PAY');
PaySystem\Logger::addDebugInfo(
'Yandex: yandexSum='.roundEx($sum, 2)."; paymentSum=".roundEx($paymentSum, 2)
);
return roundEx($paymentSum, 2) == roundEx($sum, 2);
}
/**
* @param PaySystem\ServiceResult $result
* @param Request $request
* @return mixed
*/
public function sendResponse(PaySystem\ServiceResult $result, Request $request)
{
global $APPLICATION;
$APPLICATION->RestartBuffer();
$data = $result->getData();
if (!$result->isResultApplied() && $data['CODE'] === 0)
{
$data['CODE'] = 200;
}
$dateISO = date("Y-m-d\TH:i:s").substr(date("O"), 0, 3).":".substr(date("O"), -2, 2);
header("Content-Type: text/xml");
header("Pragma: no-cache");
$text = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
if (strlen($data['HEAD']) > 0)
{
$text .= "<".$data['HEAD']." performedDatetime=\"".$dateISO."\"";
$text .= " code=\"".$data['CODE']."\" shopId=\"".$data['SHOP_ID']."\" invoiceId=\"".$data['INVOICE_ID']."\"";
if (strlen($data['TECH_MESSAGE']) > 0)
$text .= " techMessage=\"".$data['TECH_MESSAGE']."\"";
$text .= "/>";
}
PaySystem\Logger::addDebugInfo('Yandex: response: '.$text);
echo $text;
die();
}
/**
* @param Request $request
* @return mixed
*/
public function getPaymentIdFromRequest(Request $request)
{
return $request->get('orderNumber');
}
/**
* @param Payment $payment
* @param Request $request
* @return PaySystem\ServiceResult
*/
private function processCheckAction(Payment $payment, Request $request)
{
$result = new PaySystem\ServiceResult();
$data = $this->extractDataFromRequest($request);
if ($this->isCorrectSum($payment, $request))
{
$data['CODE'] = 0;
}
else
{
$data['CODE'] = 100;
$errorMessage = 'Incorrect payment sum';
$result->addError(new Error($errorMessage));
PaySystem\Logger::addError('Yandex: checkOrderResponse: '.$errorMessage);
}
$result->setData($data);
return $result;
}
/**
* @param Request $request
* @return array
*/
private function extractDataFromRequest(Request $request)
{
return array(
'HEAD' => $request->get('action').'Response',
'SHOP_ID' => $request->get('shopId'),
'INVOICE_ID' => $request->get('invoiceId')
);
}
/**
* @param Payment $payment
* @param Request $request
* @return PaySystem\ServiceResult
*/
private function processNoticeAction(Payment $payment, Request $request)
{
$result = new PaySystem\ServiceResult();
$data = $this->extractDataFromRequest($request);
$modeList = static::getHandlerModeList();
$description = Loc::getMessage('SALE_HPS_YANDEX_TRANSACTION').": ".$request->get('invoiceId')."; ";
if ($request->get('paymentDatetime'))
{
$description .= Loc::getMessage('SALE_HPS_YANDEX_DATE_PAYED').": ".$request->get('paymentDatetime');
}
$fields = array(
"PS_STATUS_CODE" => substr($data['HEAD'], 0, 5),
"PS_STATUS_DESCRIPTION" => $description,
"PS_STATUS_MESSAGE" => $modeList[$request->get('paymentType')],
"PS_SUM" => $request->get('orderSumAmount'),
"PS_CURRENCY" => substr($request->get('orderSumCurrencyPaycash'), 0, 3),
"PS_RESPONSE_DATE" => new DateTime(),
"PS_INVOICE_ID" => $request->get('invoiceId')
);
if ($this->isCorrectSum($payment, $request))
{
$data['CODE'] = 0;
$fields["PS_STATUS"] = "Y";
PaySystem\Logger::addDebugInfo(
'Yandex: 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
{
$data['CODE'] = 200;
$fields["PS_STATUS"] = "N";
$errorMessage = 'Incorrect payment sum';
$result->addError(new Error($errorMessage));
PaySystem\Logger::addError('Yandex: paymentAvisoResponse: '.$errorMessage);
}
$result->setData($data);
$result->setPsData($fields);
return $result;
}
/**
* @param Payment $payment
* @param Request $request
* @return PaySystem\ServiceResult
*/
private function processCancelAction(Payment $payment, Request $request)
{
$result = new PaySystem\ServiceResult();
$data = $this->extractDataFromRequest($request);
if ($this->isCorrectHash($payment, $request))
{
$data['CODE'] = 0;
$result->setOperationType(PaySystem\ServiceResult::MONEY_LEAVING);
}
else
{
$data['CODE'] = 1;
$errorMessage = 'Incorrect payment hash sum';
$result->addError(new Error($errorMessage));
PaySystem\Logger::addError('Yandex: cancelOrderResponse: '.$errorMessage);
}
$result->setData($data);
return $result;
}
/**
* @return mixed
*/
protected function getUrlList()
{
return array(
'pay' => array(
self::TEST_URL => 'https://demomoney.yandex.ru/eshop.xml',
self::ACTIVE_URL => 'https://money.yandex.ru/eshop.xml'
),
'confirm' => array(
self::ACTIVE_URL => 'https://penelope.yamoney.ru/webservice/mws/api/confirmPayment',
self::TEST_URL => 'https://penelope-demo.yamoney.ru:8083/webservice/mws/api/confirmPayment'
),
'cancel' => array(
self::ACTIVE_URL => 'https://penelope.yamoney.ru/webservice/mws/api/cancelPayment',
self::TEST_URL => 'https://penelope-demo.yamoney.ru:8083/webservice/mws/api/cancelPayment'
),
'return' => array(
self::ACTIVE_URL => 'https://penelope.yamoney.ru/webservice/mws/api/returnPayment',
self::TEST_URL => 'https://penelope-demo.yamoney.ru:8083/webservice/mws/api/returnPayment',
)
);
}
/**
* @param Payment $payment
* @param Request $request
* @return PaySystem\ServiceResult
*/
public function processRequest(Payment $payment, Request $request)
{
$result = new PaySystem\ServiceResult();
$action = $request->get('action');
if ($this->isCorrectHash($payment, $request))
{
if ($action == 'checkOrder')
{
return $this->processCheckAction($payment, $request);
}
else if ($action == 'cancelOrder')
{
return $this->processCancelAction($payment, $request);
}
else if ($action == 'paymentAviso')
{
return $this->processNoticeAction($payment, $request);
}
else
{
$data = $this->extractDataFromRequest($request);
$data['TECH_MESSAGE'] = 'Unknown action: '.$action;
$result->setData($data);
$result->addError(new Error('Unknown action: '.$action.'. Request='.join(', ', $request->toArray())));
}
}
else
{
$data = $this->extractDataFromRequest($request);
$data['TECH_MESSAGE'] = 'Incorrect hash sum';
$data['CODE'] = 1;
$result->setData($data);
$result->addError(new Error('Incorrect hash sum'));
}
if (!$result->isSuccess())
{
$error = 'Yandex: processRequest: '.$action.': '.join('\n', $result->getErrorMessages());
PaySystem\Logger::addError($error);
}
return $result;
}
/**
* @param Payment $payment
* @return bool
*/
protected function isTestMode(Payment $payment = null)
{
return ($this->getBusinessValue($payment, 'PS_IS_TEST') == 'Y');
}
/**
* @param Payment $payment
* @return PaySystem\ServiceResult
*/
public function confirm(Payment $payment)
{
$result = new PaySystem\ServiceResult();
$httpClient = new HttpClient();
$url = $this->getUrl($payment, 'confirm');
$requestDT = date('c');
$request = array(
'orderId' => $this->getBusinessValue($payment, 'PAYMENT_ID'),
'amount' => $this->getBusinessValue($payment, 'PAYMENT_SHOULD_PAY'),
'currency' => $this->getBusinessValue($payment, 'PAYMENT_CURRENCY'),
'requestDT' => $requestDT
);
$responseString = $httpClient->post($url, $request);
if ($responseString !== false)
{
$element = $this->parseXmlResponse('confirmPaymentResponse', $responseString);
$status = (int)$element->getAttribute('status');
if ($status == 0)
$result->setOperationType(PaySystem\ServiceResult::MONEY_COMING);
else
$result->addError(new Error('Error on try to confirm payment. Status: '.$status));
}
else
{
$result->addError(new Error("Error sending request. URL=".$url." PARAMS=".join(' ', $request)));
}
if (!$result->isSuccess())
{
$error = 'Yandex: confirmPayment: '.join('\n', $result->getErrorMessages());
PaySystem\Logger::addError($error);
}
return $result;
}
/**
* @param Payment $payment
* @return PaySystem\ServiceResult
*/
public function cancel(Payment $payment)
{
$result = new PaySystem\ServiceResult();
$httpClient = new HttpClient();
$url = $this->getUrl($payment, 'cancel');
$requestDT = date('c');
$request = array(
'orderId' => $this->getBusinessValue($payment, 'PAYMENT_ID'),
'requestDT' => $requestDT
);
$responseString = $httpClient->post($url, $request);
if($responseString !== false)
{
$element = $this->parseXmlResponse('cancelPaymentResponse', $responseString);
$status = (int)$element->getAttribute('status');
if ($status == 0)
$result->setOperationType(PaySystem\ServiceResult::MONEY_LEAVING);
else
$result->addError(new Error('Error on try to cancel payment. Status: '.$status));
}
else
{
$result->addError(new Error('Error sending request. URL='.$url.' PARAMS='.join(' ', $request)));
}
if (!$result->isSuccess())
{
$error = 'Yandex: cancelPayment: '.join('\n', $result->getErrorMessages());
PaySystem\Logger::addError($error);
}
return $result;
}
/**
* @param $operation
* @param $requestString
* @return \CDataXMLNode
*/
private function parseXmlResponse($operation, $requestString)
{
$xmlParser = new \CDataXML();
$xmlParser->LoadString($requestString);
$tree = $xmlParser->GetTree();
$elements = $tree->elementsByName($operation);
return $elements[0];
}
/**
* @param Payment $payment
* @param $xml
* @return PaySystem\ServiceResult
* @throws \Bitrix\Main\ArgumentNullException
*/
private function signRequest(Payment $payment, $xml)
{
$result = new PaySystem\ServiceResult();
$dataFile = self::createTmpFile($xml);
$signedFile = self::createTmpFile();
$shopId = $this->getBusinessValue($payment, 'YANDEX_SHOP_ID');
$CertPem = PaySystem\YandexCert::getValue('CERT', $shopId);
$PkeyPem = PaySystem\YandexCert::getValue('PKEY', $shopId);
if ($PkeyPem && $CertPem)
{
if (openssl_pkcs7_sign($dataFile, $signedFile, $CertPem, $PkeyPem, array(), PKCS7_NOCHAIN + PKCS7_NOCERTS))
{
$signedData = explode("\n\n", file_get_contents($signedFile));
$pkcs7 = "-----BEGIN PKCS7-----\n".$signedData[1]."\n-----END PKCS7-----";
$result->setData(array('PKCS7' => $pkcs7));
}
else
{
$result->addError(new Error(Loc::getMessage('SALE_HPS_YANDEX_REFUND_ERROR')));
}
}
else
{
$result->addError(new Error(Loc::getMessage('SALE_HPS_YANDEX_REFUND_ERROR')));
}
return $result;
}
/**
* @param null $data
* @return mixed
*/
private static function createTmpFile($data = null)
{
$filePath = tempnam(sys_get_temp_dir(), 'YaMWS');
if ($data !== null)
file_put_contents($filePath, $data);
return $filePath;
}
/**
* @return array
*/
public function getCurrencyList()
{
return array('RUB');
}
/**
* @return array
*/
public static function getHandlerModeList()
{
return array(
"PC" => Loc::getMessage("SALE_HPS_YANDEX_YMoney"),
"AC" => Loc::getMessage("SALE_HPS_YANDEX_Cards"),
"GP" => Loc::getMessage("SALE_HPS_YANDEX_Terminals"),
"MC" => Loc::getMessage("SALE_HPS_YANDEX_Mobile"),
"WM" => "WebMoney",
"SB" => Loc::getMessage("SALE_HPS_YANDEX_Sberbank"),
"MP" => Loc::getMessage("SALE_HPS_YANDEX_mPOS"),
"AB" => Loc::getMessage("SALE_HPS_YANDEX_AlphaClick"),
"MA" => Loc::getMessage("SALE_HPS_YANDEX_MasterPass"),
"PB" => Loc::getMessage("SALE_HPS_YANDEX_Promsvyazbank"),
"QW" => Loc::getMessage("SALE_HPS_YANDEX_Qiwi"),
// "KV" => Loc::getMessage("SALE_HPS_YANDEX_TinkoffBank"),
"QP" => Loc::getMessage("SALE_HPS_YANDEX_YKuppiRu"),
"" => Loc::getMessage("SALE_HPS_YANDEX_Smart")
);
}
/**
* @return bool
*/
public function isRefundableExtended()
{
$whiteList = array('PC', 'AC', 'MC', 'WM', 'MP', 'AB', 'MA', 'QW', 'KV', 'QP');
return in_array($this->service->getField('PS_MODE'), $whiteList);
}
/**
* @return bool
*/
public function isTuned()
{
$personTypeList = PaySystem\Manager::getPersonTypeIdList($this->service->getField('ID'));
$personTypeId = array_shift($personTypeList);
$shopId = BusinessValue::get('YANDEX_SHOP_ID', $this->service->getConsumerName(), $personTypeId);
return !empty($shopId);
}
/**
* @param array $paySystemList
* @return array
*/
public static function findMyDataRefundablePage(array $paySystemList)
{
$result = array();
$personTypeList = BusinessValue::getPersonTypes();
$handler = PaySystem\Manager::getFolderFromClassName(get_called_class());
$description = PaySystem\Manager::getHandlerDescription($handler);
foreach ($paySystemList as $data)
{
foreach ($personTypeList as $personType)
{
$shopId = BusinessValue::get('YANDEX_SHOP_ID', PaySystem\Service::PAY_SYSTEM_PREFIX.$data['ID'], $personType['ID']);
if ($shopId && !isset($result[$shopId]))
{
$cert = PaySystem\YandexCert::getCert($shopId);
$result[$shopId] = array(
'EXTERNAL_ID' => $shopId,
'NAME' => $description['NAME'],
'HANDLER' => 'yandex',
'LINK_PARAMS' => 'shop_id='.$shopId,
'CONFIGURED' => ($cert) ? 'Y' : 'N'
);
}
}
}
return $result;
}
}