Doug
Doug

Reputation: 1724

Applying monthly referral discounts to a subscription fee

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

Answers (1)

Wendy Smoak
Wendy Smoak

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

Related Questions