Website development
17 November 2009

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

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

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

Такое ограничение сразу приводит к вычеркиванию из списка методов оплаты заполнение квитанции в Сбербанке. Да, это тоже метод, но метод небыстрый. Особенно, если на дворе поздний вечер, пользователь расслабился за бутылкой пива чашкой чая. Какой Сбербанк, тёпленьким его брать, тёпленьким!



Какие же способы организации приема более-менее моментальных платежей приходят на ум в первую очередь? Лично мне только что пришли на ум сразу три:
  • смс
  • пластиковые карты
  • электронные деньги
У каждого метода свои достоинства и свои недостатки.
Платежи через смс — большая комиссия, но зато мобильник почти у каждого пользователя.
Пластиковые карты — недостаточно распространены (да, да, жители Дефолт-Сити, за МКАДом тоже есть жизнь, готовая платить!), зато комиссия минимальная.
Электронные деньги — множество разных систем, технические и юридические трудности подключения, зато и аудитория большая! А уж способов пополнения!

Не останавливаясь в этой статье на первых двух методах, хочу поговорить об организации приема на сайте электронных валют. Таких как ВебМани, Яндекс.Деньги, Деньги@Mail.Ru и им подобных. Реализация работы в каждом конкретном случае нас не волнует, об этом можно почитать и в документации к этим платежным системам (ПС), и в различного рода историях успеха и неудач.

Основная мысль, которая должна возникнуть после прочтения статьи (и надеюсь, возникнет) — правильно спроектировав систему приема платежей, подключать каждую новую систему не составит труда.

Сразу оговорюсь, дальше под «счетом» я буду понимать не account, а скорее invoice. И для удобства, чтобы не путаться между счетом пользователя, который содержит сведения о платежах, размер платежного баланса пользователя итп, и счетом на оплату чего-либо, последний буду называть «заявкой» или «заказом».

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

Поэтому, поговорим о вторых.
Типичный сценарий работы с такими системами с точки зрения продавца выглядит так:
  1. инициализация пользователем процесса проведения платежа, сохранение заявки на оплату
  2. формирование заявки на основе данных нашего биллинга, подпись заявки секретным ключом или сертификатом
  3. отправка данных в платежную систему и получение номера заказа в их системе учета
  4. сохранение этого номера в нашей системе
  5. опрос платежной системы на предмет изменения статуса этого заказа
  6. изменение статуса заявки в нашей системе, оказание услуги пользователю


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

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

Итак, начнем с общей архитектуры.

Как это не покажется странным, рецепт архитектуры системы приема платежей по описанному сценарию очень и очень прост. Нам потребуется:

очередь заявок — 1 шт.
диспетчер обработки очереди — 1 шт.
общая библиотека работы с заявками — 1 шт.
общая библиотека веб-интерфейса — 1 шт.
общая библиотека работы с платежными системами — 1 шт.
библиотеки работы с конкретными платежными системами — N штук, по числу систем.

Теперь подробнее о каждой из частей.

1. Что такое очередь заявок?
Всё просто — это таблица заявок различных статусов (в самом простом приближении — «оплачена», «неоплачена», «в обработке»). Она нам понадобится и для того, чтобы сохранять информацию о заявке, и для того, чтобы понимать какие заявки нужно отдать на обработку диспетчеру.

Читатели, уже узнавшие слова «нормализация» и «реляционная база данных», возразят, чтобы таблиц должно быть больше, но я утверждаю, что достаточно одной.

2. Диспетчер обработки очереди.
Это некий скрипт, запущенный в бэкграунде как демон или просто по крону, неважно.
Его задача — обрабатывать очередь заявок, обращаться к платежным системам и менять статусы заявок.

3. Библиотека работы с заявками.
По сути это библиотека для работы с очередью (читай «базой данных»). От нее нужно всего несколько функций:
  • создание заявки
  • получение информации о заявке
  • Изменение статуса и других данных заявки
  • получение заявки из очереди
  • удаление заявки

4. Веб-интерфейс.
Для чего он нужен, понятно — чтобы вывести пользователю информацию о заказе и чтобы пользователь смог нажать на кнопку «Оплатить».

От этой библиотеки нужно, чтобы она умела отдавать одни и те же данные с нескольких форматах: как минимум html и json/xml. HTML понятно для чего, а JSON — чтобы использовать AJAX и не перегружать странички. В принципе, можно без него.

От веб-интерфеса нужны всего 4 функции:
  • вызов функции создания заявки
  • вызов функции получения данных о заявке
  • вызов функции удаления заявки
  • и базовая функция обработки ошибок и вывода данных в нужном формате.

5. Библиотеки работы с конкретными платежными системами (БПС) должны уметь
  • формировать URL и данные для запроса на создание заявки в ПС.
  • разбирать ответ платежной системы и возвращать номер заказа в нумерации платежной системы
  • формировать URL и данные для запроса статуса заявки
  • разбирать ответ платежной системы и возвращать статус заявки.

6. От общей библиотеки работы с платежными системами (ОБ) потребуется тоже немного:
  • функция для получения от БПС данных, необходимых для создания заявки, и оправки этих данных в платежную систему.
  • функция для передачи ответа в БПС и получение оттуда статуса заявки
  • функция для работы с общими настройками (например, информацией о подключенных платежных системах)


Как же изменится описанный сценарий работы в новой терминологии?

0. инициализация пользователем процесса проведения платежа.

Веб-интерфейс выводит список доступных платежных систем и информацию о платеже.
Пользователь жмет «Оплатить», создается заявка со статусом «новая», информация о ней попадает в очередь. Пользователю в этот момент выдаем сообщение «Ща всё будет, подожди» и проверяем время от времени статус заявки. Вот тут понадобятся те же данные о заявке в JSON — пока мы рисуем пользователю на экране всякие часики, прогрессбары итп, с помощью AJAX опрашиваем очередь о статусе заявки.

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


Диспетчер выгребает из очереди заявку со статусом «новая», ставит ей статус «в обработке», передает данные в ОБ, получает данные, необходимые для отправки в ПС. Отправляет их, получает ответ, отдает его в ОБ, та в БПС и получает номер заказа. Сохраняет номер заказа и URL для оплаты в нашу очередь, ставит заявке статус «готова к оплате».

При очередном опросе очереди пользователя перекидываем на URL для продолжения оплаты. Что он там сделает — его дело. На случай, если поймет, что этой электронной валюты у него нет, на странице со статусом заказа дадим ему ссылку на удаление заявки и создания новой с возможностью выбрать другую валюту.

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

5. изменение статуса заявки в нашей системе, оказание услуги пользователю.
Как только статус поменялся, в зависимости от ответа платежной системы диспетчер помечает заявку как «оплаченная» или «отклоненная». Больше она диспетчеру не попадет. Теперь фоновые скрипты биллинга получат информацию, что счет оплачен и окажут пользователю заказанную услугу.

Чем хороша описанная система?

1. Она позволяет все операции с пользователем выполнять крайне быстро.
Сохранение заявки в очередь, получение данных о заявке по уникальному номеру — очень быстрые операции.
Вся медленная работа выполняется диспетчером в фоне.

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

3. Она проста.
Подключение новой платежной системы — всего лишь добавление новой библиотеки, которая должна уметь всего 4 вещи, описанные выше.

Конкретные детали реализации я не озвучиваю в этой статье — инструменты неважны.

Это может быть Perl или PHP, или Питон для веба/работы с БД.
Это может быть MySQL, или SQLite, или Oracle, или PosgreSQL для организации очереди.
Это может быть file_get_contents(), LWP::UserAgent или CURL для работы с HTTP.
Вы можете увеличивать интервал опроса статуса заявки в платежной системе вдвое. Можете в 3.14159…

Реализация — за вами.

upd: Полный текст (в том числе со схемами и дополнениями) выложен у меня на сайте

+58
9.8k 244
Comments 76
Top of the day