2018-03-25 16:15:39 +00:00
# License: AGPL-3.0-or-later WITH Web-Template-Output-Additional-Permission-3.0-or-later
2018-03-25 17:30:42 +00:00
require 'psql'
require 'qexpr'
require 'calculate/calculate_fees'
require 'stripe'
require 'get_data'
require 'active_support/core_ext'
require 'query/billing_plans'
require 'stripe_account' unless ! Settings . payment_provider . stripe_connect
module InsertCharge
# In data, pass in: amount, nonprofit_id, supporter_id, card_id, statement
# Optionally pass in :metadata for stripe and donation_id to connect to donation?
# @raise [ParamValidation::ValidationError] parameter validation occurred
# @raise [Stripe::StripeError] the stripe account couldn't be accessed or created
def self . with_stripe ( data )
begin
ParamValidation . new ( data || { } , {
:amount = > {
:required = > true ,
:is_integer = > true ,
:min = > 0
} ,
:nonprofit_id = > {
:required = > true ,
:is_integer = > true
} ,
:supporter_id = > {
:required = > true ,
:is_integer = > true
} ,
:card_id = > {
:required = > true ,
:is_integer = > true
} ,
:statement = > {
:required = > true ,
:not_blank = > true
}
} )
np = Nonprofit . where ( 'id = ?' , data [ :nonprofit_id ] ) . first
unless np
raise ParamValidation :: ValidationError . new ( " #{ data [ :nonprofit_id ] } is not a valid Nonprofit " , { :key = > :nonprofit_id } )
end
supporter = Supporter . where ( 'id = ?' , data [ :supporter_id ] ) . first
unless supporter
raise ParamValidation :: ValidationError . new ( " #{ data [ :supporter_id ] } is not a valid Supporter " , { :key = > :supporter_id } )
end
card = Card . where ( 'id = ?' , data [ :card_id ] ) . first
unless card
raise ParamValidation :: ValidationError . new ( " #{ data [ :card_id ] } is not a valid card " , { :key = > :card_id } )
end
unless np == supporter . nonprofit
raise ParamValidation :: ValidationError . new ( " #{ data [ :supporter_id ] } does not belong to this nonprofit #{ np . id } " , { :key = > :supporter_id } )
end
unless card . holder == supporter
if ( data [ :old_donation ] )
#these are not new donations so we let them fly (for now)
Airbrake . notify ( ParamValidation :: ValidationError . new ( " #{ data [ :card_id ] } does not belong to this supporter #{ supporter . id } as warning " , { :key = > :card_id } ) )
else
raise ParamValidation :: ValidationError . new ( " #{ data [ :card_id ] } does not belong to this supporter #{ supporter . id } " , { :key = > :card_id } )
end
end
result = { }
# Catch errors thrown by the stripe gem so we can respond with a 422 with an error message rather than 500
begin
stripe_customer_id = card . stripe_customer_id
stripe_account_id = StripeAccount . find_or_create ( data [ :nonprofit_id ] )
rescue = > e
Airbrake . notify ( e , other_data : data )
raise e
end
nonprofit_currency = Qx . select ( :currency ) . from ( :nonprofits ) . where ( " id=$id " , id : data [ :nonprofit_id ] ) . execute . first [ 'currency' ]
stripe_charge_data = {
customer : stripe_customer_id ,
amount : data [ :amount ] ,
currency : nonprofit_currency ,
description : data [ :statement ] ,
statement_descriptor : data [ :statement ] [ 0 .. 21 ] . gsub ( / [<>"'] / , '' ) ,
metadata : data [ :metadata ]
}
if Settings . payment_provider . stripe_connect
stripe_account_id = StripeAccount . find_or_create ( data [ :nonprofit_id ] )
# Get the percentage fee on the nonprofit's billing plan
platform_fee = BillingPlans . get_percentage_fee ( data [ :nonprofit_id ] )
fee = CalculateFees . for_single_amount ( data [ :amount ] , platform_fee )
stripe_charge_data [ :application_fee ] = fee
# For backwards compatibility, see if the customer exists in the primary or the connected account
# If it's a legacy customer, charge to the primary account and transfer with .destination
# Otherwise, charge directly to the connected account
begin
stripe_cust = Stripe :: Customer . retrieve ( stripe_customer_id )
params = [ stripe_charge_data . merge ( destination : stripe_account_id ) , { } ]
rescue
params = [ stripe_charge_data , { stripe_account : stripe_account_id } ]
end
else
fee = 0
stripe_charge_data [ :source ] = card [ 'stripe_card_id' ]
params = [ stripe_charge_data , { } ]
end
begin
stripe_charge = Stripe :: Charge . create ( * params )
rescue Stripe :: CardError = > e
failure_message = " There was an error with your card: #{ e . json_body [ :error ] [ :message ] } "
Airbrake . notify ( e )
rescue Stripe :: StripeError = > e
failure_message = " We're sorry, but something went wrong. We've been notified about this issue. "
Airbrake . notify ( e )
end
charge = Charge . new
charge . amount = data [ :amount ]
charge . fee = fee
charge . stripe_charge_id = GetData . chain ( stripe_charge , :id )
charge . failure_message = failure_message
charge . status = GetData . chain ( stripe_charge , :paid ) ? 'pending' : 'failed'
charge . card = card
charge . donation = Donation . where ( 'id = ?' , data [ :donation_id ] ) . first
charge . supporter = supporter
charge . nonprofit = np
charge . save!
result [ 'charge' ] = charge
if stripe_charge && stripe_charge . status != 'failed'
payment = Payment . new
payment . gross_amount = data [ :amount ]
payment . fee_total = - fee
payment . net_amount = data [ :amount ] - fee
payment . towards = data [ :towards ]
payment . kind = data [ :kind ]
payment . donation = Donation . where ( 'id = ?' , data [ :donation_id ] ) . first
payment . nonprofit = np
payment . supporter = supporter
payment . refund_total = 0
payment . date = data [ :date ] || result [ 'charge' ] . created_at
payment . save!
result [ 'payment' ] = payment
charge . payment = payment
charge . save!
result [ 'charge' ] = charge
end
return result
rescue = > e
Airbrake . notify ( e )
raise e
end
end
def self . with_sepa ( data )
result = { }
entities = RetrieveActiveRecordItems . retrieve_from_keys ( data , DirectDebitDetail = > :direct_debit_detail_id , Supporter = > :supporter_id , Nonprofit = > :nonprofit_id )
nonprofit_currency = entities [ :nonprofit_id ] . currency
# TODO
fee = 0
#todo charge should be changed to SEPA charge
c = Charge . new
c . direct_debit_detail = entities [ :direct_debit_detail_id ]
c . amount = data [ :amount ]
c . fee = fee
c . status = 'pending'
c . nonprofit = entities [ :nonprofit_id ]
c . supporter = entities [ :supporter_id ]
c . save!
result [ 'charge' ] = c
p = Payment . new
p . gross_amount = data [ :amount ]
p . fee_total = - fee
p . net_amount = data [ :amount ] - fee
p . towards = data [ :towards ]
p . kind = data [ :kind ]
p . nonprofit = entities [ :nonprofit_id ]
p . supporter = entities [ :supporter_id ]
p . refund_total = 0
p . date = data [ :date ] || result [ 'charge' ] . created_at
p . save!
result [ 'payment' ] = p
c . payment = p
c . save!
p . save!
result
end
end