houdini/lib/pay_recurring_donation.rb
2020-06-15 10:26:57 -05:00

150 lines
6.6 KiB
Ruby

# frozen_string_literal: true
# License: AGPL-3.0-or-later WITH WTO-AP-3.0-or-later
# Full license explanation at https://github.com/houdiniproject/houdini/blob/master/LICENSE
require 'insert/insert_donation'
require 'insert/insert_supporter_notes'
require 'timespan'
module PayRecurringDonation
# Pay ALL recurring donations that are currently due; each payment gets a queued delayed_job
# Returns the number of queued jobs
def self.pay_all_due_with_stripe
# Bulk insert the delayed jobs with a single expression
ids = Psql.execute_vectors(
QueryRecurringDonations._all_that_are_due
)[1..-1].flatten
PayRecurringDonationsJob.perform_later(*id)
ids
end
# run the payrecurring_donation in development so I can make sure we have the expected failures
# def self._____test_do_not_use_pay_all_due_with_stripe
# # Bulk insert the delayed jobs with a single expression
# ids = Psql.execute_vectors(
# QueryRecurringDonations._all_that_are_due
# )[1..-1].flatten
#
# output = ids.map{|id|
# begin
# i = PayRecurringDonation.with_stripe(id)
# result = {is_error:false, value: i}
# rescue => e
# result = {is_error: true, error_type: e.class.to_s, message: e.message, backtrace: e.backtrace}
# end
#
# result
# }
#
#
#
# return output
# end
# Charge an existing donation via stripe, only if it is due
# Pass in an instance of an existing RecurringDonation
def self.with_stripe(rd_id)
ParamValidation.new({ rd_id: rd_id },
rd_id: {
required: true,
is_integer: true
})
rd = RecurringDonation.where('id = ?', rd_id).first
unless rd
raise ParamValidation::ValidationError.new("#{rd_id} is not a valid recurring donation", key: :rd_id)
end
return false unless QueryRecurringDonations.is_due?(rd_id)
donation = Donation.where('id = ?', rd['donation_id']).first
unless donation
raise ParamValidation::ValidationError.new("#{rd['donation_id']} is not a valid donation", {})
end
result = {}
result = result.merge(InsertDonation.insert_charge(
'card_id' => donation['card_id'],
'recurring_donation' => true,
'designation' => donation['designation'],
'amount' => donation['amount'],
'nonprofit_id' => donation['nonprofit_id'],
'donation_id' => donation['id'],
'supporter_id' => donation['supporter_id'],
'old_donation' => true
))
if result['charge']['status'] != 'failed'
rd.update(n_failures: 0)
result['recurring_donation'] = rd
Houdini.event_publisher.announce(:recurring_donation_payment_succeeded, donation, donation&.supporter&.locale || 'en')
InsertActivities.for_recurring_donations([result['payment']['id']])
else
rd.n_failures += 1
rd.save!
result['recurring_donation'] = rd
Houdini.event_publisher.announce(:recurring_donation_payment_failed, donation)
InsertSupporterNotes.create([{ content: "This supporter had a payment failure for their recurring donation with ID #{rd_id}", supporter_id: donation['supporter_id'], user_id: 540 }])
end
result
end
def self.fail_a_recurring_donation(rd, donation, notify_nonprofit = false)
recurring_donation = Psql.execute(
Qexpr.new.update(:recurring_donations, n_failures: 3)
.where('id=$id', id: rd['id']).returning('*')
).first
FailedRecurringDonationPaymentDonorEmailJob.perform_later Donation.find(rd['donation_id'])
if notify_nonprofit
FailedRecurringDonationPaymentNonprofitEmailJob.perform_later Donation.find(rd['donation_id'])
end
InsertSupporterNotes.create([{ content: "This supporter had a payment failure for their recurring donation with ID #{rd['id']}", supporter_id: donation['supporter_id'], user_id: 540 }])
recurring_donation
end
# Charge an existing donation via stripe, NO MATTER WHAT
# Pass in an instance of an existing RecurringDonation
def self.with_stripe_BUT_NO_MATTER_WHAT(rd_id, enter_todays_date, run_this = false, set_this_true = false, this_one_needs_to_be_false = true, is_this_run_dangerously = 'no')
if PayRecurringDonation::ULTIMATE_VERIFICATION(enter_todays_date, run_this, set_this_true, this_one_needs_to_be_false, is_this_run_dangerously)
rd = Psql.execute("SELECT * FROM recurring_donations WHERE id=#{rd_id}").first
donation = Psql.execute("SELECT * FROM donations WHERE id=#{rd['donation_id']}").first
result = {}
result = result.merge(InsertDonation.insert_charge(
'card_id' => donation['card_id'],
'recurring_donation' => true,
'designation' => donation['designation'],
'amount' => donation['amount'],
'nonprofit_id' => donation['nonprofit_id'],
'donation_id' => donation['id'],
'supporter_id' => donation['supporter_id']
))
if result['charge']['status'] != 'failed'
result['recurring_donation'] = Psql.execute(
Qexpr.new.update(:recurring_donations, n_failures: 0)
.where('id=$id', id: rd_id).returning('*')
).first
## add PaymentNotificationJobHere
InsertActivities.for_recurring_donations([result['payment']['id']])
else
result['recurring_donation'] = Psql.execute(
Qexpr.new.update(:recurring_donations, n_failures: rd['n_failures'] + 1)
.where('id=$id', id: rd_id).returning('*')
).first
FailedRecurringDonationPaymentDonorEmailJob.perform_later Donation.find(rd['donation_id'])
if rd['n_failures'] >= 3
FailedRecurringDonationPaymentNonprofitEmailJob.perform_later Donation.find(rd['donation_id'])
end
InsertSupporterNotes.create([{ content: "This supporter had a payment failure for their recurring donation with ID #{rd_id}", supporter_id: donation['supporter_id'], user_id: 540 }])
end
return result
end
false
end
def self.ULTIMATE_VERIFICATION(enter_todays_date, run_this = false, set_this_true = false, this_one_needs_to_be_false = true, is_this_run_dangerously = 'no')
(Date.parse(enter_todays_date) == Date.today && run_this && set_this_true && !this_one_needs_to_be_false && is_this_run_dangerously == 'run dangerously')
end
end