Высокоуровневая архитектура платежных систем

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

Как я упоминал ранее, систем целое множество, но работают они все по одному и тому же принципу:

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

2. Вы перенаправляете пользователя на страницу платежной системы, где он может выбрать способ оплаты и оплатить заказ.

3.A. Если пользователь отменяет платеж, то он будет перенаправлен на страницу отмененного платежа.

4.A. Данной страницей может быть любая страница на ваше усмотрение. Например, это может быть домашняя страница или же страница где вы спрашиваете у пользователя о возможных проблемах при оплате заказа, с которыми он столкнулся и пытаетесь помочь ему все-таки совершить покупку.

3.B. Если пользователь оплачивает заказ, то он будет перенаправлен на страницу удачного платежа.

4.B. На этой странице я обычно благодарю пользователя за совершенную покупку. Обратите внимание: когда пользователь попадает на эту страницу, то это не значит, что прошла реальная оплата заказа. Вы можете написать что-то вроде: «Спасибо за покупку. Услуга будет предоставлена вам сразу же после обработки платежа». Никогда не думайте об этой странице как о факте платежа для определения и предоставления услуги/товара пользователю.

5. После того, как реальный платеж прошел, платежная система отправит запрос на callback URL, который вы задали.

6. Это не публичная страница. Здесь вы должны обработать запрос от платежного шлюза. Они отправляют вам все необходимые данные, чтобы вы могли понять какой именно заказ был оплачен. К примеру, это могут быть ID заказа в вашей системе, ID транзакции в их системе, сумма оплаты, валюта и т.д.

7. Некоторые платежные системы позволяют клиентам отменять ранее совершенные оплаты. Это значит, что если вы получите такой запрос, то должны обработать его правильным образом. Например, отменить услугу (премиум аккаунт и т.д.), обновить статистику по платежам в своей системе и прочее.

Безопасность

1. Ваш callback URL должен быть доступен анонимным пользователям. В Symfony это можно сделать следующим образом:

  1. security:
  2. firewalls:
  3. payment_callbacks:
  4. pattern: ^/paymentcallback/
  5. security: false

2. Некоторые платежные шлюзы публикуют список белых IP адресов. Вы должны проверять откуда именно пришел запрос и считать или нет его допустимым.

3. Большинство платежных шлюзов шифруют данные при передаче с помощью закрытых ключей.

Упрощенный контроллер

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

  1. <?php
  2.  
  3. // …
  4.  
  5. class OrderController extends Controller
  6. {
  7. /**
  8. * @Route(«/pay/{hash}», name=»OrderView»)
  9. * @Method({«GET»})
  10. */
  11. public function payOrderAction($hash)
  12. {
  13. $order = // получаем заказ в вашей системе по хешу или id
  14. $payUrl = // URL для осуществления платежа. Обычно для его формирования потребуются такие параметры как id заказа в вашей системе, стоимость, валюта и т.д. Точный список параметров зависит от самого платежного шлюза, обратитесь к документации.
  15.  
  16. return new RedirectResponse($url);
  17. }
  18.  
  19. /**
  20. * @Route(«/thank-you/{orderId}», name=»OrderThankYou», requirements={«orderId»: «\d+»})
  21. * @Method({«GET»})
  22. * @Template()
  23. */
  24. public function thankYouAction($orderId)
  25. {
  26. return [
  27. ‘id’ => $orderId,
  28. ];
  29. }
  30.  
  31. /**
  32. * @Route(«/paymentcallback/custom-super-secret-endpoint», name=»PaymentCallback»)
  33. */
  34. public function callbackAction(Request $request)
  35. {
  36. // логируем обращение от шлюза
  37. // получаем данные из запроса ($_GET, $_POST…)
  38. $isValid = // проверяем данные на валидность
  39. if ($isValid) {
  40. // предоставляем пользователю услугу
  41. }
  42.  
  43. // возвращаем платежному шлюзу ответ о том, что его обращение обработано успешно. Обычно это 200 статус код и сообщение «OK».
  44. return new Response(‘OK’);
  45. }
  46. }

Итак, у нас есть три главных метода:

1. Pay order.

Здесь мы формируем ссылку для пользователя. Перейдя по ней он попадет на страницу выбора способа или способов оплаты. Обычно URL содержит такие параметры как:

orderId — ID заказа в вашей системе

amount — величина платежа

currency — валюта, такая как USD, EUR и т.д.

email/userId — идентификатор пользователя в вашей системе

accepturl — URL на страницу успешного платежа. Обычно я добавляю сюда id самого платежа, чтобы была возможность показать больше информации пользователю.

cancelurl — URL на страницу отмены платежа. Может быть любая страница на Ваш выбор.

callbackurl — URL для уведомления платежным шлюзом вашего сервера о реальном статусе платежа.

2. Thank you

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

3. Callback

Только здесь вы обрабатываете результаты платежа. Сначала вам необходимо получить данные из запроса, затем провалидировать и только затем предоставить пользователю услугу, вернув при этом правильный ответ самому платежному шлюзу (200). Обычо такой ответ помимо 200 статуса должен содержать что-нибудь вроде «OK» в своем теле. Точный ответ вы сможете найти в документации конкретного платежного сервиса. Вы также можете инициализировать свои события, а затем добавлять к ним слушателей, например для отправки email’ов, обновления статистики и т.д.

Тестирование

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

1. Включить тестовый режим на стороне платежной системы. Обычно сводится к банальному чекбоксу в админ панели.

2. Разрешить запросы с любых доменов. Множество платежных систем имеют понятие доверенных серверов и принимают запросы только с них. Их вы также настраиваете у них в админ панели.

3. В URL для платежа необходимо будет добавить дополнительный параметр, обычно это «test».

Отлично, теперь мы знаем как включить тестовый режим, но у нас по прежнему есть одна проблема. После того, как пользователь осуществляет платеж, сам платежный шлюз обратится к вашему серверу (callback URL). Т.к. мы делаем все с локального домена, то платежный шлюз не сможет до него достучаться.

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

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

2. Некоторые из шлюзов идут другим путем и добавляют те данные, которые они прислали бы на callback URL к success/accept URL. Данный способ также решает нашу проблему, нам лишь необходимо будет немного модифицировать код:

  1. <?php
  2.  
  3. //…
  4. public function thankYouAction($orderId, Request $request) {
  5. $testModeEnabled = // проверяем включен ли тестовый режим в вашем проекте
  6.  
  7. if ($testModeEnabled) {
  8.   // получаем данные из запроса ($_GET, $_POST…)
  9.   $isValid = // валидируем их
  10.   if ($isValid) {
  11.   // предоставляем услугу пользователю
  12.   }  
  13.   }
  14.  
  15.   return [
  16.   ‘id’ => $orderId,
  17.   ];
  18. }
  19.  

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

Потенциальные проблемы

  • устаревшая документация. Да, такое до сих пор встречается. В таких случаях вам остается лишь обратиться к технической поддержке или дважды подумать стоит ли вообще использовать такой платежный шлюз.
  • отсутствие инструментов для тестов платежей перед запуском. Если это ваш случай, то вы можете логировать запросы от платежного шлюза к вашему основному серверу или же сделать платежную систему/новую функциональность доступной только тестовому пользователю.
  • некоторые платежные сервисы требуют подтверждение персональных данных даже для осуществления тестовых платежей.
  • платежный шлюз не поддерживает рекуррентные платежи. Тут уже вам стоит исходить из ваших собственных потребностей.

Заключение

Не стоит бояться, ведь интеграция платежной системы это просто вне зависимости от конкретного выбора.