heap.tech
лаборатория велосипедов
×

Интеграция робокассы и Net.Core 2.0

26 октября 2018
В ходе разработки одного онлайн-сервиса мне нужно было организовать прием денег от населения с максимально большим количеством способов платежей и, главное, чтобы просто и быстро. Еще одним значимым фактором является то, что сервис работает не как юридическое лицо (или ИП), а как физическое. То есть физическое лицо будет принимать деньги от физических или юридических лиц. Такой вариант работы – временный, так как регистрировать ип или ооо накладно как по времени, так и по деньгам (юридические аспекты я опишу в следующей статье).

Суть задачи


- Разработать функционал приема платежей с использованием
- пластиковых карт visa/mastercard/мир
- платежных систем яндекс.деньги/webmoney/qiwi
- терминалов оплаты

- Зачисление средств должно проходить в течении 10 минут (максимум) после факта оплаты
- Платформа .Net core 2 – 2.1

Здорово, что в 2018 году есть некоторое количество универсальных сервисов приема платежей: yandex.деньги, robokassa и rbk-money. Правда последний вариант отпадает – он прекратил работу с физическими лицами.

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

А значит план таков:
0. Теория "как это работает"
1. Регистрация на робокассе, получение доступов, паролей и явок
2. Реализация программной части
3. Тестирование в тестовом режиме
4. Переход из тестового режима в боевой
5. Продолжение регистрации в робокассе и на сайте партнера qiwi

Пункт 0. Теория. Как работает robokassa


Цепочка действий
1. Пользователь хочет оплатить товары/услуги на вашем сайте. Нажимает на кнопку оплатить
2. Ваш сайт генерирует уникальный ID нового инвойса
3. На вашем сайте открывается форма оплаты с текстовым полем и зеленой кнопкой
4. На форме оплаты (вашего сайта) пользователь вводит сумму оплаты и нажимает на зеленую кнопку «оплатить»
5. Ваш сайт формирует подпись транзакции, с помощью этой подписи робокасса точно определяет ваш магазин
5. Ваш сайт перемещает пользователя на сайт робокассы (сюда https://auth.robokassa.ru/Merchant/Index.aspx, передав сгенерированную ранее подпись)
6. Находясь на сайте робокассы пользователь вводит реквизиты своей карты и успешно завершает процесс оплаты
7. В этот же момент робокасса делает callback-запрос на ваш сайт (сallback url это ничто иное, как «Result Url»)
8. Вы получаете callback запрос, обрабатываете его и выполняете необходимую логику (увеличиваете баланса пользователя)
9. Робокасса перебрасывает пользователя обратно на ваш сайт, на страницу, указанную как Success Url

Если оплата пользователя не прошла – callback-запрос не выполняется, вместо этого пользователь сразу перемещается обратно на ваш сайт, на страницу, указанную как Fail Url.

Пункт 1 – регистрация и получение прав доступа


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

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


Магазин создан.Теперь для работы с API робокассы нужно получить пароли – переходим в раздел технические настройки магазина и заполняем форму сверху вниз:
1. Алгоритм расчета хешей – MD5. Это быстрый алгоритм и, в данном случае, он обеспечивает достаточную безопасность.
1. Задаем пароли один и два и сохраняем их на листочке бумаги. Листочек съедаем убираем в сейф
2. Указываем Result Url – это callback URL, который будет дергать робокасса после совершения УСПЕШНОГО платежа
3. Указываем метод запроса result url. Я выбрал POST - он более канонично и отвечает стандартам взаимодействия web-API
4. СМС-оповещение можно пропустить
5. SUCCESS + FAIL URL – это страницы, на которые пользователь будет перемещен после совершения оплаты, в зависимости от их статуса
6. Для обоих я выбрал GET-метод

Форма настроек для ТЕСТОВЫХ платежей
1. Задаем алгоритм расчета, опять же MD5
2. Указываем пароли один и два. Записываем их на листочек, этот листочек можно не проглатывать

почему столько паролей
Первый пароль – с помощью него формируется подпись запроса, которая передается (качестве параметра) на страницу начала оплаты, то есть вот сюда https://auth.robokassa.ru/Merchant/Index.aspx, выглядит это так

Второй пароль будет использоваться для проверки запроса, прилетающего от robokassы на указанный ResultUrl. Этот запрос будет содержать параметр SignatureValue, который нужно сверить с нашим SignatureValue (он генерируется основе второго пароля). Если полученный и сгенерированный параметр SignatureValue совпадают – платеж действительно пришел от робокассы и можно добавлять циферки на счет пользователя.
Тестовые пароли один и два служат для этих же целей, но с условием - если присутствует параметр isTest=1.


Шаг два. Написание логики на net.core 2.1


Для перемещения пользователя на форму начала оплаты (https://auth.robokassa.ru/Merchant/Index.aspx) нужно сгенерировать правильную подпись, чтобы робокасса могла однозначно идентифицировать получателя средств, именно ваш магазин. Кроме этого на страницу нужно передавать значение инвойса и сумму, нужную для его оплаты.

Пример реализации
public static string GenerateAuthLink(decimal amount, int invoiceId) { const bool isTest = true; const string roboShopName = "GAZMIASSHOP"; const string roboPassw1 = "robokassa password 1"; string amountStr = amount.ToString("0.00", System.Globalization.CultureInfo.InvariantCulture), invoiceIdStr = invoiceId.ToString(), srcBase = string.Format("{0}:{1}:{2}:{3}", roboShopName, amountStr, invoiceIdStr, roboPassw1); string signatureValue = generateMd5Hash(srcBase); string authPaymentString; if (isTest) { authPaymentString = "https://auth.robokassa.ru/Merchant/Index.aspx" + "?isTest=1" + "&MrchLogin=" + roboShopName + "&InvId=" + invoiceIdStr + "&OutSum=" + amountStr + "&SignatureValue=" + signatureValue + "&Culture=ru"; } else { authPaymentString = "https://auth.robokassa.ru/Merchant/Index.aspx" + "?MrchLogin=" + roboShopName + "&InvId=" + invoiceIdStr + "&OutSum=" + amountStr + "&SignatureValue=" + signatureValue + "&Culture=ru"; } return authPaymentString; } static string generateMd5Hash(string stringToHash) { using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider()) { byte[] bSignature = md5.ComputeHash(Encoding.ASCII.GetBytes(stringToHash)); StringBuilder sbSignature = new StringBuilder(); foreach (byte b in bSignature) sbSignature.AppendFormat("{0:x2}", b); return sbSignature.ToString(); } }

Вызвав этот метод с параметрами 111 (сумма оплаты) и 1 (номер инвойса) мы получим строку вида
"https://auth.robokassa.ru/Merchant/Index.aspx?isTest=1&MrchLogin=GAZMIASSHOP&InvId=1&OutSum=111.00&SignatureValue=cb6f821a0df9008ad4fb818a5c1ebdf7&Culture=ru"

То, что нужно. Копируем ссылку и вставляем в браузер. Если тестовый пароль номер один верный – мы увидим форму начала оплаты (с водяными знаками «образец»). Если форма выдала ошибку – проверяем пароль.
Для упрощения восприятия, наглядности и унификации кода я специально не использовал фишки c# 6.0 (форматирование строк), а также запихнул все параметры в виде констант в начало метода. Значения переменных isTest, roboShopName и roboPassw1 нужно изменить согласно вашим настройкам (на шаге 1).
Я рекомендую обернуть эту логику в отдельно-стоящий модуль, так как к нему будет адресовано достаточно много вызовов.

Прием запроса о совершенном платеже
Этот запрос будет приходить от робокассы на URL, указанный как ResultURL в настройках магазина. Запрос будет содержать в себе:
1. исходящий баланс (после удержания всех комиссий)
2. входящий баланс (с учетом комиссии robokass`ы)
3. Номер инвойса
4. Подпись (SignatureValue, который был сформирован робокассой и который нужно проверить перед зачислением средств)
5. Email, указанный пользователем на форме оплаты
6. Лейбл валюты

Полученный запрос необходимо проверить, чтобы исключить вероятность отправки «левых» запросов от посторонних пользователей. Проверять запрос нужно исходя из расчета SignatureValue, и ни в коем случае не опираясь на адрес хоста, отправившего запрос (черевато тем, что при получении действительного запроса от робокассы, который будет отправлен с нового адреса сервиса – вы посчитаете этот платеж поддельным и не зачислите деньги пользователю, который честно их оплатил).

реализация проверки запроса
public static bool CheckResult(RobokassaResult roboResult) { const string roboPassw2 = "robokassa password DVA,222@"; if (roboResult == null || roboResult.InvId == 0) return false; string outSummStr = roboResult.OutSum, outInvIdStr = roboResult.InvId.ToString(), srcBase = string.Format("{0}:{1}:{2}", outSummStr, outInvIdStr, roboPassw2); string srcMD5Hash = generateMd5Hash(srcBase); return roboResult.SignatureValue.ToLower().Equals(srcMD5Hash); }


Реализация контракта RobokassaResult
public class RobokassaResult { /// <summary> /// Исходящий баланс: сумма к зачислению (сумма, которую указал пользователь) /// </summary> public string OutSum { get; set; } /// <summary> /// Входящий баланс: сумма оплаты + комиссия /// </summary> public string IncSum { get; set; } public decimal OutSumDec { get { try { return System.Convert.ToDecimal(OutSum); } catch { return 0M; } } set { try { OutSum = value.ToString("0.000000", System.Globalization.CultureInfo.InvariantCulture); } catch { OutSum = "0.000000"; } } } public decimal IncSumDec { get { try { return System.Convert.ToDecimal(IncSum); } catch { return 0M; } } set { try { IncSum = value.ToString("0.000000", System.Globalization.CultureInfo.InvariantCulture); } catch { IncSum = "0.000000"; } } } public int InvId { get; set; } public string SignatureValue { get; set; } public string IncCurrLabel { get; set; } public string EMail { get; set; } }

Типы параметров OutSum и IncSum строковые. Нет, это не провал – дело в том, что робокасса, после выхода из тестового режима отправляет значения этих параметров в формате четырьмя нолями после запятой, и могут быть проблемы при специфических языковых параметрах и настройках десериализации параметров в net.core (кстати, для десериализации в .net core, по умолчанию , используется библиотека Json.Net, она же Newtonsoft и таки да, в 90% случая параметра сериализации/десериализации придется изменить). Проблемы, лично у меня возникли, поэтому в контракте RobokassaResult я специально сделал эти переменные строковыми и ввел дополнительную конвертацию в decimal.


Шаг 3. Тестирование в тестовом режиме


Создаем пустое MVC-приложение, добавляем в него обработку запросов по URL, указанный как Result Url (еще нужно сделать проброс http-трафика с сервера (с публичным IP) на свой компьютер).
Пример: чтобы удовлетворить указанному узлу Result Url "/payments/result" нужно добавить контроллер PaymetsController с методом Result. Именно в этот метод будет стучаться робокасса при успешной оплате.

Реализация метода IActionResult Result (RobokassaResult roboResult), в контроллере PaymetsController
[HttPost(“/payments/result”)] public IActionResult Result(RobokassaResult result) { bool isCallbackRequestValid = CheckResult(result); if (isCallbackRequestValid) { try { /** * some logic here... */ return Content($"OK{result.InvId}"); } catch (Exception e) { return BadRequest(e.Message); } } return BadRequest(); }


Важно!
Метод Result, в случае валидности callback-запроса, должен возвращать строку “ОК%INVID%”. То есть, если уникальный код инвойса равен одному (1) – то метод должен вернуть строку “OK1”. Этот ответ анализируется робокассой, если получено значение ОК%NUMBER% - то робокасса считает платеж полностью обработанным. В ином случае ваше приложение будет спамиться запросами на метод Result (пять раз точно).

Отладка
1. Запускаем приложение с единственным контроллером и методом Result
2. Придумываем номер инвойса и сумму оплаты
3. Вызываем метод GenerateAuthLink(%amount%, %invoiceId%)
4. Проходим по сгенерированной ссылке - должна открыться форма оплаты с выбором способа, суммой оплаты, названием магазина и логотипом (если загрузили картинку в кабинете робокассы, в настройках магазина)
5. Выбираем способ, жмем оплатить. Так как режим тестовый – плательщику доступны кнопки «оплата успешна» и «ошибка оплаты». Нажимаем «оплата успешна».
6. Получаем запрос в метод Result. Если запрос получен – отлично, остается только проверить правильность данных. Если запрос не получен – надо искать причины (проброс трафика, корректность ссылки и т.д.)
7. Вызываем метод CheckResult и передаем полученный объект result. Если метод вернул true – отлично, можно зачислять оплату на счет пользователя и обязательно вернуть строку OK%invoiceId%. Если false - запрос неверный, что делать в этом случае решать вам.

В это же самое время робокасса переместит пользователя, производящего оплату, на страницу, указанную как SuccessUrl. Туда будут переданы параметры: номер инвойса, средства, и т.д. Эта страничка нужно пользователю, чтобы он убедился в том, что его платеж прошел и деньги получены.


Еще нужно добавить страницу для платежей, которые не были обработаны. С информацией и контактами куда обращаться.


Страницы success/failed необязательные, они не влияют на процесс обработки платежей. Их основная цель – донести пользователю о статусе его платежа.

Пункт 4. Перевод из тестового режима


Для перехода из тестового режима в нормальный нужно активировать ваш магазин. Это делается в личном кабинете робокассы, в разделе «Мои магазины». Напротив магазина нужно нажать на большую зеленую кнопку «Запрос на активацию».
Следом идем в метод GenerateAuthLink и меняем значение константы isTest на false.
Ждем момента, когда поддержка робокассы проверит и активирует магазин.

Пункт 5. Вывод средств


Для вывода средств с вашего расчетного счета нужно зарегистрироваться на сайте-партнере Qiwi. Это известная платежная с собственным банком, и без регистрации выводить деньги физическим лицам не представляется возможным.
Для регистрации на Qiwi достаточно указать мобильный телефон, приходит СМС с паролем. Далее авторизуетесь и проходите идентификацию: анонимная (идентификация не нужна, но есть серьезные ограничения на сумму операций и максимальную сумму хранения), стандартная (что-то вроде оборота 200 тысяч в месяц) и продвинутая (600 тысяч в месяц). Идентификация бесплатная, по крайней мере, стандартная, и не займет много времени.
После регистрации указываете номер телефона в системе Qiwi как способ вывода средств. Далее ждете неделю, а может и меньше (это нужно для прохождения кросс-идентификации робокассы и киви), и готово – можете выводить средства на свои реквизиты.
 
1700
0

Оставлять комментарии могут только зарегистрированные пользователи

пока никто не оставлял комментариев

Последние статьи

Компьютер на водяном охлаждении

Основы моддинга

Моддинг. Домашние компьютеры больше не унылые