jam-cloud/ruby/lib/jam_ruby/models/charge.rb

151 lines
4.5 KiB
Ruby
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

module JamRuby
class Charge < ActiveRecord::Base
attr_accessor :stripe_charge
belongs_to :user, class_name: "JamRuby::User"
validates :sent_billing_notices, inclusion: {in: [true, false]}
def max_retries
raise "not implemented"
end
def do_charge(force)
raise "not implemented"
end
def do_send_notices
raise "not implemented"
end
def do_send_unable_charge
raise "not implemented"
end
def charge_retry_hours
24
end
def charged_user
raise "not implemented"
end
def record_charge(stripe_charge)
self.stripe_charge_id = stripe_charge.id
self.billed = true
self.billed_at = Time.now
self.save(validate: false)
end
def charge(force = false)
@stripe_charge = nil
if !self.billed
# check if we can bill at the moment
if !force && last_billing_attempt_at && (charge_retry_hours.hours.ago < last_billing_attempt_at)
return false
end
if !force && !billing_should_retry
return false
end
# bill the user right now. if it fails, move on; will be tried again
self.billing_attempts = self.billing_attempts + 1
self.billing_should_retry = self.billing_attempts < max_retries
self.last_billing_attempt_at = Time.now
self.save(validate: false)
begin
stripe_charge = do_charge(force)
# record the charge in this context (meaning, in our transaction)
record_charge(@stripe_charge) if @stripe_charge
rescue Stripe::StripeError => e
stripe_handler(e)
subject = "Unable to charge user #{charged_user.email} for lesson #{self.id} (stripe)"
body = "user=#{charged_user.email}\n\nbilling_error_reason=#{billing_error_reason}\n\nbilling_error_detail = #{billing_error_detail}"
AdminMailer.alerts({subject: subject, body: body}).deliver
do_send_unable_charge
return false
rescue Exception => e
# record the charge even if there was an unhandled exception at some point
record_charge(@stripe_charge) if @stripe_charge
unhandled_handler(e)
subject = "Unable to charge user #{charged_user.email} for lesson #{self.id} (unhandled)"
body = "user=#{charged_user.email}\n\nbilling_error_reason=#{billing_error_reason}\n\nbilling_error_detail = #{billing_error_detail}"
AdminMailer.alerts({subject: subject, body: body}).deliver
return false
end
end
if !self.sent_billing_notices
# If the charge is successful, then we post the charge to the students payment history,
# and associate the charge with the lesson, so that everyone knows the student has paid, and we send an email
do_send_notices
self.sent_billing_notices = true
self.sent_billing_notices_at = Time.now
self.post_processed = true
self.post_processed_at = Time.now
self.save(validate: false)
end
return stripe_charge
end
def unhandled_handler(e)
self.billing_error_reason = e.to_s
if e.cause
self.billing_error_detail = e.cause.to_s + "\n" + e.cause.backtrace.join("\n\t") if e.cause.backtrace
self.billing_error_detail << "\n\n"
self.billing_error_detail << e.to_s + "\n" + e.backtrace.join("\n\t") if e.backtrace
else
self.billing_error_detail = e.to_s + "\n" + e.backtrace.join("\n\t") if e.backtrace
end
#puts "Charge: unhandled exception #{billing_error_reason}, #{billing_error_detail}"
self.save(validate: false)
end
def is_card_declined?
billed == false && billing_error_reason == 'card_declined'
end
def is_card_expired?
billed == false && billing_error_reason == 'card_expired'
end
def last_billed_at_date
last_billing_attempt_at.strftime("%B %d, %Y") if last_billing_attempt_at
end
def stripe_handler(e)
msg = e.to_s
if msg.include?('declined')
self.billing_error_reason = 'card_declined'
self.billing_error_detail = msg
elsif msg.include?('expired')
self.billing_error_reason = 'card_expired'
self.billing_error_detail = msg
elsif msg.include?('processing')
self.billing_error_reason = 'processing_error'
self.billing_error_detail = msg
else
self.billing_error_reason = 'stripe'
self.billing_error_detail = msg
end
self.save(validate: false)
end
end
end