%PDF- %PDF-
| Direktori : /home/bitrix/www/bitrix/modules/sale/handlers/paysystem/yandexinvoice/ |
| Current File : /home/bitrix/www/bitrix/modules/sale/handlers/paysystem/yandexinvoice/handler.php |
<?php
namespace Sale\Handlers\PaySystem;
use Bitrix\Main\Application;
use Bitrix\Main\ArgumentException;
use Bitrix\Main\Error;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\Request;
use Bitrix\Main\Type\DateTime;
use Bitrix\Main\Web;
use Bitrix\Sale\BusinessValue;
use Bitrix\Sale\Internals\YandexSettingsTable;
use Bitrix\Sale\Payment;
use Bitrix\Sale\PaySystem;
use Bitrix\Sale\PriceMaths;
Loc::loadMessages(__FILE__);
class YandexInvoiceHandler extends PaySystem\ServiceHandler
{
/**
* @param Payment $payment
* @param Request|null $request
* @return PaySystem\ServiceResult
*/
public function initiatePay(Payment $payment, Request $request = null)
{
$errors = '';
if ($request === null)
{
$instance = Application::getInstance();
$context = $instance->getContext();
$request = $context->getRequest();
}
$serviceResult = new PaySystem\ServiceResult();
if ($request->get('phone') !== null)
{
$payload = array(
'payer' => array('phone' => $request->get('phone')),
'recipient' => array(
'shopId' => $this->getBusinessValue($payment, 'YANDEX_INVOICE_SHOP_ID'),
'shopArticleId' => $this->getBusinessValue($payment, 'YANDEX_INVOICE_SHOP_ARTICLE_ID'),
),
'order' => array(
'clientOrderId' => $this->getBusinessValue($payment, 'PAYMENT_ID'),
'customerId' => $request->get('phone'),
'amount' => $this->getBusinessValue($payment, 'PAYMENT_SHOULD_PAY'),
'currency' => $payment->getField('CURRENCY'),
'parameters' => array(
'pay_system_id' => $this->service->getField('ID')
)
),
'schemes' => array($this->service->getField('PS_MODE'))
);
$params = $this->prepareRequest($payment, $payload);
$http = new Web\HttpClient();
$http->setCharset("utf-8");
$result = $http->post($this->getUrl($payment, 'payments'), array('request' => $params));
if ($result)
{
try
{
$result = Web\Json::decode($result);
}
catch (ArgumentException $e)
{
$errors .= $e->getMessage();
}
if ($errors === '')
{
if (in_array($result['status'], array('Created', 'Approved')))
{
$scheme = current($result['schemes']);
if ($scheme)
{
$billUrl = $this->getUrl($payment, 'bill');
$payload = array(
'payer' => array('phone' => $request->get('phone')),
'scheme' => $scheme['scheme'],
'orderId' => $result['orderId'],
);
$http = new Web\HttpClient();
$http->setCharset("utf-8");
$result = $http->post($billUrl, array('request' => $this->prepareRequest($payment, $payload)));
if ($result)
{
try
{
$result = Web\Json::decode($result);
}
catch (ArgumentException $e)
{
$errors .= $e->getMessage();
}
if (in_array($result['status'], array('Authorized', 'Processing')))
$serviceResult->setPsData(array('PS_INVOICE_ID' => $result['orderId']));
else
$errors .= $result['error'];
}
else
{
$errors .= implode("\n", $http->getError());
}
}
}
else if ($result['status'] == 'Refused')
{
$errors .= $result['error'];
}
}
}
else
{
$errors .= implode("\n", $http->getError());
}
if ($errors === '')
{
$templateName = 'success';
$this->setExtraParams(array('PAYMENT_SUM' => $payment->getSum()));
}
else
{
$serviceResult->addError(new Error($errors));
$templateName = 'failure';
}
$template = $this->showTemplate($payment, $templateName);
$serviceResult->setTemplate($template->getTemplate());
}
else
{
/** @var \Bitrix\Sale\PaymentCollection $paymentCollection */
$paymentCollection = $payment->getCollection();
/** @var \Bitrix\Sale\Order $order */
$order = $paymentCollection->getOrder();
$this->setExtraParams(
array(
'ORDER_ID' => $order->getId(),
'ACCOUNT_NUMBER' => $order->getField('ACCOUNT_NUMBER'),
'PAYSYSTEM_ID' => $this->service->getField('ID')
)
);
return $this->showTemplate($payment, 'template');
}
return $serviceResult;
}
/**
* @return array
*/
public function getCurrencyList()
{
return array('RUB');
}
/**
* @param Payment $payment
* @param Request $request
* @return mixed
*/
public function processRequest(Payment $payment, Request $request)
{
$serviceResult = new PaySystem\ServiceResult();
if ($request->get('request') === null)
return $serviceResult;
list($header, $payload, $sign) = explode('.', $request->get('request'));
if (!$this->isSignCorrect($payment, $header.'.'.$payload, $sign))
{
$payload = Web\Json::decode(self::base64Decode($payload));
$data = array(
'notificationType' => 'PaymentAviso',
'orderId' => $payload['orderId'],
'shopId' => $this->getBusinessValue($payment, 'YANDEX_INVOICE_SHOP_ID'),
'shopArticleId' => $this->getBusinessValue($payment, 'YANDEX_INVOICE_SHOP_ARTICLE_ID'),
'status' => 'Refused',
'error' => 'IllegalSignature'
);
$serviceResult->setData(array('response' => $this->prepareRequest($payment, $data)));
return $serviceResult;
}
$header = Web\Json::decode(self::base64Decode($header));
$payload = Web\Json::decode(self::base64Decode($payload));
if ($payload['notificationType'] === 'PaymentAviso' && $header['iss'] === 'Yandex.Money')
{
return $this->processNoticeAction($payment, $payload);
}
$data = array(
'notificationType' => 'PaymentAviso',
'orderId' => $payload['orderId'],
'shopId' => $this->getBusinessValue($payment, 'YANDEX_INVOICE_SHOP_ID'),
'shopArticleId' => $this->getBusinessValue($payment, 'YANDEX_INVOICE_SHOP_ARTICLE_ID'),
'status' => 'Refused',
'error' => 'SyntaxError'
);
$serviceResult->setData(array('response' => $this->prepareRequest($payment, $data)));
return $serviceResult;
}
/**
* @param Payment $payment
* @param array $payload
* @return PaySystem\ServiceResult
*/
private function processNoticeAction(Payment $payment, array $payload)
{
$serviceResult = new PaySystem\ServiceResult();
$data = array(
'notificationType' => 'PaymentAviso',
'orderId' => $payload['orderId'],
'shopId' => $this->getBusinessValue($payment, 'YANDEX_INVOICE_SHOP_ID'),
'shopArticleId' => $this->getBusinessValue($payment, 'YANDEX_INVOICE_SHOP_ARTICLE_ID'),
);
$paymentPrice = PriceMaths::roundPrecision($this->getBusinessValue($payment, 'PAYMENT_SHOULD_PAY'));
$yandexPrice = PriceMaths::roundPrecision($payload['order']['amount']);
if ($yandexPrice === $paymentPrice)
{
$serviceResult->setOperationType($serviceResult::MONEY_COMING);
$psData = array(
'PS_INVOICE_ID' => $payload['orderId'],
'PS_STATUS' => ($payload['status'] == 'Authorized') ? 'Y' : 'N',
'PS_SUM' => $payload['order']['amount'],
'PS_CURRENCY' => substr($payload['order']['currency'], 0, 3),
'PS_RESPONSE_DATE' => new DateTime(),
);
$serviceResult->setPsData($psData);
$data['status'] = 'Delivered';
}
else
{
$data['status'] = 'Refused';
$data['error'] = 'SyntaxError';
$serviceResult->addError(new Error(Loc::getMessage('SALE_HPS_YANDEX_INVOICE_ERROR_SUM')));
}
$serviceResult->setData(array('response' => $this->prepareRequest($payment, $data)));
return $serviceResult;
}
/**
* @param Request $request
* @return string
*/
public function getPaymentIdFromRequest(Request $request)
{
list($header, $payload, $sign) = explode('.', $request->get('request'));
if ($payload)
{
$payload = Web\Json::decode(self::base64Decode($payload));
return $payload['order']['clientOrderId'];
}
return '';
}
/**
* @return array
*/
protected function getUrlList()
{
return array(
'test' => array(
self::TEST_URL => 'http://angius.yandex.ru:8082/payment-api/json-api/api/version',
self::ACTIVE_URL => 'https://money.yandex.ru/api/v2/version'
),
'payments' => array(
self::TEST_URL => 'http://angius.yandex.ru:8082/payment-api/json-api/api/payments',
self::ACTIVE_URL => 'https://money.yandex.ru/api/v2/payments'
),
'bill' => array(
self::TEST_URL => 'http://angius.yandex.ru:8082/payment-api/json-api/api/mobileInvoice',
self::ACTIVE_URL => 'https://money.yandex.ru/api/v2/payments/mobileInvoice'
)
);
}
/**
* @return array
*/
public static function getHandlerModeList()
{
return array(
'Sberbank' => Loc::getMessage('SALE_HPS_YANDEX_INVOICE_SBERBANK'),
'Wallet' => Loc::getMessage('SALE_HPS_YANDEX_INVOICE_WALLET')
);
}
/**
* @param Payment $payment
* @param array $payload
* @return string
* @throws \Bitrix\Main\ArgumentTypeException
*/
private function prepareRequest(Payment $payment, array $payload)
{
$header = array(
"alg" => "ES256",
"iat" => round(microtime(true) * 1000),
"iss" => "shopId:".$this->getBusinessValue($payment, 'YANDEX_INVOICE_SHOP_ID'),
"aud" => $this->isTestMode($payment) ? 'test' : 'production'
);
$jsonHeader = Web\Json::encode((object)$header);
$jsonPayload = Web\Json::encode((object)$payload);
$data = self::base64Encode($jsonHeader).'.'.self::base64Encode($jsonPayload);
$sign = '';
$shopId = $this->getBusinessValue($payment, 'YANDEX_INVOICE_SHOP_ID');
if ($shopId)
{
$dbRes = YandexSettingsTable::getById($shopId);
if ($settings = $dbRes->fetch())
$sign = $this->sign($data, $this->getKeyResource($settings['PKEY']));
}
return $data.'.'.self::base64Encode($sign);
}
/**
* @param $data
* @return mixed
*/
static private function base64Encode($data)
{
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
/**
* @param $data
* @return mixed
*/
static private function base64Decode($data)
{
return base64_decode(strtr($data, '-_', '+/'));
}
/**
* @param PaySystem\ServiceResult $result
* @param Request $request
* @return mixed
*/
public function sendResponse(PaySystem\ServiceResult $result, Request $request)
{
global $APPLICATION;
$APPLICATION->RestartBuffer();
$data = $result->getData();
echo $data['response'];
$APPLICATION->FinalActions();
die();
}
/**
* @param Payment $payment
* @param $data
* @param $sign
* @return bool
*/
private function isSignCorrect(Payment $payment, $data, $sign)
{
$binary = self::base64Decode($sign);
list($r, $s) = str_split($binary, (int) (strlen($binary) / 2));
$r = ltrim($r, "\x00");
$s = ltrim($s, "\x00");
if (ord($r[0]) > 0x7f) $r = "\x00" . $r;
if (ord($s[0]) > 0x7f) $s = "\x00" . $s;
$binary = PaySystem\ASN1::encodeDER(
PaySystem\ASN1::SEQUENCE,
PaySystem\ASN1::encodeDER(PaySystem\ASN1::INTEGER_TYPE, $r).PaySystem\ASN1::encodeDER(PaySystem\ASN1::INTEGER_TYPE, $s),
false
);
$shopId = $this->getBusinessValue($payment, 'YANDEX_INVOICE_SHOP_ID');
$dbRes = YandexSettingsTable::getById($shopId);
if ($settings = $dbRes->fetch())
{
$verify = openssl_verify($data, $binary, $settings['PUB_KEY'], 'SHA256');
return $verify === 1;
}
return false;
}
/**
* @param $input
* @param $keyResource
* @return null|string
*/
private function sign($input, $keyResource)
{
$signature = null;
$signAlgo = version_compare(phpversion(), '5.4.8', '<') ? 'SHA256' : OPENSSL_ALGO_SHA256;
$r = openssl_sign($input, $signature, $keyResource, $signAlgo);
if ($r === true)
{
$offset = 0;
$offset += PaySystem\ASN1::readDER($signature, $offset, $value);
$offset += PaySystem\ASN1::readDER($signature, $offset, $r);
$offset += PaySystem\ASN1::readDER($signature, $offset, $s);
$r = ltrim($r, "\x00");
$s = ltrim($s, "\x00");
// $r = str_pad($r, $keyResource->getSize() / 8, "\x00", STR_PAD_LEFT);
// $s = str_pad($s, $keyResource->getSize() / 8, "\x00", STR_PAD_LEFT);
$signature = $r . $s;
}
return $signature;
}
/**
* @param $key
* @return mixed
*/
private function getKeyResource($key)
{
if (is_resource($key))
return $key;
return openssl_pkey_get_public($key) ?: openssl_pkey_get_private($key);
}
/**
* @param Payment|null $payment
* @return bool
*/
protected function isTestMode(Payment $payment = null)
{
return $this->getBusinessValue($payment, 'PS_IS_TEST') == 'Y';
}
/**
* @return array
*/
static public function getIndicativeFields()
{
return array('request');
}
/**
* @param Request $request
* @param $paySystemId
* @return bool
*/
protected static function isMyResponseExtended(Request $request, $paySystemId)
{
list($header, $payload, $sign) = explode('.', $request->get('request'));
if ($payload)
{
$payload = Web\Json::decode(self::base64Decode($payload));
if (!array_key_exists('parameters', $payload['order']))
return false;
if (!array_key_exists('pay_system_id', $payload['order']['parameters']))
return false;
return $paySystemId == $payload['order']['parameters']['pay_system_id'];
}
return false;
}
/**
* @return bool
*/
public function isTuned()
{
$personTypeList = PaySystem\Manager::getPersonTypeIdList($this->service->getField('ID'));
$personTypeId = array_shift($personTypeList);
$shopId = BusinessValue::get('YANDEX_INVOICE_SHOP_ID', $this->service->getConsumerName(), $personTypeId);
return !empty($shopId);
}
}