2019-07-30 21:29:24 +00:00
# frozen_string_literal: true
2020-06-12 20:03:43 +00:00
# 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
2018-03-25 17:30:42 +00:00
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
2019-07-30 21:29:24 +00:00
2019-11-07 23:06:45 +00:00
PayRecurringDonationsJob . perform_later ( * id )
2019-07-30 21:29:24 +00:00
ids
2018-03-25 17:30:42 +00:00
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 )
2019-07-30 21:29:24 +00:00
ParamValidation . new ( { rd_id : rd_id } ,
rd_id : {
required : true ,
is_integer : true
} )
2018-03-25 17:30:42 +00:00
rd = RecurringDonation . where ( 'id = ?' , rd_id ) . first
unless rd
2019-07-30 21:29:24 +00:00
raise ParamValidation :: ValidationError . new ( " #{ rd_id } is not a valid recurring donation " , key : :rd_id )
2018-03-25 17:30:42 +00:00
end
2019-07-30 21:29:24 +00:00
return false unless QueryRecurringDonations . is_due? ( rd_id )
2018-03-25 17:30:42 +00:00
donation = Donation . where ( 'id = ?' , rd [ 'donation_id' ] ) . first
unless donation
raise ParamValidation :: ValidationError . new ( " #{ rd [ 'donation_id' ] } is not a valid donation " , { } )
end
2021-02-08 20:55:31 +00:00
trx = nonprofit . transactions . build ( amount : donation [ 'amount' ] )
2018-03-25 17:30:42 +00:00
result = { }
2019-07-30 21:29:24 +00:00
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
) )
2018-03-25 17:30:42 +00:00
if result [ 'charge' ] [ 'status' ] != 'failed'
2020-05-20 21:03:16 +00:00
rd . update ( n_failures : 0 )
2020-02-10 20:01:49 +00:00
result [ 'recurring_donation' ] = rd
2021-02-08 20:55:31 +00:00
2020-06-12 18:03:59 +00:00
Houdini . event_publisher . announce ( :recurring_donation_payment_succeeded , donation , donation & . supporter & . locale || 'en' )
2021-02-08 20:55:31 +00:00
donation = trx . donations . build ( amount : donation [ 'amount' ] , designation : donation [ 'designation' ] , dedication : donation [ " dedication " ] )
trx . save!
donation . save!
donation . publish_created
2018-03-25 17:30:42 +00:00
InsertActivities . for_recurring_donations ( [ result [ 'payment' ] [ 'id' ] ] )
else
2020-02-10 20:01:49 +00:00
rd . n_failures += 1
rd . save!
result [ 'recurring_donation' ] = rd
2020-06-12 18:03:59 +00:00
Houdini . event_publisher . announce ( :recurring_donation_payment_failed , donation )
2021-02-15 23:22:19 +00:00
InsertSupporterNotes . create ( { supporter : Supporter . find ( donation [ 'supporter_id' ] ) , user : User . find ( 540 ) , content : " This supporter had a payment failure for their recurring donation with ID #{ rd_id } " } )
2018-03-25 17:30:42 +00:00
end
2019-07-30 21:29:24 +00:00
result
2018-03-25 17:30:42 +00:00
end
2019-07-30 21:29:24 +00:00
def self . fail_a_recurring_donation ( rd , donation , notify_nonprofit = false )
2018-03-25 17:30:42 +00:00
recurring_donation = Psql . execute (
2019-07-30 21:29:24 +00:00
Qexpr . new . update ( :recurring_donations , n_failures : 3 )
. where ( 'id=$id' , id : rd [ 'id' ] ) . returning ( '*' )
2018-03-25 17:30:42 +00:00
) . first
2019-11-07 22:52:54 +00:00
FailedRecurringDonationPaymentDonorEmailJob . perform_later Donation . find ( rd [ 'donation_id' ] )
2018-03-25 17:30:42 +00:00
if notify_nonprofit
2019-11-07 22:52:54 +00:00
FailedRecurringDonationPaymentNonprofitEmailJob . perform_later Donation . find ( rd [ 'donation_id' ] )
2018-03-25 17:30:42 +00:00
end
2019-07-30 21:29:24 +00:00
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
2018-03-25 17:30:42 +00:00
end
# Charge an existing donation via stripe, NO MATTER WHAT
# Pass in an instance of an existing RecurringDonation
2019-07-30 21:29:24 +00:00
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 )
2018-03-25 17:30:42 +00:00
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 = { }
2019-07-30 21:29:24 +00:00
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' ]
) )
2018-03-25 17:30:42 +00:00
if result [ 'charge' ] [ 'status' ] != 'failed'
result [ 'recurring_donation' ] = Psql . execute (
2019-07-30 21:29:24 +00:00
Qexpr . new . update ( :recurring_donations , n_failures : 0 )
. where ( 'id=$id' , id : rd_id ) . returning ( '*' )
2018-03-25 17:30:42 +00:00
) . first
2019-11-07 18:35:38 +00:00
## add PaymentNotificationJobHere
2018-03-25 17:30:42 +00:00
InsertActivities . for_recurring_donations ( [ result [ 'payment' ] [ 'id' ] ] )
else
result [ 'recurring_donation' ] = Psql . execute (
2019-07-30 21:29:24 +00:00
Qexpr . new . update ( :recurring_donations , n_failures : rd [ 'n_failures' ] + 1 )
. where ( 'id=$id' , id : rd_id ) . returning ( '*' )
2018-03-25 17:30:42 +00:00
) . first
2019-11-07 22:52:54 +00:00
FailedRecurringDonationPaymentDonorEmailJob . perform_later Donation . find ( rd [ 'donation_id' ] )
2018-03-25 17:30:42 +00:00
if rd [ 'n_failures' ] > = 3
2019-11-07 22:52:54 +00:00
FailedRecurringDonationPaymentNonprofitEmailJob . perform_later Donation . find ( rd [ 'donation_id' ] )
2018-03-25 17:30:42 +00:00
end
2019-07-30 21:29:24 +00:00
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 } ] )
2018-03-25 17:30:42 +00:00
end
return result
end
2019-07-30 21:29:24 +00:00
false
2018-03-25 17:30:42 +00:00
end
2019-07-30 21:29:24 +00:00
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' )
2018-03-25 17:30:42 +00:00
end
end