Несмотря на недружелюбную (чего стоит только таблица с историей правок в начале документа!) и трудно дающуюся для понимания документацию, алгоритмы подготовки данных и протокол обмена данными с Платроном не лишен логики и красоты. Но до закономерностей еще надо докопаться в блоках XML.
В этой заметке я расскажу о том, как начать интеграцию своего магазина и как провести проверки в тестовом режиме.
Под интеграцией я понимаю создание view, в который в качестве параметров GET запроса контроллером передаются нужные величины из БД из application.conf. А после передачи управления и получения уведомление из Платрона происходит обновление данных в БД и пользователь видит прошел или нет его платеж, и перенаправляется на страничку магазина.
Первым реальным шажком в понимании механизма работы стала генерация текста для кнопки в админке Платрона:
Сыылка для кнопки:
<a href="https://www.platron.ru/payment.php?pg_amount=4&pg_currency=RUB&pg_merchant_id=8888&pg_language=ru&pg_description=%D0%9F%D0%BE%D0%B4%D0%BF%D0%B8%D1%81%D0%BA%D0%B0+%D0%BD%D0%B0+2+%D0%B4%D0%BD%D1%8F&pg_salt=aaaaaaaaaa&pg_sig=7b893059b970ff94003b3ac962d19cb5">Купить Подписку на 4 дня (4 руб.)</a> |
Не совсем понятно, как потом по принятию уведомления идентифицировать, какой заказ и кем оплачен, ведь не передается pg_order_id, но это дало наводку на то, какой URL требуется для передачи управления в платежный агрегатор.
В результате проб и ошибок мне удалось подобрать такую тестовую конфигурацию формы для передачи управления на сторону платежной системы (некоторые значения я изменил, но хеши не пересчитывал. Так что могут быть несовпадения). Соль в параметре pg_salt должна быть случайной строкой. В контроллере нужно выдать такой view:
<form method="GET" action="https://www.platron.ru/payment.php"> <input type="hidden" name="pg_amount" value="100.0" /> <input type="hidden" name="pg_currency" value="RUB" /> <input type="hidden" name="pg_order_id" value="275" /> <input type="hidden" name="pg_merchant_id" value="8888" /> <input type="hidden" name="pg_salt" value="391fa6cfaf8d4ce8976364db3578c323" /> <input type="hidden" name="pg_language" value="ru" /> <input type="hidden" name="pg_description" value="some item" /> <input type="hidden" name="pg_sig" value="c15c46a50064d726a71078e2f4d772a2" /> <input type="hidden" name="pg_success_url" value="http://vb.ru/notify/success.php" /> <input type="hidden" name="pg_success_url_method" value="GET" /> <input type="hidden" name="pg_failure_url" value="http://vb.ru/notify/fail.php" /> <input type="hidden" name="pg_failure_url_method" value="GET" /> <input type="hidden" name="pg_result_url" value="http://vb.ru/notify/result.php" /> <input type="hidden" name="pg_request_method" value="GET" /> <p><input type="submit" value="Заказать"/></p> </form> |
Параметр pg_order_id вернется в уведомлении из Платрона и вы сможете идентифицировать заказ, который был оплачен.
При отправке получается GET запрос вида:
https://www.platron.ru/payment.php?pg_amount=100.0&pg_currency=RUB&pg_order_id=275&pg_merchant_id=8888&pg_salt=da53d7764fe94330bed5d223376b3954&pg_language=ru&pg_description=some+item&pg_sig=8778ff60c9d16b397852ef215f1dcb6b&pg_success_url=http%3A%2F%2Fvb.ru%2Fnotify%2Fsuccess.php&pg_success_url_method=GET&pg_failure_url=http%3A%2F%2Fvb.ru%2Fnotify%2Ffail.php&pg_failure_url_method=GET&pg_result_url=http%3A%2F%2Fvb.ru%2Fnotify%2Fresult.php&pg_request_method=GET |
Параметры pg_success_url, pg_success_url_method, pg_failure_url, pg_failure_url_method, pg_result_url в данном случае передаются в запросе и в общем случае могут быть свои у каждой транзакции. Если вам не нужна такая гибкость (у вас что, нет маркетологов? 🙂 ), то их можно прописать в админке.
Порядок параметров в запросе (определяется порядком полей в форме) не важен. Для получения подписи на стороне Платрона они будут выстроены в алфавитном порядке (имя скрипта/службы — в данном случае payment.php — и секретный ключ
payment.php;100.0;RUB;some item;http://vb.ru/notify/fail.php;GET;ru;8888;275;GET;http://vb.ru/notify/result.php;da53d7764fe94330bed5d223376b3954;http://vb.ru/notify/success.php;GET;secret_key |
Для наглядности приведу имена параметров в том же порядке:
pg_amount;pg_currency;pg_description;pg_failure_url;pg_failure_url_method;pg_language; ... |
Не светите свой секретный ключ в HTML форме, части URL или еще как-то. Он задействован в генерации MD5 хеша для подписи. Значит, утечка ключа может повлечь прием подделанных платежей (злоумышленник сможет сгенерировать подпись и отправить подтверждение оплаты своего заказа на pg_result_url) и финансовые потери.
Я не загромождаю страничку параметрами, пользователь все равно не может их редактировать. Из всей это формы я вывожу в заголовок странички только ID заказа и сумму платежа, все поля формы — невидимые.
Как с минимальными усилиями получить из Map
params.put("pg_salt", "my random salt"); params.put("pg_language", "ru"); params.put("pg_currency", "RUB"); ... |
Строковые и численные константы можно вынести в конфиг application.conf и читать их в контроллере — в этом случае для правок требуется меньшая квалификация, чем в случае правки java исходника контроллера. К тому же, в этом случае правки сосредоточены в одном файле.
Теперь перебираем ключи и конкатенируем строку:
SortedSet<String> keys = new TreeSet</string><string>(params.keySet()); for (String key : keys) { String value = params.get(key); sb.append(value + DELIMITER); // DELIMITER = ';' } // use sb.toString() |
Останется только в начало строки прилепить имя сервиса/скрипта, а в конец — секретный ключ. От этой строки теперь можно посчитать MD5:
import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public static String MD5hash (String source){ MessageDigest md = null; try { md = MessageDigest.getInstance("MD5"); } catch (NoSuchAlgorithmException e){ log.error("error getting MD5 instance",e); e.printStackTrace(); } md.update(source.getBytes()); byte byteData[] = md.digest(); //convert the byte to hex format method 1 StringBuilder sb = new StringBuilder(); for (int i = 0; i < byteData.length; i++) { sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16).substring(1)); } return sb.toString(); } |
Для отладки генерации подписи есть полезная страничка: ссылка для отладки.
Она покажет вам, какая строка (от нее считается MD5) должна получиться при распарсивании вашего УРЛа и объединения параметров в строку.
Вернемся к HTML форме.
Скрипт по адресу http://vb.ru/notify/success.php просто сохраняет запрошенный URL в БД:
< ?php include("include.php"); $url = "http://{$_SERVER['HTTP_HOST']}{$_SERVER['REQUEST_URI']}"; $escaped_url = htmlspecialchars( $url, ENT_QUOTES, 'UTF-8' ); echo '<a href="' . $escaped_url . '">' . $escaped_url . ''; $link = mysql_connect($mysql_host, $mysql_user, $mysql_pass) or die ("Sorry, can't connect to database."); mysql_select_db($mysql_database); $q = "INSERT INTO play_requests (url) VALUES ('" . $url . "')"; $statresult = mysql_query($q); mysql_close($link); ?> |
Остальные скрипты работают аналогично. Если один и тот же скрипт у вас принимает разные виды уведомлений, то для уникальности можно добавить еще одно поле в БД со значениями «success», «fail», «result», «check».
Вообще, в плане настройки уведомлений на разные сервисы у Платрона богатейшие возможности. У каждой транзакции в общем случае свои адреса для уведомлений. Это пригодится, если у вас несколько магазинов в одной учетной записи:
Вот что важно знать про URL-ы. Надежным средством узнать прошел платеж или нет, является только Result URL, причем при некорректном ответе он может быть запрошен платроном многократно. Пара Success URL и Fail URL служат только для того, чтоб перенаправить покупателя и показать ему статус платежа. Их нельзя использовать в качестве источника данных для обновления БД, т.к. переход на них может вообще не произойти, к примеру, если нарушена передача данных в сети.
Не забудьте о том, что в ответ на уведомление на Result URL Платрон ждет не просто ответ 200 OK, а составленный особым образом XML:
< ?xml version="1.0" encoding="utf-8"?> <response> <pg_salt>kdjdope983</pg_salt> <pg_status>ok</pg_status> <pg_description>Товар передан покупателю</pg_description> <pg_sig>9bfc5f602d287096ed237952f56bd05c</pg_sig> </response> |
Обратите внимание на то, что подпись для XML подсчитывается от строки, которая по-прежнему начинается с имени скрипта/сервиса : «result.php;[params];secret_key» (если уведомление пришло на http://myshop.ru/result.php).