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 'hash'
module InsertCard
# Create a new card
# If a stripe_customer_id is present, then update that customer's primary source; otherwise create a new customer
# @param [ActiveSupport::HashWithIndifferentAccess] card_data card data
# @option card_data [Integer] holder_id the primary key of the card's holder
# @option card_data [String] holder_type the type of the card holder. Must be 'Nonprofit' or 'Supporter'
# @option card_data [String] stripe_card_token the card token from stripe
# @option card_data [String] stripe_card_id the card id from stripe
# @option card_data [String] name the card name
# @option card_data [String] cardholder_name (nil) the name of the cardholder
# @option card_data [String] stripe_customer_id (nil) the stripe customer id as provided by stripe
# @param [String] stripe_account_id not clear what this should do.
# @param [Integer] event_id id for events with when you want it associated with an event
2019-07-30 21:29:24 +00:00
# @param [User] current_user the user making the request. Used for validating that the current_user can make a long term token request
def self . with_stripe ( card_data , _stripe_account_id = nil , event_id = nil , current_user = nil )
2018-03-25 17:30:42 +00:00
begin
2019-07-30 21:29:24 +00:00
ParamValidation . new ( card_data . merge ( event_id : event_id ) ,
holder_type : { required : true , included_in : %w[ Nonprofit Supporter ] } ,
holder_id : { required : true } ,
stripe_card_id : { not_blank : true , required : true } ,
stripe_card_token : { not_blank : true , required : true } ,
name : { not_blank : true , required : true } ,
event_id : { is_reference : true } )
2018-03-25 17:30:42 +00:00
rescue ParamValidation :: ValidationError = > e
2019-07-30 21:29:24 +00:00
return { json : { error : " Validation error \n #{ e . message } " , errors : e . data } , status : :unprocessable_entity }
2018-03-25 17:30:42 +00:00
end
# validate that the user is with the correct nonprofit
2019-07-30 21:29:24 +00:00
card_data = card_data . keep_keys ( :holder_type , :holder_id , :stripe_card_id , :stripe_card_token , :name )
holder_types = { 'Nonprofit' = > :nonprofit , 'Supporter' = > :supporter }
2018-03-25 17:30:42 +00:00
holder_type = holder_types [ card_data [ :holder_type ] ]
holder = nil
begin
if holder_type == :nonprofit
2019-07-30 21:29:24 +00:00
holder = Nonprofit . select ( 'id, email' ) . includes ( :cards ) . find ( card_data [ :holder_id ] )
2018-03-25 17:30:42 +00:00
elsif holder_type == :supporter
2019-07-30 21:29:24 +00:00
holder = Supporter . select ( 'id, email, nonprofit_id' ) . includes ( :cards , :nonprofit ) . find ( card_data [ :holder_id ] )
2018-03-25 17:30:42 +00:00
end
rescue ActiveRecord :: RecordNotFound
2019-07-30 21:29:24 +00:00
return { json : { error : 'Sorry, you need to provide a nonprofit or supporter' } , status : :unprocessable_entity }
2018-03-25 17:30:42 +00:00
end
begin
if holder_type == :supporter && event_id
event = Event . where ( 'id = ?' , event_id ) . first
unless event
2019-07-30 21:29:24 +00:00
raise ParamValidation :: ValidationError . new ( " #{ event_id } is not a valid event " , key : :event_id )
2018-03-25 17:30:42 +00:00
end
2019-07-30 21:29:24 +00:00
if holder . nonprofit != event . nonprofit
raise ParamValidation :: ValidationError . new ( " Event #{ event_id } is not for the same nonprofit as supporter #{ holder . id } " , key : :event_id )
2018-03-25 17:30:42 +00:00
end
unless QueryRoles . is_authorized_for_nonprofit? ( current_user . id , holder . nonprofit . id )
2019-07-30 21:29:24 +00:00
raise AuthenticationError
2018-03-25 17:30:42 +00:00
end
end
rescue AuthenticationError
2019-07-30 21:29:24 +00:00
return { json : { error : " You're not authorized to perform that action " } , status : :unauthorized }
rescue StandardError = > e
return { json : { error : " Oops! There was an error: #{ e . message } " } , status : :unprocessable_entity }
2018-03-25 17:30:42 +00:00
end
stripe_account_hash = { } # stripe_account_id ? {stripe_account: stripe_account_id} : {}
begin
if card_data [ :stripe_customer_id ]
stripe_customer = Stripe :: Customer . retrieve ( card_data [ :stripe_customer_id ] , stripe_account_hash )
else
stripe_customer = Stripe :: Customer . create ( customer_data ( holder , card_data ) , stripe_account_hash )
end
stripe_customer . sources . create ( source : card_data [ :stripe_card_token ] )
card_data [ :stripe_customer_id ] = stripe_customer . id
rescue Stripe :: CardError = > e
2019-07-30 21:29:24 +00:00
return { json : { error : " Oops! #{ e . json_body [ :error ] [ :message ] } " } , status : :unprocessable_entity }
2018-03-25 17:30:42 +00:00
rescue Stripe :: StripeError = > e
2019-07-30 21:29:24 +00:00
return { json : { error : " Oops! There was an error processing your payment, and it did not complete. Please try again in a moment. Error: #{ e } " } , status : :unprocessable_entity }
2018-03-25 17:30:42 +00:00
end
card = nil
source_token = nil
begin
2019-07-30 21:29:24 +00:00
Card . transaction do
if holder_type == :nonprofit
2018-03-25 17:30:42 +00:00
# @type [Nonprofit] holder
card = holder . create_active_card ( card_data )
2019-07-30 21:29:24 +00:00
elsif holder_type == :supporter
2018-03-25 17:30:42 +00:00
# @type [Supporter] holder
card = holder . cards . create ( card_data )
params = { }
2019-07-30 21:29:24 +00:00
params [ :event ] = event if event
2018-03-25 17:30:42 +00:00
source_token = InsertSourceToken . create_record ( card , params ) . token
end
card . save!
2019-07-30 21:29:24 +00:00
end
2018-03-25 17:30:42 +00:00
rescue ActiveRecord :: ActiveRecordError = > e
2019-07-30 21:29:24 +00:00
return { json : { error : " Oops! There was an error saving your card, and it did not complete. Please try again in a moment. Error: #{ e } " } , status : :unprocessable_entity }
2018-03-25 17:30:42 +00:00
rescue e
2019-07-30 21:29:24 +00:00
return { json : { error : " Oops! There was an error saving your card, and it did not complete. Please try again in a moment. Error: #{ e } " } , status : :unprocessable_entity }
2018-03-25 17:30:42 +00:00
rescue e
2019-07-30 21:29:24 +00:00
return { json : { error : " Oops! There was an error saving your card, and it did not complete. Please try again in a moment. Error: #{ e } " } , status : :unprocessable_entity }
2018-03-25 17:30:42 +00:00
end
2019-07-30 21:29:24 +00:00
{ status : :ok , json : card . attributes . with_indifferent_access . merge ( token : source_token ) }
end
2018-03-25 17:30:42 +00:00
def self . customer_data ( holder , card_data )
{ email : holder [ 'email' ] , metadata : { cardholders_name : card_data [ :cardholders_name ] , holder_id : card_data [ :holder_id ] , holder_type : card_data [ :holder_type ] } }
end
end