Обработка подписок, сальдо и изменений тарифного плана [закрыто]

11

Преамбула
Моя цель - создать повторно используемый код для нескольких проектов (а также опубликовать его на github) для управления подписками. Я знаю о чередующихся поставщиках биллинга, но это не то, к чему стремится этот модуль. Он должен быть просто оболочкой / помощником для расчета баланса аккаунта, простых уведомлений о продлении подписки и обработки расчетов цены.

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

Ситуация
Предположим, у вас есть модель, которая может подписаться на план подписки (например User). Эта модель имеет поле, в котором хранится идентификатор плана подписки, на который она в данный момент подписана. Таким образом, при каждом изменении плана оно записывается.

Существует модель (например SubscriptionPlanChanges) со следующими полями, записывающими упомянутые изменения:

  • subscriberотносящиеся к модели подписки ( Userв данном случае)
  • from_plan определение идентификатора плана, который модель имела до изменения
  • to_plan определение идентификатора плана, выбранного моделью
  • created_at поле даты и времени, в котором хранятся изменения
  • valid_until сохраняет дату до фактической подписки
  • paid_at также поле даты и времени, которое определяет, была ли (и когда) подписка оплачена

Конечно, этот макет является дискуссионным.

Вопрос об остатке на счете.
Когда пользователь меняет свой план подписки, мне нужно сравнить поля плана, получить цены и рассчитать вычет для нового плана на основе текущего плана valid_untilи его цены. Скажем: вы подписались на год плана А, но через 6 месяцев вы переходите на план Б, поэтому вы получаете половину уплаченной цены за 6 месяцев плана А.

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

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

Другим вариантом будет создание дополнительной записи для этого, где from_planи to_planимеют одинаковый идентификатор (таким образом символизирующий «без изменений»). Но не мешает ли это каким-то образом рассчитать остаток на счете?

Если бы кто-то мог указать мне правильное направление относительно логики обработки таких подписок, я был бы очень признателен.


ОБНОВЛЕНИЕ
Спасибо за помощь к настоящему времени. Я думаю, что мой вопрос был слишком расплывчатым, поэтому я постараюсь быть более точным, используя меньше абстракций. К сожалению, я еще не смог решить свою проблему.

Случай А
User можно выбрать Subscription Plan A. Это в настоящее время хранит, SubscriptionPlanChangeчтобы отслеживать это. Например, через 5 месяцев Userобновляет подписку на Subscription Plan B. Таким образом, он платит цену за свою новую подписку, вычитая цену плана a за неиспользованные 7 месяцев.

Дело Б
Через 3 месяца Userоткатывается на свое Subscription Plan A. Он не должен платить, но получает за это баланс, так что в конце подписки он вычитает этот баланс для своей новой подписки.

В случае C
User можно выбрать план подписки для подуслуги, которая имеет независимые планы подписки. То же самое Case Aи Case Bможет применяться к этой подписке на субсервис.

_Case D_ Пользователь отменяет одну из своих подписок. Это приводит к пополнению его баланса.

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

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

Некоторые вещи стоит отметить, хотя я не думаю, что они должны создавать проблемы:

  • Это не должно быть User, это может быть что угодно, поэтому Subscriberполиморфна
  • Plansне обязательно должны быть планы, но могут быть, например, Magazinesкак упомянуто. Это то, что я описал с Case C и Case D .
pduersteler
источник
1
Одна вещь, которую вы наверняка могли бы сделать, это назначить цену каждого выпуска (которая может зависеть от плана таким образом, чтобы комбинация [план, выпуск] соответствовала [цене выпуска]), а затем просто отслеживать баланс каждого подписчика на журнал. (или какую терминологию вы предпочитаете).
CVn
Спасибо всем, мне нужно было обновить вопрос, потому что я еще не мог решить свою проблему.
pduersteler
1
Могу я спросить, как ты это реализовал?
JCM

Ответы:

6

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

Другими словами, ваша таблица SubscriptionPlanChanges будет иметь следующую информацию для своего ключа:

  • подписчик
  • строить планы
  • Действует с

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

  • действует до
  • заплатил до
  • ставка (также 0, если бесплатно)

Обратите внимание, что нет «плана от» или «плана на». Хотя они могут быть у вас, информация излишня и может быть рассчитана самостоятельно (хранение такой информации означает, что у вас есть дополнительная задача по обеспечению ее согласованности). Когда начинается новый план, вместо того, чтобы изменять существующие планы, вы оставляете их и просто добавляете новую запись. Если после нового плана существует другой перекрывающийся план, вы можете решить, что хотите удалить его (более интуитивно понятный способ). Когда вы загружаете эти планы для подписчика, вы сортируете их по дате их вступления в силу.

Как только вы получите это, расчет кредита пользователя является относительно простым. Если два плана не могут перекрываться, вы просто берете меньшую из двух дат между датой «действителен до» предыдущего плана и датой «действителен с» текущего плана, чтобы определить дату окончания. Дата начала - это наибольшая из двух дат между датой «действует с» и датой «оплачено до» (если она определена). Затем платеж (или кредит) можно рассчитать по ставке, умноженной на интервал времени между вышеупомянутыми датами начала и окончания этого плана.

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

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

Я надеюсь, что это отвечает на ваш вопрос.

Нил
источник
Большое спасибо, что пролил немного света! Хотя я чувствую, что мне неясно, что такое «правильное» поле. valid_untilбыла моя терминология вашей paid_until. Нет максимальной длины плана подписки.
pduersteler
@pduersteler Ах, тогда моя ошибка. Это только делает расчет намного проще, так как «конечная» дата - это только начало нового плана.
Нил
1
Оценить сумму, уплаченную? Если да, то это может быть другой объект, например, Счет, я прав?
JCM
3

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

ID, UserId, TransactionDate, Credit (положительный, когда вы предоставляете кредиты пользователю, и отрицательный, когда пользователь использует кредит)

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

Надеюсь, это вам пригодится ...

Ларс Хофвандер
источник