Skip to content

Instantly share code, notes, and snippets.

@alooze
Created April 30, 2014 09:01
Show Gist options
  • Select an option

  • Save alooze/d2857971e8bafac38baf to your computer and use it in GitHub Desktop.

Select an option

Save alooze/d2857971e8bafac38baf to your computer and use it in GitHub Desktop.
getYaD snippet
<?php
/**
* getYaD snippet
* @description Прием платежей через шлюз ЯД на сайте под управлением MODX Evo + шопкипера
* @author alooze (a.looze@gmail.com)
* @version 0.1a
* @date 23.04.2014
*/
/**
* Инсталлятор, выполняется один раз
*/
/*$q = "CREATE TABLE IF NOT EXISTS ".$modx->getFullTableName('yad_payments')." (";
$q.= " id INT(11) NOT NULL auto_increment,";
$q.= " order_id INT(11) NOT NULL DEFAULT 0,";
$q.= " date INT(12) NOT NULL DEFAULT 0,";
$q.= " status INT(1) NOT NULL DEFAULT 0,";
$q.= " label VARCHAR(255) NOT NULL DEFAULT '',";
$q.= " user_id INT(11) NOT NULL DEFAULT 0,";
$q.= " order_data TEXT,";
$q.= " PRIMARY KEY (id)";
$q.= ")";
$modx->db->query($q);*/
/**
* ДЕБАГ
*/
$log = MODX_BASE_PATH.'assets/files/yad.txt';
$ff = fopen($log, 'a+');
/**
* ==============================
* 1. Настройки платежей
* ==============================
*/
//АККАУНТ ЯД ДЛЯ ПРИЕМА СРЕДСТВ
$accNo = '41001950123850';
// СЕКРЕТ
$secret = 'n9pTqQT+N0xOmLLbUUctQI/l';
//НАЗВАНИЕ ПЛАТЕЖА
$payName = $modx->config['site_name'].': заказ № [+orderNo+]';
//НАЗНАЧЕНИЕ ПЛАТЕЖА
$payTrgt = 'Оплата услуг';
//МЕТКА ПЛАТЕЖА (уникальная, 64 символа)
$payLabel = '[+UNIXtime+]-[+orderNo+]';
/**
* ==============================
* 2. Параметры вызова сниппета
* ==============================
*/
/**
* Значение в $_SESSION['shk_payment_method'], при котором требуется оплата через ЯД
* Можно использовать 2, через запятую, в этом случае первый будет означать оплату через ЯД,
* второй - оплату через карту на сайте ЯД
*/
$payIfValue = isset($payIfValue) ? $payIfValue : 'yad';
/**
* Если нужно принять оплату от конкретного веб-пользователя, указываем этим параметром
* Если пользователь был авторизован на сайте, ШК сам подхватит его ID в сессию
*/
$userId = isset($userId) ? $userId : $_SESSION['shk_order_user_id'];
/**
* Если требуется передать конкретный email, указываем этим параметром
* По умолчанию берется из $_SESSION['shk_order_user_email']
*/
$userEmail = isset($userEmail) ? $userEmail : $_SESSION['shk_order_user_email'];
/**
* Если требуется передать конкретный ID заказа, указываем этим параметром
* По умолчанию берется сохраненный в таблице заказов ШК ID из $_SESSION['shk_order_id']
*/
$orderId = isset($orderId) ? $orderId : $_SESSION['shk_order_id'];
/**
* Стоимость заказа
* По умолчанию берется из $_SESSION['shk_order_price']
*/
$orderSum = isset($orderSum) ? $orderSum : $_SESSION['shk_order_price'];
/**
* Теоретически ЯД принимает оплату только в рублях, поэтому валюта не особо нужна
* Резервируем на всякий пожарный
*/
$orderCur = isset($orderCur) ? $orderCur : $_SESSION['shk_currency'];
/**
* Шаблон формы, из которой будет перенаправляться пользователь на ЯД
*/
$formTpl = isset($formTpl) ? $formTpl : '';
/**
* Название чанка или текст; будет показан, если из формы не получен нужный способ оплаты
*/
$noFormTpl = isset($noFormTpl) ? $noFormTpl : '';
/**
* Режим жадности: к сумме добавится указанный %
* Для оплаты с ЯД кошелька добавляет 0.5%, для карты - 2%
*/
$greedMode = isset($greedMode) ? $greedMode : false;
/**
* ==============================
* 3. Варианты выполнения
* ==============================
*/
/**
* Если получен запрос от ЯД с подтверждением платежа, обрабатываем запрос
*/
if (isset($_POST['sha1_hash'])) {
$data = "\n==ПОЛУЧЕНЫ данные от ЯД==\n";
$data.= "\n*************************\n";
$data.= var_export($_POST, true);
$data.= "\n*************************\n";
$strForHash = $_POST['notification_type'].'&';
$strForHash.= $_POST['operation_id'].'&';
$strForHash.= $_POST['amount'].'&';
$strForHash.= $_POST['currency'].'&';
$strForHash.= $_POST['datetime'].'&';
$strForHash.= $_POST['sender'].'&';
$strForHash.= $_POST['codepro'].'&';
$strForHash.= $secret.'&';
$strForHash.= $_POST['label'];
/**
* Проверяем, что сумма соответствует нужной
* ЯД позволяют менять сумму и ставить протекцию
* В таких случаях менять статус м.б. не нужно
*/
$err = '';
$yLabel = $_POST['label'];
$where = 'label="'.$modx->db->escape($yLabel).'"';
$res = $modx->db->select('*', $modx->getFullTableName('yad_payments'), $where);
$row = $modx->db->getRow($res);
if (!$row) {
$data.= "\n~~Не найден заказ с такой меткой\n";
fwrite($ff, $data);
fclose($ff);
header("HTTP/1.1 403 Forbidden");
die();
}
if ($_POST['withdraw_amount'] != $row['order_data']) {
$err.= 'Не совпадает сумма заказа и сумма оплаты. ';
}
/**
* Этот вариант не поддается отладке, оставляем на потом
*/
// if ($_POST['codepro'] != false) {
// $err.= 'Перевод защищен кодом протекции. ';
// }
/**
* В любом случае, независимо от правильной суммы, нужно ответить ЯД-ам
*/
$hash = sha1($strForHash);
if ($hash == $_POST['sha1_hash']) {
header("HTTP/1.1 200 OK");
} else {
$err.= 'Строка с хешем не совпадает с полученной ('.$hash.' vs '.$_POST['sha1_hash'].'). ';
header("HTTP/1.1 403 Forbidden");
}
/**
* Если не было ошибок, меняем статус заказу
*/
if ($err == '') {
$upd['status'] = '5';
$upd['order_data'] = $modx->db->escape(serialize($_POST));
$modx->db->update($upd, $modx->getFullTableName('yad_payments'), 'id='.$row['id']);
unset($upd);
$upd['status'] = '6';
$modx->db->update($upd, $modx->getFullTableName('manager_shopkeeper'), 'id='.$row['order_id']);
$modx->invokeEvent('OnSHKChangeStatus',
array('order_id'=>$row['order_id'],'status'=>6)
);
$data.= "\n++Изменен статус оплаты на 5, статус заказа на 6\n";
fwrite($ff, $data);
fclose($ff);
} else {
$upd['status'] = '2';
$upd['order_data'] = $modx->db->escape($err.'::'.serialize($_POST));
$modx->db->update($upd, $modx->getFullTableName('yad_payments'), 'id='.$row['id']);
$data.= "\n~~Изменен статус оплаты на 2\n";
fwrite($ff, $data);
fclose($ff);
}
die();
}
/*notification_type&operation_id&amount&currency&datetime&sender&codepro&
notification_secret&label*/
/*operation_id = 441361714955017004
notification_type = card-incoming
datetime = 2013-12-26T08:28:34Z
sha1_hash = ac13833bd6ba9eff1fa9e4bed76f3d6ebb57f6c0
sender =
codepro = false
currency = 643
amount = 98.00
withdraw_amount = 100.00
label = ML23045
*/
/*нет notification_type string Для переводов из кошелька — p2p-incoming.
Для переводов с произвольной карты — card-
incoming.
operation_id string Идентификатор операции в истории счета
получателя.
amount amount Сумма, которая зачислена на счет получателя.
withdraw_amount amount Сумма, которая списана со счета отправителя.
currency string Код валюты — всегда 643 (рубль РФ согласно
ISO 4217).
datetime datetime Дата и время совершения перевода.
sender string Для переводов из кошелька — номер счета
отправителя.
Для переводов с произвольной карты —
параметр содержит пустую строку.
codepro Для переводов из кошелька — перевод
защищен кодом протекции.
Для переводов с произвольной карты —
всегда false.
label string Метка платежа. Если ее нет, параметр
содержит пустую строку.
sha1_hash string SHA-1 hash параметров уведомления.
*/
/**
* Если данных достаточно для перехода на платежный шлюз
*/
$tmpAr = explode(',',$payIfValue);
$yadFlag = isset($tmpAr[0]) ? trim($tmpAr[0]) : NULL;
$cardFlag = isset($tmpAr[1]) ? trim($tmpAr[1]) : NULL;
if (isset($_SESSION['shk_payment_method']) &&
($_SESSION['shk_payment_method'] == $yadFlag || $_SESSION['shk_payment_method'] == $cardFlag) &&
floatval($orderSum) > 0
) {
// проверяем параметры запуска
if (!$userId || intval($userId) < 1) {
$userId = 0;
}
if (!$userEmail || trim($userEmail) == '') {
$userEmail = $modx->config['email_sender']; //??? надо ли???
}
if (!$orderId || intval($orderId) < 1) {
$orderId = time(); //??? надо ли???
}
if ($_SESSION['shk_payment_method'] == $yadFlag) {
$paymentType = 'PC';
$notificationType = 'p2p-incoming';
} else {
$paymentType = 'AC';
$notificationType = 'card-incoming';
}
if ($greedMode) {
$orderSum = ($paymentType == 'PC') ? $orderSum * 1.005 : $orderSum * 1.02;
}
if ($formTpl == '') {
$formTpl = <<<TPL
<h3>Спасибо за ваш заказ</h3>
<div class="message">
Ваш заказ принят. Данные для оплаты ниже.
<ul>
<li>Сумма к оплате: {$orderSum} {$orderCur}</li>
<li>Номер заказа: {$orderId}</li>
<li>Ваш email: {$userEmail}</li>
</ul>
Чтобы перейти к оплате, нажмите кнопку ниже.
</div>
<form method="POST" action="https://money.yandex.ru/quickpay/confirm.xml">
<input type="hidden" name="receiver" value="{$accNo}">
<input type="hidden" name="formcomment" value="{$payName}">
<input type="hidden" name="short-dest" value="{$payName}">
<input type="hidden" name="label" value="{$payLabel}">
<input type="hidden" name="quickpay-form" value="shop">
<input type="hidden" name="targets" value="{$payTrgt}">
<input type="hidden" name="sum" value="{$orderSum}" data-type="number" >
<input type="hidden" name="comment" value="" >
<input type="hidden" name="need-fio" value="false">
<input type="hidden" name="need-email" value="false" >
<input type="hidden" name="need-phone" value="false">
<input type="hidden" name="need-address" value="false">
<input type="hidden" name="paymentType" value="{$paymentType}" />
<input type="submit" name="submit-button" value="Оплатить">
</form>
TPL;
} else {
/**
* @todo сделать подстановку плейсхолдеров в случае использования чанков
*/
$formTpl = $modx->getChunk($formTpl);
}
$ph['orderNo'] = $orderId;
$ph['UNIXtime'] = time();
foreach ($ph as $key => $value) {
$formTpl = str_replace('[+'.$key.'+]', $value, $formTpl);
$payLabel = str_replace('[+'.$key.'+]', $value, $payLabel);
$payName = str_replace('[+'.$key.'+]', $value, $payName);
}
/**
* Если форма еще не показывалась, делаем запись в сессию и в БД
*/
if (!isset($_SESSION['yad_pay_label']) || $_SESSION['yad_pay_label'] == 0) {
$data = "\n==ПОСЕЩЕНИЕ СТРАНИЦЫ С КНОПКОЙ, сессии нет или 0==\n";
$data.= "\n*************************\n";
$data.= var_export($_SESSION, true);
$data.= "\n*************************\n";
/**
* Прежде, чем отправлять юзера на оплату, сохраняем данные платежа
*/
$_SESSION['yad_pay_label'] = $payLabel;
$where = 'label="'.$_SESSION['yad_pay_label'].'"';
$res = $modx->db->select('*', $modx->getFullTableName('yad_payments'), $where);
$row = $modx->db->getRow($res);
if (!$row) {
// unset($_SESSION['yad_pay_label']);
$ins['order_id'] = $orderId;
$ins['user_id'] = $userId;
$ins['date'] = $ph['UNIXtime'];
$ins['status'] = 0;
$ins['label'] = $payLabel; // самое важное поле, по нему будет сверка от ЯД
$ins['order_data'] = $orderSum; // тут можно будет хранить сериализованные данные
$modx->db->insert($ins, $modx->getFullTableName('yad_payments'));
$data.= "\n++Добавлена запись о новой оплате, статус 0, метка ".$payLabel."\n";
} else {
$upd['date'] = $ph['UNIXtime'];
$upd['status'] = 0;
$modx->db->update($upd, $modx->getFullTableName('yad_payments'), $where);
$data.= "\n++Обновлена запись об оплате, статус 0, метка ".$payLabel."\n";
}
/**
* Меняем статус в модуле ШК
*/
$upd['status'] = '2';
$modx->db->update($upd, $modx->getFullTableName('manager_shopkeeper'), 'id='.$orderId);
$modx->invokeEvent('OnSHKChangeStatus',
array('order_id'=>$row['order_id'],'status'=>2)
);
$data.= "\n++Обновлена запись о заказе, статус 2, ID ".$orderId."\n";
fwrite($ff, $data);
fclose($ff);
/**
* Все готово для показа формы
*/
return $formTpl;
} else {
$data = "\n==ПОСЕЩЕНИЕ СТРАНИЦЫ С КНОПКОЙ, есть сессия==\n";
$data.= "\n*************************\n";
$data.= var_export($_SESSION, true);
$data.= "\n*************************\n";
/**
* Пользователь либо обновил страницу с формой, либо пришел после оплаты
* Проверяем статус платежа
*/
$where = 'label="'.$_SESSION['yad_pay_label'].'"';
$res = $modx->db->select('*', $modx->getFullTableName('yad_payments'), $where);
$row = $modx->db->getRow($res);
if (!$row) {
// такого быть не может, поэтому просто выходим с ошибкой
return '<div class="message">Извините, возникла ошибка (E95). Обратитесь к администратору сайта.</div>';
} else {
switch ($row['status']) {
case '0':
/**
* Повторная загрузка страницы, от ЯД еще не было известий
*/
$preText = '<div class="message">Оплата от ЯД еще не подтверждена.</div>';
$data.= "\n++Показано сообщение о неподтверждении оплаты\n";
fwrite($ff, $data);
fclose($ff);
return $preText.$formTpl;
break;
case '2':
/**
* От ЯД пришло сообщение, возникла ошибка
*/
$data.= "\n++Показано сообщение об ошибке оплаты, сессия сброшена\n";
fwrite($ff, $data);
fclose($ff);
$_SESSION['yad_pay_label'] = 0; //???
return '<div class="message">Извините, возникла ошибка при переводе средств (E60). Обратитесь к администратору сайта.</div>';
break;
case '5':
/**
* От ЯД пришло сообщение, оплата прошла
*/
$data.= "\n++Показано сообщение об успехе оплаты, сессия сброшена\n";
fwrite($ff, $data);
fclose($ff);
$_SESSION['yad_pay_label'] = 0;
return '<div class="message">Оплата получена. Спасибо за покупку.</div>';
break;
default:
/**
* Тут мы не должны оказаться никогда, но мало ли...
*/
$data.= "\n++Показано сообщение об ошибке Е99, сессия не сброшена\n";
fwrite($ff, $data);
fclose($ff);
return '<div class="message">Ошибка (E99). Как вы это делаете?</div>';
break;
}
}
}
} else {
$data.= "\n++Показано сообщение о ненужности оплаты\n";
fwrite($ff, $data);
fclose($ff);
/**
* Не нужно показывать кнопку перехода к оплате
*/
$retStr = $modx->getChunk($noFormTpl);
if (!$retStr || $retStr == '') {
return $noFormTpl;
} else {
return $retStr;
}
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment