2
votes

i am trying to connect StipeConnect to my app. I downloaded simple code example from https://github.com/rfunduk/rails-stripe-connect-example I update and install bundle, copy and change some session files, but receive an error:

NameError in Users#show

Showing /home/ppl/rps/09.25/app_simple_alpha/app/views/users/_connect.html.erb where line #18 raised:

uninitialized constant ActionView::CompiledTemplates::StripeStandalone

Extracted source (around line #18):

            <small>Create a standalone Stripe account in</small>
            <select class="country" name="country">
#error line     <% StripeStandalone::COUNTRIES.each do |country| %>
                <option value="<%= country[:code] %>">
                  <%= country[:name] %>
                </option>

Trace of template inclusion: app/views/users/show.html.erb

Here is my files:

models/user.rb

class User < ActiveRecord::Base
  attr_accessor :remember_token, :activation_token, :reset_token
  before_save   :downcase_email
  before_create :create_activation_digest

    validates :name, presence: true, length: { maximum: 50 }
    VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i
    validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, 
                      uniqueness: { case_sensitive: false }
    has_secure_password
    validates :password, length: { minimum: 6 }, allow_nil: true

  serialize :stripe_account_status, JSON


  def connected?; !stripe_user_id.nil?; end


  def managed?; stripe_account_type == 'managed'; end
  def standalone?; stripe_account_type == 'standalone'; end
  def oauth?; stripe_account_type == 'oauth'; end

  def manager
    case stripe_account_type
    when 'managed' then StripeManaged.new(self)
    when 'standalone' then StripeStandalone.new(self)
    when 'oauth' then StripeOauth.new(self)
    end
  end

  def can_accept_charges?
    return true if oauth?
    return true if managed? && stripe_account_status['charges_enabled']
    return true if standalone? && stripe_account_status['charges_enabled']
    return false
  end

#...
    end

controllers/users_controller.rb

    class UsersController < ApplicationController
      before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
      before_action :correct_user,   only: [:edit, :update]
      before_action :admin_user,     only: :destroy
      before_action :require_user, except: %w{ new create }
      require 'will_paginate/array'
      def index
        @users = User.all.paginate(page: params[:page])
      end
    # ...

      def show
        @user = User.find(params[:id])
        @plans = Stripe::Plan.all
      end

      def pay

        user = User.find( params[:id] )        
        amount = 1000
        fee = (amount * Rails.application.secrets.fee_percentage).to_i

        begin
          charge_attrs = {
            amount: amount,
            currency: user.currency,
            source: params[:token],
            description: "Test Charge via Stripe Connect",
            application_fee: fee
          }

          case params[:charge_on]
          when 'connected'
            # Use the user-to-be-paid's access token
            # to make the charge directly on their account
            charge = Stripe::Charge.create( charge_attrs, user.secret_key )
          when 'platform'
            # Use the platform's access token, and specify the
            # connected account's user id as the destination so that
            # the charge is transferred to their account.
            charge_attrs[:destination] = user.stripe_user_id
            charge = Stripe::Charge.create( charge_attrs )
          end

          flash[:notice] = "Charged successfully! <a target='_blank' rel='#{params[:charge_on]}-account' href='https://dashboard.stripe.com/test/payments/#{charge.id}'>View in dashboard &raquo;</a>"

        rescue Stripe::CardError => e
          error = e.json_body[:error][:message]
          flash[:error] = "Charge failed! #{error}"
        end

        redirect_to user_path( user )
      end


      def subscribe
        user = User.find( params[:id] )
        fee_percent = (Rails.application.secrets.fee_percentage * 100).to_i
        begin

          customer = Stripe::Customer.create(
            {
              source: params[:token],
              email: current_user.email,
              plan: params[:plan],
              application_fee_percent: fee_percent
            },
            user.secret_key
          )
          flash[:notice] = "Subscribed! <a target='_blank' rel='platform-account' href='https://dashboard.stripe.com/test/customers/#{customer.id}'>View in dashboard &raquo;</a>"

        rescue Stripe::CardError => e
          error = e.json_body[:error][:message]
          flash[:error] = "Charge failed! #{error}"
        end

        redirect_to user_path( user )
      end
# ...    

      private

        def user_params
          params.require(:user).permit(:name, :email, :password,
                                       :password_confirmation, :activation_token)
        end

        def admin_user
          redirect_to(root_url) unless current_user.admin?
        end


        def logged_in_user
          unless logged_in?
            store_location
            flash[:danger] = "Please log in."
            redirect_to login_url
          end
        end

        def correct_user
          @user = User.find(params[:id])
          redirect_to(root_url) unless current_user?(@user)
        end
    end

controllers/stripe_controller.rb

class StripeController < ApplicationController

  def managed
    connector = StripeManaged.new( current_user )
    account = connector.create_account!(
      params[:country], params[:tos] == 'on', request.remote_ip
    )

    if account
      flash[:notice] = "Managed Stripe account created! <a target='_blank' rel='platform-account' href='https://dashboard.stripe.com/test/applications/users/#{account.id}'>View in dashboard &raquo;</a>"
    else
      flash[:error] = "Unable to create Stripe account!"
    end
    redirect_to user_path( current_user )
  end

  def standalone
    connector = StripeStandalone.new( current_user )
    account = connector.create_account!( params[:country] )

    if account
      flash[:notice] = "Standalone Stripe account created! <a target='_blank' rel='platform-account' href='https://dashboard.stripe.com/test/applications/users/#{account.id}'>View in dashboard &raquo;</a>"
    else
      flash[:error] = "Unable to create Stripe account!"
    end
    redirect_to user_path( current_user )
  end


  def oauth
    connector = StripeOauth.new( current_user )
    url, error = connector.oauth_url( redirect_uri: stripe_confirm_url )

    if url.nil?
      flash[:error] = error
      redirect_to user_path( current_user )
    else
      redirect_to url
    end
  end


  def confirm
    connector = StripeOauth.new( current_user )
    if params[:code]
      # If we got a 'code' parameter. Then the
      # connection was completed by the user.
      connector.verify!( params[:code] )

    elsif params[:error]
      # If we have an 'error' parameter, it's because the
      # user denied the connection request. Other errors
      # are handled at #oauth_url generation time.
      flash[:error] = "Authorization request denied."
    end

    redirect_to user_path( current_user )
  end


  def deauthorize
    connector = StripeOauth.new( current_user )
    connector.deauthorize!
    flash[:notice] = "Account disconnected from Stripe."
    redirect_to user_path( current_user )
  end

end

controllers/hooks_controller.rb

class HooksController < ApplicationController

  skip_before_action :verify_authenticity_token

  def stripe

    user = params[:user_id] && User.find_by( stripe_user_id: params[:user_id] )


    args = [ params[:id], user.try(:secret_key) ].compact


    begin
      event = Stripe::Event.retrieve( *args )
    rescue Stripe::InvalidRequestError

      render nothing: true, status: 200
      return
    rescue Stripe::AuthenticationError

      if user && user.connected?
        connector = StripeConnect.new( user )
        connector.deauthorized
      end

      render nothing: true, status: 200
      return
    end


    case event.try(:type)

    when 'account.application.deauthorized'

      if user && user.connected?
        user.manager.deauthorized
      end

    when 'account.updated'

      if user && user.connected?

        user.manager.update_account!
      end

    when 'charge.succeeded'
      Rails.logger.info "**** STRIPE EVENT **** #{event.type} **** #{event.id}"
    when 'invoice.payment_succeeded'
      Rails.logger.info "**** STRIPE EVENT **** #{event.type} **** #{event.id}"
    when 'invoice.payment_failed'
      Rails.logger.info "**** STRIPE EVENT **** #{event.type} **** #{event.id}"

    end


    render nothing: true, status: 200
  end

end

controllers/application_controller.rb

class ApplicationController < ActionController::Base include SessionsHelper include ApplicationHelper

protect_from_forgery with: :exception

def require_user if session[:user_id].blank? redirect_to new_sessions_path return end end end

helpers/application_helper.rb

module ApplicationHelper


  def full_title(page_title)
    base_title = "GoodApp"
    if page_title.empty?
      base_title
    else
      "#{base_title} | #{page_title}"
    end
  end

  #  # Lookup logged in user from session, if applicable.
  #def current_user
  #  @_current_user ||= User.find_by_id( session[:user_id] )
  #end

  def is_myself?
    @user == current_user
  end
end

assets/javascrips/application.js

//= require jquery
//= require jquery_ujs
//= require bootstrap
//= require turbolinks

// require bootstrap-sprockets

//= require app/niceties
//= require app/connect
//= require app/pay
//= require app/subscribe
//= require_tree .

assets/javascript/app/connect.coffee

$(document).ready ->
  # pre-connection
  setupManaged()
  setupStandalone()

  # connected user, but we need info
  setupFieldsNeeded()

setupManaged = ->
  container = $('#stripe-managed')
  return if container.length == 0
  tosEl = container.find('.tos input')
  countrySelect = container.find('.country')
  form = container.find('form')
  createButton = form.find('.btn')

  tosEl.change -> createButton.toggleClass 'disabled', !tosEl.is(':checked')
  form.submit ( e ) ->

    if !tosEl.is(':checked')
      e.preventDefault()
      return false

    createButton.addClass('disabled').val('...')


  countrySelect.change ->
    termsUrl = "https://stripe.com/#{countrySelect.val().toLowerCase()}/terms"
    tosEl.siblings('a').attr( href: termsUrl )

setupStandalone = ->
  container = $('#stripe-standalone')
  return if container.length == 0
  countrySelect = container.find('.country')
  form = container.find('form')
  createButton = form.find('.btn')

  form.submit ( e ) ->
    createButton.addClass('disabled').val('...')

setupFieldsNeeded = ->
  container = $('.needed')
  return if container.length == 0

  form = container.find('form')

  form.submit ( e ) ->
    button = form.find('.buttons .btn')
    button.addClass('disabled').val('Saving...')


    if (baContainer = form.find('#bank-account')).length > 0
      Stripe.setPublishableKey baContainer.data('publishable')
      tokenField = form.find('#bank_account_token')
      if tokenField.is(':empty')
        e.preventDefault()
        Stripe.bankAccount.createToken form, ( _, resp ) ->
          if resp.error
            button.removeClass('disabled').val('Save Info')
            alert( resp.error.message )
          else
            tokenField.val( resp.id )
            form.get(0).submit()
        return false

assets/javascript/app/niceties.coffee
$(document).ready ->
  setTimeout(
    -> $('.alert.alert-info.auto').slideUp('fast')
    3500
  )

  $('body').on 'click', 'a[rel=platform-account]', ( e ) ->
    return confirm("This link will only work if you're logged in as the **application owner**. Continue?")
  $('body').on 'click', 'a[rel=connected-account]', ( e ) ->
    return confirm("This link will only work if you're logged in as the **connected account**. Continue?")

assets/javascript/app/pay.coffee

$(document).ready ->
  return unless StripeCheckout?


  submitting = false

  payButton = $('.pay-button')
  form = payButton.closest('form')
  destination = form.find('select[name=charge_on]')
  indicator = form.find('.indicator').height( form.outerHeight() )
  handler = null

  createHandler = ->
    handler = StripeCheckout.configure

      key: window.publishable[destination.val()]


      email: window.currentUserEmail

      allowRememberMe: false
      closed: ->
        form.removeClass('processing') unless submitting
      token: ( token ) ->
        submitting = true
        form.find('input[name=token]').val( token.id )
        form.get(0).submit()

  destination.change createHandler
  createHandler()

  payButton.click ( e ) ->
    e.preventDefault()
    form.addClass( 'processing' )

    handler.open
      name: 'Rails Connect Example'
      description: '$10 w/ 10% fees'
      amount: 1000

assets/javascript/app/subscribe.coffee

$(document).ready ->
  return unless StripeCheckout?

  # Holds the plan selected by the user in the interface.
  currentPlan = null


  submitting = false

  subscribeButton = $('.subscribe-button')
  planButtons = $('.plan-choice')
  form = subscribeButton.closest('form')
  indicator = form.find('.indicator').height( form.outerHeight() )

  handler = StripeCheckout.configure

    key: window.stripePublishableKey

    # The email of the logged in user.
    email: window.currentUserEmail

    allowRememberMe: false
    closed: ->
      subscribeButton.attr( disabled: true )
      planButtons.removeClass('active')
      currentPlan = null
      form.removeClass('processing') unless submitting
    token: ( token ) ->
      submitting = true
      form.find('input[name=token]').val( token.id )
      form.get(0).submit()

  planButtons.click ( e ) ->
    e.preventDefault()

    planButton = $(this)
    planButton.addClass('active').siblings().removeClass('active')
    subscribeButton.attr( disabled: false )


    currentPlan =
      id: planButton.data('id')
      name: planButton.data('name')
      currency: planButton.data('currency')
      amount: parseInt planButton.data('amount'), 10

    form.find('input[name=plan]').val( currentPlan.id )

    subscribeButton.show()

  subscribeButton.click ( e ) ->
    e.preventDefault()
    form.addClass('processing')

    if currentPlan == null
      alert "Choose a plan first!"
      return

    handler.open
      name: 'Rails Connect Example'
      description: "#{currentPlan.name} Subscription"
      amount: currentPlan.amount

services/stripe_managed.rb

class StripeManaged < Struct.new( :user )
  ALLOWED = [ 'US', 'CA' ] # public beta
  COUNTRIES = [
    { name: 'United States', code: 'US' },
    { name: 'Canada', code: 'CA' },
    { name: 'Australia', code: 'AU' },
    { name: 'United Kingdom', code: 'GB' },
    { name: 'Ireland', code: 'IE' }
  ]

  def create_account!( country, tos_accepted, ip )
    return nil unless tos_accepted
    return nil unless country.in?( COUNTRIES.map { |c| c[:code] } )

    begin
      @account = Stripe::Account.create(
        managed: true,
        country: country,
        email: user.email,
        tos_acceptance: {
          ip: ip,
          date: Time.now.to_i
        },
        legal_entity: {
          type: 'individual',
        }
      )
    rescue
      nil # TODO: improve
    end

    if @account
      user.update_attributes(
        currency: @account.default_currency,
        stripe_account_type: 'managed',
        stripe_user_id: @account.id,
        secret_key: @account.keys.secret,
        publishable_key: @account.keys.publishable,
        stripe_account_status: account_status
      )
    end

    @account
  end

  def update_account!( params: nil )
    if params
      if params[:bank_account_token]
        account.bank_account = params[:bank_account_token]
        account.save
      end

      if params[:legal_entity]

        params[:legal_entity][:dob] = {
          year: params[:legal_entity].delete('dob(1i)'),
          month: params[:legal_entity].delete('dob(2i)'),
          day: params[:legal_entity].delete('dob(3i)')
        }


        params[:legal_entity].entries.each do |key, value|
          if [ :address, :dob ].include? key.to_sym
            value.entries.each do |akey, avalue|
              next if avalue.blank?

              account.legal_entity[key] ||= {}
              account.legal_entity[key][akey] = avalue
            end
          else
            next if value.blank?

            account.legal_entity[key] = value
          end
        end


        pa = account.legal_entity['address'].dup.to_h
        account.legal_entity['personal_address'] = pa

        account.save
      end
    end

    user.update_attributes(
      stripe_account_status: account_status
    )
  end

  def legal_entity
    account.legal_entity
  end

  def needs?( field )
    user.stripe_account_status['fields_needed'].grep( Regexp.new( /#{field}/i ) ).any?
  end

  def supported_bank_account_countries
    country_codes = case account.country
                    when 'US' then %w{ US }
                    when 'CA' then %w{ US CA }
                    when 'IE', 'UK' then %w{ IE UK US }
                    when 'AU' then %w{ AU }
                    end
    COUNTRIES.select do |country|
      country[:code].in? country_codes
    end
  end

  protected

  def account_status
    {
      details_submitted: account.details_submitted,
      charges_enabled: account.charges_enabled,
      transfers_enabled: account.transfers_enabled,
      fields_needed: account.verification.fields_needed,
      due_by: account.verification.due_by
    }
  end

  def account
    @account ||= Stripe::Account.retrieve( user.stripe_user_id )
  end

end

services/stripe_standalone.rb

class StripeStandalone < Struct.new( :user )
  COUNTRIES = [
    { name: 'United States', code: 'US' },
    { name: 'Canada', code: 'CA' },
    { name: 'Australia', code: 'AU' },
    { name: 'United Kingdom', code: 'GB' },
    { name: 'Ireland', code: 'IE' }
  ]

  def create_account!( country )
    return nil unless country.in?( COUNTRIES.map { |c| c[:code] } )

    begin
      @account = Stripe::Account.create(
        email: user.email,
        managed: false,
        country: country
      )
    rescue
      nil # TODO: improve
    end

    if @account
      user.update_attributes(
        currency: @account.default_currency,
        stripe_account_type: 'standalone',
        stripe_user_id: @account.id,
        secret_key: @account.keys.secret,
        publishable_key: @account.keys.publishable,
        stripe_account_status: account_status
      )
    end

    @account
  end

  protected

  def account_status
    {
      details_submitted: account.details_submitted,
      charges_enabled: account.charges_enabled,
      transfers_enabled: account.transfers_enabled
    }
  end

  def account
    @account ||= Stripe::Account.retrieve( user.stripe_user_id )
  end

end

views/users/_connect.html.erb

<div class="panel panel-primary">
  <div class="panel-body">
    <h3>Connect</h3>
    <p>There are 3 ways to create/connect your Stripe account.</p>
    <ul class="list-group">
      <li class="list-group-item" id="stripe-oauth">
        <a class="pull-right btn btn-lg btn-primary" href="<%= stripe_oauth_path %>">Connect</a>
        <h3>OAuth</h3>
        <p>Connect or create a Stripe account via OAuth.</p>
      </li>
      <li class="list-group-item" id="stripe-standalone">
        <%= form_tag stripe_standalone_path, method: 'POST' do %>
          <input class="pull-right btn btn-lg btn-primary" type="submit" value="Create"></input>
          <h3>Standalone</h3>
          <p>
            <small>Create a standalone Stripe account in</small>
            <select class="country" name="country">
              <% StripeStandalone::COUNTRIES.each do |country| %>
                <option value="<%= country[:code] %>">
                  <%= country[:name] %>
                </option>
              <% end %>
            </select>
          </p>
        <% end %>
      </li>
      <% # managed accounts are in public beta %>
      <% # see services/stripe_managed.rb#ALLOWED %>
      <% if Stripe::Account.retrieve('self').country.in? StripeManaged::ALLOWED %>
        <li class="list-group-item" id="stripe-managed">
          <%= form_tag stripe_managed_path, method: 'POST' do %>
            <input class="pull-right btn btn-lg disabled btn-primary" type="submit" value="Create"></input>
            <h3>Managed</h3>
            <p>
              <small>Create a managed Stripe account in</small>
              <select class="country" name="country">
                <% StripeManaged::COUNTRIES.each do |country| %>
                  <option value="<%= country[:code] %>">
                    <%= country[:name] %>
                  </option>
                <% end %>
              </select>
              <br/>
              <label class="tos">
                <input checked="<%= false %>" name="tos" type="checkbox"></input>
                I accept the
                <a href="https://stripe.com/us/terms" target="_blank">Stripe Terms of Service</a>
              </label>
            </p>
          <% end %>
        </li>
      <% end %>
    </ul>
  </div>

views/users/show.html.erb

<div class="col-md-6 col-md-offset-3 col-xs-12">
  <%= render partial: 'nav' %>
  <% if flash[:notice] %>
    <div class="alert alert-info">
      <p>
        <%= flash[:notice].html_safe %>
      </p>
    </div>
  <% end %>
  <% if flash[:error] %>
    <div class="alert alert-danger">
      <p>
        <%= flash[:error].html_safe %>
      </p>
    </div>
  <% end %>
  <h1>
    <%= @user.name %>
  </h1>
  <h4>
    <%= @user.email %>
  </h4>
  <% if @user.connected? %>
    <% if is_myself? %>
      <% # you're looking at your own 'profile', so you can %>
      <% # update/deauthorize/etc your Stripe account %>
      <%= render partial: 'settings' %>
    <% else %>
      <%= render partial: 'pay' %>
    <% end %>
  <% else %>
    <% if is_myself? && !current_user.connected? %>
      <% # you're looking at your own 'profile', so you can %>
      <% # create/connect/etc your Stripe account %>
      <%= render partial: 'connect' %>
    <% else %>
      <div class="panel panel-danger not-connected">
        <div class="panel-body">
          <h3>Not Connected</h3>
          <p>
            This user is not connected to Stripe, so
            you can't pay them.
          </p>
        </div>
      </div>
    <% end %>
  <% end %>
</div>

more details: https://groups.google.com/forum/#!topic/rubyonrails-talk/BxqBda8LsR0

Thanks for any help

1
Why all your templates are in erb format, but the template in the repo are in haml? What version of Ruby on Rails do you use?Jeiwan
rails 4.2.2 and ruby 2.2.1 Original app using haml, my app - erb. Something I am converting to erb, something not, I add haml file for sure that I am nothing missing from original stripe app. Do you think that it can be a reason for an error?anndrew78
No, the views cannot be a reason. I just wanted to know what changes had you done. Did you add Spring to the project? Also try to run StripeStandalone in Rails console.Jeiwan
Yes, gem was installed. rails c: NameError: uninitialized constant StripeStandaloneanndrew78

1 Answers

0
votes

Rails cannot load StripeStandalone service. This is strange, because its name and path are correct, so Rails should autoload them. But to do so you have to restart Rails after adding new services.

You also said that you're using Spring. Spring allows you to load Rails faster by preloading it on the first run and keeping that Rails app in the background. So if you use Spring, after adding new services you have to restart Spring as well by running:

spring stop

and running Rails again.

This should help in your case. But if not, then add this line to config/application.rb (inside class Application < Rails::Application):

config.autoload_paths << Rails.root.join('app', 'services')

(I hope you won't need this, because Rails autoloads services by default)