Search code examples
ruby-on-railsrubystripe-paymentsruby-on-rails-5

Add application fee for first payment of every month but not for months which dont receive any payments


I am trying to figure out the logic for a fee addition which renews everymonth. The idea is a charge candidates for only processing months, which means only when they get a contribution.

Currently, what I am doing is checking the month of current contribution and if that changes, I charge the Candidate the extra 7.99 fee. Shared below is a snippet from my candidate.rb

   def determine_fee(amount_cents)
    amount = ((amount_cents * ((merchant_rate.to_f * 100) + 2.9) / 100) + 25).to_i #normal fee 
    unless fee_paid_on&.month == Time.zone.now.month # comparing with current month
      amount = ((amount_cents * ((merchant_rate.to_f * 100) + 2.9) / 100) + 25 + 799).to_i #fee to be added for first contribution of every month
      self.update_attributes(fee_paid_on: Time.zone.now)
    end
    amount
  end 

However, the draw back with this approach is that Candidates are not charged as per 30 day time interval.For instance, the first contribution came at 28th of Nov, it would be charged the extra 7.99 and the rest of the contributions for Nov would be charged wihtout the 7.99 fee. And again, if they receive a contribution on 1st of dec, it would be charged the extra 7.99. Instead, i would want to the first contribution which comes after 28th of Dec to be charged the extra 7.99.

I am considering adding an extra month and comparing the fee, but cant establish the logic given the limited time. Any suggestion would be appreciated

Contribution_form.rb:

def process_stripe_payment
  applicable_fee = candidate.determine_fee(amount_cents)
  Stripe::Charge.create({
    amount: amount_cents,
    currency: candidate.candidate_country[candidate.country.to_s.to_sym][:currency],
    source: stripe_token,
    application_fee_amount: applicable_fee,
  # application_fee_amount: ((amount_cents * ((candidate.merchant_rate.to_f * 100) + 2.9) / 100) + 25).to_i,
   statement_descriptor_suffix: "#{get_statement_descriptor.to_s.upcase}",
    on_behalf_of: candidate.stripe_gateway_id,
    transfer_data: {
      destination: candidate.stripe_gateway_id,
    },
  }, stripe_version: '2019-12-03',)
end

Solution

  • Your code will be simpler to follow (now and in the future) if you break the calculation into its parts.

    MONTHLY_FEE = 799 # constant so it's easy to change in the future
    
    def determine_amount(contribution_cents)
      if fee_paid_this_month?
        processing_fee(contribution_cents) + MONTHLY_FEE
      else
        processing_fee(contribution_cents)
      end
    end
    
    def processing_fee(contribution_cents)
      # Note: use to_d instead of to_f to force the use of decimal over float
      ((amount_cents * ((merchant_rate.to_d * 100) + 2.9) / 100) + 25).to_i
    end
    
    def fee_paid_this_month?
      last_paid_on >= Date.today.beginning_of_month
    end
    

    This code intentionally skips the update of the last_paid_on date. That logic does not belong in the method that calculates the payment. For the sake of your business, put it after the successful call to Stripe in process_stripe_payment.