Как сделать платное скачивание файлов с сайта

 

Схема процесса платного скачивания файлов с сайта

Обработчик страницы ввода данных об оплате

Что произойдёт после оплаты

Какие данные придут в уведомлении

Обработчик ответа платёжной системы

Сохранение данных, полученных после оплаты

Отправка сообщения пользователю

Страница личных загрузок пользователя

Вывод файла для загрузки (обработчик страницы личных загрузок)

Как тестировать систему оплаты

 

В статье "Как сделать скачивание файлов с сайта" был подробно разобран общий принцип загрузки файлов на компьютер пользователя, код представления файлов на странице (изображение, пояснительный текст, кнопка скачивания) и код обработчика, управляющего данным процессом, причём таким образом, что путь (адрес) скачиваемого файла не выводится на страницу. Теперь пришло время поговорить о том, как сделать загрузку платной.

В качестве основы и примера будет использован код компонента для CMS Joomla, созданного в вышеуказанной статье, но пользователям других типов CMS не составит труда адаптировать его под свою систему: важно понять смысл происходящего, ведь около 98% кода универсальна и подходит для любого варианта!

 Если вы не читали статью "Как сделать скачивание файлов с сайта", то рекомендуем с ней ознакомиться, так как здесь часть начального кода будет приведена без подробного разбора каждой строки. Однако если вы легко читаете HTML и PHP, можете сразу приступить к прочтению данного материала.

В качестве платёжной системы будет использована Yoomoney (не путать с Юkassa!), позволяющая принимать платежи на кошелёк и привязанную к нему карту с мобильного телефона, кошелька этой же системы или любой банковской карты. Разобравшись в принципе работы, вы сможете легко переустановить любую другую систему приёма платежей, так как общие принципы довольно схожи, разница только в условиях обслуживания. Так же будем считать, что у вас уже есть кошелёк в платёжной системе Yoomoney.

 Обратите внимание: по новым правилам кошелёк, получающий любые платежи и переводы, должен быть идентифицированным - это касается любой платёжной системы! 

 Обратите внимание: платёжный сервис Yoomoney хотя и позволяет принимать платежи, на серьёзный бизнес он не рассчитан. Для сайта с большими продажами используйте системы Юkassa, ЮBusiness или Юkassa для самозанятых.

Итак, начнём.

 Если вы ещё не создавали отдельную папку с контентом - создайте её. Эта папка должна находится в корне сайта и (в качестве примера) содержать три файла с любым содержимым:   content.zip content.jpg content.pdf .

 Схема процесса платного скачивания файлов с сайта

Что бы сделать скачивание платным, в общую схему этого процесса нужно добавить ещё несколько php-файлов. Изначально схема (для простого скачивания) выглядела так:

 Схема загрузки файлов

Ничего сложного: блоки с информацией о скачиваемых файлах располагаются на каких-либо страницах или на отдельной странице ("Загрузки"), обработчик запроса при получении такового подготавливает нужный файл к загрузке и открывает диалоговое окно на экране пользователя. 

При организации платного скачивания схема будет такая:

Схема платной загрузки файлов

 

Как видно из схемы, обработчик запроса не выводит диалоговое окно для скачивания (как в первом случае), а открывает страницу, на которой можно выбрать вариант оплаты, ввести сумму и необходимые данные. После ввода данные будут переданы обработчику страницы оплаты, который в свою очередь передаст их на страницу платёжной системы. После произведённой оплаты обработчик ответа получит всю необходимую подтверждающую информацию, обработает её и в случае успешного завершения процесса откроет пользователю диалоговое окно загрузки.

Возможно у вас появился вопрос: зачем нужен дополнительный промежуточный обработчик страницы ввода платёжных данных? Не проще их сразу передать платёжной системе? Это нужно по некоторым причинам. Основная причина - контроль введённых пользователем данных, ведь их можно легко подделать! Так же вполне возможна ситуация, когда некоторые данные не должны находиться на странице ввода, или когда вы хотите сделать дополнительное уведомление администратору о предстоящем переводе и так далее.

Так же есть чуть более расширенный и удобный вариант, при котором обработчик ответа платёжной системы не открывает диалоговое окно, а только сохраняет в базе данных всю информацию о проведённом платеже и отправляет пользователю email с уведомлением, что он может скачать данный файл (например, в Личном Кабинете или на специальной странице); при этом дополнительно устанавливается отдельный php-файл, контролирующий скачивание, а так же время, доступное для скачивания:

 Схема платной загрузки файлов

Чем это удобно? Тем, что пользователь, купивший контент, может скачать файл в удобное для него время просто зайдя на определённую страницу. Почему так лучше? Потому, что могут возникнуть различные причины, из-за которых у пользователя не будет возможности скачать контент сразу после оплаты.

Теперь рассмотрим всю схему в подробностях.

Для начала вспомним, как выглядит блок кода, выводящий на страницу информацию о скачиваемом файле:

<!-- Блок 1 -->
<div style='min-height: 270px;'>
    <hr>
    <div style='float: left; margin-right: 12px;'> <img src='путь к изображению' title='' alt='' /></div>
    <h2>Файлы .zip</h2>
    <p style="text-align: justify;">Пояснительный текст</p>
    <p style='text-align: center;'>
         <button name='btn_1'  value='ontype='submitstyle='background: none; border: none;'>
             <img src='путь к изображению на кнопке' title='Скачать' alt='Скачать'>
        </button>
    </p>

</div>

Изображение, справа от него заголовок (название) и пояснительный текст, под ним - кнопка "Скачать". Блоки, соответственно, расположены внутри формы, которая отправляет обработчику на сервере данные в виде ключ > значение (в данном случае btn_1 > 'on'). Кнопка второго блока будет иметь имя btn_2, третьего - btn_3 и так далее. Какая именно кнопка "нажата" обработчик определяет по этому имени (значение в данном случае роли не играет):

<?php
    defined('_JEXEC'or die('Restricted access');
    if (isset($_POST['btn_1'])){
        $label = 'file_001_id=';
    } elseif (isset($_POST['btn_2'])){
        $label = 'file_002_id=';
    } elseif (isset($_POST['btn_3'])){
      $label = 'file_003_id=';
    }

Здесь в зависимости от нажатой кнопки переменная $label получает соответствующее значение (об этом чуть позже). Так же  обратите внимание на то, что применять вместо isset() выражение типа  if ($_POST['btn_1'] == 'on'){ ... }  нельзя - это вызовет ошибку в версиях PHP выше 7.0.

А вот дальнейший код полностью отличается от обработчика созданного для обычного бесплатного скачивания, так как выполняет совершенно другие функции. На этот раз обработчик должен создать на экране страницу для ввода платёжных данных, а так же обработать и передать значение переменной $label, которое в дальнейшем (после выполнения платежа) будет использовано для определения скачиваемого файла и пользователя, оплатившего скачивание. Сделано это будет так.

Для начала получим от системы идентификатор пользователя - нужно же знать, кто именно скачивает файл! Эти данные понадобятся для дальнейших действий:

$user = JFactory::getUser();
$user_id = $user->get('id');

Обратите внимание: способ получения идентификатора различается в зависимости от CMS; соответственно, если вы используете не Joomla, выясните, как в вашей CMS это делается. Главная задача - присвоить переменной  $user_id значение в виде id пользователя. 

Теперь нужно дополнить переменную $label:

$label$label$user_id;

В результате переменная получит значение вроде file_001_id=712, где указано имя скачиваемого файла (в укороченном виде) и id пользователя. 

Далее идёт HTML-код страницы. Заголовок:

<h1 style='margin: 15px 0 10px 30px; text-decoration: underline;'>Загрузки</h1>

Изображение и какой-либо пояснительный текст справа:

<p style='margin: 0 0 21px 15px;'>
    <img src='путь к изображению' style='display: inline-block; float: left; margin-right: 12px;'/><br />
    <span style='font-size: 16px; font-size: larger; font-family: Georgia; font-style: italic;'>Текст</span>
</p>

Далее следует самая главная часть: поля для ввода данных и форма для их отправки:  

<form name="form_money" method="post" action="адрес обработчика" enctype="multipart/form-data">
<div class='f_money'>
    <span>Сумма:</span>
    <input required type="text" name="sum" value='300' data-type="number">
    <span style='margin-left: 12px;'>рублей</span><br />

Здесь всё просто: форма и поле "Сумма" с установленным предварительным значением, которое пользователь может изменить. При необходимости можно добавить дополнительные атрибуты, запрещающие изменение значения.

Далее следует код переключателя способа оплаты:


     <label>
          <input checked type="radio" name="paymentType" value="AC">
          <img src='адрес изображения' style='margin-left:7px;'>
     </label>
     <label>
          <input type="radio" name="paymentType" value="PC">
          <img src='адрес изображения' style='margin-left:7px;'>
     </label>
     <label>
          <input type="radio" name="paymentType" value="MC">
          <img src='адрес изображения' style='margin-left:7px;'>
     </label>
     <input type="hidden" name="label" value="<?php echo $label; ?>">
</div>

Здесь важно разобрать всё очень подробно.

Код создаёт на странице переключатель (type="radio") на 3 позиции; первая позиция получает checked автоматически при открытии страницы. Для каждого варианта установлено своё маленькое изображение, которое можно заменить на простой текст ("мобильный", "карты" и т.д.).

При отправке формы данный элемент передаст одно из трёх значений: АС - для карт, РС - для кошелька Yoomoney и МС - для оплаты с мобильного. Эти значения менять нельзя - это требования платёжной системы.

В конце находится скрытое поле, принимающее и передающее в форме значение переменной $label, полученное в самом начале.

Далее следует код кнопки, отправляющей форму:

<div style='text-align: center; margin-top: 12px;'>
     <button type="submit" name='btn_perevod' value='q1v2j3p4s7'>
          <img src='адрес изображения'>
     </button>
</div>

Итак, со страницей ввода данных разобрались. В принципе, здесь вводится только сумма и выбирается способ оплаты, а так же автоматически передаётся значение переменной $label; при необходимости вы можете создать внутри формы дополнительные поля для ввода других данных, например, телефона, адреса пользователя и т.п. Адрес электронной почты вводить не нужно - он будет автоматически определён позже.

Теперь переходим к следующему этапу - к обработчику страницы ввода данных об оплате. 

 

Обработчик страницы ввода данных об оплате

Задачи данного обработчика следующие:

  • проверить введённые пользователем данные;
  • ввести недостающие данные, которые не зависят от пользователя;
  • сформировать форму и отправить её на страницу платёжной системы.

Проверять входящие данные нужно хотя бы для того, что бы убедиться в правильности ввода. Например, если у вас допускается произвольный ввод суммы, то пользователь может ввести сумму, которая вас не устроит. Если имеются дополнительные поля для ввода комментариев, email, адресов и телефонов, это можно использовать для ввода некорректной информации. Не будем тратить время и разбирать какой именно, важно просто понять: необходимо проверять любые входящие данные - это основа безопасности вашего сайта.

<?php
     if(isset($_POST['btn_perevod'])){
         if($_POST['btn_perevod'] == 'q1v2j3p4s7'){
             if(isset($_POST['sum'])){
                 $f_sum = mb_eregi_replace('[^0-9]', '', $_POST['sum']);
             }

             if(isset($_POST['paymentType'])){
                 $f_type = mb_eregi_replace('[^A-Z]', '', $_POST['paymentType']);
             }

             if(isset($_POST['comment'])){
                 $f_comment = mb_eregi_replace('[^0-9A-Za-zА-Яа-я\,\!\? ]', '',  $_POST['comment']);
             }

             if(isset($_POST['label'])){
                 $f_label = mb_eregi_replace('[^0-9a-z\-]', '', $_POST['label']);
             }

        }
   }

В данном коде сначала при помощи функции isset  проверяется наличие входящей переменной $_POST['btn_perevod']: если этого не сделать, система выдаст ошибку в версиях PHP выше 7.0; аналогично будут проверяться и все другие переменные $_POST. Далее проверяется значение, отправленное кнопкой, а затем и все другие переданные данные. Например, $_POST['sum'] не может содержать ничего кроме цифр, $_POST['paymentType'] ничего кроме заглавных букв. Переменная $_POST['comment'], содержащая комментарий (если это предусмотрено кодом исходной страницы), может включать в себя буквы, цифры, и некоторые знаки; применение функции mb_eregi_replace()  исключит возможность передачи вредоносного кода, так как эта функция удалит типичные для HTML и PHP символы типа <, >, кавычки и т.п. По такому же принципу необходимо сделать обработку дополнительных полей, если они будут присутствовать на странице ввода данных.

Если сумма вводится самим пользователем, то после проверки входящих данных вы можете применить конструкцию, которая будет обрабатывать введённую сумму и возвращать обратно на страницу ввода в случае, если данные введены неправильно (например, сумма окажется меньше). Такой код будет выглядеть приблизительно так:

if($_POST['sum'] < 500){
    header ("Location: https://адрес страницы ввода&mn=no");
 }

Конструкция if проверяет истинность выражения $_POST['sum'] < 500; если введённое значение окажется меньше, то произойдёт переадресация обратно на страницу ввода (заголовок header). Обратите внимание, что к адресу добавляется переменная mn со значением no. Это делать необязательно, но очень удобно: браузер не просто вернётся на страницу ввода, а выведет на страницу предупреждение о неправильно введённой сумме. Если вы вы собираетесь использовать такую функцию, добавьте в код страницы ввода сразу после заголовка <h1>Загрузки</h1> вот такую конструкцию:

<?php  if (isset($_GET['mn'])) {  ?>
       <p style='color: red;'><b>Ошибка при вводе суммы!</b></p>
<?php } ?>

Теперь, если при открытии страницы в адресе окажется переменная $_GET['mn'], на экран будет выведена фраза "Ошибка при вводе суммы!".

Возвращаемся к коду обработчика. После проверки введённых данных создаётся форма со скрытыми полями и единственным выводимым на экран словом, например, "Подождите...":

<p>Подождите...</p>
<form method="POST" id="form_yoomoney" action="https://yoomoney.ru/quickpay/confirm.xml">
<input type="hidden" name="quickpay-form" value="donate">
<input type="hidden" name="receiver" value="41001234567890">
<input type="hidden" name="targets" value="Оплата скачивания">
<input type="hidden" name="sum" value="<?php echo $f_sum; ?>" data-type="number">
<input type="hidden" name="paymentType" value="<?php echo $f_type; ?>">
<input type="hidden" name="formcomment" value="Оплата скачивания">
<input type="hidden" name="short-dest" value="Оплата скачивания">
<input type="hidden" name="label" value="<?php echo $f_label; ?>">
<input type="hidden" name="successURL" value="адрес страницы">
</form>

Обратите внимание: изменять адрес, указанный в форме (значение action) и имена полей (значения name) нельзя: это уже требования платёжной системы, иначе ничего работать просто не будет.

В данном запросе передаётся:

  • quickpay-form - тип формы (donate, shop, small);
  • receiver - номер вашего счёта (кошелька), куда придут деньги;
  • targets - назначение платежа (будет указано в качестве заголовка);
  • sum - сумма;
  • paymentType - способ оплаты;
  • formcomment - название перевода в кошельке отправителя;
  • short-dest - название на странице скачивания;
  • label - метка перевода;
  • successURL - адрес страницы, которую нужно открыть после перевода.

Разберём что здесь для чего.

Тип формы большой роли не играет: donate означает, что форма создана для сбора денег, shop - форма для принятия платежей, small - просто кнопка (в данном случае не используется).

Текст для targets, formcomment, short-dest проще указать один и тот же; как правило, это просто указатель назначения платежа ,наименования товара (услуги). Последние два поля указывать (и использовать) необязательно. 

Поле successURL указывает адрес страницы, на которую нужно перейти после обработки платежа; это должен быть адрес обработчика ответа платёжной системы (об этом ниже).

Поле label содержит значение переменной $label, которое составлено из кода скачиваемого файла и id пользователя (в выше приведённом примере - file_001_id=712).

 Обратите внимание: поля successURL и label для платёжной системы не являются обязательными, но в данном случае их присутствие необходимо, так как на их значениях строится весь дальнейший код.

 Если вы добавляли другие поля на страницу ввода, не забудьте добавить и соответствующие скрытые поля в обработчике! Более подробно обо всём читайте в Справке Yoomoney.

И последний элемент кода обработчика: скрипт для автоматического отправки формы на страницу платёжной системы:

<script type="text/javascript">document.getElementById('form_yoomoney').submit();</script>

Этот скрипт отправляет данные автоматически, поэтому через 1-2 секунды у пользователя откроется страница платёжной системы Yoomoney для оплаты выбранным способом. 

Дополнительные возможности обработчика страницы ввода данных

Так же на данной странице по желанию можно разместить какой-либо ещё код. Например, автоматически отправляющий администратору email о том, что сейчас будет сделан денежный перевод с такой-то целью: в некоторых случаях это может быть полезным.

 

Что произойдёт после оплаты

Итак, пользователь совершил оплату. Теперь необходимо получить подтверждение и запустить на сайте дальнейшие действия. Всё это, естественно, должно происходить  в автоматическом режиме - пользователь не должен ждать несколько часов пока вы вручную всё это сделаете.

Для того, что бы получить подтверждение, необходимо подключить данную функцию в Настройках кошелька (значок настроек > Пакеты и другие сервисы > Сбор денег > Уведомления > Настроить). Здесь нужно указать адрес, на который нужно отправлять сообщение, получить секретный код и поставить "галочку" "Отправлять уведомления". Что очень удобно, здесь же находится кнопка "Протестировать", отправляющая тестовое уведомление; это позволит в дальнейшем быстро и удобно наладить всю систему на вашем сайте. 

 

Какие данные придут в уведомлении

 С полной и подробной информацией об уведомлениях вы можете ознакомится на странице платёжного сервиса Yoomoney, а здесь мы разберём только то, что используется для составления нижеприведённого кода, позволяющего создать простую систему платного скачивания контента с вашего сайта.

Итак, в ответе вы получите:

  • информацию когда и как сделан перевод (карта, кошелёк) и идентификатор операции;
  • сумму списания у отправителя;
  • сумму зачисления в ваш кошелёк;
  • номер кошелька отправителя (для переводов с кошелька);
  • метку платежа (то самое значение переменной $label);
  • SHA-1 hash параметров уведомления (очень важно для безопасности);
  • unaccepted (информация о зачислении);

а так же другие запрошенные данные (адрес, почта, ФИО, телефон) если таковые запрашивались, но имейте в виду: для передачи таких данных сайт должен работать на HTTPS.

 

Обработчик ответа платёжной системы: что он делает с полученными данными

В первую очередь необходимо позаботиться о безопасности вашего сайта: проверить входящие данные и удостовериться, что это настоящие данные, присланные платёжной системой; для этого используется SHA-1 hash параметров уведомления.

Рассмотрим подробно, как это работает.

 Приведённый ниже код можно немного упростить, однако такой вариант очень наглядно показывает, как обеспечить безопасность входящих данных.

if(isset($_POST['notification_type'])){$notification_type = $_POST['notification_type'];}else{$notification_type = '';}
if(isset($_POST['withdraw_amount'])){$withdraw_amount=$_POST['withdraw_amount'];}else{$withdraw_amount = '';}
if(isset($_POST['operation_id'])){$operation_id = $_POST['operation_id'];}else{$operation_id = '';}
if(isset($_POST['unaccepted'])){$unaccepted = $_POST['unaccepted'];}else{$unaccepted = '';}
if(isset($_POST['sha1_hash'])){$sha1_hash = $_POST['sha1_hash'];}else{$sha1_hash = '';}
if(isset($_POST['currency'])){$currency = $_POST['currency'];}else{$currency = '';}
if(isset($_POST['datetime'])){$datetime = $_POST['datetime'];}else{$datetime = '';}
if(isset($_POST['codepro'])){$codepro = $_POST['codepro'];}else{$codepro = '';}
if(isset($_POST['amount'])){$amount = $_POST['amount'];}else{$amount = '';}
if(isset($_POST['sender'])){$sender = $_POST['sender'];}else{$sender = '';}
if(isset($_POST['label'])){$label = $_POST['label'];}else{$label = '';}

Здесь всё просто: проверяется наличие каждой используемой переменной в ассоциативном массиве данных $_POST и если таковая присутствует, то её значение присваивается другой переменной с похожим именем; если значение по каким-то причинам отсутствует, то новой переменной присваивается пустое значение. Если этого не сделать, то в дальнейшем коде PHP выше 7.0 выдаст ошибку (или предупреждение).

if ($notification_type != 'p2p-incoming'){if ($notification_type != 'card-incoming'){$notification_type = '';}}
$withdraw_amount = mb_eregi_replace('[^0-9.]', '', $withdraw_amount);
$operation_id = mb_eregi_replace('[^0-9a-zA-Z.-]', '', $operation_id);
$unaccepted = mb_eregi_replace('[^0-9a-z]', '', $unaccepted);
$sha1_hash = mb_eregi_replace('[^0-9a-z]', '', $sha1_hash);
$currency = mb_eregi_replace('[^0-9]', '', $currency);
$datetime = mb_eregi_replace('[^0-9TZ:-]', '', $datetime);
$codepro = mb_eregi_replace('[^0-9a-z]', '', $codepro);
$amount = mb_eregi_replace('[^0-9.]', '', $amount);
$sender = mb_eregi_replace('[^0-9]', '', $sender);
$label = mb_eregi_replace('[^0-9a-z.-]', '', $label);

Этот блок кода проверяет входящие данные на безопасность. Например, переменная $notification_type, получившая данные от $_POST['notification_type'], не может содержать ничего кроме одного из вариантов: 'p2p-incoming' или 'card-incoming', переменная $withdraw_amount - только цифры и точку, $currency - только цифры и так далее. Другие символы будут просто удалены.

А далее следует самый важный этап: проверка подлинности:

$secret='секретный код';
$hash_ctrl = $notification_type.'&'.$operation_id.'&'.$amount.'&'.$currency.'&'.$datetime.'&'.$sender.'&'.$codepro.'&'.$secret.'&'.$label;
$sha_ctrl = sha1($hash_ctrl);
if($sha_ctrl === $sha1_hash){$h_value = 'Y';}else{$h_value = 'N';}

Переменной $secret присваивается секретный код, полученный вами в Личном кабинете - это постоянное значение, которое не должен никто знать. Далее составляется строка из полученных и проверенных выше переменных; строка составляется без пробелов, с разделителем в виде символа &. В следующей строке кода функция sha1() присваивает переменной $sha_ctrl SHA1-хеш полученной строки. В самом конце сравниваются значения двух переменных: $sha_ctrl и $sha1_hash, содержащей SHA1-хеш, присланный в  ответе (изначально $_POST['sha1_hash']): если значения совпали - полученный ответ подлинный. Обратите внимание на то, что в данном случае используется оператор сравнения "Тождественно равно" (===), а не просто "Равно" - это важно. 

Последняя строка данного кода не только производит сравнение, но и создаёт переменную $h_value c одним из значений - либо 'Y' (подлинный ответ),  'N' (поддельный ответ). Это пригодится чуть позже.

 При необходимости здесь может быть применена и третья переменная, например, 'Z'. Об этом читайте ниже.

Следующий блок кода применяется по желанию: обработка даты и времени. Дело в том, что это значение приходит в не слишком удобном для чтения виде, поэтому перед внесением в базу данных его можно переделать на более привычный вид ("дд.мм.гггг чч:мм:сс"):

$dt_arr = str_split($datetime);
$date = $dt_arr[8].$dt_arr[9].'.'.$dt_arr[5].$dt_arr[6].'.'.$dt_arr[0].$dt_arr[1].$dt_arr[2].$dt_arr[3];
$time = $dt_arr[11].$dt_arr[12].':'.$dt_arr[14].$dt_arr[15].':'.$dt_arr[17].$dt_arr[18];

Здесь всё просто: в начале переменная $dt_arr получает массив из символов строки в переменной $datetime, после чего из этого массива формируется отдельно дата и отдельно время в удобном формате.

Следующий код обрабатывает значение $label, которое содержит упрощённое имя скачиваемого файла и id пользователя:

if(!($label == '')){
$arr_lbl = str_split($label);
$name_value = $arr_lbl[0].$arr_lbl[1].$arr_lbl[2].$arr_lbl[3].$arr_lbl[4].$arr_lbl[5].$arr_lbl[6].$arr_lbl[7];
$id_value = $arr_lbl[12].$arr_lbl[13].$arr_lbl[14];
if(isset($arr_lbl[15])){$id_value = $id_value . $arr_lbl[15];}
}

Сначала проверяется, имеет ли переменная значение, затем точно так же как и в предыдущем блоке кода оно разбивается на символы. Далее переменная $name_value получает имя, а $id_value - идентификатор пользователя. Здесь очень важно, что бы все имена имели строго одинаковую длину, в данном случае -  8 символов (file_001). Так же обратите внимание на небольшую, но существенную деталь: если на сайте окажется много зарегистрированных пользователей, вполне возможно, что id станут не только 3-х-значными, но и 4-х-значными; именно это и предусмотрено в предпоследней строке. 

 

Сохранение данных, полученных после оплаты

Далее полученные и обработанные данные заносятся в базу данных. Это нужно делать по разным причинам, основными из которых являются:

  • что бы иметь отчёт по скачанному контенту (по транзакциям);
  • что бы управлять скачиванием.

Для хранения таких данных потребуется создать отдельную таблицу в базе. Эта таблица должна иметь следующие столбцы:

  • id - порядковый номер (не путайте с идентификатором операции!);
  • h_value - сохранение переменной подлинности (пригодится для дальнейшей работы);
  • notification_type - тип операции (вариант оплаты);
  • withdraw_amount - сумма, списанная со счёта пользователя;
  • amount - сумма, зачисленная на ваш счёт;
  • operation_id - идентификатор операции;
  • date - дата операции;
  • time - время операции;
  • codepro - код протекции (см. подробно в инструкциях Yoomoney);
  • sender - номер кошелька отправителя;
  • currency - код валюты (643);
  • unaccepted - зачислен или не зачислен перевод (см. подробно в инструкциях Yoomoney);
  • code_file - укороченное имя файла из $label;
  • id_user - id пользователя.

Для сохранения используется стандартный запрос для CMS Joomla; для других CMS следует использовать соответствующие системе запросы или стандартный синтаксис для SQL:

$db = JFactory::getDBO();
$query = $db
->getQuery(true)
->select('MAX(id)')
->from($db->quoteName('#__assist_admin_money'));
$db->setQuery($query);
$id = $db->loadResult();

Этот блок кода производит соединение с базой данных и получает номер последней её строки (переменная $id). Далее создаётся новая строка:

$query = $db
->getQuery(true)
->insert($db->quoteName('#__my_money')) 

->columns($db->quoteName(array('id', 'h_value', 'notification_type', 'withdraw_amount', 'amount', 'operation_id', 'date', 'time', 'codepro', 'sender', 'currency', 'unaccepted', 'code_file', 'id_user')))

->values(implode(',', array(++$id, $db->quote($h_value), $db->quote($notification_type), $db->quote($withdraw_amount), $db->quote($amount), $db->quote($operation_id), $db->quote($date), $db->quote($time), $db->quote($codepro), $db->quote($sender), $db->quote($currency), $db->quote($unaccepted), $db->quote($name_value), $db->quote($id_value))));

$db->setQuery($query);
$db->execute();

 

Отправка сообщения пользователю

И последнее, что осталось сделать в файле обработчика - отправить сообщение пользователю. Здесь применяется стандартная система отправления email с сайта; для разных CMS код может различаться, поэтому стоит обратиться к документации системы. Для CMS Joomla данный код будет выглядеть так.

Сначала нужно получить email-адрес пользователя; для этого используется простой запрос к базе данных:

$query = $db
->getQuery(true)
->select($db->quoteName('email'))
->from($db->quoteName('#__users'))
->where($db->quoteName('id') . " = " . $db->quote($id_value));
$db->setQuery($query);
$to = $db->loadResult();

Данный код получает значение столбца 'email' из таблицы '#__users' (строка определяется по значению $id_value в столбце 'id'). В результате переменная $to получает электронный адрес пользователя.

Далее подготавливается текст сообщения, который зависит от значения переменной $h_value:

if($h_value == 'Y'){
$email_text = 'Спасибо за ваш выбор!<br />Вы можете скачать ... в разделе ...  ';
}elseif($h_value == 'N'){
$email_text = 'Уважаемый пользователь!<br />Произошёл технический сбой. Мы решим эту проблему в течении 24 часов и оповестим Вас по email. Примите наши извинения за доставленные неудобства! ';
}

Теперь нужно создать остальные переменные и внести в них значения. Обратите внимание: значение переменной $email_from (адрес отправителя, "От кого:") получен из файла конфигурации и является адресом почты сайта; как вариант, вы можете просто внести его в виде строки:

$config = JFactory::getConfig();
$email_from = $config->get('fromname');
$subject = 'текст заголовка (тема)';

Код текста ("тела") сообщения:

$message =
'
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Заголовок письма</title>
</head>
<body>
<p>' . $email_text . '<br>С уважением, *ваша подпись*.</p>
</body>
</html>
';

Заголовки и команда отправки:

$headers = 'MIME-Version: 1.0' . "\r\n" . 'Content-type: text/html; charset=iso-8859-1' . "\r\n" . 'From: ' . $email_from . "\r\n" . 'Reply-To:' . $row_chbx[1] . "\r\n" . 'X-Mailer: PHP/' . phpversion();
mail($to, $subject, $message, $headers);

Всё! Уведомление об оплате обработано, пользователю отправлено сообщение и открыт доступ на скачивание. Теперь он может зайти в свой личный кабинет или в специальный раздел (например, "Мои загрузки") и скачать необходимый файл. Как это реализовано?

 

Страница личных загрузок пользователя

Эта страница не представляет собой ничего сложного: она открывается при помощи пункта в главном меню (показывается только для зарегистрированных пользователей), получает идентификатор пользователя и проверяет принадлежащие ему загрузки по последнему столбцу базы данных ('id_user'). Если ничего не обнаружено - выводится сообщение типа "У вас ещё нет загрузок!..".

Рассмотрим код такой страницы для CMS Joomla ( его так же легко можно адаптировать для любых других).

$user = JFactory::getUser();
$user_id = $user->get('id');

Здесь, как вы поняли, переменная $user_id получает id пользователя. Далее идёт подключение к базе данных, проверка соединения и получение номера последней строки:

if (!$db->connected()) {echo 'Ошибка подключения к базе данных...'; exit;}
$query = $db 
     ->getQuery(true)
     ->select('MAX(id)')
     ->from($db->quoteName('#__my_money'));
$db->setQuery($query);
$row_num = $db->loadResult();

Номер последней строки сохранён в переменной $row_num. Теперь можно переходить к созданию элементов страницы, но сначала создаём форму:

<form name="kf" method="post" action="адрес обработчика формы" enctype="multipart/form-data">

Создаём заголовок страницы:

<h1>Мои загрузки</h1>

И переходим к построчному обходу базы данных, используя столбец id_user в качестве ключа: если значение в этом столбце совпадёт с id пользователя, элемент будет выведен на страницу:  

<?php
  $i_com = 0;
  while ($row_num){
  $query = $db
     ->getQuery(true)
     ->select($db->quoteName(array('h_value', 'code_file', 'u_id')))
     ->from($db->quoteName('#__my_money'))
     ->where($db->quoteName('id') . " = " . $db->quote($row_num)); 
$db->setQuery($query);
$result = $db->loadRow();
if ($result[2] == $user_id){
    if($result[1] == 'file_001'){$c_name = 'Полное название файла';}

    elseif($result[1]=='file_002'){$c_name='Полное название файла';}
?>

Что здесь происходит?

В начале каждого цикла осуществляется запрос к базе данных; запросы начинаются с последней строки. Запрос получает данные не всей строки, а только столбцов  'h_value' (Y/N), 'id_user' (id пользователя, которому принадлежит элемент) и 'code_file' - краткое именование файла. Если значение столбца 'id_user' и id пользователя совпали, то по столбцу 'h_value' определяется имя файла и присваивается переменной $c_name, причём уже в полном варианте; соответственно, конструкцию elseif($result[1]=='file_002'){$c_name='Полное название файла';} необходимо прописать для всех имеющихся файлов. Так же обратите внимание: самая первая конструкция if пока осталась незакрытой!

<hr>
<div>
    <img src='адрес изображения'>
    <p style='display: inline-block; width: 400px; vertical-align: text-top;'><?php echo $c_name; ?></p>
   <?php if($result[0] == 'Y'){ ?>
     <button name='btn_component' type='submit' value='<?php echo $result[1]; ?>'><img src='адрес рисунка              зелёной кнопки'></button>
   <?php }elseif($result[0] == 'Z'){ ?>
     <img src='адрес рисунка красной кнопки'>
   <?php }elseif($result[0] == 'N'){ ?>
     <img src='адрес рисунка желтой кнопки'>
   <?php } ?>

Как видно из следующего кода, далее создаётся блок для каждого элемента, заключённый в теги <div> ... </div>. Сначала следует изображение (слева),  затем справа устанавливается название (переменная $c_name). В правой части блока выводится в зависимости от ситуации либо кнопка для скачивания (тег <button>) в виде соответствующего рисунка в синем или зелёном цветовом варианте, либо только рисунок кнопки в желтом и красном вариантах.

Синяя кнопка Красная кнопка

 

Для чего это нужно?

Зелёная (синяя) кнопка сообщает, что элемент готов к скачиванию: это определяется конструкцией if/elseif в случае, если значение в столбце 'h_value' базы данных (переменная $result[0]) окажется 'Y'. Изображение жёлтой кнопки будет выведено в том случае, если указанное значение окажется 'N'; это означает, что контент временно недоступен для скачивания, из-за технической ошибки (нет подтверждения безопасности, см. выше). Красная кнопка не является необходимой и означает, что время скачивания элемента вышло - это применяется в том случае, если вы ограничиваете доступность загрузки по времени; для того, что бы именно это изображение было выведено на экран, используется значение 'Z'. Так как жёлтая и красная кнопки представляют собой только рисунок, то они, естественно, ничего не загружают и поэтому должны быть снабжены дополнительным пояснительным текстом.

И далее идёт заключительная часть кода:

<?php
           $row_num--; $i_com = 1;
       }else{
           $row_num--;
       }
   }
   if($i_com == 0){
?>
   <p>Расширений не найдено...</p>
<?php } ?>
</form>

Как вы помните, конструкция if осталась незакрытой, т.е. вывод блока с элементом происходит только при выполнении соответствующего условия, а именно совпадения id пользователя и значения в столбце 'id_user'. После вывода блока выполняется ещё два действия: уменьшается номер строки и изменяется значение переменной $i_com (об этом чуть ниже). Если же в указанной конструкции эти значения не совпадут, то ничего на страницу не выводится, а просто уменьшается номер строки и переменная $i_com так и остаётся неизменной.

Здесь важно отметить такой момент, как управление циклом. Когда значение переменной $row_num станет равно 1, после выполнения вышеуказанного кода за счёт конструкции $row_num-- значение этой переменной станет равно нулю и цикл будет остановлен, т.е. будут обработаны все строки таблицы.

И последний блок кода на этой странице: конструкция if($i_com == 0). Если значение истинно, т.е. ни одной загрузки не обнаружено, на экран будет выведена фраза "Расширений не найдено...".

Итак, со страницей разобрались. Осталось разобрать код обработчике её формы.

 

Вывод файла для загрузки

Смысл создавать отдельный файл-обработчик формы, а не выводить диалоговое окно загрузки прямо со страницы "Мои загрузки" заключается в том, что при таком варианте адрес скачиваемого файла не виден пользователю, так как он находится не на странице, а на сервере - эта тема подробно разбиралась в статье про обычное скачивание (без оплаты).

Код обработчика такой:

 if (isset($_POST['btn_component'])){

     if ($_POST['btn_component'] == 'file_001'){
            $file = $_SERVER['DOCUMENT_ROOT'] . '/my_content/content.zip';
            $fyles_type = '.zip';
     } elseif ($_POST['btn_component'] == 'file_002'){
            $file = $_SERVER['DOCUMENT_ROOT'] . '/my_content/content.pdf';
            $fyles_type = '.pdf';
    } elseif ($_POST['btn_component'] == 'file_003'){
            $file = $_SERVER['DOCUMENT_ROOT'] . '/my_content/content.jpg';
            $fyles_type = '.jpg';
    }
    if (file_exists($file)) {
        header('Content-Description: File Transfer');
        header('Content-Type: application/' . $fyles_type);
        header('Content-Disposition: attachment; filename="'.basename($file).'"');
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        header('Content-Length: ' . filesize($file));
        readfile($file);
        exit;
    }

}

В начале проверяется наличие переменной $_POST['btn_component'], затем по значению этой переменной определяется название файла и вводятся данные в переменные $file и $fyles_type (путь к файлу и его тип). Обратите особое внимание: именно путь к файлу на сервере, а не URL-адрес типа https://... - это важно, поэтому здесь и применяется $_SERVER['DOCUMENT_ROOT'].

Далее создаются необходимые заголовки, в том числе и Content-Disposition, который указывает браузеру создать диалоговое окно для загрузки и вывести файл с именем, указанным в filename. В данном коде это имя совпадает с именем изначального файла, но его можно заменить и передать пользователю то же файл с другим именем, но с тем же расширением. В конце присутствие функции exit  обязательно, иначе окно не откроется.

Что ещё нужно сделать?

Выше был рассмотрен только код, касающийся именно системы скачивания. При создании страниц "Загрузки", "Мои загрузки" и страницы ввода данных не забывайте об обязательных атрибутах любых страниц: тегах Description, Keywords, Title, а так же Robots: для первой страницы необходимо указать "index, follow", а для других наоборот - "noindex, nofollow". Не забудьте указывать метатеги для изображений - всё это важно для поисковых систем.

И ещё одна тема, которую нет смысла подробно рассматривать, так как никаких сложностей она не представляет: вывод данных таблицы. Как вы понимаете, данные созданной таблицы вам могут время от времени понадобиться, поэтому неплохо было бы создать отдельную страничку для администратора, на которую эти данные будут выводится в удобной форме. 

 

Как тестировать систему оплаты

И последнее, что осталось сделать - протестировать всю созданную систему. 

Для начала воспользуйтесь кнопкой, расположенной в Настройках платёжного сервиса: она позволяет отправить на ваш сайт тестовое уведомление. В пришедшем уведомлении будут присутствовать не все данные, но главная задача будет выполнена: вы узнаете, правильно ли всё сделано. Для этого просто достаточно кликнуть на кнопку и перезагрузить страницу PhpMyAdmin с открытой базой данных: если всё сделано правильно, вы увидите новую строку. Обратите внимание на значение "Y" - оно показывает отсутствие ошибок.

Теперь можно протестировать проведение платежа. Для этого вам понадобиться войти в другой кошелёк (нельзя платить самому себе!) или карта. При проведении такого платежа укажите сумму 2 рубля (допустимый минимум), а назначение платежа - "Тестовый платёж", т.е. на время измените в блоке на странице "Загрузки" название контента. Далее войдите на сайт как пользователь, откройте страницу "Загрузки", нажмите кнопку "Скачать"; у вас должна открыться страница ввода данных. На этой странице нужно ввести сумму, выбрать способ и нажать "Оплатить". Далее должна открыться страница платёжной системы, где вы проведёте платёж. После платежа вы получите уже рабочее уведомление; снова перезагрузите страницу PhpMyAdmin и проверьте наличие всех данных в соответствующих столбцах. Если всё сделано правильно, то вам (как пользователю) должно прийти сообщение на email (если это предусмотрено). Откройте раздел "Мои загрузки" - на нём теперь появится "купленный" контент и синяя (зелёная) кнопка. Кликните на неё и через пару секунд появится окно для загрузки. Если весь указанный сценарий выполнился - вы всё сделали правильно. 

Осталось протестировать работу жёлтой и красной кнопок. Для этого в PhpMyAdmin измените значение в таблице с "Y" на "N" или "Z" и проверьте, какую "кнопку" выводит страница "Мои загрузки".

- - - - - - - - - - - - - - - 

 Удачной Вам работы!

 

 

Добавлять комментарии и ответы могут только пользователи, прошедшие регистрацию!
Зарегистрируйтесь и напишите своё мнение или ответ!