2

我正在尝试将 StipeConnect 连接到我的应用程序。我从https://github.com/rfunduk/rails-stripe-connect-example下载了简单的代码示例 我更新和安装包,复制和更改一些会话文件,但收到错误:

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

这是我的文件:

模型/用户.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

控制器/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

控制器/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

控制器/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

控制器/application_controller.rb

类 ApplicationController < ActionController::Base 包括 SessionsHelper 包括 ApplicationHelper

使用: :exception 保护_from_forgery

def require_user if session[:user_id].blank? 重定向到新会话路径返回结束结束结束

助手/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

资产/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 .

资产/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?")

资产/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

服务/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

服务/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

意见/用户/_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>

意见/用户/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>

更多详情:https ://groups.google.com/forum/#!topic/rubyonrails-talk/BxqBda8LsR0

谢谢你的帮助

4

1 回答 1

0

Rails 无法加载StripeStandalone服务。这很奇怪,因为它的名称和路径是正确的,所以 Rails 应该自动加载它们。但要这样做,您必须在添加新服务后重新启动 Rails。

您还说您正在使用Spring。Spring 允许您通过在第一次运行时预加载 Rails 并将 Rails 应用程序保留在后台来更快地加载 Rails。因此,如果您使用 Spring,在添加新服务之后,您还必须通过运行以下命令重新启动 Spring:

spring stop

并再次运行 Rails。

这应该对您的情况有所帮助。但如果不是,则将此行添加到config/application.rb(内部class Application < Rails::Application):

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

(我希望你不需要这个,因为 Rails 默认会自动加载服务)

于 2015-12-29T02:33:01.137 回答