功能描述:
所以我有一个基于订阅的网站,它有两个计划:每月和每年。包月按月收取月费,包年则按年收取10个月的一次性累积费用。(29 美元/月和 290 美元/年)
对于每个被推荐的活跃会员,我每个月都有 10% 的经常性推荐折扣(最多 100%)。因此,如果 Bob 在 10 月份注册了每月订阅,并且整个下个月有 5 人注册,他应该累积 50% 的折扣。这将应用于他在 11 月的订阅费,即当月的订阅费为 14.50 美元。但假设他的一位推荐人在 11 月取消了。这将使他在 12 月的折扣减少 10%,这将给他 40% 的折扣,有 4 个活跃推荐人,12 月的收入为 17.40 美元。
工作代码示例:
如果 Bob 的每个推荐都是每月一次,那么使用 webhook 应用这个方法相当简单。如果连接到该付款的用户是 Bob 的推荐人之一,则每次成功付款时都会从 Bob 的当前余额中扣除一笔金额。
# Webhooks Controller
def payment_success
referred_user = User.find_by_customer_id(@subscription.customer.reference)
if referring_user = User.find_by_affiliate_code(referred_user.referral_code)
balance = referring_user.subscription.balance_in_cents
referring_user.subscription.adjustment({"amount_in_cents"=>"-290","memo"=>"Balance adjusted for referring #{referred_user.email}. Payment for referral was successful."})
referring_user.subscription.save
end
head :ok
end
这会向我正在使用的支付处理器Chargify发送一个请求,以相应地调整余额。
我真正的问题是:
但是,当 Bob 的推荐人之一是年度用户时,事情就没有这么简单了。
由于年度订阅是一次性付款,因此payment_success
webhook 并不是每个月都通过,这意味着 Bob 的订阅的 10% 不能每月从该年度用户的活跃推荐中扣除。
我想把 Bob 的计划价格设置为比标准价格低 10%,而不是每个月调整余额。不过,这将需要 9 个不同的计划,因为计划价格直接引用Plan
Chargify 上的一个对象,该对象指向其price
属性。这意味着需要有 a 90%_monthly_plan
、 a80%_monthly_plan
等,它们都存在于 Chargify 服务器上。这似乎过于复杂,如果它引起了我现在甚至没有考虑的其他问题,我也不会感到惊讶。
我当前(不满意)的解决方案:
因此,我决定在我的服务器上创建一个调度程序,该调度程序是在年度用户注册时创建的,并且每个月都会触发。
我为此使用了rufus-scheduler。需要注意的一件重要事情 - 当服务器重新启动时,计划会被破坏,因此我正在创建计划记录,并且初始化程序会遍历这些记录并在每次服务器重新启动时创建这些计划。
# modified Webhooks Controller
def payment_success
referred_user = User.find_by_customer_id(@subscription.customer.reference)
if referring_user = User.find_by_affiliate_code(referred_user.referral_code)
balance = referring_user.subscription.balance_in_cents
referring_user.subscription.adjustment({"amount_in_cents"=>"-290","memo"=>"Balance adjusted for referring #{referred_user.email}. Payment for referral was successful."})
referring_user.subscription.save
if @subscription.product.handle == 'yearly'
schedule = ReferralSchedule.create(
type_of: "yearly discount",
referred_user_id: referred_user.id,
referring_user_id: referring_user.id,
time_to_execute: "30.41d"
)
ReferralScheduler::create_yearly_discounts_schedule(schedule)
end
end
head :ok
end
以及处理计划创建的模块:
module ReferralScheduler
def self.create_yearly_discounts_schedule(schedule)
referred_user = User.find(schedule.referred_user_id)
referring_user = User.find(schedule.referring_user_id)
# first_date is calculated to be the first date in the future, that the scheduler should run.
first_date = Time.now + (30.41-(DateTime.now - schedule.created_at.to_datetime).fdiv(30.41)%1*30.41).days
scheduler = Rufus::Scheduler.new
scheduler.every schedule.time_to_execute, first: first_date , last_at: (schedule.created_at+12.months) do
if referring_user && referred_user.subscription_status == :active
balance = referring_user.subscription.balance_in_cents
referring_user.subscription.adjustment({"amount_in_cents"=>"-290","memo"=>"Balance adjusted for referring #{referred_user.email}. Referral is still active."})
referring_user.subscription.save
end
end
scheduler.at (schedule.created_at+12.months) do
schedule.destroy!
end
end
end
和初始化器:
require 'rufus-scheduler'
ReferralSchedule.where(type_of: "yearly discount").each do |schedule|
ReferralScheduler::create_yearly_discounts_schedule(schedule)
end
我真的很讨厌使用这种方法,因为它看起来不太干净。当我尝试以更短的时间间隔运行它时,它也已经搞砸了我的本地服务器。
我还考虑过使用 rake 任务和/或 Grunt 来执行此操作,但我对这些任务的了解非常有限,我不确定它们是否一定会是更容易/更好的解决方案。
有任何想法吗?