Hi all. i am trying to connect StipeConnect to my app.
I downloaded simple code example from
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:
db/schema.rb
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at"
t.datetime "updated_at"
t.string "password_digest"
t.boolean "admin", default: false
t.boolean "email_confirmed", default: false
t.string "confirm_token"
t.string "activation_digest"
t.boolean "activated", default: false
t.datetime "activated_at"
t.string "remember_digest"
t.string "reset_digest"
t.datetime "reset_sent_at"
t.string "publishable_key"
t.string "secret_key"
t.string "stripe_user_id"
t.string "currency"
t.string "stripe_account_type"
t.text "stripe_account_status", default: "{}"
end
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
# General 'has a Stripe account' check
def connected?; !stripe_user_id.nil?; end
# Stripe account type checks
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
# Returns the hash digest of the given string.
def self.digest(string)
cost = ActiveModel::SecurePassword.min_cost ?
BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def self.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(remember_token)
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Activates an account.
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
def create_remember_token
self.remember_token = User.encrypt(User.new_remember_token)
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 new
@user = User.new#(params[:user])
end
def edit
@user = User.find(params[:id])
end
#def update
# @user = User.find(params[:id])
# if @user.update_attributes(user_params)
# flash[:success] = "Profile updated"
# redirect_to @user
# else
# render 'edit'
# end
#end
def update
manager = current_user.manager
manager.update_account! params: params
redirect_to user_path( current_user )
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted."
redirect_to users_url
end
def create
@user = User.new(user_params)
if @user.save
log_in @user
#UserMailer.welcome_email(@user).deliver_now
@user.send_activation_email
flash[:info] = "Please check your email to activate your
account."
#UserMailer.registration_confirmation(@user).deliver_now
#flash[:success] = “Welcome to the Sample App!”
redirect_to @user
else
render ‘new’
end
end
# Make a one-off payment to the user.
# See app/assets/javascripts/app/pay.coffee
def pay
# Find the user to pay.
user = User.find( params[:id] )
# Charge $10.
amount = 1000
# Calculate the fee amount that goes to the application.
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=‘Stripe Login | Sign in to the Stripe Dashboard’>View in
dashboard »"
rescue Stripe::CardError => e
error = e.json_body[:error][:message]
flash[:error] = "Charge failed! #{error}"
end
redirect_to user_path( user )
end
# Subscribe the currently logged in user to
# a plan owned by the application.
# See app/assets/javascripts/app/subscribe.coffee
def subscribe
# Find the user to pay.
user = User.find( params[:id] )
# Calculate the fee percentage that applies to
# all invoices for this subscription.
fee_percent = (Rails.application.secrets.fee_percentage *
100).to_i
begin
# Create a customer and subscribe them to a plan
# in one shot.
# Normally after this you would store customer.id
# in your database so that you can keep track of
# the subscription status/etc. Here we’re just
# fire-and-forgetting it.
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! View
in
dashboard »”
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
# Before filters
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
# Confirms the correct user.
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
# Create a manage Stripe account for yourself.
# Only works on the currently logged in user.
# See app/services/stripe_managed.rb for details.
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=‘Stripe Login | Sign in to the Stripe Dashboard’>View
in dashboard »"
else
flash[:error] = “Unable to create Stripe account!”
end
redirect_to user_path( current_user )
end
# Create a standalone Stripe account for yourself.
# Only works on the currently logged in user.
# See app/services/stripe_unmanaged.rb for details.
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=‘Stripe Login | Sign in to the Stripe Dashboard’>View
in dashboard »"
else
flash[:error] = “Unable to create Stripe account!”
end
redirect_to user_path( current_user )
end
# Connect yourself to a Stripe account.
# Only works on the currently logged in user.
# See app/services/stripe_oauth.rb for #oauth_url details.
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
# Confirm a connection to a Stripe account.
# Only works on the currently logged in user.
# See app/services/stripe_connect.rb for #verify! details.
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
# Deauthorize the application from accessing
# the connected Stripe account.
# Only works on the currently logged in user.
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
# Webhooks can't possibly know our CSRF token.
# So disable that feature entirely for this controller.
skip_before_action :verify_authenticity_token
def stripe
# If the request has a 'user_id' key, then this is a webhook
# event sent regarding a connected user, and not to a webhook
# handler setup on the application owner's account.
# So, use the user_id to look up a connected user on our end.
user = params[:user_id] && User.find_by( stripe_user_id:
params[:user_id] )
# If we didn't find a user, we'll have nil instead
# so build our arguments to the Event API taking that into
account.
# We’ll end up with one of:
# args = [ ‘EVENT_ID’ ]
# args = [ ‘EVENT_ID’, ‘ACCESS_TOKEN’ ]
args = [ params[:id], user.try(:secret_key) ].compact
# Retrieve the event from Stripe so that we can be
# sure it wasn't spoofed/faked by someone being mean.
begin
event = Stripe::Event.retrieve( *args )
rescue Stripe::InvalidRequestError
# The event doesn't exist for some reason... this might
# happen if you've got other apps maybe?
render nothing: true, status: 200
return
rescue Stripe::AuthenticationError
# If we get an authentication error, and the event belongs to
# a user, that means the account deauthorized
# our application. We can't look up and verify the event
# because the event belongs to the connected account, and
we’re
# no longer authorized to access their account!
if user && user.connected?
connector = StripeConnect.new( user )
connector.deauthorized
end
render nothing: true, status: 200
return
end
# Here we're actually done, but if you wanted to handle
# other events (charges or invoice payment failures, etc)
# then this is how you would do it.
case event.try(:type)
when 'account.application.deauthorized'
# This is what the account.application.deauthorized
# handler will hopefully look like some day, where
# the event is still accessible somehow and we verified
# it came from Stripe.
if user && user.connected?
user.manager.deauthorized
end
when 'account.updated'
# This webhook is used for standalone and managed
# accounts. It will notify you about new information
# required for the account to remain in good standing.
if user && user.connected?
# we don't actually need to pass the event here
# we'll request the account details directly inside
# the manager
user.manager.update_account!
end
# These others simply log the event because there's
# not much to do with them for this example app.
# You might do more useful things like sending receipt emails,
etc.
# Of course you can handle as many event types as you need:
# Stripe API reference – curl
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
# We just need to respond in the affirmative.
# No body is necessary.
render nothing: true, status: 200
end
end
controllers/application_controller.rb
class ApplicationController < ActionController::Base
include SessionsHelper
include ApplicationHelper
Prevent CSRF attacks by raising an exception.
For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
def require_user
if session[:user_id].blank?
redirect_to new_sessions_path
return
end
end
end
controllers/sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user && user.authenticate(params[:session][:password])
if user.activated?
log_in user
params[:session][:remember_me] == '1' ? remember(user) :
forget(user)
redirect_back_or user
else
message = "Account not activated. "
message += “Check your email for the activation link.”
flash[:warning] = message
redirect_to root_url
end
else
flash.now[:danger] = ‘Invalid email/password combination’
render ‘new’
end
end
def destroy
log_out if logged_in?
redirect_to root_url
end
end
helpers/application_helper.rb
module ApplicationHelper
# Returns the full title on a per-page basis.
def full_title(page_title)
base_title = "GoodsLuck. Thanks a lot"
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
# Simply checks if the @user instance variable
# is the current user. Used to check if we're
# looking our own profile page, basically.
# See app/views/users/show.html.haml
def is_myself?
@user == current_user
end
end
helpers/sessions_helper.rb
module SessionsHelper
# Logs in the given user.
def log_in(user)
session[:user_id] = user.id
end
# Remembers a user in a persistent session.
def remember(user)
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end
# Returns the user corresponding to the remember token cookie.
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember,
cookies[:remember_token])
log_in user
@current_user = user
end
end
end
# Returns true if the user is logged in, false otherwise.
def logged_in?
!current_user.nil?
end
# Forgets a persistent session.
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
# Logs out the current user.
def log_out
session.delete(:user_id)
@current_user = nil
end
# Redirects to stored location (or to the default).
def redirect_back_or(default)
redirect_to(session[:forwarding_url] || default)
session.delete(:forwarding_url)
end
# Stores the URL trying to be accessed.
def store_location
session[:forwarding_url] = request.url if request.get?
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 ) →
# prevent creation unless ToS is checked
if !tosEl.is(‘:checked’)
e.preventDefault()
return false
createButton.addClass('disabled').val('...')
# populate appropriate ToS link depending on dropdown
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...')
# bank account tokenization
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?
# Keeps track of whether we're in the middle of processing
# a payment or not. This way we can tell if the 'closed'
# event was due to a successful token generation, or the user
# closing it by hand.
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
# Grab the correct publishable key. Depending on
# the selection in the interface.
key: window.publishable[destination.val()]
# The email of the logged in user.
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
# Keeps track of whether we're in the middle of processing
# a payment or not. This way we can tell if the 'closed'
# event was due to a successful token generation, or the user
# closing it by hand.
submitting = false
subscribeButton = $('.subscribe-button')
planButtons = $('.plan-choice')
form = subscribeButton.closest('form')
indicator = form.find('.indicator').height( form.outerHeight() )
handler = StripeCheckout.configure
# The publishable key of the **connected account**.
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 )
# Get current plan info from the clicked element's data
attributes
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]
# clean up dob fields
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)')
}
# update legal_entity hash from the params
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?
# Rails.logger.error "#{akey} - #{avalue.inspect}"
account.legal_entity[key] ||= {}
account.legal_entity[key][akey] = avalue
end
else
next if value.blank?
# Rails.logger.error "#{key} - #{value.inspect}"
account.legal_entity[key] = value
end
end
# copy 'address' as 'personal_address'
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_oauth.rb
class StripeOauth < Struct.new( :user )
def oauth_url( params )
url = client.authorize_url( {
scope: 'read_write',
stripe_landing: 'login',
stripe_user: {
email: user.email
}
}.merge( params ) )
# Make a request to this URL by hand before
# redirecting the user there. This way we
# can handle errors (other than access_denied, which
# could come later).
# See
begin
response = RestClient.get url
# If the request was successful, then we’re all good to return
# this URL.
rescue => e
# On the other hand, if the request failed, then
# we can't send them to connect.
json = JSON.parse(e.response.body) rescue nil
if json && json['error']
case json['error']
# The application is configured incorrectly and
# does not have the right Redirect URI
when 'invalid_redirect_uri'
return nil, <<-EOF
Redirect URI is not setup correctly.
Please see the <a
href=‘#{Rails.configuration.github_url}/blob/master/README.markdown’
target=‘_blank’>README.
EOF
# Something else horrible happened? Network is down,
# Stripe API is broken?...
else
return [ nil, params[:error_description] ]
end
end
# If there was some problem parsing the body
# or there's no 'error' parameter, then something
# _really_ went wrong. So just blow up here.
return [ nil, "Unable to connect to Stripe. #{e.message}" ]
end
[ url, nil ]
end
# Upon redirection back to this app, we'll have
# a 'code' that we can use to get the access token
# and other details about our connected user.
# See app/controllers/users_controller.rb#confirm for counterpart.
def verify!( code )
data = client.get_token( code, {
headers: {
'Authorization' => "Bearer
#{Rails.application.secrets.stripe_secret_key}"
}
} )
user.stripe_user_id = data.params['stripe_user_id']
user.stripe_account_type = 'oauth'
user.publishable_key = data.params['stripe_publishable_key']
user.secret_key = data.token
user.currency = default_currency
user.save!
end
# Deauthorize the user. Straight-forward enough.
# See app/controllers/users_controller.rb#deauthorize for
counterpart.
def deauthorize!
response = RestClient.post(
‘Stripe Login | Sign in to the Stripe Dashboard’,
{ client_id: Rails.application.secrets.stripe_client_id,
stripe_user_id: user.stripe_user_id },
{ ‘Authorization’ => “Bearer
#{Rails.application.secrets.stripe_secret_key}” }
)
user_id = JSON.parse( response.body )[‘stripe_user_id’]
deauthorized if response.code == 200 && user_id ==
user.stripe_user_id
end
# The actual deauthorization on our side consists
# of 'forgetting' the now-invalid user_id, API keys, etc.
# Used here in #deauthorize! as well as in the webhook handler:
# app/controllers/hooks_controller.rb#stripe
def deauthorized
user.update_attributes(
stripe_user_id: nil,
secret_key: nil,
publishable_key: nil,
currency: nil
)
end
private
# Get the default currency of the connected user.
# All transactions will use this currency.
def default_currency
Stripe::Account.retrieve( user.stripe_user_id, user.secret_key
).default_currency
end
# A simple OAuth2 client we can use to generate a URL
# to redirect the user to as well as get an access token.
# Used in #oauth_url and #verify!
def client
@client ||= OAuth2::Client.new(
Rails.application.secrets.stripe_client_id,
Rails.application.secrets.stripe_secret_key,
{
site: 'https://connect.stripe.com',
authorize_url: '/oauth/authorize',
token_url: '/oauth/token'
}
).auth_code
end
end
class StripeOauth < Struct.new( :user )
def oauth_url( params )
url = client.authorize_url( {
scope: 'read_write',
stripe_landing: 'login',
stripe_user: {
email: user.email
}
}.merge( params ) )
# Make a request to this URL by hand before
# redirecting the user there. This way we
# can handle errors (other than access_denied, which
# could come later).
# See
begin
response = RestClient.get url
# If the request was successful, then we’re all good to return
# this URL.
rescue => e
# On the other hand, if the request failed, then
# we can't send them to connect.
json = JSON.parse(e.response.body) rescue nil
if json && json['error']
case json['error']
# The application is configured incorrectly and
# does not have the right Redirect URI
when 'invalid_redirect_uri'
return nil, <<-EOF
Redirect URI is not setup correctly.
Please see the <a
href=‘#{Rails.configuration.github_url}/blob/master/README.markdown’
target=‘_blank’>README.
EOF
# Something else horrible happened? Network is down,
# Stripe API is broken?...
else
return [ nil, params[:error_description] ]
end
end
# If there was some problem parsing the body
# or there's no 'error' parameter, then something
# _really_ went wrong. So just blow up here.
return [ nil, "Unable to connect to Stripe. #{e.message}" ]
end
[ url, nil ]
end
# Upon redirection back to this app, we'll have
# a 'code' that we can use to get the access token
# and other details about our connected user.
# See app/controllers/users_controller.rb#confirm for counterpart.
def verify!( code )
data = client.get_token( code, {
headers: {
'Authorization' => "Bearer
#{Rails.application.secrets.stripe_secret_key}"
}
} )
user.stripe_user_id = data.params['stripe_user_id']
user.stripe_account_type = 'oauth'
user.publishable_key = data.params['stripe_publishable_key']
user.secret_key = data.token
user.currency = default_currency
user.save!
end
# Deauthorize the user. Straight-forward enough.
# See app/controllers/users_controller.rb#deauthorize for
counterpart.
def deauthorize!
response = RestClient.post(
‘Stripe Login | Sign in to the Stripe Dashboard’,
{ client_id: Rails.application.secrets.stripe_client_id,
stripe_user_id: user.stripe_user_id },
{ ‘Authorization’ => “Bearer
#{Rails.application.secrets.stripe_secret_key}” }
)
user_id = JSON.parse( response.body )[‘stripe_user_id’]
deauthorized if response.code == 200 && user_id ==
user.stripe_user_id
end
# The actual deauthorization on our side consists
# of 'forgetting' the now-invalid user_id, API keys, etc.
# Used here in #deauthorize! as well as in the webhook handler:
# app/controllers/hooks_controller.rb#stripe
def deauthorized
user.update_attributes(
stripe_user_id: nil,
secret_key: nil,
publishable_key: nil,
currency: nil
)
end
private
# Get the default currency of the connected user.
# All transactions will use this currency.
def default_currency
Stripe::Account.retrieve( user.stripe_user_id, user.secret_key
).default_currency
end
# A simple OAuth2 client we can use to generate a URL
# to redirect the user to as well as get an access token.
# Used in #oauth_url and #verify!
def client
@client ||= OAuth2::Client.new(
Rails.application.secrets.stripe_client_id,
Rails.application.secrets.stripe_secret_key,
{
site: 'https://connect.stripe.com',
authorize_url: '/oauth/authorize',
token_url: '/oauth/token'
}
).auth_code
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
OAuth
Connect or create a Stripe account via OAuth.
<%= form_tag stripe_standalone_path, method: ‘POST’ do %>
Standalone
Create a standalone Stripe account in
<% StripeStandalone::COUNTRIES.each do |country| %>
<%= country[:name] %>
<% end %>
<% end %>
<% # managed accounts are in public beta %>
<% # see services/stripe_managed.rb#ALLOWED %>
<% if Stripe::Account.retrieve(‘self’).country.in?
StripeManaged::ALLOWED %>
<%= form_tag stripe_managed_path, method: ‘POST’ do %>
Managed
Create a managed Stripe account in
<% StripeManaged::COUNTRIES.each do |country| %>
<%= country[:name] %>
<% end %>
I accept the
Stripe Terms of Service
<% end %>
<% end %>
views/users/_nav.html.erb
<nav class='navbar navbar-inverse'>
<div class='container-fluid'>
<div class='navbar-header'>
<button class='navbar-toggle collapsed'>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
</button>
<p class='navbar-text'>
<span class='glyphicon glyphicon-chevron-right'></span>
<span class='hidden-xs'>Logged in as</span>
<a>
<strong></strong>
</a>
</p>
</div>
<div class='collapse navbar-collapse'>
<p class='navbar-text navbar-right'>
<span class='sep'> | </span>
<a class='navbar-link'>Logout</a>
</p>
</div>
</div>
</nav>
views/users/_pay.html.erb
<% if @user.can_accept_charges? %>
<div class="panel panel-primary">
<div class="panel-body">
<%= form_tag pay_user_path( @user ) do %>
<div class="indicator"></div>
<%= hidden_field_tag :token %>
<button class="pull-right btn btn-primary btn-lg
pay-button">Pay $10
Make a one-off charge
Pay a one-time charge of $10 #{@user.currency.upcase}.
Responsible for fees, refunds and
chargebacks?
Connected Account
Platform Account
<% end %>
<%= form_tag subscribe_user_path( @user ) do %>
<%= hidden_field_tag :token %>
<%= hidden_field_tag :plan %>
Subscribe
Subscribe to a plan
<% if @plans.any? %>
The application defines the following plans:
<% else %>
The application has no plans.
Add Plans »
<% end %>
<% if @plans.any? %>
<% @plans.each do |plan| %>
<%= plan.name %>
-
<%= number_to_currency( plan.amount / 100 ) %>
<%= plan.currency.upcase %>
<% end %>
Responsible for fees, refunds and
chargebacks?
Connected Account
<% # charging on the platform is currently not %>
<% # supported for subscriptions %>
<% # %option{ value: ‘platform’ } Platform Account %>
<% end %>
<% end %>
<% else %>
Nope
<% end %>
<%= javascript_include_tag ‘https://checkout.stripe.com/checkout.js’
%>
// You can select to pay either directly or via the platform, so
// we need both publishable keys here.
window.publishable = {
platform:
#{Rails.application.secrets.stripe_publishable_key.to_json},
connected: #{@user.publishable_key.to_json}
};
window.currentUserEmail = #{current_user.email.to_json};
window.payPath = #{pay_user_path( @user ).to_json};
</script>
views/users/_settings.html.haml
.panel.panel-primary
.panel-body
- if current_user.oauth?
%a.pull-right.btn.btn-danger.btn-lg{ href:
stripe_deauthorize_path } Deauthorize
%h3 Disconnect
%p
Since this is you, you can
%br
disconnect from this application.
- if current_user.managed?
%h2 Stripe Account Management
%p
Account Type:
%strong Managed
- # the current account status, whether the
- # account can make charges or can receive transfers
%p
.account-status
Status:
- charges = [ 'Charges',
@user.stripe_account_status[‘charges_enabled’] ]
- transfers = [ ‘Transfers’,
@user.stripe_account_status[‘transfers_enabled’] ]
%table
- [ charges, transfers ].each do |text, yn|
%tr
%td
%span.label{ class: yn ? ‘label-primary’ :
‘label-danger’ }
%span.glyphicon{ class: yn ? ‘glyphicon-ok’ :
‘glyphicon-remove’ }
= text
- # if we need more information from this user to keep
- # their account in good standing
- if @user.stripe_account_status['fields_needed'].any?
%hr
.needed
= form_for @user, html: { class: 'form-horizontal' } do
|f|
%h3 Needed Information
- if @user.stripe_account_status[‘due_by’]
%p
Due by:
%strong= Time.at(
@user.stripe_account_status[‘due_by’]
).strftime(“%Y-%m-%d”)
- if params[:debug]
= debug @user.stripe_account_status['fields_needed']
- manager = StripeManaged.new( current_user )
%ul.list-group
- # this account needs a bank account
- if manager.needs? 'bank_account'
%li.list-group-item#bank-account{ data: {
publishable:
@user.publishable_key } }
%script{ src: ‘https://js.stripe.com/v2/’ }
%h3 Bank Account
= hidden_field_tag :bank_account_token
.form-group
- countries =
manager.supported_bank_account_countries
%label.control-label.col-xs-12.col-sm-3 Country:
.col-xs-12.col-sm-9
%select.form-control{ data: { stripe:
‘country’ }
}
- countries.each do |country|
%option{ value: country[:code] }=
country[:name]
.form-group
%label.control-label.col-xs-12.col-sm-3 Account
Number:
.col-xs-12.col-sm-9
%input.form-control{ type: ‘text’, data: {
stripe: ‘account_number’ } }
.form-group#bank-routing-container
%label.control-label.col-xs-12.col-sm-3 Routing
Number:
.col-xs-12.col-sm-9
%input.form-control{ type: ‘text’, data: {
stripe: ‘routing_number’ } }
- # this account needs legal entity info
- if manager.needs? 'legal_entity.'
%li.list-group-item
%h3 Legal Entity
.form-group
%label.control-label.col-xs-12.col-sm-3 First
Name:
.col-xs-12.col-sm-9
%input.form-control{ type: ‘text’, name:
‘legal_entity[first_name]’, value: manager.legal_entity.first_name }
.form-group
%label.control-label.col-xs-12.col-sm-3 Last
Name:
.col-xs-12.col-sm-9
%input.form-control{ type: ‘text’, name:
‘legal_entity[last_name]’, value: manager.legal_entity.last_name }
.form-group
%label.control-label.col-xs-12.col-sm-3 Date of
Birth:
.col-xs-12.col-sm-9
- dob = manager.legal_entity.dob
- selected = Date.new( dob.year, dob.month,
dob.day ) rescue nil
= date_select ‘legal_entity’, ‘dob’,
{ selected: selected,
prompt: true,
start_year: 90.years.ago.year,
end_year: 13.years.ago.year },
{ class: ‘form-control’ }
- if manager.needs?
‘legal_entity.personal_id_number’
.form-group
%label.control-label.col-xs-12.col-sm-3
Personal
ID Number:
.col-xs-12.col-sm-9
%input.form-control{ type: ‘text’, name:
‘legal_entity[personal_id_number]’ }
- elsif manager.needs? ‘legal_entity.ssn_last_4’
.form-group
%label.control-label.col-xs-12.col-sm-3 SSN
Last
4:
.col-xs-12.col-sm-9
%input.form-control{ type: ‘text’, name:
‘legal_entity[ssn_last_4]’ }
.form-group
%label.control-label.col-xs-12.col-sm-3 Address
Line 1:
.col-xs-12.col-sm-9
%input.form-control{ type: ‘text’, name:
‘legal_entity[address][line1]’, value:
manager.legal_entity.address.line1 }
.form-group
%label.control-label.col-xs-12.col-sm-3 Address
Line 2:
.col-xs-12.col-sm-9
%input.form-control{ type: ‘text’, name:
‘legal_entity[address][line2]’, value:
manager.legal_entity.address.line2 }
.form-group
%label.control-label.col-xs-12.col-sm-3 City:
.col-xs-12.col-sm-9
%input.form-control{ type: 'text', name:
‘legal_entity[address][city]’, value: manager.legal_entity.address.city
}
.form-group
%label.control-label.col-xs-12.col-sm-3
State/Province:
.col-xs-12.col-sm-9
%input.form-control{ type: ‘text’, name:
‘legal_entity[address][state]’, value:
manager.legal_entity.address.state }
.form-group
%label.control-label.col-xs-12.col-sm-3
ZIP/Postal
Code:
.col-xs-12.col-sm-9
%input.form-control{ type: ‘text’, name:
“legal_entity[address][postal_code]”, value:
manager.legal_entity.address.postal_code }
%br
.buttons
= f.submit 'Save Info', class: 'btn btn-lg
btn-primary’
- if current_user.standalone?
%h3 Stripe Account Management
%p
Account Type:
%strong Standalone
- # the current account status, whether the
- # account can make charges or can receive transfers
%p
.account-status
Status:
- charges = [ 'Charges',
@user.stripe_account_status[‘charges_enabled’] ]
- transfers = [ ‘Transfers’,
@user.stripe_account_status[‘transfers_enabled’] ]
%table
- [ charges, transfers ].each do |text, yn|
%tr
%td
%span.label{ class: yn ? ‘label-primary’ :
‘label-danger’ }
%span.glyphicon{ class: yn ? ‘glyphicon-ok’ :
‘glyphicon-remove’ }
= text
- unless @user.stripe_account_status[‘details_submitted’]
%hr
%ul.list-group
%li.list-group-item
%a.btn.btn-primary.btn-lg.pull-right{ href:
‘Stripe Login | Sign in to the Stripe Dashboard’ }
%span.glyphicon.glyphicon-arrow-right
%h3 Claim or activate your account.
%p
You will also receive a link from Stripe via email.
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>
Thanks for any help