Автоматический прием WM-платежей. Сервис WebMoney Merchant. Версия для печати

Существует 3 способа автоматического приема WebMoney на сайте:

Источник - «Лучшие статьи Рунета для онлайн-предпринимателя»

Автор: Никита Сенченко / Источник: oWebMoney

Введение

Существует 3 способа автоматического приема WebMoney на сайте:

  1. Web Merchant Interface
  2. Click&Buy Interface
  3. Выписка WM-счета с последующей проверкой оплаты. Реализуется с помощью XML-интерфейсов X1 и X3.

Я рекомендую использовать 1-й способ, так как он наиболее универсален - позволяет принимать оплату от пользователей как Keeper Classic, так и Keeper Light, а также от пользователей системы Telepat. Кроме того, он позволяет вашему покупателю оплачивать товар/услугу непосредственно с WM-карты или чека Paymer без необходимости регистрации в системе WebMoney.

2-й и 3-й способы такой универсальностью похвастаться не могут, так как поддерживают только Keeper Classic и Light, а 3-й способ, кроме того, довольно сложен в реализации и не обладает абсолютной моментальностью.

Поэтому в данной статье мы поговорим о Web Merchant Interface и рассмотрим подключение сайта к нему на конкретном примере.

Прежде, чем начать, ответим на важный вопрос: можно ли подключить прием WebMoney в автоматическом режиме, совершенно не обладая навыками программирования? Ответ таков: нет, нельзя. Ни по одному из 3-х способов. Так же, впрочем, как и в случае подключения к сайту любой другой системы электронных денег, вам нужно иметь минимальные навыки программирования.

В некоторых наиболее простых случаях возможно подключение Web Merchant Interface по этому упрощенному руководству без необходимости что-то программировать. Но при этом, хотя сам прием платежей и будет автоматизирован, их обработка все равно останется ручным трудом. А это означает, что не будет и моментальности, поэтому такой подход равносилен обычной публикации на сайте номеров своих WM-кошельков (как, кстати, до сих пор многие и поступают).

Так или иначе, в данном материале я предполагаю, что вы умеете программировать под web хотя бы на минимальном уровне. Если не умеете, то дальше можно не читать - у вас ничего не выйдет. Просто опубликуйте на сайте номера своих кошельков и принимайте платежи вручную. Либо наймите программиста.

Для демонастрации программного кода я буду использовать PHP + СУБД MySQL. Но поскольку я постараюсь сдобрить статью подробными комментариями, для вас не составит труда реализовать подключение к Web Merchant Interface на другом серверном языке, который вы знаете. Если что-то все же останется не понятым, прибегайте к техническому описанию интерфейса, которое находится здесь.

Общий принцип

Автоматизация приема WM-платежей может понадобиться самым разным сайтам - от интернет-магазинов до хостинг-провайдеров. Web Merchant Interface (далее для простоты мы будем называть этот сервис просто Мерчант) позволит:
1) выполнять заказ моментально после его оплаты, например, отправлять покупателю цифровой товар, пополнять баланс, активировать услугу и т.д.;
2) принимать платежи круглосуточно, без остановок и без ручного участия ваших сотрудников.

Общий принцип работы Мерчанта такой (см. рис.1). Вы формируете на своем сайте необходимую информацию о заказе (точка А) и отправляете ее сайту Мерчанта https://merchant.webmoney.ru. Одновременно с этим и ваш покупатель попадает на этот сайт для совершения платежа. Мерчант авторизует покупателя, предлагает выбрать способ оплаты, проверяет наличие нужной суммы на кошельке или WM-карте, т.е. проводит ряд необходимых идентификаций и проверок. После этого Мерчант списывает WM с кошелька или карты покупателя. В тот же момент уплаченная сумма поступает на ваш кошелек. Мерчант уведомляет ваш сайт о том, что покупка успешно произведена или о том, что возникла какая-либо ошибка (точка D).


Рис.1. Схема взаимодействия вашего сайта с Мерчантом

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

Подготовительные работы

Для использования Web Merchant Interface ваш WMID должен иметь персональный аттестат. Идем по этой ссылке и за несколько секунд получаем аттестат продавца.

Теперь на странице Настройки нужно подключить к Мерчанту кошелек и установить некоторые опции. Для этого напротив нужного кошелька жмем "настроить". Попадаем на страницу с настройкам. Приводим ее ниже:


Рис.2. Пример заполнения настроек при подключении кошелька к Мерчанту

Что означают эти опции (см. рис.2)?

  • Торговое имя - впишите сюда название вашего сервиса, который будет оплачиваться на данный кошелек. "Торговое имя" плательщик будет видеть на странице Мерчанта перед оплатой (см. рис.3). Ни на что больше оно не влияет.
  • Secret Key - запишем сюда произвольную строку из 15-20 символов, состоящую из английских букв, цифр и других символов, кроме кавычек. Для чего нужен Secret Key, я расскажу чуть позже.
  • Result URL, Success URL, Fail URL. В эти поля нужно вписать адреса 3-х страниц вашего сайта (обязательно с http:// или https:// в начале). Сами страницы мы будем программировать чуть позже, а пока разберемся, зачем они нужны.
    На Success URL покупатель будет отправлен Мерчантом в случае успешной оплаты. На Fail URL покупатель будет отправлен, если оплата по какой-то причине не произошла (например, покупатель передумал платить и нажал на кнопку отказа, либо у него оказалось недостаточно средств на кошельке). Страница Result URL - секретная, пользователь ее не видит. Она служит для "общения" сервера Мерчанта с вашим сайтом, т.е. для обмена информацией. Желательно выбирать для скрипта (файла), прописанного в Result URL, не банальное имя.
  • Рядом с Success URL и Fail URL выбираем в качестве метода вызова POST.
  • Позволять использовать URL, передаваемые в форме - галочку не ставим, эта опция нам не нужна.
  • Высылать оповещение об ошибке платежа на кипер - будет ли Мерчант отправлять вам по внутренней WM-почте сообщение в случае возникновения ошибки при совершении платежа пользователем. Ставим галочку, так как это полезная фишка.
  • Метод формирования контрольной подписи - выбираем MD5. Вернемся к разговору о контрольной подписи позже.
  • Тестовый/Рабочий режимы - выбираем "Тестовый", так как в тестовом режиме деньги с кошелька плательщика не списываются, и это очень удобно для проведения тестовых покупок. В самом конце, когда всё будет готово, мы вернемся к данной опции и переведем ее в рабочий режим.
  • Активность - включениевыключение возможности приема платежей на этот кошелек через Мерчант. Естественно, выбираем "Вкл".
  • Прием чеков Paymer.com (ВМ-карт) и Прием платежей с телефонов Telepat.ru - эти опции определяют, разрешена ли оплата с чеков Paymer и WM-кошельков, подключенных к системе Телепат. Ставим "Вкл" для обеих, так как именно они и придают сервису Web Merchant Interface ту универсальность, которая отличает его от других 2-х способов приема платежей в системе WebMoney.

Жмем кнопку [Сохранить].

На скриншоте (рис.2) я устанавливал настройки для одного из своих Z-кошельков. Точно так же можно настроить и другие кошельки, в т.ч. и кошельки других типов (R, U и т.д.). Если, например, вы планируете принимать оплату за товар/услугу в WMZ и WMR на выбор покупателя, то вам нужно настроить один Z- и один R-кошелек. Настройки совершенно идентичны.

Формирование заказа. Начало платежа.

Чтобы инициировать платеж, в точке А (см. рис.1) нам нужно передать Мерчанту ряд параметров, для того чтобы Мерчант знал, какую сумму и в пользу какого продавца списывать с кошелька покупателя. Какие именно действия пользователя будут предшествовать точке А - решать вам. Но помните - к моменту, когда покупатель нажмет кнопку в точке А и перейдет на Мерчант для оплаты, вам обязательно нужно иметь информацию об этом пользователе и его покупке. Как правило, это решается одним из способов в зависимости от вашей конкретной ситуации:

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

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

Пусть, нам требуется продавать покупателям некие электронные товары (скажем, коды пополнения мобильных операторов) и выслать их по email после оплаты. Также договоримся для простоты, что покупатель сможет выбирать из каталога и оплачивать за раз только один товар т.е. без формирования "корзины покупок".

Очевидно, что у нас в базе данных должна быть таблица с характеристиками товаров:

Таблица goods
id id товара
tname название товара
cost стоимость товара
... ...

Покупатель выбирает товар из каталога, и попадает в точку А. Эта страница на вашем сайте (назовем ее order.php) должна содержать такую html-форму:

<form method="POST" action="https://merchant.webmoney.ru/lmi/payment.asp">
<input type="hidden" name="LMI_PAYMENT_AMOUNT" value="0.05">
<input type="hidden" name="LMI_PAYMENT_DESC" value="код пополнения Super Mobile">
<input type="hidden" name="LMI_PAYEE_PURSE" value="Z155771820786">
<input type="hidden" name="id" value="345">
Укажите email для отправки товара: <input type="text" name="email" size="15">
<input type="submit" value="Перейти к оплате">
</form>

Будем называть это формой заказа платежа. Вот как выглядит такая страница в нашем примере. Рассмотрим форму детальнее:

  • action - Атрибут action должен отправлять на https://merchant.webmoney.ru/lmi/payment.asp.
  • method - Должен использоваться метод посыла формы POST.
  • LMI_PAYMENT_AMOUNT - Поле, которое содержит сумму платежа. Дробная часть отделяется точкой. В нашем примере значение LMI_PAYMENT_AMOUNT=0.05.
  • LMI_PAYMENT_DESC - Поле, которое содержит примечание платежа. Должно содержать текст, желательно - наименование товара или услуги, которая продается. Максимальная длина - 255 символов. В нашем примере значение LMI_PAYMENT_DESC="код пополнения Super Mobile".
  • LMI_PAYEE_PURSE - Ваш кошелек, только что подключенный к Мерчанту (см. главу "Предварительные настройки"). Должен состоять из буквы (Z-, R- и т.д.) и 12 цифр. По значению этого поля Мерчант распознает, в пользу какого продавца (иначе говоря, на чей кошелек) производится платеж. В нашем примере LMI_PAYMENT_PURSE="Z155771820786".

Обращаю ваше внимание, что поля LMI_PAYMENT_AMOUNT, LMI_PAYMENT_DESC, LMI_PAYEE_PURSE должны называться именно так, и никак иначе. При этом поля LMI_PAYMENT_AMOUNT и LMI_PAYEE_PURSE являются обязательными.

В той же форме можно передавать и свои произвольные параметры, если это необходимо. При чем, названы эти параметры могут быть как угодно, главное, чтобы они не начинались с префикса LMI_!). В нашем примере мы добавили в форму:

  • Поле id - содержит id выбранного покупателем товара. Берется из таблицы goods.
  • Поле email - покупатель указывает здесь свой email, на который он получит товар после оплаты.

После нажатия на кнопку покупатель попадает на https://merchant.webmoney.ru/lmi/payment.asp, то есть переходит на сайт Мерчанта. Одновременно с этим сайту Мерчанта, естественно, передаются и все параметры нашей формы.

Прохождение платежа.

Первое, что видит покупатель, попав на сайт сервиса Мерчант:


Рис.3. Первая страница сервиса Мерчанта.

Здесь видно сумму будущего платежа, кошелек и WMID продавца, торговое имя (помните, мы заполняли его, когда подключали кошелек в главе "Подготовительные работы"?) и содержимое LMI_PAYMENT_DESC (название товара или услуги).

Нажав кнопку [Оплатить], покупатель попадает на следующую страницу:


Рис.4. Страница Мерчанта с выбором способа авторизации и оплаты.

Здесь он выбирает способ оплаты - с Keeper Classic, Light, кошелька в системе Telepat, WM-карты, либо выбирает авторизацию с помощью сервиса Enum. Для примера авторизуемся Кипером Classic и попадаем на следующую страницу:


Рис.5. Страница Мерчанта с выбором кошелька.

Здесь покупатель выбирает кошелек, с которого будет списана стоимость покупки.

После нажатия на кнопку [Платеж подтверждаю] начинается самое интересное. Мы оказываемся в точке B (см.рис.1), и сервер Мерчанта отправляет на наш Result URL форму предварительного запроса. Отправка происходит методом POST. Форма содержит такие параметры:

  • LMI_PREREQUEST, равный 1. Это индикатор предварительного запроса. Зачем он нужен, мы узнаем чуть позже.
  • LMI_PAYEE_PURSE - кошелек продавца (должен совпадать с LMI_PAYEE_PURSE, переданным в order.php).
  • LMI_PAYMENT_AMOUNT - сумма платежа, которую оплачивает покупатель (должна совпадать с LMI_PAYEE_AMOUNT, переданным в order.php).
  • LMI_PAYER_WM - WMID покупателя.
  • LMI_PAYMER_NUMBER - номер WM-карты или чека Paymer, если покупатель выбрал оплату WM-картой.
  • LMI_TELEPAT_PHONENUMBER - номер телефона покупателя в системе Telepat, если выбрана оплата по системе Telepat.
  • Ряд других менее важных "системных" параметров. Полный их список можно посмотреть здесь. Все системные параметры имеют префикс LMI_.
  • Все остальные параметры, которые мы передавали Мерчанту в точке А из формы запроса платежа. В нашем случае это id и email.

Зачем нужна эта форма? Дело в том, что еще в точке А недобросовестный покупатель может заменить значения параметров в форме запроса платежа, т.е. произвести "взлом" html-кода. Это совсем не сложный трюк. В результате Мерчанту будут переданы совсем не те значения параметров, которые нужно.

Например, в нашем примере покупатель мог бы заменить в поле LMI_PAYMENT_AMOUNT сумму платежа с 0.05 на 0.01, в результате он заплатил бы 0.01 WMZ вместо 0.05 WMZ и спокойно получил бы товар за меньшую цену. Другой вариант: подставить в форму id более дорогого товара и, таким образом, купить его дешевле.

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

В ответ на такой запрос мы должны в том же скрипте Result URL произвести все необходимые проверки и выдать Мерчанту ответ: строку "YES",если всё верно, или строку с текстом ошибки, если что-то не так. В частности, в нашем примере нужно произвести такие проверки:

  1. Проверить, существует ли товар с полученным от Мерчанта id в нашей базе данных (в таблице goods), то есть не произошла ли подмена параметра id.
  2. Проверить, совпадает ли сумма в LMI_PAYMENT_AMOUNT с реальной стоимостью товара, прописанной в нашей БД (в таблице goods), то есть не произошла ли подмена параметра LMI_PAYMENT_AMOUNT.
  3. Проверить, действительно ли кошелек получателя в LMI_PAYEE_PURSE является вашим кошельком, подключенным к Мерчанту, то есть не произошла ли подмена параметра LMI_PAYEE_PURSE.
  4. Проверить, передан ли параметр email, так как если он не передан, отправлять товар будет некуда, а значит покупку нужно прервать прямо сейчас.

Ниже приводим PHP-код, реализующий эти проверки в нашем примере. Напомню, что в качестве Result URL мы прописали скрипт test_results.php (см.рис.2). Поэтому приведенный ниже код мы помещаем в этот файл. В качестве сигнала о том, что получена именно форма предварительного запроса, используем параметр LMI_PREREQUEST, равный 1.

// Соединяемся с БД
...
// Если это форма предварительного запроса, то идем дальше...
IF($_POST['LMI_PREREQUEST']==1) {
// 1) Проверяем, есть ли товар с таким id в базе данных.
// Если такой товар не обнаружен, то выводим ошибку и прерываем работу скрипта.
$q="SELECT `id`, `cost` FROM `goods` WHERE id='$_POST['id']'";
$res=mysql_fetch_row(mysql_query($q));
if(!$res[0] or $res[0]=="") {
$err=1;
echo "ERR: НЕТ ТАКОГО ТОВАРА";
exit;
}
// 2) Проверяем, не произошла ли подмена суммы.
// Cравниваем стоимость товара в базе данных с той суммой, что передана нам Мерчантом.
// Если сумма не совпадает, то выводим ошибку и прерываем работу скрипта.
if(trim($res[1])!=trim($_POST['LMI_PAYMENT_AMOUNT'])) {
$err=1;
echo "ERR: НЕВЕРНАЯ СУММА ".$_POST['LMI_PAYMENT_AMOUNT'];
exit;
}
// 3) Проверяем, не произошла ли подмена кошелька.
// Cравниваем наш настоящий кошелек с тем кошельком, который передан нам Мерчантом.
// Если кошельки не совпадают, то выводим ошибку и прерываем работу скрипта.
if(trim($_POST['LMI_PAYEE_PURSE'])!="Z155771820786") {
$err=1;
echo "ERR: НЕВЕРНЫЙ КОШЕЛЕК ПОЛУЧАТЕЛЯ ".$_POST['LMI_PAYEE_PURSE'];
exit;
}
// 4) Проверяем, указал ли пользователь свой email.
// Если параметр $email пустой, то выводим ошибку и прерываем работу скрипта.
if(!trim($_POST['email']) or trim($_POST['email'])=="") {
$err=1;
echo "ERR: НЕ УКАЗАН EMAIL";
exit;
}
// Если ошибок не возникло, то выводим YES
if(!$err) echo "YES";
}

Естественно, в вашем случае набор проверок будет отличаться.

В случае, если Мерчант получил от Result URL ответ "YES", средства списываются с кошелька плательщика и тут же поступают на ваш. В случае получения другого ответа Мерчант прерывает платеж, средства с кошелька плательщика не списываются, а на экран выводится текст ошибки:


Рис.6. Мерчант получил от Result URL ошибку и прервал выполнение платежа.

На этом скриншоте показано, как сработала наша проверка № 2. Очевидно, хитроумный покупатель решил нас одурачить и заплатить 0.01 WMZ вместо 0.05, изменив параметр LMI_PAYMENT_AMOUNT в форме order.php. Но мы пресекли его план. Обман не удался.

Как я только что говорил, если Мерчант получает от вас "YES", то происходит списание. Сразу после этого мы попадаем в точку С, и Мерчант направляет тому же скрипту Result URL форму оповещения о платеже. Отправка происходит методом POST. Форма содержит такие параметры:

  • LMI_PAYEE_PURSE - кошелек продавца, на который покупатель совершил платеж.
  • LMI_PAYMENT_AMOUNT - сумма, которую заплатил покупатель.
  • LMI_PAYER_PURSE - кошелек покупателя, с которого он совершил платеж.
  • LMI_PAYER_WM - WMID покупателя.
  • LMI_PAYMER_NUMBER - номер WM-карты или чека Paymer, если покупатель выбрал оплату WM-картой.
  • LMI_TELEPAT_PHONENUMBER - номер телефона покупателя в системе Telepat, если выбрана оплата по системе Telepat.
  • LMI_HASH - контрольная подпись. Что это такое, расскажем через минуту.
  • LMI_SYS_TRANS_DATE - дата и время совершения платежа с точностью до секунд.
  • Ряд других менее важных "системных" параметров. Полный их список можно посмотреть здесь. Все системные параметры имеют префикс LMI_.
  • Все остальные параметры, которые мы передавали Мерчанту в точке А в форме запроса платежа. В нашем случае это id и email.

Как видите, это очень похоже на форму предварительного запроса. Но есть и различия. Например, мы уже не получаем параметра LMI_PREREQUEST, зато появляется LMI_HASH и ряд других переменных.

Зачем нужна форма оповещения о платеже? Для того, чтобы произвести необходимые действия по обработке заказа или покупки. Оплата к этому моменту уже совершена, а значит и мы должны выполнить свои обязательства перед покупателем. Напомню, что в нашем примере мы должны отправить ему товар.

Кроме того, создадим таблицу orders, куда будем записывать завершенные покупки. Это может пригодиться нам для статистики и истории.

Таблица orders
order_id id покупки. INDEX, auto_increment
id id купленного товара
odate дата покупки
purse кошелек покупателя
email email покупателя
tovar содержимое купленного товара
... ...

То, что получена именно форма оповещения о платеже, мы можем узнать по отсутствию параметра LMI_PREREQUEST. Дописываем наш test_results.php:

// Соединяемся с БД
...
// Если это форма предварительного запроса, то идем дальше...
IF($_POST['LMI_PREREQUEST']==1) {
...
}
// Если нет LMI_PREREQUEST, следовательно это форма оповещения о платеже...
ELSE {
// Выбираем из базы данных нужный товар, записываем его в переменную $tovar;
...
// Вносим покупку в таблицу orders
$q="insert into `orders` set `id`='$_POST['id']', `odate`='$_POST['LMI_SYS_TRANS_DATE']',
`purse`='$_POST['LMI_PAYER_PURSE']', `email`='$_POST['email']', `tovar`='$tovar'";
mysql_query($q);
// Отправляем товар на email покупателя
$text="Ваш товар: ".$tovar;
mail($_POST['email'], convert_cyr_string("Ваш товар",w,k), convert_cyr_string($text,w,k),
"From: robot@site.rurnContent-Type: text/plain; charset="koi8-r"");
}

Вроде бы всё? Не совсем. Дело в том, что есть вероятность того, что злоумышленник узнает адрес нашего Result URL (скрипт test_results.php) и симитирует посыл на него ложной формы оповещения о платеже. В этом случае наш Result URL будет ошибочно "думать", что получает форму от Мерчанта из точки С и что оплата за товар произведена, тогда как на самом деле никакой оплаты не поступит.

Для того, чтобы избежать этой ситуации и пресечь подобное мошенничество, нам нужно определить, кто прислал форму оповещения о платеже - Мерчант или кто-то посторонний. Для этого используется параметр LMI_HASH. И это, пожалуй, самый сложный момент.

Мерчант формирует LMI_HASH так. Сначала он склеивает в одну строку (без пробелов и разделителей) значения следующих полей:

  1. Кошелек продавца (LMI_PAYEE_PURSE);
  2. Сумма платежа (LMI_PAYMENT_AMOUNT);
  3. Внутренний номер покупки продавца (LMI_PAYMENT_NO);
  4. Флаг тестового режима (LMI_MODE);
  5. Внутренний номер счета в системе WebMoney Transfer (LMI_SYS_INVS_NO);
  6. Внутренний номер платежа в системе WebMoney Transfer (LMI_SYS_TRANS_NO);
  7. Дата и время выполнения платежа (LMI_SYS_TRANS_DATE);
  8. Secret Key;
  9. Кошелек покупателя (LMI_PAYER_PURSE);
  10. WMId покупателя (LMI_PAYER_WM).

Склейка значений происходит именно в том порядке, в котором они приведены выше. Все эти параметры, кроме 8-го, передаются Мерчантом на Result URL в той же форме оповещения о платеже.

После склейки Мерчант формирует контрольную подпись. В нашем случае мы указали в настройках (см.рис.2), что контрольная подпись будет формироваться по алгоритму MD5.

Справка. MD5 (сокращенно от Message Digest 5) - алгоритм шифрования. В результате применения этого алгоритма к строке любой длины на выходе получается строка из 32-символов, которая называется "хэшем" (другие названия - "контрольная сумма", "контрольная подпись", "свёртка").
Не существует двух разных строк, которые будучи зашифрованными в MD5, имели бы одинаковые контрольные подписи. К тому же, расшифровать MD5-хэш невозможно, то есть алгоритм необратим и работает только в одном направлении. Поэтому MD5 используется для хранения секретной информации, такой как пароли. Например, при регистрации пользователя на форуме пароль кодируется MD5 и полученная свертка сохраняется в базе данных. Когда пользователь авторизуется на форуме, указывая пароль, то этот пароль также кодируется MD5, и полученная свёртка сравнивается с той, что хранится в базе. Если они совпадают, то и пароль был указан верно. Если не совпадают, то неверно.
В PHP имеется стандартная функция шифрования по алгоритму MD5: md5($str).

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

А в чем смысл? - спросите вы. Ведь точно так же и хакер может сформировать LMI_HASH на основе ложных параметров, и передать нам эти параметры вместе с LMI_HASH. Изюминка заключается в том, что значение одного из полей, участвующих в формировании LMI_HASH - Secret Key - знаете только вы и Мерчант. И никто больше. Если ошибка будет хотя бы в одной букве Secret Key (равно как и в значении любой другой переменной, участвующей в формировании "склеенной" строки), то контрольные подписи уже не совпадут.

Давайте вернемся в начало статьи и обратим внимание на опцию "Secret Key" в настройках кошелька на рис.2. Туда мы ввели бессмысленную и длинную последовательность символов. Теперь мы знаем, где она используется. И знаем, почему желательно задавать в Secret Key строку подлинее и посложнее: так мы уменьшаем шансы ее подбора злоумышленником.

Итак, перед тем как выполнять заказ, мы должны сформировать контрольную сумму и сравнить ее с LMI_HASH. Если значния совпадают - выполняем заказ. Если не совпадают - то предполагаем, что была попытка взлома, и заказ не выполняем от греха подальше.

Следующий PHP-код демонстрирует проверку контрольной подписи:

// Задаем значение $secret_key.
// Оно должно совпадать с Secret Key, указанным нами в настройках кошелька.
$secret_key="dflsj4k!;fm3afd";
// Склеиваем строку параметров
$common_string = $_POST['LMI_PAYEE_PURSE'].$_POST['LMI_PAYMENT_AMOUNT'].$_POST['LMI_PAYMENT_NO'].
$_POST['LMI_MODE'].$_POST['LMI_SYS_INVS_NO'].$_POST['LMI_SYS_TRANS_NO'].
$_POST['LMI_SYS_TRANS_DATE'].$secret_key.$_POST['LMI_PAYER_PURSE'].$_POST['LMI_PAYER_WM'];
// Шифруем полученную строку в MD5 и переводим ее в верхний регистр
$hash = strtoupper(md5($common_string));
// Прерываем работу скрипта, если контрольные суммы не совпадают
if($hash!=$_POST['LMI_HASH']) exit;

Таким образом, окончательный вариант Result URL (test_results.php) будет выглядеть так:

// Соединяемся с БД
...
// ЕСЛИ ЭТО ФОРМА ПРЕДВАРИТЕЛЬНОГО ЗАПРОСА, ТО ИДЕМ ДАЛЬШЕ...
IF($_POST['LMI_PREREQUEST']==1) {
// 1) Проверяем, есть ли товар с таким id в базе данных.
// Если такой товар не обнаружен, то выводим ошибку и прерываем работу скрипта.
$q="SELECT `id`, `cost` FROM `goods` WHERE id='$_POST['id']'";
$res=mysql_fetch_row(mysql_query($q));
if(!$res[0] or $res[0]=="") {
$err=1;
echo "ERR: НЕТ ТАКОГО ТОВАРА";
exit;
}
// 2) Проверяем, не произошла ли подмена суммы.
// Cравниваем стоимость товара в базе данных с той суммой, что передана нам Мерчантом.
// Если сумма не совпадает, то выводим ошибку и прерываем работу скрипта.
if(trim($res[1])!=trim($_POST['LMI_PAYMENT_AMOUNT'])) {
$err=1;
echo "ERR: НЕВЕРНАЯ СУММА ".$_POST['LMI_PAYMENT_AMOUNT'];
exit;
}
// 3) Проверяем, не произошла ли подмена кошелька.
// Cравниваем наш настоящий кошелек с тем кошельком, который передан нам Мерчантом.
// Если кошельки не совпадают, то выводим ошибку и прерываем работу скрипта.
if(trim($_POST['LMI_PAYEE_PURSE'])!="Z155771820786") {
$err=1;
echo "ERR: НЕВЕРНЫЙ КОШЕЛЕК ПОЛУЧАТЕЛЯ ".$_POST['LMI_PAYEE_PURSE'];
exit;
}
// 4) Проверяем, указал ли пользователь свой email.
// Если параметр $email пустой, то выводим ошибку и прерываем работу скрипта.
if(!trim($_POST['email']) or trim($_POST['email'])=="") {
$err=1;
echo "ERR: НЕ УКАЗАН EMAIL";
exit;
}
// Если ошибок не возникло, то выводим YES
if(!$err) echo "YES";
}
// ЕСЛИ НЕТ LMI_PREREQUEST, СЛЕДОВАТЕЛЬНО ЭТО ФОРМА ОПОВЕЩЕНИЯ О ПЛАТЕЖЕ...
ELSE {
// Задаем значение $secret_key.
// Оно должно совпадать с Secret Key, указанным нами в настройках кошелька.
$secret_key="dflsj4k!;fm3afd";
// Склеиваем строку параметров
$common_string = $_POST['LMI_PAYEE_PURSE'].$_POST['LMI_PAYMENT_AMOUNT'].$_POST['LMI_PAYMENT_NO'].
$_POST['LMI_MODE'].$_POST['LMI_SYS_INVS_NO'].$_POST['LMI_SYS_TRANS_NO'].
$_POST['LMI_SYS_TRANS_DATE'].$secret_key.$_POST['LMI_PAYER_PURSE'].$_POST['LMI_PAYER_WM'];
// Шифруем полученную строку в MD5 и переводим ее в верхний регистр
$hash = strtoupper(md5($common_string));
// Прерываем работу скрипта, если контрольные суммы не совпадают
if($hash!=$_POST['LMI_HASH']) exit;
// Выбираем из базы данных нужный товар, записываем его в переменную $tovar;
...
// Вносим покупку в таблицу orders
$q="insert into `orders` set `id`='$_POST['id']', `odate`='$_POST['LMI_SYS_TRANS_DATE']',
`purse`='$_POST['LMI_PAYER_PURSE']', `email`='$_POST['email']', `tovar`='$tovar'";
mysql_query($q);
// Отправляем товар на email покупателя
$text="Ваш товар: ".$tovar;
mail($_POST['email'], convert_cyr_string("Ваш товар",w,k), convert_cyr_string($text,w,k),
"From: robot@site.rurnContent-Type: text/plain; charset="koi8-r"");
}

Отправляя форму оповещения о платеже, Мерчант уже не ждет от вас ответа, как в форме предварительного запроса, поскольку от вашего ответа уже ничего не зависело бы: деньги списаны с покупателя и зачислены продавцу.

Важно! Игнорирование проверок формы предварительного запроса и формы оповещения о платеже - это основная ошибка программистов, которые подключают к сайту прием WM-платежей. Это становится причиной взломов и несанкционированных покупок, среди которых самый распространенный взлом заключается в подмене хакером суммы платежа и покупки товара/услуги по значительно заниженной стоимости. Не повторяйте чужих ошибок!

Примечание. Если пользователь выбрал оплату не с Кипера, а с WM-карты или через систему Телепат (см.рис.4), то для вас всё остается прозрачным. Приведенный выше программный код будет работать и в этих случаях тоже. Единственная разница заключается в получении от Мерчанта таких параметров как LMI_PAYMER_NUMBER (содержит номер WM-карты, с которой производилась оплата), LMI_TELEPAT_PHONENUMBER (содержит номер телефона покупателя в системе Телепат) и некоторые другие. Но эти параметры несут не более чем просто информационную нагрузку, поскольку продавцу, за исключением редких случаев, абсолютно всё равно, откуда поступили деньги.
Обратите внимание, что при оплате с WM-карты Мерчант запрашивает у пользователя email, поскольку это единственный способ связи с таким пользователем. Этот email передается в параметре LMI_PAYMER_EMAIL.

Окончание покупки

Что ж, всё самое сложное позади. Осталось разобраться с Success URL и Fail URL.

После успешного платежа покупатель попадает на такую страницу:


Рис.7. Последняя страница Мерчанта после оплаты.

Это точка D схемы на рис.1. При нажатии на кнопку [Вернуться на сайт] Мерчант перенаправляет пользователя на Success URL. Этой странице Мерчант передает форму выполненного платежа, содержащую несколько "системных" параметров (см. список), а также все остальные "несистемные" параметры, которые мы передавали Мерчанту еще в точке А в форме запроса платежа. В нашем случае это id и email. Таким образом, Мерчант как бы пропускает сквозь себя эти параметры, возвращая на выходе то, то получал на входе.

Передача Мерчантом параметров на Success URL позволяет нам внести и сюда элемент интерактивности. Например, вывести на экран какое-нибудь персонифицированное сообщение об успешном окончании покупки. Ниже приведем один из вариантов Success URL (скрипт yes.php) для нашего примера:

echo "Покупка успешно произведена. Товар номер ".$_POST['id']." выслан на ваш email ".$_POST['email'];

Важно! Ни в коем случае не выполняйте заказ в скрипте Success URL, поскольку она никак не защищена от взломов и посыла ложных форм. Так, например, если бы мы отправляли товар покупателю именно в Success URL, а не в Result URL, то злоумышленник легко мог бы отправить на Success URL форму, содержащую email и id - и мы выслали бы ему товар, не получив при этом никакой оплаты.

А что же Fail URL? На него Мерчант отправляет покупателя в следующих случаях:

  1. На какой-либо из страниц Мерчанта покупатель нажал кнопку [От платежа отказываюсь].
  2. В качестве источника платежа покупатель выбрал кошелек, на котором недостаточно средств.
  3. Мерчанту не получил от Продавца "YES" в ответ на форму предварительного запроса.

Как вы уже знаете, во всех этих случаях деньги с покупателя не списывается, покупатель перенаправляется прямиком на Fail URL, а форма оповещения о платеже на Result URL не передается.

Странице Fail URL (в нашем примере это скрипт no.php) передаются такие же параметры, как и Success URL. Но, конечно, на этой странице нужно выводить совсем иной текст. Это должно быть что-то вроде "Оплата не прошла. Возможно, вы отказались от платежа или возникла другая ошибка".

Последние штрихи и рекомендации

Теперь всё готово! Не забудьте перед тем, как начать принимать платежи, вернуться на страницу настроек (см.рис.2) и установить опцию Тестовый/Рабочий режимы в положение "Рабочий".

В заключение приведем несколько важных рекомендаций.

  • Не пренебрегайте формой предварительного запроса и формой оповещения о платеже, которые присылает вам Мерчант. Используйте их для проверок валидности параметров платежа. Продумайте все варианты подмены и взлома, которые могут быть применены против вас - и учтите эти варианты в таких проверках.
  • Производите действия по выполнению заказа (отгрузка товара, предоставление услуги, пополнение баланса и т.д.) только в Result URL и только после успешных проверок формы оповещения о платеже. Не производите действия по выполнению заказа в Success URL.
  • Используйте в качестве Result URL скрипт со сложным названием.