yoomoney_api-2.5.2/yoomoney_api.module
yoomoney_api.module
<?php
use YooKassa\Client;
use YooKassa\Model\Notification\NotificationFactory;
use YooKassa\Model\NotificationEventType;
use YooKassa\Model\Payment;
use YooKassa\Model\PaymentMethodType;
use YooKassa\Model\PaymentStatus;
use YooKassa\Request\Payments\Payment\CreateCaptureRequest;
use YooKassa\Request\Payments\Payment\CreateCaptureResponse;
define('MODULE_PATH', drupal_get_path('module', 'yoomoney_api'));
require_once MODULE_PATH.DIRECTORY_SEPARATOR.'YooMoneyLogger.php';
require_once MODULE_PATH.DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'autoload.php';
require_once MODULE_PATH.DIRECTORY_SEPARATOR.'YooMoneySecondReceiptModel.php';
require_once MODULE_PATH.DIRECTORY_SEPARATOR.'YooMoneyOauth.php';
require_once MODULE_PATH.DIRECTORY_SEPARATOR.'YooMoneyClientFactory.php';
require_once MODULE_PATH.DIRECTORY_SEPARATOR.'YooMoneyWebhookSubscriber.php';
require_once MODULE_PATH.DIRECTORY_SEPARATOR.'YooMoneyKassaLogger.php';
define('YOOMONEY_API_CHECK_RESULT_CODE_SUCCESS', 0);
define('YOOMONEY_API_CHECK_RESULT_CODE_ERROR_MD5', 1);
define('YOOMONEY_API_CHECK_RESULT_CODE_ERROR_CUSTOM', 100);
define('YOOMONEY_API_CHECK_RESULT_CODE_ERROR_REQUEST', 200);
define('YOOMONEY_API_DEFAULT_TAX_RATE_ID', 1);
define('YOOMONEY_MODULE_VERSION', '2.5.2');
define('YOOMONEY_API_ORDER_STATUS_PENDING', 'pending');
define('YOOMONEY_API_ORDER_STATUS_PROCESSING', 'processing');
define('YOOMONEY_API_ORDER_STATUS_CANCELED', 'canceled');
define('YOOMONEY_API_ORDER_STATUS_COMPLETED', 'completed');
define('CMS_NAME_COMMERCE', 'yoo_api_drupal_commerce_epl');
define('CMS_NAME_COMMERCE_OLD', 'yoo_api_drupal_commerce');
define('CMS_NAME_UBERCART', 'yoo_api_drupal_ubercart_epl');
define('CMS_NAME_UBERCART_OLD', 'yoo_api_drupal_ubercart');
define('MODULES_NAME_COMMERCE', 'yoo_commerce_api');
define('MODULES_NAME_UBERCART', 'yoo_ubercart_api');
/**
* Implements hook_init().
*/
function yoomoney_api_init()
{
module_load_include('inc', 'yoomoney_api', 'transactions');
}
/**
* Проверяет, включен или выключен модуль
* @param string $moduleName
* @param int $status
* @return bool
*/
function isModuleEnabled($moduleName, $status)
{
$existing = db_query("SELECT status FROM {system} WHERE type = :type AND name = :name", array(
':type' => 'module',
':name' => $moduleName))
->fetchObject();
if (empty($existing)) {
return false;
}
if ($existing->status == $status) {
return true;
}
return false;
}
/**
* Обработка hook_modules_disabled().
* Отключение подмодулей в списке модулей
* @param array $modules
* @return void
*/
function yoomoney_api_modules_disabled($modules)
{
foreach ($modules as $module) {
if ($module === MODULES_NAME_COMMERCE || $module === MODULES_NAME_UBERCART) {
$kassaLogger = getKassaLog();
$kassaLogger->sendHeka(array("module.uninstall.init"));
if (isModuleEnabled($module, 0)) {
$kassaLogger->sendHeka(array("module.uninstall.success"));
return;
}
$kassaLogger->sendHeka(array("module.uninstall.fail"));
}
}
}
/**
* Обработка hook_modules_enabled().
* Включение подмодулей в списке модулей
* @param array $modules
* @return void
*/
function yoomoney_api_modules_enabled($modules)
{
foreach ($modules as $module) {
if ($module === MODULES_NAME_COMMERCE || $module === MODULES_NAME_UBERCART) {
$kassaLogger = getKassaLog();
$kassaLogger->sendHeka(array("module.install.init"));
if (isModuleEnabled($module, 1)) {
$kassaLogger->sendHeka(array("module.install.success"));
return;
}
$kassaLogger->sendHeka(array("module.install.fail"));
}
}
}
/**
* Создание объекта YooMoneyKassaLogger
* @return YooMoneyKassaLogger
*/
function getKassaLog()
{
return new YooMoneyKassaLogger(variable_get('yoomoney_api_shop_id', 'null'));
}
/**
* Implements hook_permission().
*/
function yoomoney_api_permission()
{
return array(
'access yoomoney_api_ money transaction' => array(
'title' => t('Access YooMoney transaction'),
'description' => t('Display the YooMoney transaction.'),
),
'access own yoomoney_api_ money transaction' => array(
'title' => t('Access own YooMoney transaction'),
'description' => t('Display the YooMoney transaction by own user.'),
),
'access yoomoney_api settings' => array(
'title' => t('Access YooMoney API'),
'description' => t('Access to YooMoney API variables.'),
),
);
}
/**
* Implements hook_menu().
*/
function yoomoney_api_menu()
{
$items = array();
// Config page
$items['admin/config/system/yoomoney_api'] = array(
'title' => t('YooMoney Core'),
'description' => t('YooMoney base settings page.'),
'page callback' => 'drupal_get_form',
'page arguments' => array('yoomoney_api_admin_settings'),
'access arguments' => array('access yoomoney_api settings'),
'type' => MENU_NORMAL_ITEM,
'file' => 'yoomoney_api.admin.inc',
);
// Transactions page menu
$items['admin/reports/yoomoney_api_transactions'] = array(
'title' => t('YooMoney transactions '),
'description' => t('YooMoney all transactions page.'),
'page callback' => 'yoomoney_api_transactions_page',
'access arguments' => array('access yoomoney_api_ money transaction'),
'type' => MENU_NORMAL_ITEM,
'file' => 'transactions.inc',
);
// yoomoney_api actions
$items['admin/yoomoney_api/actions'] = array(
'page callback' => 'yoomoney_api_actions',
'access arguments' => array('access yoomoney_api_ money transaction'),
'type' => MENU_CALLBACK,
);
// Account transaction table
$items['user/%user/yoomoney_api_payments'] = array(
'title' => t('My transactions'),
'description' => t('YooMoney user transactions page.'),
'page callback' => 'yoomoney_api_transactions_user_page',
'page arguments' => array(1),
'access arguments' => array('access own yoomoney_api_ money transaction'),
'type' => MENU_LOCAL_TASK,
'file' => 'transactions.inc',
);
// Payment callbacks
$items['yoomoney_api/commerce/return'] = array(
'page callback' => 'yoomoney_api_commerce_return_page',
'access callback' => true,
'type' => MENU_CALLBACK,
);
// Payment callbacks
$items['yoomoney_api/ubercart/return'] = array(
'page callback' => 'yoomoney_api_ubercart_return_page',
'access callback' => true,
'type' => MENU_CALLBACK,
);
$items['yoomoney_api/ubercart/notify'] = array(
'page callback' => 'yoomoney_api_ubercart_notify',
'access callback' => true,
'type' => MENU_CALLBACK,
);
$items['yoomoney_api/commerce/notify'] = array(
'page callback' => 'yoomoney_api_commerce_notify',
'access callback' => true,
'type' => MENU_CALLBACK,
);
$items['yoomoney_api/success'] = array(
'page callback' => 'yoomoney_api_complete_page',
'access callback' => true,
'type' => MENU_CALLBACK,
);
$items['yoomoney_api/fail'] = array(
'page callback' => 'yoomoney_api_fail_page',
'access callback' => true,
'type' => MENU_CALLBACK,
);
$items['yoomoney_api/temp'] = array(
'page callback' => 'yoomoney_api_temp_page',
'access callback' => true,
'type' => MENU_CALLBACK,
);
$items['yoomoney_api/get_oauth_url'] = array(
'page callback' => 'yoomoney_api_get_oauth_url',
'access callback' => true,
'type' => MENU_CALLBACK,
);
$items['yoomoney_api/get_oauth_token'] = array(
'page callback' => 'yoomoney_api_get_oauth_token',
'access callback' => true,
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* @return Client
* @throws Exception
*/
function yoomoney_api__common__get_api_client()
{
return YooMoneyClientFactory::getYooKassaClient();
}
function yoomoney_api_ubercart_return_page()
{
YooMoneyLogger::info('Return url init ubercart');
$failUrl = 'yoomoney_api/fail';
$successUrl = 'yoomoney_api/success';
$backToCart = 'cart/checkout';
$params = drupal_get_query_parameters();
if (!isset($params['orderId'])) {
YooMoneyLogger::info('Payment failed. Empty params orderId');
drupal_goto($failUrl);
}
$order = uc_order_load($params['orderId']);
if (!$order) {
YooMoneyLogger::info('Payment failed. Can not load oder id #'.$params['orderId']);
drupal_goto($failUrl);
}
YooMoneyLogger::info('Order: '.json_encode($order));
$transaction = yoomoney_api_transaction_load_by_order($order->order_id);
if (!$transaction) {
YooMoneyLogger::info('Payment failed. Transaction not found for order id #'.$params['orderId']);
drupal_goto($failUrl);
}
YooMoneyLogger::info('Transaction: '.json_encode($transaction));
$apiClient = yoomoney_api__common__get_api_client();
$paymentId = $transaction->payment_id;
try {
$payment = $apiClient->getPaymentInfo($paymentId);
if (!$payment) {
YooMoneyLogger::info('Payment failed. Empty payment info. PaymentId #'.$paymentId);
drupal_goto($failUrl);
}
$transaction->status = $payment->getStatus();
yoomoney_api_transaction_save($transaction);
$url = $payment->getPaid() ? $successUrl : $backToCart;
uc_cart_complete_sale($order, true);
drupal_goto($url);
} catch (Exception $e) {
YooMoneyLogger::info('Payment failed. Redirect: '.url($failUrl, array('absolute' => true)));
YooMoneyLogger::error('Error: '.$e->getMessage());
drupal_goto($failUrl);
}
}
function yoomoney_api_commerce_return_page()
{
YooMoneyLogger::info('Return url init');
$failUrl = 'yoomoney_api/fail';
$successUrl = 'yoomoney_api/success';
$params = drupal_get_query_parameters();
if (!isset($params['orderId'])) {
YooMoneyLogger::info('Payment failed. Empty params orderId');
drupal_goto($failUrl);
}
$order = commerce_order_load($params['orderId']);
if (!$order) {
YooMoneyLogger::info('Payment failed. Can not load oder id #'.$params['orderId']);
drupal_goto($failUrl);
}
$transaction = yoomoney_api_transaction_load_by_order($order->order_id);
if (!$transaction) {
YooMoneyLogger::info('Payment failed. Transaction not found for order id #'.$params['orderId']);
drupal_goto($failUrl);
}
$apiClient = yoomoney_api__common__get_api_client();
$paymentId = $transaction->payment_id;
try {
$payment = $apiClient->getPaymentInfo($paymentId);
if (!$payment) {
YooMoneyLogger::info('Payment failed. Empty payment info. PaymentId #'.$paymentId);
drupal_goto($failUrl);
}
$transaction->status = $payment->getStatus();
yoomoney_api_transaction_save($transaction);
$url = $payment->getPaid() ? $successUrl : $failUrl;
drupal_goto($url);
} catch (Exception $e) {
YooMoneyLogger::info('Payment failed. Redirect: '.url($failUrl, array('absolute' => true)));
YooMoneyLogger::error('Api error: '.$e->getMessage());
drupal_goto($failUrl);
}
}
function yoomoney_api_ubercart_notify()
{
$kassaLogger = getKassaLog();
$kassaLogger->sendHeka(array('payment.notification.init'));
YooMoneyLogger::info('Return notification init ubercart');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
$kassaLogger->sendHeka(array('payment.notification.skip'));
exit();
}
$callbackParams = yoomoney_api_common_get_payment_info_from_input();
try {
$factory = new NotificationFactory();
$notification = $factory->factory($callbackParams);
} catch (Exception $e) {
$kassaLogger->sendAlertLog(
'Invalid notification object',
array('exception' => $e),
array('payment.notification.fail'));
YooMoneyLogger::error('Invalid notification object');
header('HTTP/1.1 400 Bad Request');
exit();
}
$metadata = $notification->getObject()->getMetadata();
if (
!empty($metadata['cms_name'])
&& !in_array($metadata['cms_name'], array(CMS_NAME_UBERCART, CMS_NAME_UBERCART_OLD))
) {
$kassaLogger->sendHeka(array('payment.notification.skip'));
YooMoneyLogger::info('This notification not for Drupal 7 Ubercart. This notification for: ' . $metadata['cms_name']);
yoomoney_api_common_exit_200();
}
$paymentId = $notification->getObject()->getId();
$apiClient = yoomoney_api__common__get_api_client();
$transaction = yoomoney_api_transaction_load_by_payment($paymentId);
if (!$transaction) {
$kassaLogger->sendHeka(array('payment.notification.skip'));
YooMoneyLogger::error('Transaction not found');
header('HTTP/1.1 404 Not Found');
exit();
}
$orderResult = uc_order_load($transaction->order_id);
if ($orderResult && is_array($orderResult)) {
$order = $orderResult[0];
} else {
$order = $orderResult;
}
if (!$order) {
$kassaLogger->sendHeka(array('payment.notification.skip'));
YooMoneyLogger::error('Order not found');
header('HTTP/1.1 404 Not Found');
exit();
}
if ($order->order_status === YOOMONEY_API_ORDER_STATUS_COMPLETED) {
$kassaLogger->sendHeka(array('payment.notification.skip'));
yoomoney_api_common_exit_200();
}
$payment = $apiClient->getPaymentInfo($paymentId);
if (!$payment) {
$kassaLogger->sendHeka(array('payment.notification.fail'));
YooMoneyLogger::error('Empty payment info');
header('HTTP/1.1 400 Bad Request');
exit();
}
if ($notification->getEvent() === NotificationEventType::PAYMENT_WAITING_FOR_CAPTURE
&& $payment->getStatus() === PaymentStatus::WAITING_FOR_CAPTURE
) {
if (yoomoney_api__common__is_enable_hold_mode()
&& $payment->getPaymentMethod()->getType() === PaymentMethodType::BANK_CARD
) {
$kassaLogger->sendHeka(array(
'shop.'.$kassaLogger->getShopId().'.payment.waiting_for_capture',
'order-status.change.init'
));
YooMoneyLogger::info('Hold payment for order: '.$order->order_id);
if (uc_order_update_status($order->order_id, YOOMONEY_API_ORDER_STATUS_PROCESSING)) {
$kassaLogger->sendHeka(array('order-status.change.success'));
} else {
$kassaLogger->sendHeka(array('order-status.change.fail'));
}
uc_order_comment_save($order->order_id, 0,
sprintf(t('New payment received. Capture until %1$s, after that date it will be automatically canceled'),
$payment->getExpiresAt()->format('d.m.Y H:i')));
} else {
$payment = capturePayment($apiClient, $payment, $kassaLogger);
if (!$payment) {
$kassaLogger->sendHeka(array('payment.notification.fail'));
YooMoneyLogger::error('Empty capture payment info');
header('HTTP/1.1 400 Bad Request');
exit();
}
$kassaLogger->sendHeka(array('shop.'.$kassaLogger->getShopId().'.payment.succeeded'));
}
sendPaymentCountMetrics($kassaLogger);
yoomoney_api_update_transaction_status($paymentId, $payment->getStatus());
$kassaLogger->sendHeka(array(
'shop.'.$kassaLogger->getShopId().'.payment.succeeded',
'payment.notification.success'
));
yoomoney_api_common_exit_200();
}
if ($notification->getEvent() === NotificationEventType::PAYMENT_SUCCEEDED
&& $payment->getStatus() === PaymentStatus::SUCCEEDED
) {
YooMoneyLogger::info('Order status completed');
$kassaLogger->sendHeka(array('order-status.change.init'));
if (uc_order_update_status($order->order_id, YOOMONEY_API_ORDER_STATUS_COMPLETED)) {
$kassaLogger->sendHeka(array('order-status.change.success'));
} else {
$kassaLogger->sendHeka(array('order-status.change.fail'));
}
yoomoney_api_update_transaction_status($paymentId, $payment->getStatus());
yoomoney_api_update_transaction_amount($paymentId, $payment->getAmount()->getValue());
$kassaLogger->sendHeka(array(
'shop.'.$kassaLogger->getShopId().'.payment.succeeded',
'payment.notification.success'
));
sendPaymentCountMetrics($kassaLogger);
yoomoney_api_common_exit_200();
}
YooMoneyLogger::error('Wrong payment status: '.$payment->getStatus());
$kassaLogger->sendHeka(array('payment.notification.skip'));
yoomoney_api_common_exit_400();
}
function yoomoney_api_commerce_notify()
{
$kassaLogger = getKassaLog();
$kassaLogger->sendHeka(array('payment.notification.init'));
YooMoneyLogger::info('Return notification init commerce');
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
$kassaLogger->sendHeka(array('payment.notification.skip'));
exit();
}
$callbackParams = yoomoney_api_common_get_payment_info_from_input();
try {
$factory = new NotificationFactory();
$notification = $factory->factory($callbackParams);
} catch (Exception $e) {
$kassaLogger->sendAlertLog(
'Invalid notification object',
array('exception' => $e),
array('payment.notification.fail'));
YooMoneyLogger::error('Invalid notification object');
header('HTTP/1.1 400 Bad Request');
exit();
}
$metadata = $notification->getObject()->getMetadata();
if (
!empty($metadata['cms_name'])
&& !in_array($metadata['cms_name'], array(CMS_NAME_COMMERCE, CMS_NAME_COMMERCE_OLD))
) {
$kassaLogger->sendHeka(array('payment.notification.skip'));
YooMoneyLogger::info('This notification not for Drupal 7 Commerce. This notification for: ' . $metadata['cms_name']);
yoomoney_api_common_exit_200();
}
$paymentId = $notification->getObject()->getId();
$transaction = yooCommerceApiPaymentTransactioLoad($paymentId);
if (!$transaction) {
$kassaLogger->sendHeka(array('payment.notification.skip'));
YooMoneyLogger::error('Transaction not found');
header('HTTP/1.1 404 Not Found');
exit();
}
$order = commerce_order_load($transaction->order_id);
if (!$order) {
$kassaLogger->sendHeka(array('payment.notification.skip'));
YooMoneyLogger::error('Order not found');
header('HTTP/1.1 404 Not Found');
exit();
}
$apiClient = yoomoney_api__common__get_api_client();
if ($order->status === YOOMONEY_API_ORDER_STATUS_COMPLETED) {
$kassaLogger->sendHeka(array('payment.notification.skip'));
yoomoney_api_common_exit_200();
}
$payment = $apiClient->getPaymentInfo($paymentId);
if (!$payment) {
$kassaLogger->sendHeka(array('payment.notification.fail'));
YooMoneyLogger::error('Empty payment info');
header('HTTP/1.1 400 Bad Request');
exit();
}
if ($notification->getEvent() === NotificationEventType::PAYMENT_WAITING_FOR_CAPTURE
&& $payment->getStatus() === PaymentStatus::WAITING_FOR_CAPTURE
) {
if (yoomoney_api__common__is_enable_hold_mode()
&& $payment->getPaymentMethod()->getType() === PaymentMethodType::BANK_CARD
) {
$kassaLogger->sendHeka(array(
'shop.'.$kassaLogger->getShopId().'.payment.waiting_for_capture',
'order-status.change.init'
));
YooMoneyLogger::info('Hold payment for order: '.$order->order_number);
$order = commerce_order_status_update($order, YOOMONEY_API_ORDER_STATUS_PROCESSING);
if ($order->status === YOOMONEY_API_ORDER_STATUS_PROCESSING) {
$kassaLogger->sendHeka(array('order-status.change.success'));
} else {
$kassaLogger->sendHeka(array('order-status.change.fail'));
}
$transaction->remote_status = $payment->getStatus();
$transaction->message = sprintf(t('New payment received. Capture until %1$s, after that date it will be automatically canceled'),
$payment->getExpiresAt()->format('d.m.Y H:i'));
commerce_payment_transaction_save($transaction);
} else {
$payment = capturePayment($apiClient, $payment, $kassaLogger);
if (!$payment) {
$kassaLogger->sendHeka(array('payment.notification.fail'));
YooMoneyLogger::error('Empty capture payment info');
header('HTTP/1.1 400 Bad Request');
exit();
}
}
sendPaymentCountMetrics($kassaLogger);
yoomoney_api_update_transaction_status($paymentId, $payment->getStatus());
$kassaLogger->sendHeka(array(
'shop.'.$kassaLogger->getShopId().'.payment.succeeded',
'payment.notification.success'
));
yoomoney_api_common_exit_200();
}
if ($notification->getEvent() === NotificationEventType::PAYMENT_SUCCEEDED
&& $payment->getStatus() === PaymentStatus::SUCCEEDED
) {
YooMoneyLogger::info('Order status completed');
commerce_checkout_complete($order);
$kassaLogger->sendHeka(array('order-status.change.init'));
$order = commerce_order_status_update($order, YOOMONEY_API_ORDER_STATUS_PROCESSING);
if ($order->status === YOOMONEY_API_ORDER_STATUS_PROCESSING) {
$kassaLogger->sendHeka(array('order-status.change.success'));
} else {
$kassaLogger->sendHeka(array('order-status.change.fail'));
}
$transaction->status = COMMERCE_PAYMENT_STATUS_SUCCESS;
$transaction->remote_status = $payment->getStatus();
commerce_payment_transaction_save($transaction);
yoomoney_api_update_transaction_status($paymentId, $payment->getStatus());
yoomoney_api_update_transaction_amount($paymentId, $payment->getAmount()->getValue());
$kassaLogger->sendHeka(array(
'shop.'.$kassaLogger->getShopId().'.payment.succeeded',
'payment.notification.success'
));
sendPaymentCountMetrics($kassaLogger);
yoomoney_api_common_exit_200();
}
YooMoneyLogger::error('Wrong payment status: '.$payment->getStatus());
$kassaLogger->sendHeka(array('payment.notification.skip'));
yoomoney_api_common_exit_400();
}
/**
* @return array
*/
function yoomoney_api_common_get_payment_info_from_input()
{
$body = @file_get_contents('php://input');
YooMoneyLogger::info('Notification: ' . $body);
return json_decode($body, true);
}
function yoomoney_api_actions()
{
$action = filter_input(INPUT_POST, 'action', FILTER_SANITIZE_STRING);
$paymentId = filter_input(INPUT_POST, 'paymentId', FILTER_SANITIZE_STRING);
$module = filter_input(INPUT_POST, 'module', FILTER_SANITIZE_STRING);
$transaction = yoomoney_api_transaction_load_by_payment($paymentId);
yoomoney_api_common_check_value_is_not_empty($transaction, '404 Not Found', 'Transaction not found');
$apiClient = yoomoney_api__common__get_api_client();
$payment = $apiClient->getPaymentInfo($paymentId);
yoomoney_api_common_check_value_is_not_empty($payment, '400 Bad Request', 'Empty payment info');
$rightStatus = $payment->getStatus() === PaymentStatus::WAITING_FOR_CAPTURE;
yoomoney_api_common_check_value_is_not_empty($rightStatus, '400 Bad Request',
'Capture payment error: wrong payment status: '.$payment->getStatus());
if ($module === 'uc_payment') {
$orderResult = uc_order_load($transaction->order_id);
if ($orderResult && is_array($orderResult)) {
$order = $orderResult[0];
} else {
$order = $orderResult;
}
yoomoney_api_common_check_value_is_not_empty($order, '404 Not Found', 'Order not found');
switch ($action) {
case 'capturePayment':
yoo_ubercart_api_capture_payment($payment, $order);
break;
case 'cancelPayment':
yoo_ubercart_api_cancel_payment($paymentId, $order);
break;
}
} elseif ($module === 'commerce') {
$order = commerce_order_load($transaction->order_id);
yoomoney_api_common_check_value_is_not_empty($order, '404 Not Found', 'Order not found');
switch ($action) {
case 'capturePayment':
yoo_commerce_api_capture_payment($payment, $order);
break;
case 'cancelPayment':
yoo_commerce_api_cancel_payment($paymentId, $order);
break;
}
}
YooMoneyLogger::error('Capture payment error: module or order not found');
}
function yoomoney_api_common_check_json_last_error()
{
if (json_last_error()) {
YooMoneyLogger::info('Parse POST body failed');
header('HTTP/1.1 400 Bad Request');
header('Status: 400 Bad Request');
exit();
}
}
/**
* @param mixed $value
* @param string $status
* @param string $logMessage
*/
function yoomoney_api_common_check_value_is_not_empty($value, $status, $logMessage)
{
if (!$value) {
YooMoneyLogger::error($logMessage);
header('HTTP/1.1 '.$status);
header('Status: '.$status);
exit();
}
}
function yoomoney_api_common_exit_200()
{
header('HTTP/1.1 200 OK');
header('Status: 200 OK');
exit();
}
function yoomoney_api_common_exit_400()
{
header('HTTP/1.1 400 Bad Request');
header('Status: 400 Bad Request');
exit();
}
/**
* Returns text message for success transaction
* @return mixed
*/
function yoomoney_api_complete_page()
{
drupal_set_title(t('Congratulations!'));
module_invoke_all('yoomoney_api_complete');
$success = variable_get('yoomoney_api_success_text', array('value' => ''));
$page = array(
'#markup' => $success['value'],
);
drupal_alter('yoomoney_api_complete_page', $page);
return $page;
}
/**
* Returns text message for failed transaction.
* @return mixed
*/
function yoomoney_api_fail_page()
{
drupal_set_title(t('Sorry!'));
module_invoke_all('yoomoney_api_fail');
$fail = variable_get('yoomoney_api_fail_text', array('value' => ''));
$page = array(
'#markup' => $fail['value'],
);
drupal_alter('yoomoney_api_fail_page', $page);
return $page;
}
/**
* One callback to handle both Success and Fail pages.
*/
function yoomoney_api_temp_page()
{
if (isset($_GET['action']) && $_GET['action'] === 'PaymentSuccess') {
drupal_goto('yoomoney_api/complete', array('query' => $_GET));
} else {
drupal_goto('yoomoney_api/fail', array('query' => $_GET));
}
}
/**
* Check YooMoney IP
* @return bool
*/
function yoomoney_api_check_server_ip()
{
$ip = ip_address();
$allowed_ips = variable_get('yoomoney_api_ip', '0.0.0.0');
$allowed_ips = explode("\n", $allowed_ips);
foreach ($allowed_ips as $allowed_ip) {
$allowed_ip = trim($allowed_ip);
if (empty($allowed_ip)) {
continue;
}
if ($allowed_ip === '0.0.0.0' || $ip === $allowed_ip) {
return true;
}
}
return false;
}
/**
* @param YooMoneyApiTransaction $transaction
*
* @return array
*/
function yoomoney_api_get_order_submission_params(YooMoneyApiTransaction $transaction)
{
if (variable_get('yoomoney_api_shop', 0)) {
$params = yoomoney_api_get_shop_params($transaction);
}
drupal_alter('yoomoney_api_order_submission_params', $params);
return $params;
}
/**
* @param YooMoneyApiTransaction $transaction
*
* @return array
*/
function yoomoney_api_get_shop_params(YooMoneyApiTransaction $transaction)
{
$params = array();
// Идентификатор Контрагента
$params['shopId'] = variable_get('yoomoney_api_shop_id', '');
// Идентификатор товара
//$params['shopArticleId'] = '';
// Сумма заказа
$params['sum'] = $transaction->amount;
// Идентификатор плательщика. Номер оплачиваемого мобильного телефона, договора и т. п., специфично для Контрагента.
$params['customerNumber'] = $transaction->uid;
// Уникальный для данного shopId номер заказа в ИС Контрагента.
$params['orderNumber'] = $transaction->order_id;
// URL, на который должен быть осуществлен редирект в случае успеха перевода (urlencoded значение).
$params['shopSuccessURL'] = url('yoomoney_api/complete', array('absolute' => true));
// URL, на который должен быть осуществлен редирект в случае ошибки (urlencoded значение).
$params['shopFailURL'] = url('yoomoney_api/fail', array('absolute' => true));
// Provide CMS name
$params['cms_name'] = 'drupal';
// Internally used field
$params['order_id'] = $transaction->order_id;
// Internally used field
$params['transaction_id'] = $transaction->ymid;
drupal_alter('yoomoney_api_shop_params', $params);
return $params;
}
/**
* @return string
*/
function yoomoney_api_get_order_submission_url()
{
if (variable_get('yoomoney_api_shop')) {
return '/';
}
}
/**
* @return bool
*/
function yoomoney_api__common__is_enable_hold_mode()
{
return (bool)variable_get('yoomoney_api_enable_hold_mode', 0);
}
function yoomoney_api_send_second_receipt($orderInfo)
{
$kassaLogger = getKassaLog();
$kassaLogger->sendHeka(array('second-receipt.send.init'));
$transaction = yoomoney_api_transaction_load_by_order($orderInfo['order_id']);
$result = array(
'is_send' => false,
'message' => null,
);
if (empty($transaction->payment_id)) {
YooMoneyLogger::info("Second receipt isn't need, reason: paymentId is empty");
return (object)$result;
}
try {
$apiClient = yoomoney_api__common__get_api_client();
$paymentInfo = $apiClient->getPaymentInfo($transaction->payment_id);
} catch (Exception $e) {
YooMoneyLogger::info("Second receipt isn't need, reason: {$e->getMessage()}");
return (object)$result;
}
$secondReceiptModel = new YooMoneySecondReceiptModel($paymentInfo, $orderInfo, $apiClient, $kassaLogger);
if (!$secondReceiptModel->sendSecondReceipt()) {
$kassaLogger->sendHeka(array('second-receipt.send.fail'));
return (object)$result;
}
$kassaLogger->sendHeka(array('second-receipt.send.success'));
YooMoneyLogger::info("Second receipt send. Write history change");
$sum = number_format($secondReceiptModel->getSettlementsSum(), 2, '.', ' ');
$result['is_send'] = true;
$result['message'] = sprintf(t('Second receipt sent. Amount %s rubles'), $sum);
return (object)$result;
}
/**
* @param $new_status string
* @return bool
*/
function yoomoney_api_is_need_second_receipt($new_status)
{
$isSendReceipt = variable_get('yoomoney_api_send_check', 0);
$isSendSecondReceipt = variable_get('yoomoney_api_send_second_receipt', 0);
$secondReceiptStatus = variable_get('yoomoney_api_second_receipt_status', 0);
if (!$isSendReceipt) {
YooMoneyLogger::error('54 fz dont activate');
return false;
} elseif (!$isSendSecondReceipt) {
YooMoneyLogger::error('Send second receipt dont activate');
return false;
} elseif (!$secondReceiptStatus) {
YooMoneyLogger::error('Not selected second receipt status');
return false;
} elseif ($secondReceiptStatus != $new_status) {
YooMoneyLogger::error('Incorrect order status, expected status = ' . $secondReceiptStatus
. ' current status = ' . $new_status);
return false;
}
return true;
}
/**
* Делает запрос к OAuth приложению для получения ссылки на авторизацию
*
* @return void
* @throws Exception
*/
function yoomoney_api_get_oauth_url()
{
header('Content-Type: application/json');
if (
empty($_SERVER['HTTP_X_REQUESTED_WITH'])
|| strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest'
) {
echo json_encode(array('status' => 'error', 'error' => 'Unknown', 'code' => 'unknown'));
exit;
}
$kassaLogger = getKassaLog();
$kassaLogger->sendHeka(array("oauth.process.init"));
try {
$result = YooMoneyOauth::generateOauthUrl();
} catch (Exception $e) {
$kassaLogger->sendHeka(array('oauth.process.fail'));
yoomoney_api_error_exit('Exception: ' . $e->getMessage(), $e->getMessage(), 500);
}
$code = $result['code'];
if ($code != 200) {
$kassaLogger->sendHeka(array('oauth.process.fail'));
yoomoney_api_error_exit('Got error while getting OAuth link.');
}
$body = json_decode($result['response'], true);
if (!isset($body['oauth_url'])) {
$kassaLogger->sendHeka(array('oauth.process.fail'));
$error = empty($body['error']) ? 'OAuth URL not found' : $body['error'];
yoomoney_api_error_exit('Got error while getting OAuth link. Response body: ' . json_encode($body), $error);
}
$kassaLogger->sendHeka(array('oauth.process.success'));
echo json_encode(array('oauth_url' => $body['oauth_url']));
exit;
}
/**
* Функция обработки ajax запроса на получение OAuth токена через OAuth-приложение
*
* @return void
* @throws Exception
*/
function yoomoney_api_get_oauth_token()
{
header('Content-Type: application/json');
$kassaLogger = getKassaLog();
$kassaLogger->sendHeka(array('oauth.callback.init'));
if (
empty($_SERVER['HTTP_X_REQUESTED_WITH'])
|| strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) != 'xmlhttprequest'
) {
$kassaLogger->sendHeka(array('oauth.callback.skip'));
echo json_encode(array('status' => 'error', 'error' => 'Unknown', 'code' => 'unknown'));
exit;
}
$state = YooMoneyOauth::getOauthState();
try {
$kassaLogger->sendHeka(array('oauth.get-token.init'));
$result = YooMoneyOauth::generateOauthToken($state);
} catch (Exception $e) {
$kassaLogger->sendHeka(array('oauth.callback.fail', 'oauth.get-token.fail'));
yoomoney_api_error_exit('Exception: ' . $e->getMessage(), $e->getMessage(), 500);
}
$code = $result['code'];
if ($code != 200) {
if ($code == 422) {
$kassaLogger->sendHeka(array('oauth.callback.fail', 'oauth.get-token.fail'));
$error = empty($body['error']) ? 'Access token not found' : $body['error'];
yoomoney_api_error_exit($error, 'Авторизация не пройдена');
}
$kassaLogger->sendHeka(array('oauth.callback.fail', 'oauth.get-token.fail'));
yoomoney_api_error_exit('Got error while getting OAuth token.');
}
$body = json_decode($result['response'], true);
if (!isset($body['access_token'])) {
$kassaLogger->sendHeka(array('oauth.callback.fail', 'oauth.get-token.fail'));
$error = empty($body['error']) ? 'Access token not found' : $body['error'];
yoomoney_api_error_exit('Got error while getting OAuth token. Key access_token not found. Response body: '. json_encode($body), $error);
}
if (!isset($body['expires_in'])) {
$kassaLogger->sendHeka(array('oauth.callback.fail', 'oauth.get-token.fail'));
$error = empty($body['error']) ? 'Expires_in parameter not found' : $body['error'];
yoomoney_api_error_exit('Got error while getting OAuth token. Key expires_in not found. Response body: '. json_encode($body), $error);
}
$token = variable_get('yoomoney_api_access_token');
variable_set('yoomoney_api_access_token', $body['access_token']);
variable_set('yoomoney_api_token_expires_in', $body['expires_in']);
$kassaLogger->sendHeka(array('oauth.get-token.success'));
if ($token) {
try {
$kassaLogger->sendHeka(array('token.revoke.init'));
YooMoneyLogger::info('Old token found. Trying to revoke.');
YooMoneyOauth::revokeOldToken($token, $state);
$kassaLogger->sendHeka(array('token.revoke.success'));
} catch (Exception $e) {
$kassaLogger->sendHeka(array('oauth.callback.fail', 'token.revoke.fail'));
yoomoney_api_error_exit('Error when revoking old token: ' . $e->getMessage(), $e->getMessage(), 500);
}
}
try {
$client = YooMoneyClientFactory::getYooKassaClient();
} catch (Exception $e) {
$kassaLogger->sendHeka(array('oauth.callback.fail'));
yoomoney_api_error_exit('Error when creating a client: ' . $e->getMessage(), $e->getMessage(), 500);
}
try {
$kassaLogger->sendHeka(array('oauth.get-shop.init'));
YooMoneyOauth::saveShopInfoByOauth($client);
$kassaLogger->sendHeka(array('oauth.get-shop.success'));
} catch (Exception $e) {
$kassaLogger->sendHeka( array('oauth.callback.fail', 'oauth.get-shop.fail'));
yoomoney_api_error_exit('Error to get information about shop: ' . $e->getMessage(), $e->getMessage(), 500);
}
try {
$kassaLogger->sendHeka(array('webhooks.subscribe.init'));
YooMoneyLogger::info('Url:' . variable_get('notifyUrl', ''));
YooMoneyWebhookSubscriber::subscribe($client);
$kassaLogger->sendHeka(array('webhooks.subscribe.success'));
} catch (Exception $e) {
$kassaLogger->sendHeka(array('oauth.callback.fail', 'webhooks.subscribe.fail'));
yoomoney_api_error_exit('Error occurred during creating webhooks: ' . $e->getMessage(), $e->getMessage(), 500);
}
$kassaLogger->sendHeka(array('oauth.callback.success'));
echo json_encode(array(
'status' => 'success',
));
exit;
}
/**
* Вывод сообщений об ошибках и остановка скрипта
*
* @param $errorLog
* @param $errorFront
* @param $code
* @return void
*/
function yoomoney_api_error_exit($errorLog, $errorFront = null, $code = 502)
{
$errorFront = $errorFront ?: $errorLog;
YooMoneyLogger::error('Error: ' . $errorLog);
echo json_encode(array('error' => $errorFront));
exit($code);
}
/**
* Функция создания запроса на подтверждение платежа
*
* @param Client $apiClient
* @param Payment $payment
* @param YooMoneyKassaLogger $kassaLogger
* @return void|CreateCaptureResponse|null
*/
function capturePayment($apiClient, $payment, $kassaLogger)
{
try {
$captureRequest = CreateCaptureRequest::builder()->setAmount($payment->getAmount())->build();
return $apiClient->capturePayment($captureRequest, $payment->getId());
} catch (Exception $e) {
YooMoneyLogger::error('Failed to capture payment: ' . $e->getMessage());
$kassaLogger->sendAlertLog('Failed to capture payment', array(
'methodid' => 'POST/capturePayment',
'exception' => $e,
), array('payment.notification.fail'));
header('HTTP/1.1 500 Internal Server Error');
exit();
}
}
/**
* Отправка статистики по платежам
*
* @param YooMoneyKassaLogger $kassaLogger
* @return void
*/
function sendPaymentCountMetrics($kassaLogger)
{
global $base_url;
$stat = getSuccessPaymentStat();
if (empty($stat)) {
return;
}
$host = str_replace(array('http://', 'https://', '.', '/', ':'), array('', '', '-', '-', '-'), trim($base_url, '/'));
$kassaLogger->sendHeka(array(
'shop.' . $kassaLogger->getShopId() . '.host.' . $host . '.payment-count' => array(
'metric_type' => 'gauges',
'metric_count' => $stat['count']
),
'shop.' . $kassaLogger->getShopId() . '.host.' . $host . '.payment-total' => array(
'metric_type' => 'gauges',
'metric_count' => $stat['total']
),
));
}
/**
* @param string $status
* @return false|array
*/
function getSuccessPaymentStat($status = 'succeeded')
{
$existing = db_query("SELECT COUNT(`payment_id`) AS count, SUM(`amount`) as total FROM {yoomoney_api_transaction} WHERE status = :status", array(
':status' => $status))
->fetchAssoc();
if (empty($existing)) {
return false;
}
return $existing;
}
