yoomoney_api-2.5.2/yoo_commerce/yoo_commerce_api.module
yoo_commerce/yoo_commerce_api.module
<?php
use YooKassa\Common\Exceptions\ApiException;
use YooKassa\Model\Payment;
use YooKassa\Model\PaymentStatus;
use YooKassa\Request\Payments\CreatePaymentRequest;
use YooKassa\Request\Payments\CreatePaymentRequestBuilder;
use YooKassa\Request\Payments\CreatePaymentRequestSerializer;
use YooKassa\Request\Payments\Payment\CreateCaptureRequest;
use YooKassa\Request\Payments\Payment\CreateCaptureRequestBuilder;
require_once MODULE_PATH.DIRECTORY_SEPARATOR.'vendor'.DIRECTORY_SEPARATOR.'autoload.php';
require_once MODULE_PATH.DIRECTORY_SEPARATOR.'YooMoneyLogger.php';
require_once MODULE_PATH.DIRECTORY_SEPARATOR.'YooMoneyKassaLogger.php';
/**
* Implements hook_commerce_order_presave().
*/
function yoo_commerce_api_commerce_order_presave($order)
{
if (empty($order->original)) {
return;
}
if ($order->status == $order->original->status) {
return;
}
if (!yoomoney_api_is_need_second_receipt($order->status)) {
return;
}
$orderInfo = array(
'order_id' => $order->order_id,
'user_email' => empty($order->mail) ? null : $order->mail,
'user_phone' => empty($order->phone) ? null : $order->phone,
);
$result = yoomoney_api_send_second_receipt($orderInfo);
if (!$result->is_send) {
return;
}
$order->log = $result->message;
}
/**
* Implements hook_commerce_payment_method_info().
*/
function yoo_commerce_api_commerce_payment_method_info()
{
$payment_methods = array();
$icon_name = 'yookassa-logo';
$icon = theme(
'image',
array(
'path' => drupal_get_path('module', 'yoo_commerce_api').'/images/'.$icon_name.'.png',
'attributes' => array('class' => array('yoo-commerce-logo')),
)
);
$display_title = t('YooKassa (bank card, e-money, etc.)');
$display_title .= '<br/>'.$icon;
$payment_methods['yoo_commerce_api'] = array(
'base' => 'yoo_commerce_api',
'title' => t('YooMoney'),
'short_title' => t('YooMoney'),
'display_title' => $display_title,
'description' => t('Integration with YooMoney.'),
'terminal' => false,
'offsite' => true,
'offsite_autoredirect' => true,
'active' => true,
);
return $payment_methods;
}
/**
* Payment method callback: checkout form submission.
*
* @param $payment_method
* @param $pane_form
* @param $pane_values
* @param $order
* @param $charge
* @throws \YooKassa\Common\Exceptions\ExtensionNotFoundException
*/
function yoo_commerce_api_submit_form_submit($payment_method, $pane_form, $pane_values, $order, $charge)
{
global $user;
$kassaLogger = getKassaLog();
$kassaLogger->sendHeka(array('payment.create.init'));
$apiClient = yoomoney_api__common__get_api_client();
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$currency_code = $order_wrapper->commerce_order_total->currency_code->value();
$amount = $order_wrapper->commerce_order_total->amount->value();
$amount = round(commerce_currency_amount_to_decimal($amount, $currency_code), 2);
$builder = yoo_commerce_api_get_create_payment_request_builder($order, $amount,
$order_wrapper);
$paymentRequest = $builder->build();
if ($paymentRequest->getReceipt() !== null) {
$paymentRequest->getReceipt()->normalize($paymentRequest->getAmount());
}
$serializer = new CreatePaymentRequestSerializer();
$serializedData = $serializer->serialize($paymentRequest);
YooMoneyLogger::info('Create payment request: '.json_encode($serializedData));
try {
$kassaLogger->sendHeka(array('payment.request.init'));
$response = $apiClient->createPayment($paymentRequest);
if ($response && $response->status !== PaymentStatus::CANCELED) {
$transaction = commerce_payment_transaction_new($payment_method['method_id'],
$order->order_id);
$transaction->instance_id = $payment_method['instance_id'];
$transaction->remote_id = $response->id;
$transaction->amount = intval($amount) * 100;
$transaction->currency_code = $currency_code;
$transaction->status = COMMERCE_PAYMENT_STATUS_PENDING;
$transaction->remote_status = $response->getStatus();
commerce_order_status_update($order, YOOMONEY_API_ORDER_STATUS_PENDING);
$ymTransaction = new YooMoneyApiTransaction();
$ymTransaction->uid = isset($user->uid) ? $user->uid : 0;
$ymTransaction->amount = $amount;
$ymTransaction->mail = isset($user->mail) ? $user->mail : $order->mail;
$ymTransaction->order_id = $order->order_id;
$ymTransaction->payment_id = $response->getId();
$ymTransaction->status = $response->getStatus();
if (commerce_payment_transaction_save($transaction) && commerce_order_save($order)
&& yoomoney_api_transaction_save($ymTransaction)
) {
$confirmationUrl = $response->confirmation->confirmationUrl;
$kassaLogger->sendHeka(array(
'payment.create.success',
'payment.request.success',
'payment.redirect.init')
);
drupal_goto($confirmationUrl);
}
} else {
YooMoneyLogger::error('Payment not created. Order id: '.$order->order_number);
drupal_set_message(t('Unable to create payment.'), 'error');
}
} catch (ApiException $e) {
getKassaLog()->sendAlertLog('Failed to create payment', array(
'methodid' => 'POST/createPayment',
'exception' => $e,
), array('payment.request.fail', 'payment.create.fail'));
YooMoneyLogger::error('Api error: '.$e->getMessage());
drupal_set_message(t('Unable to pay with this method.'), 'error');
drupal_goto(request_uri());
}
$kassaLogger->sendHeka(array('payment.create.fail', 'payment.request.fail'));
$order->data['yoo_commerce_api'] = $pane_values;
}
/**
* Запрос на создание платежа
*
* @param $order
* @param $amount
* @param string $paymentMethod
* @param EntityMetadataWrapper $order_wrapper
*
* @return \YooKassa\Request\Payments\CreatePaymentRequestBuilder
*/
function yoo_commerce_api_get_create_payment_request_builder($order, $amount, $order_wrapper)
{
$confirmationType = \YooKassa\Model\ConfirmationType::REDIRECT;
YooMoneyLogger::info('Return url: '.yoo_commerce_api_get_return_url($order));
$builder = CreatePaymentRequest::builder()
->setAmount($amount)
->setCapture(!yoomoney_api__common__is_enable_hold_mode())
->setDescription(yoo_commerce_api_create_description($order))
->setConfirmation(
array(
'type' => $confirmationType,
'returnUrl' => yoo_commerce_api_get_return_url($order),
)
)
->setMetadata(array(
'cms_name' => CMS_NAME_COMMERCE,
'module_version' => YOOMONEY_MODULE_VERSION,
));
yoo_commerce_api_set_receipt_if_needed($builder, $order, $order_wrapper);
return $builder;
}
/**
* @param CreatePaymentRequestBuilder|CreateCaptureRequestBuilder $builder
* @param $order
* @param EntityMetadataWrapper $order_wrapper
*/
function yoo_commerce_api_set_receipt_if_needed($builder, $order, $order_wrapper)
{
if (!variable_get('yoomoney_api_send_check', false)) {
return;
}
$kassaLogger = getKassaLog();
$kassaLogger->sendHeka(array('receipt.create.init'));
$builder->setReceiptEmail($order->mail);
foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
if ($line_item_wrapper->value()->type == 'product') {
$product = $line_item_wrapper->commerce_product->value();
$components = $product->commerce_price['und'][0]['data']['components'];
$tax_id = false;
foreach ($components as $component) {
$info = explode('|', $component['name']);
if (count($info) && $info[0] == 'tax') {
$tax_id = $info[1];
break;
}
}
$quantity = $line_item_wrapper->quantity->value();
$tax = $tax_id && variable_get('yoomoney_api_kassa_tax_'.$tax_id)
? variable_get('yoomoney_api_kassa_tax_'.$tax_id)
: variable_get('yoomoney_api_kassa_tax_default', YOOMONEY_API_DEFAULT_TAX_RATE_ID);
$amount = commerce_currency_amount_to_decimal(
$product->commerce_price['und'][0]['amount'],
$product->commerce_price['und'][0]['currency_code']
);
$builder->addReceiptItem($product->title, $amount, $quantity, $tax,
variable_get('yoomoney_kassa_payment_mode'),
variable_get('yoomoney_kassa_payment_subject'));
}
if ($line_item_wrapper->value()->type == 'shipping') {
$shipping = $line_item_wrapper->value();
$amount = commerce_currency_amount_to_decimal(
$shipping->commerce_total['und'][0]['amount'],
$shipping->commerce_total['und'][0]['currency_code']
);
$builder->addReceiptShipping('Доставка', $amount, YOOMONEY_API_DEFAULT_TAX_RATE_ID,
variable_get('yoomoney_kassa_delivery_payment_mode'),
variable_get('yoomoney_kassa_delivery_payment_subject'));
}
}
$kassaLogger->sendHeka(array('receipt.create.success'));
}
/**
* Payment method callback: redirect form.
*
* A wrapper around the module's general use function for building a submit form.
*/
function yoo_commerce_api_redirect_form($form, &$form_state, $order, $payment_method)
{
global $user;
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$currency_code = $order_wrapper->commerce_order_total->currency_code->value();
$amount = $order_wrapper->commerce_order_total->amount->value();
$amount = round(commerce_currency_amount_to_decimal($amount, $currency_code), 2);
$transaction = new YooMoneyApiTransaction();
$transaction->uid = isset($user->uid) ? $user->uid : 0;
$transaction->amount = $amount;
$transaction->mail = isset($user->mail) ? $user->mail : '';
$transaction->order_id = $order->order_id;
if (!yoomoney_api_transaction_save($transaction)) {
$error_message = t('Can not save transaction.');
// create failure commerce transaction
yoo_commerce_create_commerce_transaction($transaction, COMMERCE_PAYMENT_STATUS_FAILURE, '');
// show message to the user
drupal_set_message(t('Payment failed: %message', array('%message' => $error_message)), 'error');
// log error to watchdog
watchdog('yoo_commerce_api', 'Payment failed: %message', array('%message' => $error_message), WATCHDOG_WARNING);
// redirect back to checkout
$cancel_url = yoo_commerce_api_get_checkout_url($order, false);
drupal_goto($cancel_url);
}
$target_url = yoomoney_api_get_order_submission_url();
$params = yoomoney_api_get_order_submission_params($transaction);
$form['#action'] = $target_url;
foreach ($params as $key => $value) {
if ($key == 'fio') {
$form[$key] = array(
'#type' => 'hidden',
'#value' => $order->data['yoo_commerce_api']['customer_name'],
);
} else {
$form[$key] = array(
'#type' => 'hidden',
'#value' => $value,
);
}
}
$form['actions'] = array('#type' => 'actions');
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit order'),
);
$form['cms_name'] = array(
'#type' => 'hidden',
'#value' => "drupal-commerce",
);
return $form;
}
/**
* Payment method callback: redirect form return validation.
*/
function yoo_commerce_api_redirect_form_validate($order, $payment_method)
{
return true;
}
/**
* Implements hook_yoomoney_api_shop_params_alter().
*
* @param $params
*/
function yoo_commerce_api_yoomoney_api_shop_params_alter(&$params)
{
$order = commerce_order_load($params['order_id']);
if ($order) {
// Return to the payment redirect page for processing successful payments
$params['shopSuccessURL'] = yoo_commerce_api_get_checkout_url($order, true);
// Return to the previous page when payment is canceled
$params['shopFailURL'] = yoo_commerce_api_get_checkout_url($order, false);
}
}
/**
* Implements hook_yoomoney_quick_params_alter().
*
* @param $params
*/
function yoo_commerce_api_yoomoney_api_quick_params_alter(&$params)
{
$order = commerce_order_load($params['order_id']);
// Selected payment method
$params['paymentType'] = $order->data['yoo_commerce_api']['payment_method'];
}
/**
* Process successful payment to update Commerce entities.
* Implements hook_yoomoney_api_process_payment_alter().
*
* @param array $payment
*/
function yoo_commerce_api_yoomoney_api_process_payment_alter(&$payment)
{
/** @var YooMoneyApiTransaction $transaction */
$transaction = $payment['transaction'];
$order = commerce_order_load($transaction->order_id);
if ($order) {
$paymentId = $transaction->ymid;
yoomoney_api_update_transaction_payment_id($transaction->ymid, $paymentId);
$transaction->status = yoomoney_api_update_transaction_status($transaction->ymid, YooMoneyApiTransaction::STATUS_COMPLETED);
yoo_commerce_create_commerce_transaction(
$transaction,
COMMERCE_PAYMENT_STATUS_SUCCESS,
'',
array(),
$payment['request']
);
commerce_order_status_update($order, YooMoneyApiTransaction::STATUS_COMPLETED);
$payment['success'] = true;
} else {
$payment['success'] = false;
$payment['error'] = t('Can not find order with id ').$transaction->order_id;
}
}
/**
* @param stdClass $order
* @param bool $success
*
* @return string
*/
function yoo_commerce_api_get_checkout_url($order, $success = true)
{
return url(
'checkout/'.$order->order_id.'/payment/'.($success ? 'return' : 'back').'/'.$order->data['payment_redirect_key'],
array('absolute' => true)
);
}
/**
* @param YooMoneyApiTransaction $transaction
* @param string $status
* @param string $message
* @param array $message_params
* @param null $request
*
* @return bool
*/
function yoo_commerce_create_commerce_transaction(
YooMoneyApiTransaction $transaction,
$status,
$message = '',
$message_params = array(),
$request = null
) {
$order = commerce_order_load($transaction->order_id);
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$currency_code = $order_wrapper->commerce_order_total->currency_code->value();
$payment_method_instance_id = 'yoo_commerce|commerce_payment_yoo_commerce';
// Prepare a transaction object to log the API response.
$commerce_transaction = commerce_payment_transaction_new($payment_method_instance_id, $order->order_id);
$commerce_transaction->instance_id = $payment_method_instance_id;
$commerce_transaction->uid = $transaction->uid;
$commerce_transaction->remote_id = $transaction->ymid;
$commerce_transaction->message = $message;
$commerce_transaction->message_variables = $message_params;
$commerce_transaction->amount = $transaction->amount * 100;
$commerce_transaction->currency_code = $currency_code;
$commerce_transaction->status = $status;
$commerce_transaction->remote_status = $transaction->status;
if ($request) {
$commerce_transaction->payload[REQUEST_TIME] = $request;
}
// Save the transaction information.
return !commerce_payment_transaction_save($commerce_transaction);
}
function yoo_commerce_api_get_return_url($order)
{
return url(
'yoomoney_api/commerce/return',
array(
'absolute' => true,
'query' => array('orderId' => $order->order_id),
)
);
}
/**
* Implements hook_yoomoney_api_complete();
*/
function yoo_commerce_yoomoney_api_complete()
{
if (isset($_GET['shopSuccessURL'])) {
drupal_goto($_GET['shopSuccessURL']);
}
}
/**
* Implements hook_yoomoney_api_fail();
*/
function yoo_commerce_api_yoomoney_api_fail()
{
if (isset($_GET['shopFailURL'])) {
drupal_goto($_GET['shopFailURL']);
}
}
/**
* @param $orderInfo
*
* @return bool|string
*/
function yoo_commerce_api_create_description($orderInfo)
{
$descriptionTemplate = variable_get('yoomoney_api_description_template', t('Payment for order No. %order_number%'));
$replace = array();
foreach ($orderInfo as $key => $value) {
if (is_scalar($value)) {
$replace['%'.$key.'%'] = $value;
}
}
$description = strtr($descriptionTemplate, $replace);
return mb_substr($description, 0, Payment::MAX_LENGTH_DESCRIPTION);
}
/**
* @param $paymentId
*
* @return mixed|null
*/
function yooCommerceApiPaymentTransactioLoad($paymentId)
{
$transactions = commerce_payment_transaction_load_multiple(
array(),
array('remote_id' => $paymentId)
);
return !empty($transactions) ? array_shift($transactions) : null;
}
/**
* @param \YooKassa\Model\PaymentInterface $payment
* @param $order
* @throws Exception
*/
function yoo_commerce_api_capture_payment($payment, $order)
{
$kassaLogger = getKassaLog();
$kassaLogger->sendHeka(array('capture.create.init'));
$apiClient = yoomoney_api__common__get_api_client();
try {
$builder = CreateCaptureRequest::builder();
$order_wrapper = entity_metadata_wrapper('commerce_order', $order);
$currency_code = $order_wrapper->commerce_order_total->currency_code->value();
$amount = $order_wrapper->commerce_order_total->amount->value();
$amount = round(commerce_currency_amount_to_decimal($amount, $currency_code), 2);
$builder->setAmount($amount);
yoo_commerce_api_set_receipt_if_needed($builder, $order, $order_wrapper);
$request = $builder->build();
if ($request->getReceipt() !== null) {
$request->getReceipt()->normalize($request->getAmount());
}
$response = $apiClient->capturePayment($request, $payment->getId());
} catch (\Exception $e) {
$this->getKassaLog()->sendAlertLog('Failed to capture payment', array(
'methodid' => 'POST/capturePayment',
'exception' => $e,
), array('capture.create.fail'));
YooMoneyLogger::error('Capture error: ' . $e->getMessage());
$response = $payment;
}
yoomoney_api_common_check_value_is_not_empty($response, '400 Bad Request', 'Empty payment info');
if ($response->getStatus() !== \YooKassa\Model\PaymentStatus::SUCCEEDED) {
YooMoneyLogger::error('Capture payment error: capture failed');
$kassaLogger->sendHeka(array('capture.create.fail'));
return;
}
yoomoney_api_update_transaction_status($response->getId(), $response->getStatus());
$transaction = yooCommerceApiPaymentTransactioLoad($response->getId());
if ($transaction) {
$transaction->message = t('Вы подтвердили платёж в ЮKassa.');
commerce_payment_transaction_save($transaction);
}
$kassaLogger->sendHeka(array('capture.create.success', 'shop.'.$kassaLogger->getShopId().'.payment.succeeded'));
echo "OK";
exit();
}
/**
* @param string $paymentId
* @param $order
*/
function yoo_commerce_api_cancel_payment($paymentId, $order)
{
$kassaLogger = getKassaLog();
$kassaLogger->sendHeka(array('cancel.create.init'));;
$apiClient = yoomoney_api__common__get_api_client();
try {
$response = $apiClient->cancelPayment($paymentId);
} catch (Exception $e) {
$this->getKassaLog()->sendAlertLog('Failed to cancel payment', array(
'methodid' => 'POST/cancelPayment',
'exception' => $e,
), array('cancel.create.fail'));
YooMoneyLogger::error('Cancel payment error: ' . $e->getMessage());
}
if (!$response || $response->getStatus() !== PaymentStatus::CANCELED) {
YooMoneyLogger::error('Cancel payment error: cancel failed');
$kassaLogger->sendHeka(array('cancel.create.fail'));
return;
}
commerce_order_status_update($order, YOOMONEY_API_ORDER_STATUS_CANCELED);
yoomoney_api_update_transaction_status($paymentId, PaymentStatus::CANCELED);
$transaction = yooCommerceApiPaymentTransactioLoad($paymentId);
if ($transaction) {
$transaction->status = COMMERCE_PAYMENT_STATUS_FAILURE;
$transaction->remote_status = $response->getStatus();
$transaction->message = t('Вы отменили платёж в ЮKassa. Деньги вернутся клиенту.');
commerce_payment_transaction_save($transaction);
}
$kassaLogger->sendHeka(array('cancel.create.success', 'shop.'.$kassaLogger->getShopId().'.payment.canceled'));
echo "OK";
exit();
}