Stripe计划变更定价服务

207 阅读2分钟

Stripe是一个流行的支付系统。而且是非常成熟的一个。这个服务有很多内置的功能,是所有当代企业在网上需要的。 这些功能的一个例子是订阅和计划。当一个订阅计划在结算周期中被改变时,我们可能希望只向客户收取当前结算周期剩余部分的新计划金额,并退回当前结算周期未使用的金额。 因此,订阅的价格是公平的,并每天计算。

例如,目前的计划费用为10美元。假设客户已经支付,新的计划费用为20美元。客户在计费周期的中间转换计划,新计划在当前计费周期剩余时间的每日金额是20美元的1/2,即10美元。 由于这是一个计划的升级,新计划的成本更高,我们可以直接向他们收取这10美元。 但等等,当前计划的每日金额不是10美元的1/2,是5美元。换句话说,客户已经为当前计费周期的剩余时间支付了一些钱。 因此,我们从收费中扣除这些钱。 结果,在这个例子中,我们只需要向客户收取5美元。 计算方法是10美元(当前计费周期的新计划剩余时间)-5美元(该期间已经支付的钱)。 这种计划变更的计算过程被称为比例计算。

从图形上看,它可以解释如下:

Stripe可以为我们计算比例,但这个功能只适用于每个座位的计划。在这篇文章中,您将看到如何从头开始建立一个用Ruby编写的计算比例的服务。它还展示了Ruby服务对象设计模式的实际应用。

按比例计算

使用Ruby编程语言和一些技巧,我们得到了下面这个类:

class PlanChangeProration
  extend Dry::Initializer
  extend Memoist

  option :user
  option :plan_id

  def unused_time_cost
    -(unused_time_line_item&.amount || 0)
  end

  def total
    [remaining_time_cost - unused_time_cost, 0].max
  end

  private

  def remaining_time_cost
    new_plan.price * 100 * new_plan_remaining_time / new_plan_billing_cycle_time.to_f
  end

  def new_plan_billing_cycle_time
    current_plan_used_time + new_plan_remaining_time
  end

  def current_plan_used_time
    proration_date - (unused_time_line_item&.period&.start || proration_date)
  end

  def new_plan_remaining_time
    (remaining_time_line_item&.period&.end || new_plan_period_end) - proration_date
  end

  def new_plan_period_end
    proration_date + (new_plan.is_yearly? ? 365.days.to_i : 30.days.to_i)
  end

  memoize def new_plan
    Plan.find(plan_id)
  end

  # For information about upcoming_invoice https://stripe.com/docs/billing/subscriptions/prorations
  def upcoming_invoice
    Stripe::Invoice.upcoming(
      customer: customer.id,
      subscription: subscription.id,
      subscription_plan: new_plan.name,
      subscription_proration_date: proration_date
    )
  end

  memoize def customer
    Stripe::Customer.retrieve(user.stripe_customer_id)
  end

  memoize def subscription
    customer.subscriptions.first
  end

  memoize def proration_date
    Time.now.to_i
  end

  # Stripe always generates 2 or 3 line items for the "upcoming invoice"
  memoize def upcoming_line_items
    return [] unless subscription
    upcoming_invoice.lines.data
  end

  # Line item with description "Unused time on Medium Team after 16 Jun 2022" (Medium Team is current plan)
  def unused_time_line_item
    upcoming_line_items[0]
  end

  # Line item with description "Remaining time on xl_team_without after 16 Jun 2022" (xl_team_without is new plan)
  def remaining_time_line_item
    upcoming_line_items[1]
  end
end

而这就是如何使用它:

service = PlanChangeProration.new(current_user, new_plan_id)
service.unused_time_cost # would return 5$ in our example
service.total # also 5$, but this is the charge amount

memoistdry-initializer使这个服务变得干净和可读。

Stripe计划变更定额服务最初由Andrei Kaleshka于2022年6月18日在WideFix发布。