Reputation: 1724
Description of functionality:
So I have a subscription based website that has two plans: Monthly and yearly. A monthly subscription is charged a monthly fee every month, while a yearly subscription is charged a one-time cumulative fee of 10 months, each year. ($29/mo and $290/yr)
I have 10% recurring referral discounts that are applied each month, for every active member who was referred (100% max). So if Bob signs up for a monthly subscription in October, and throughout the next month refers 5 people who sign up, he should have a 50% discount accumulated. This will be applied to his subscription fee in November, which will result in $14.50 for that month. But assume one of his referrals cancels in November. This will drop his discount for December by 10%, which will give him a 40% discount, with 4 active referrals, resulting in $17.40 for December.
Example of working code:
If every one of Bob's referrals is monthly, this is fairly simple to apply with webhooks. An amount is just deducted from Bob's current balance every time a successful payment goes through, if the user connected to that payment is one of Bob's referrals.
# 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
This sends a request to Chargify, the payment processor I'm using, to adjust the balance accordingly.
My real problem:
It isn't this straightforward, though, when one of Bob's referrals is a yearly user.
Since the yearly subscription is a one-time payment, the payment_success
webhooks aren't coming through every month, which means 10% of Bob's subscription can't be deducted each month from this yearly user's active referral.
I thought of just setting Bob's plan price to 10% less than standard, instead of adjusting the balance each month. This would require 9 different plans, though, since the plan price directly refers to a Plan
object on Chargify, which points to its price
attribute. That means that there would need to be a 90%_monthly_plan
, a 80%_monthly_plan
, and so on, which all live on the Chargify servers. This seems overly complex, and I also wouldn't be surprised if it caused other issues I'm not even considering at this point.
My current (unsatisfactory) solution:
So instead I decided to just create a scheduler on my server that is created when a yearly user signs up, and it fires every month.
I'm using rufus-scheduler for this. One important thing to note - Schedules are destroyed when the server is restarted, so I'm creating schedule records, and an initializer goes through these records and creates these schedules every time the server restarts.
# 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
And the module handling the schedule creation:
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
And the initializer:
require 'rufus-scheduler'
ReferralSchedule.where(type_of: "yearly discount").each do |schedule|
ReferralScheduler::create_yearly_discounts_schedule(schedule)
end
I really hate using this method, as it doesn't seem very clean. It has also already screwed up my local server when I tried to run it at shorter intervals.
I also thought about using rake tasks and/or Grunt to perform this, but I have very limited knowledge on each of these, and I'm not sure if they will necessarily be easier/better solutions.
Any ideas?
Upvotes: 1
Views: 147
Reputation: 168
One option might be to use the upcoming_renewal_notice
webhook on Bob's subscription as a trigger to go see how many referrals he has made that are on annual plans, and apply any additional discounts.
https://docs.chargify.com/webhooks
upcoming_renewal_notice
- A subscription set to renew within 3 days
Upvotes: 0