【问题标题】:Database design for subscription billing订阅计费的数据库设计
【发布时间】:2016-01-24 15:46:57
【问题描述】:

寻找一些关于定期计费系统数据库基本设计的指导。

我想出的设计有一个表来存储订阅的下一个周期(相同或新的计划,相同或不同的价格,或者不续订),另一个用于存储应用的订阅(什么产品在什么时候以什么价格买的)。这就是我所拥有的:

Subscriptions
+----+------------+--------+-----------------+------------------+-------------------+
| ID | customerID | itemID | nextBillingDate | nextBillingPrice | notRenewingReason |
+----+------------+--------+-----------------+------------------+-------------------+
|  1 |         10 |      2 | NULL            |              280 | Too expensive     |
|  2 |         10 |      3 | NULL            |              120 | Too expensive     |
|  3 |         11 |      2 | 2015-06-18      |              290 |                   |
|  4 |         10 |      2 | 2016-10-14      |              290 |                   |
+----+------------+--------+-----------------+------------------+-------------------+


SubscriptionHistory

+----+--------+------------+------------+-------+--------------+-------+
| ID | subsID | startDate  |  endDate   | price | extInvoiceID | paid  |
+----+--------+------------+------------+-------+--------------+-------+
|  1 |      1 | 2012-09-04 | 2013-09-03 |   280 | 81654        | TRUE  |
|  2 |      2 | 2013-03-01 | 2013-03-31 |     0 | NULL         | TRUE  |
|  3 |      2 | 2013-04-01 | 2013-09-03 |   120 | 81812        | TRUE  |
|  4 |      1 | 2013-09-04 | 2014-09-03 |   280 | 84221        | TRUE  |
|  5 |      2 | 2013-09-04 | 2014-09-03 |   120 | 84221        | TRUE  |
|  6 |      3 | 2014-06-18 | 2015-06-17 |   290 | 85312        | TRUE  |
|  7 |      4 | 2015-10-14 | 2016-10-13 |   290 | 87421        | FALSE |
+----+--------+------------+------------+-------+--------------+-------+

它必须支持以下用例:

  1. 订阅期限为一年或三年
  2. 客户订阅了产品计划
  3. 一个客户可以订阅多个产品
  4. 产品的附加组件可以包含在订阅中
  5. 可以通过订阅部分添加插件
  6. 可以在订阅期间添加插件作为试用期
  7. 某些订阅可能会降低价格(例如,由于特殊情况同意免费第二次订阅)
  8. 续订计划时,附加内容和价格可能会发生变化
  9. 能够记录不续订的原因
  10. 任何客户都应该可以看到完整的历史记录,例如在上面的数据库中您可以看到客户 10:

    • 2012-09-04加入
    • 试用一个月后于 2013 年 4 月 1 日在订阅中添加了一个插件
    • 没有因为太贵而续订,所以于 2014-09-03 过期
    • 2015 年 10 月 14 日再次以更高的价格订阅,但未付款

任何指针?

【问题讨论】:

  • 如果你展示你到目前为止所做的事情,你可能会得到一些帮助。这里的人不会为你做的。
  • @w0051977 我已经添加了到目前为止的内容
  • @marcus 您的解决方案的状态如何?也许您已经找到了一些开源解决方案? 4年后调查这个问题,很有趣。谢谢。

标签: database-design recurring-billing


【解决方案1】:

这是一个包含您的插件的表格。你没有明确说你的插件要花钱,但你提到了这一点,所以我已经包括了一个价格。我还假设插件与特定产品相关联。如果您的插件随时间而变化,我将在此表中添加 beg_date 和 end_date,就像在 product 表中一样。

addon
    id              unsigned int(P)
    product_id      unsigned int(F product_id)
    description     varchar(255)
    price           double

+----+------------+-----------------+-------+
| id | product_id | description     | price |
+----+------------+-----------------+-------+
|  1 |          1 | This is addon 1 | 11.25 |
|  2 |          1 | This is addon 2 | 22.50 |
|  3 |          1 | This is addon 3 | 15.00 |
| .. | .......... | ............... | ..... |
+----+------------+-----------------+-------+

只是一个普通的旧customer 表...

customer
    id              unsigned int(P)
    salutation      varchar(4)
    first_name      varchar(50)
    ...

+----+------------+------------+-----+
| id | salutation | first_name | ... |
+----+------------+------------+-----+
|  1 |        Mr. |       John | ... |
|  2 |       Mrs. |       Jane | ... |
| .. | .......... | .......... | ... |
+----+------------+------------+-----+

这里是每个客户曾经购买或试用过的所有addon。在此示例中,end_date 默认为 NULL,并且在客户停止使用插件之前不会有值。或者,您可以根据关联的product 的到期时间来填写end_date。请注意,客户为插件 1 支付了全价,而没有为插件 2 支付任何费用(因为他们刚刚试用过),并且他们以折扣价获得了插件 3。

customer_addon
    id                      unsigned int(P)
    customer_id             unsigned int(F customer.id)
    addon_id                unsigned int(F addon.id)
    beg_date                date
    end_date                date // default NULL
    price                   double
    renewed                 enum('f','t')
    decline_reason_id       unsigned int(F decline_reason.id)

+----+-------------+----------+------------+------------+-------+---------+-------------------+
| id | customer_id | addon_id | beg_date   | end_date   | price | renewed | decline_reason_id |
+----+-------------+----------+------------+------------+-------+---------+-------------------+
|  1 |           1 |        1 | 2015-01-10 | 2016-01-10 | 11.25 |       f |                 1 |
|  2 |           1 |        2 | 2015-01-10 | 2015-02-10 |  0.00 |       f |                 2 |
|  3 |           1 |        3 | 2015-10-25 |       NULL | 10.00 |    NULL |              NULL |
| .. | ........... | ........ | .......... | .......... | ..... | ....... | ................. |
+----+-------------+----------+------------+------------+-------+---------+-------------------+

这是每位客户购买过的所有product。在此示例中,我使用订阅到期的计算日期填充end_date。您可以看到客户为产品 2 支付了全价,但为产品 3 获得了折扣。

customer_product
    id                      unsigned int(P)
    customer_id             unsigned int(F customer.id)
    product_id              unsigned int(F product.id)
    beg_date                date
    end_date                date
    price                   double
    renewed                 enum('f','t')
    decline_reason_id       unsigned int(F decline_reason.id)

+----+-------------+------------+------------+------------+-------+---------+-------------------+
| id | customer_id | product_id | beg_date   | end_date   | price | renewed | decline_reason_id |
+----+-------------+------------+------------+------------+-------+---------+-------------------+
|  1 |           1 |          2 | 2015-01-10 | 2016-01-10 | 25.00 |    NULL |              NULL |
|  2 |           1 |          3 | 2015-02-10 | 2018-02-10 | 75.00 |    NULL |              NULL |
|  3 |           1 |          4 | 2016-01-10 | 2017-01-10 | 28.00 |    NULL |              NULL |
| .. | ........... | .......... | .......... | .......... | ..... | ....... | ................. |    +----+-------------+------------+------------+------------+-------+---------+-------------------+

拒绝原因表。

decline_reason
    id              unsigned int(P)
    description     varchar(50)

+----+----------------+
| id | description    |
+----+----------------+
|  1 | Too expensive  |
|  2 | Didn't like it |
| .. | .............. |
+----+----------------+

customer 可以订阅的所有计划的表格。您会注意到有两种计划 1 产品 - 第一个计划 1 在 2013 年 1 月 1 日至 2014 年 1 月 1 日期间提供,价格为 20.00 美元。下一个计划 1 于 2014 年 1 月 1 日生效,但费用为 25.00 美元。许多产品/服务的价格会随着时间的推移而上涨,这是对您的产品进行“版本化”的一种方式。

product
    id              unsigned int(P)
    description     varchar(255)
    term            unsigned int
    price           double
    beg_date        date
    end_date        date

+----+-------------+------+--------+------------+------------+
| id | description | term | price  | beg_date   | end_date   |
+----+-------------+------+--------+------------+------------+
|  1 | Plan 1      |    1 | 20.00  | 2013-01-01 | 2014-01-01 |
|  2 | Plan 1      |    1 | 25.00  | 2014-01-01 | 2015-02-12 |
|  3 | Plan 2      |    3 | 100.00 | 2015-01-01 | 2015-09-15 |
|  4 | Plan 3      |    1 | 35.00  | 2015-01-01 | 2017-01-01 |
| .. | ........... | .... | ...... | .......... | .......... |
+----+-------------+------+--------+------------+------------+

【讨论】:

  • 感谢您的意见。但是,这并不能处理在续订时更改为不同的计划或在续订时添加附加组件。此外,当关系完全相同时,为什么要将产品/附加组件分开。如果你有一个customer_product_addons 表而不是customer_addons,我可以理解你的观点。
  • @Marcus - 我编辑了我的示例数据以显示客户在续订计划 1 时订阅计划 3(请参阅customer_product 表)。至于为什么将产品/插件分开 - 我不得不对您的数据如何相关(即业务规则)做出一堆假设。这只是为了说明鉴于我可用的信息有限,我将从哪里开始。
  • 伙计们,我知道这已经过时了,但我找不到与这种与 SaaS 产品相关的数据库设计的架构讨论相近的东西。 Marcus 和@BennyHill——你有什么可以推荐的关于这个主题的资源(除了这个线程,显然——这是金子)?
  • @pop - 我所知道的几乎所有东西都是我在过去 20 年中从客户、合作伙伴和会议中获得的行业知识。不过,这是我给你的一个建议:slideshare.net/billkarwin
  • 谢谢@BennyHill - 我会调查的。
猜你喜欢
  • 2014-05-17
  • 2018-11-20
  • 2016-06-03
  • 2012-05-04
  • 2012-02-12
  • 2011-06-10
  • 2012-06-03
  • 2021-01-01
  • 2017-08-23
相关资源
最近更新 更多