Created
April 30, 2014 09:01
-
-
Save alooze/d2857971e8bafac38baf to your computer and use it in GitHub Desktop.
getYaD snippet
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?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¤cy&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