2
\$\begingroup\$

I'm working on a Rails application that has a lot of logic in the controller that should be in the model. In the app/controllers/customers_controller.rb#create there is logic to create stripe cc info for the customer. I think this should be in the model.

How can I move this logic into the model and still handle any exceptions in the controller?

app/controllers/customers_controller.rb

def create
  ...
  card = {
        'object' => 'card', 'number' => card_number, 'exp_month' => card_expire_month, 'exp_year' => card_expire_year,
        'cvc' => card_cvc, 'address_zip' => parms['zipcode'], 'name' => "#{parms['first_name']} #{parms['last_name']}",
        'address_line1' => parms['street_address_1'], 'address_state' => parms['state'],
        'address_line2' => parms['street_address_2'], 'address_city' => parms['city']
      }

      begin
        resp = Stripe::Customer.create(
          description: 'Customer for DoctorNow',
          metadata: { customer_id: @customer.id },
          email: @customer.email,
          source: card)
        @customer.assign_attributes(
          stripe_customer_id: resp['id'],
          stripe_card_id: resp['sources']['data'][0]['id'],
          stripe_card_last4: resp['sources']['data'][0]['last4'],
          stripe_card_type: resp['sources']['data'][0]['brand']
        )

        event('card_preauth', 'customer_id', @customer.id)
      rescue Stripe::CardError => e
        @err = stripe_error(e, 'creating_card_card_error', 0)
        render :err_show, status: :payment_required, json: @err
        event('card_preauth_failed', 'customer_id', @customer.id)
        return
      rescue Stripe::InvalidRequestError => e
        @err = stripe_error(e, 'create_card_invalid', 0)
        event('card_preauth_failed', 'customer_id', @customer.id)
        render :err_show, status: :payment_required, json: @err
        return
      rescue Stripe::AuthenticationError => e
        @err = stripe_error(e, 'create_card_authentication', 0)
        event('card_preauth_failed', 'customer_id', @customer.id)
        render :err_show, status: :payment_required, json: @err
        return
      rescue Stripe::APIConnectionError => e
        @err = stripe_error(e, 'create_card_connect', 0)
        event('card_preauth_failed', 'customer_id', @customer.id)
        render :err_show, status: :payment_required, json: @err
        return
      rescue Stripe::StripeError => e
        @err = stripe_error(e, 'create_card_stripe', 0)
        event('card_preauth_failed', 'customer_id', @customer.id)
        render :err_show, status: :payment_required, json: @err
        return
      rescue => e
        @err = stripe_error(e, 'create_card_generic', 0)
        render :err_show, status: :payment_required, json: @err
        event('card_preauth_failed', 'customer_id', @customer.id)
        return
      end
  ...
end
\$\endgroup\$
  • \$\begingroup\$ Where would the Stripe::*Error's be thrown from? Inside @customer.assign_attributes(...) or from event(...)? \$\endgroup\$ – Greg Burghardt Jul 23 '15 at 18:01
  • \$\begingroup\$ @GregBurghardt Stripe::Customer.create \$\endgroup\$ – Antarr Byrd Jul 23 '15 at 19:10
2
\$\begingroup\$

I prefer @Unixmoney solutin, I would just change the way you handle error, or better said use a block to shorter the perform method

  def perform
    handle_errors do
      resp = Stripe::Customer.create(
        description: 'Customer for DoctorNow',
        metadata: { customer_id: @customer.id },
        email: @customer.email,
        source: card
      )
      @customer.assign_attributes(
        stripe_customer_id: resp['id'],
        stripe_card_id: resp['sources']['data'][0]['id'],
        stripe_card_last4: resp['sources']['data'][0]['last4'],
        stripe_card_type: resp['sources']['data'][0]['brand']
      )
      event('card_preauth', 'customer_id', @customer.id)
    end    
  end

...
private
...

  def handle_errors
    begin
      yield
    rescue Stripe::CardError => e
      log_error('creating_card_card_error')
    rescue Stripe::InvalidRequestError => e
      log_error('create_card_invalid')
    rescue Stripe::AuthenticationError => e
      log_error('create_card_authentication')
    rescue Stripe::APIConnectionError => e
      log_error('create_card_connect')
    rescue Stripe::StripeError => e
      log_error('create_card_stripe')
    rescue => e
      log_error('create_card_generic')
    end
  end
\$\endgroup\$
3
\$\begingroup\$

Personally, I wouldn't want to see this in a model either (at least not an ActiveRecord model).

I'd probably go with something more like this:

app/controllers/customers_controller.rb

def create
  create_customer = CreateCustomer.new(params)
  create_customer.perform
  if create_customer.success?
    redirect_to some_path, notice: 'success'
  else
    render :err_show, status: :payment_required, json: create_customer.err
  end
end

app/interactors/create_customer.rb

class CreateCustomer

  attr_reader :customer, :card, :err

  def initialize(customer, parms)
    @customer = customer
    @card = {
    'object' => 'card', 'number' => card_number, 'exp_month' => card_expire_month, 'exp_year' => card_expire_year,
    'cvc' => card_cvc, 'address_zip' => parms['zipcode'], 'name' => "#{parms['first_name']} #{parms['last_name']}",
    'address_line1' => parms['street_address_1'], 'address_state' => parms['state'],
    'address_line2' => parms['street_address_2'], 'address_city' => parms['city']
    }
    @err = nil
  end

  def perform
    resp = Stripe::Customer.create(
      description: 'Customer for DoctorNow',
      metadata: { customer_id: @customer.id },
      email: @customer.email,
      source: card)
    @customer.assign_attributes(
      stripe_customer_id: resp['id'],
      stripe_card_id: resp['sources']['data'][0]['id'],
      stripe_card_last4: resp['sources']['data'][0]['last4'],
      stripe_card_type: resp['sources']['data'][0]['brand']
    )
    event('card_preauth', 'customer_id', @customer.id)
  rescue Stripe::CardError => e
    log_error('creating_card_card_error')
  rescue Stripe::InvalidRequestError => e
    log_error('create_card_invalid')
  rescue Stripe::AuthenticationError => e
    log_error('create_card_authentication')
  rescue Stripe::APIConnectionError => e
    log_error('create_card_connect')
  rescue Stripe::StripeError => e
    log_error('create_card_stripe')
  rescue => e
    log_error('create_card_generic')
  end

  def success?
    @err.nil? && @customer.stripe_customer_id.present?
  end

  private

  def log_error(error)
    @err = stripe_error(e, error, 0)
    event('card_preauth_failed', 'customer_id', @customer.id)
  end

end
\$\endgroup\$
  • \$\begingroup\$ I will give it a try. \$\endgroup\$ – Antarr Byrd Jul 23 '15 at 19:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.