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
|
|
|
module InsertRecurringDonation
|
|
|
|
# Create a recurring_donation, donation, payment, charge, and activity
|
|
|
|
# See controllers/nonprofits/recurring_donations_controller#create for the data params to pass in
|
|
|
|
def self.with_stripe(data)
|
|
|
|
data = data.with_indifferent_access
|
|
|
|
|
|
|
|
ParamValidation.new(data, InsertDonation.common_param_validations
|
2019-07-30 21:29:24 +00:00
|
|
|
.merge(token: { required: true, format: UUID::Regex }))
|
2018-03-25 17:30:42 +00:00
|
|
|
|
2019-07-30 21:29:24 +00:00
|
|
|
if data[:recurring_donation].nil?
|
|
|
|
data[:recurring_donation] = {}
|
|
|
|
else
|
2018-04-12 19:22:38 +00:00
|
|
|
|
2019-07-30 21:29:24 +00:00
|
|
|
ParamValidation.new(data[:recurring_donation],
|
|
|
|
interval: { is_integer: true },
|
|
|
|
start_date: { can_be_date: true },
|
|
|
|
time_unit: { included_in: %w[month day week year] },
|
|
|
|
paydate: { is_integer: true })
|
|
|
|
if data[:recurring_donation][:paydate]
|
2018-04-12 19:22:38 +00:00
|
|
|
data[:recurring_donation][:paydate] = data[:recurring_donation][:paydate].to_i
|
|
|
|
end
|
|
|
|
|
2019-07-30 21:29:24 +00:00
|
|
|
ParamValidation.new(data[:recurring_donation],
|
|
|
|
paydate: { min: 1, max: 28 })
|
2018-03-25 17:30:42 +00:00
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
source_token = QuerySourceToken.get_and_increment_source_token(data[:token], nil)
|
|
|
|
tokenizable = source_token.tokenizable
|
|
|
|
QuerySourceToken.validate_source_token_type(source_token)
|
|
|
|
|
2019-07-30 21:29:24 +00:00
|
|
|
entities = RetrieveActiveRecordItems.retrieve_from_keys(data, Supporter => :supporter_id, Nonprofit => :nonprofit_id)
|
2018-03-25 17:30:42 +00:00
|
|
|
|
2019-07-30 21:29:24 +00:00
|
|
|
entities = entities.merge(RetrieveActiveRecordItems.retrieve_from_keys(data, { Campaign => :campaign_id, Event => :event_id, Profile => :profile_id }, true))
|
2018-03-25 17:30:42 +00:00
|
|
|
|
|
|
|
InsertDonation.validate_entities(entities)
|
|
|
|
|
|
|
|
## does the card belong to the supporter?
|
|
|
|
if tokenizable.holder != entities[:supporter_id]
|
|
|
|
raise ParamValidation::ValidationError.new("Supporter #{entities[:supporter_id].id} does not own card #{tokenizable.id}", key: :token)
|
|
|
|
end
|
|
|
|
|
|
|
|
data['card_id'] = tokenizable.id
|
|
|
|
|
|
|
|
result = {}
|
|
|
|
data[:date] = Time.now
|
|
|
|
data = data.merge(payment_provider: payment_provider(data))
|
|
|
|
data = data.except(:old_donation).except('old_donation')
|
|
|
|
# if start date is today, make initial charge first
|
|
|
|
test_start_date = get_test_start_date(data)
|
2019-07-30 21:29:24 +00:00
|
|
|
if test_start_date.nil? || Time.current >= test_start_date
|
2018-03-25 17:30:42 +00:00
|
|
|
result = result.merge(InsertDonation.insert_charge(data))
|
|
|
|
if result['charge']['status'] == 'failed'
|
2019-07-30 21:29:24 +00:00
|
|
|
raise ChargeError, result['charge']['failure_message']
|
2018-03-25 17:30:42 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2021-05-12 18:42:46 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2018-03-25 17:30:42 +00:00
|
|
|
# Create the donation record
|
|
|
|
result['donation'] = InsertDonation.insert_donation(data, entities)
|
|
|
|
entities[:donation_id] = result['donation']
|
|
|
|
# Create the recurring_donation record
|
2019-07-30 21:29:24 +00:00
|
|
|
result['recurring_donation'] = insert_recurring_donation(data, entities)
|
2018-03-25 17:30:42 +00:00
|
|
|
# Update charge foreign keys
|
|
|
|
if result['payment']
|
|
|
|
InsertDonation.update_donation_keys(result)
|
2021-05-12 18:42:46 +00:00
|
|
|
|
|
|
|
trx = entities[:supporter_id].transactions.build(amount: data['amount'], created: data['date'])
|
|
|
|
don = trx.donations.build(amount: result['donation'].amount, legacy_donation: result['donation'])
|
|
|
|
stripe_t = trx.build_subtransaction(
|
|
|
|
subtransactable: StripeTransaction.new(amount: data['amount']),
|
|
|
|
subtransaction_payments:[
|
|
|
|
SubtransactionPayment.new(
|
|
|
|
paymentable: StripeCharge.new(payment: Payment.find(result['payment']['id'])))
|
|
|
|
],
|
|
|
|
created: data['date']
|
|
|
|
);
|
|
|
|
trx.save!
|
|
|
|
don.save!
|
|
|
|
stripe_t.save!
|
|
|
|
stripe_t.subtransaction_payments.each{|stp| stp.publish_created}
|
|
|
|
stripe_t.publish_created
|
|
|
|
don.publish_created
|
|
|
|
trx.publish_created
|
|
|
|
|
2018-03-25 17:30:42 +00:00
|
|
|
# Create the activity record
|
|
|
|
result['activity'] = InsertActivities.for_recurring_donations([result['payment'].id])
|
|
|
|
end
|
2021-06-07 17:34:08 +00:00
|
|
|
|
|
|
|
recurrence = result['recurring_donation'].create_recurrence!(supporter: result['recurring_donation'].supporter, start_date:result['recurring_donation'].start_date, amount: result['recurring_donation'].amount )
|
|
|
|
recurrence.publish_created
|
2018-03-25 17:30:42 +00:00
|
|
|
# Send receipts
|
2020-06-12 18:03:59 +00:00
|
|
|
Houdini.event_publisher.announce(:recurring_donation_create, result['donation'], entities[:supporter_id].locale)
|
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.with_sepa(data)
|
2018-03-25 17:30:42 +00:00
|
|
|
data = set_defaults(data)
|
|
|
|
data = data.merge(payment_provider: payment_provider(data))
|
|
|
|
result = {}
|
|
|
|
|
|
|
|
if Time.current >= data[:recurring_donation][:start_date]
|
|
|
|
result = result.merge(InsertDonation.insert_charge(data))
|
|
|
|
end
|
|
|
|
|
|
|
|
result['donation'] = Psql.execute(Qexpr.new.insert(:donations, [
|
2019-07-30 21:29:24 +00:00
|
|
|
data.except(:recurring_donation)
|
|
|
|
]).returning('*')).first
|
2018-03-25 17:30:42 +00:00
|
|
|
|
|
|
|
result['recurring_donation'] = Psql.execute(Qexpr.new.insert(:recurring_donations, [
|
2019-07-30 21:29:24 +00:00
|
|
|
data[:recurring_donation].merge(donation_id: result['donation']['id'])
|
|
|
|
]).returning('*')).first
|
2018-03-25 17:30:42 +00:00
|
|
|
|
2019-07-30 21:29:24 +00:00
|
|
|
InsertDonation.update_donation_keys(result) if result['payment']
|
2018-03-25 17:30:42 +00:00
|
|
|
|
2020-06-12 18:03:59 +00:00
|
|
|
Houdini.event_publisher.announce(:recurring_donation_create, result['donation'], entities[:supporter_id].locale)
|
2018-03-25 17:30:42 +00:00
|
|
|
|
|
|
|
{ status: 200, json: result }
|
2019-07-30 21:29:24 +00:00
|
|
|
end
|
2018-03-25 17:30:42 +00:00
|
|
|
|
2019-07-30 21:29:24 +00:00
|
|
|
# the data model here is brutal. This needs to get cleaned up.
|
2018-03-25 17:30:42 +00:00
|
|
|
def self.convert_donation_to_recurring_donation(donation_id)
|
2019-07-30 21:29:24 +00:00
|
|
|
ParamValidation.new({ donation_id: donation_id }, donation_id: { required: true, is_integer: true })
|
2018-03-25 17:30:42 +00:00
|
|
|
don = Donation.where('id = ? ', donation_id).first
|
2019-07-30 21:29:24 +00:00
|
|
|
unless don
|
|
|
|
raise ParamValidation::ValidationError.new("#{donation_id} is not a valid donation", key: :donation_id, val: donation_id)
|
2018-03-25 17:30:42 +00:00
|
|
|
end
|
2019-07-30 21:29:24 +00:00
|
|
|
|
|
|
|
rd = insert_recurring_donation({ amount: don.amount, email: don.supporter.email, anonymous: don.anonymous, origin_url: don.origin_url, recurring_donation: { start_date: don.created_at, paydate: convert_date_to_valid_paydate(don.created_at) }, date: don.created_at }, supporter_id: don.supporter, nonprofit_id: don.nonprofit, donation_id: don)
|
2018-03-25 17:30:42 +00:00
|
|
|
don.recurring_donation = rd
|
|
|
|
don.recurring = true
|
|
|
|
|
2019-07-30 21:29:24 +00:00
|
|
|
don.payment.kind = 'RecurringDonation'
|
2018-03-25 17:30:42 +00:00
|
|
|
don.payment.save!
|
|
|
|
rd.save!
|
|
|
|
don.save!
|
|
|
|
|
|
|
|
rd
|
|
|
|
end
|
|
|
|
|
2019-07-30 21:29:24 +00:00
|
|
|
def self.insert_recurring_donation(data, entities)
|
2018-03-25 17:30:42 +00:00
|
|
|
rd = RecurringDonation.new
|
|
|
|
rd.amount = data[:amount]
|
|
|
|
rd.nonprofit = entities[:nonprofit_id]
|
|
|
|
rd.donation = entities[:donation_id]
|
|
|
|
rd.supporter_id = entities[:supporter_id].id
|
|
|
|
rd.active = true
|
|
|
|
rd.edit_token = SecureRandom.uuid
|
2019-07-30 21:29:24 +00:00
|
|
|
rd.n_failures = 0
|
|
|
|
rd.email = entities[:supporter_id].email
|
|
|
|
rd.interval = data[:recurring_donation][:interval].blank? ? 1 : data[:recurring_donation][:interval]
|
|
|
|
rd.time_unit = data[:recurring_donation][:time_unit].blank? ? 'month' : data[:recurring_donation][:time_unit]
|
|
|
|
rd.start_date = if data[:recurring_donation][:start_date].blank?
|
|
|
|
Time.current.beginning_of_day
|
|
|
|
elsif data[:recurring_donation][:start_date].is_a? Time
|
|
|
|
data[:recurring_donation][:start_date]
|
|
|
|
else
|
|
|
|
Chronic.parse(data[:recurring_donation][:start_date])
|
|
|
|
end
|
2018-03-25 17:30:42 +00:00
|
|
|
|
|
|
|
if rd.time_unit == 'month' && rd.interval == 1 && data[:recurring_donation][:paydate].nil?
|
|
|
|
rd.paydate = convert_date_to_valid_paydate(rd.start_date)
|
|
|
|
else
|
|
|
|
rd.paydate = data[:recurring_donation][:paydate]
|
|
|
|
end
|
|
|
|
|
|
|
|
rd.save!
|
|
|
|
rd
|
2019-07-30 21:29:24 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.get_test_start_date(data)
|
2018-03-25 17:30:42 +00:00
|
|
|
unless data[:recurring_donation] && data[:recurring_donation][:start_date]
|
|
|
|
return nil
|
|
|
|
end
|
|
|
|
|
2019-07-30 21:29:24 +00:00
|
|
|
Chronic.parse(data[:recurring_donation][:start_date])
|
|
|
|
end
|
2018-03-25 17:30:42 +00:00
|
|
|
|
|
|
|
def self.locale_for_supporter(supporter_id)
|
|
|
|
Psql.execute(
|
|
|
|
Qexpr.new.select(:locale).from(:supporters)
|
2019-07-30 21:29:24 +00:00
|
|
|
.where('id=$id', id: supporter_id)
|
2018-03-25 17:30:42 +00:00
|
|
|
).first['locale']
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.payment_provider(data)
|
|
|
|
if data[:card_id]
|
|
|
|
:credit_card
|
|
|
|
elsif data[:direct_debit_detail_id]
|
|
|
|
:sepa
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.convert_date_to_valid_paydate(date)
|
|
|
|
day = date.day
|
2019-07-30 21:29:24 +00:00
|
|
|
day > 28 ? 28 : day
|
2018-03-25 17:30:42 +00:00
|
|
|
end
|
|
|
|
end
|