Как спроектировать real-time списывание денег со счета пользователя?

Добрый вечер, коллеги!


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

Есть некий сервис, назовем его «платное размещение объявления». Стоимость размещения фиксирована (скажем, 100 рублей в сутки). Пользователи пополняют баланс и включают-отключают свои объявления. Причем помимо ручного включения-отключения существует еще масса критериев, по которым объявления могут включиться или отключиться.

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


Т.е. нужно в любой момент времени знать сколько денег на счете пользователя.

Плохие решения.


1. Запуск по CRON'у ежесекундно пересчета остатков на счетах всех пользователей. Не подходит — слишком ресурсоемко.


2. Рассчитывать и поддерживать в актуальном состоянии дату и время, когда у пользователя заканчиваются деньги. Именно таким образом эта задача реализована сейчас, но это довольно трудное в поддержке и реализации решение. По мере увеличения условий включения-отключения объявлений это становится адом.


Подскажите пожалуйста, какие еще варианты решения такой задачи? Поможет ли Node.JS? Или еще какие-то технологии? По каким ключам-фразам гуглить?


Сайт написан на РНР (symfony 1).
  • Вопрос задан
  • 3087 просмотров
Пригласить эксперта
Ответы на вопрос 6
strib
@strib
Прямо посекундная тарификация?
Если не секрет, то расскажите, пожалуйста, что за сервис.
Аналог из отрасли где это все стандартизировано и отработано.
В связи существует 2 метода оплаты: postpaid и prepaid. Postpaid — это грубо говоря счет, который может уходить в минус, в данном случае мимо.
Prepaid. Это тот способ который берет деньги до начала услуги, поэтому при приблежении к 0 отключает сервис. К примеру в ряде стран законодательно запрещено уводить препейдного абонента в минус, но это лирика.
Так вот, prepaid бывает двух типов (основных, на самом деле есть еще)
IEC — Immediate Event Charging и ECUR — Event Charging with Unit Reservation.
В первом случае, при возникновении события, происходит запрос к счету, счет проверяется и от него отпиливаетя стоимость услуги, после чего возвращается положительный ответ и событие продолжается. Проблема, завязка на единицу тарификации, если выбрать маленькую (секунду) — то очень ресурсоемко получается, а если квоту увеличить (до минуты) — то при отказе от услуги на 35й секунде средства за 25 секунд пропадут. Знакомая ситуация наверное…
Второй метод интереснее.
Выбирается 2 параметра. Размер квоты и единица тарификации, допустим 1 минута и 5 секунд. Событие инициируется, происходит запрос к счету, на счете блокируется сумма, и возвращаеся ответ, который содержит объем доступный для услуги (в примере время, а могут быть байты, клики, слоны...) И услуга начинает предоставляться. Как только заканчиваются ресурсы, к счету опять происходит обращение, в котором говорится о том, что ресурсы израсходованы, и хочу еще (ну или не хочу), в этом случае со счета списывается разервированная сумма, и резервитуется следующая квота. Если услуга прекращается до исчерпания квоты, то время округляется ввепх до единицы тарификации (с 32 секунл до 35) и списываются деньги только за 35 секунд, за 25 возвращаются.
Это как организуется работа со счетом.
Совсем подробно можно почитать вот тут www.3gpp.org/ftp/Specs/archive/32_series/32.299/32299-700.zip Пункт 6.3.2, пугаться не надо, картинки понятные.

Ух расписался. Это половина, вторая часть — то как контролировать каждое событие. А тут без примера не могу ничего сказать.
Ответ написан
@lesha_penguin
> Стоимость размещения фиксирована (скажем, 100 рублей в сутки).

Тогда в чем проблема?

Сколько времени минимум должно провисеть данное обьявление всегда известно!

Этот параметр меняется в меньшую строну при размещении еще одного обьявления тем же юзером.
Этот параметр меняется в большую сторону при «пополнении баланса» и при снятии других обьявлений.

Так что событийная модель обработки данных вполне решит вашу проблему.
Ответ написан
Sekira
@Sekira
Предлагаю такой вариант:
Нужно не объявления включать/отключать, а баланс пользователя проверять, если <=0, то не показывать.
А объявления выбирать для показа, где баланс >0 и объявление включено пользователем + критерии.
То есть поле включено/отключено должен менять только пользователь, а не масса критериев.
Критерии должны быть прописаны в самом объявлении.
Ответ написан
KawaiDesu
@KawaiDesu
Достаточно при каждом запросе, который имеет отношение к объявлению (старт\стоп\показ) запускать функцию, которая проверяет текущий баланс и, если он больше нуля, то рассчитывает новый баланс и показывает объявление. Как-то так, вроде ничего сложного.
Или необходимо чтобы сервер постоянно знал текущий баланс?
Ответ написан
sergiks
@sergiks Куратор тега PHP
♬♬
Только «события»: старт, стоп, ручной пуск нового оъявления, пополнение счёта — всё, что изменяет прогноз по деньгам, запускает событие, по которому обновляется дата-время окончания денег на счете, по которой сервер выключит показ.

На крон возлагается единственная задача: найти по всем счетам, кого пора отрубить.

Включение, из состояния «выкл.», возможно только по событию пополнения счёта.
Ответ написан
@ComodoHacker
Одно из решений — пересмотреть модель биллинга. Ввести понятие резервирования средств. Пользователь, добавляя объявление, сам указывает, на какой срок его включить, например на 1 сутки. И соответствующая сумма резервируется, если она есть на счету и не зарезервирована, еще до показа объявления. Таким образом, мы заранее знаем момент, когда показ объявления нужно отключить. А необходимость проверять баланс каждую секунду отпадает.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы