I want to integrate Paypal within the Devise user registration process. What I want is to have a standard rails form based on the devise resource, that also has custom fields belonging to the user's model.
When a user fills in those fields and clicks on signup, it will be redirected to Paypal, when he clears from paypal and returns to our site then the user data must be created.
For the scenario where the user fill's out the paypal form but doesn't come back to our site, we have to keep record of user before redirecting to Paypal.
For this we can create a flag in user model and use Paypal IPN and when the user transaction notified, set that flag.
But in the case when the user is redirected to Paypal but doesn't complete the transaction, if the user returns to registration and signup again, our model should not throw error saying that the email entered already exists in the table.
How can we handle all these scenarios, is there any gem or plugin available to work with?
Here i am posting the detail code for performing the whole process.
registration_controller.rb
module Auth
class RegistrationController < Devise::RegistrationsController
include Auth::RegistrationHelper
def create
#user = User.new params[:user]
if #user.valid?
redirect_to get_subscribe_url(#user, request)
else
super
end
end
end
end
registration_helper.rb
module Auth::RegistrationHelper
def get_subscribe_url(user, request)
url = Rails.env == "production" ? "https://www.paypal.com/cgi-bin/webscr/?" : "https://www.sandbox.paypal.com/cgi-bin/webscr/?"
url + {
:ip => request.remote_ip,
:cmd => '_s-xclick',
:hosted_button_id => (Rails.env == "production" ? "ID_FOR_BUTTON" : "ID_FOR_BUTTON"),
:return_url => root_url,
:cancel_return_url => root_url,
:notify_url => payment_notifications_create_url,
:allow_note => true,
:custom => Base64.encode64("#{user.email}|#{user.organization_type_id}|#{user.password}")
}.to_query
end
end
payment_notification_controller.rb
class PaymentNotificationsController < ApplicationController
protect_from_forgery :except => [:create]
layout "single_column", :only => :show
def create
#notification = PaymentNotification.new
#notification.transaction_id = params[:ipn_track_id]
#notification.params = params
#notification.status = "paid"
#custom = Base64.decode64(params[:custom])
#custom = #custom.split("|")
#user = User.new
#user.email = #custom[0]
#user.organization_type_id = #custom[1].to_i
#user.password = #custom[2]
if #user.valid?
#user.save
#notification.user = #user
#notification.save
#user.send_confirmation_instructions
end
render :nothing => true
end
def show
end
end
Related
I am trying to authenticate my new Shopify app. First, my authenticate method redirects the shop owner to Shopify's authentication page:
def authenticate
ShopifyAPI::Session.setup({:api_key => "123", :secret => "456"})
session = ShopifyAPI::Session.new("someshop.myshopify.com")
redirect_to session.create_permission_url(["read_orders"], "https://myapp.com/shopify/post_authenticate?user=someshop")
end
Once the shop owner has approved the integration, the redirect uri triggers my post_authenticate method:
def post_authenticate
ShopifyAPI::Session.setup({:api_key => "123", :secret => "456"})
session = ShopifyAPI::Session.new("#{params[:user]}.myshopify.com")
token = session.request_token(:code => params[:code], :signature => params[:signature], :timestamp => params[:timestamp])
end
But the request_token method returns the following error:
#<ShopifyAPI::ValidationException: Invalid Signature: Possible malicious login>
I have read somewhere that you need to be in the same ShopifyAPI session while doing all of this, but it does not say so in the documentation. And the example app takes a very different approach than the documentation.
As per my comment, I utilize the omniauth methodology for authenticating. Here's a gist of the code for reference. https://gist.github.com/agmcleod/7106363317ebd082d3df. Put all the snippets below.
class ApplicationController < ActionController::Base
protect_from_forgery
force_ssl
helper_method :current_shop, :shopify_session
protected
def current_shop
#current_shop ||= Shop.find(session[:shop_id]) if session[:shop_id].present?
end
def shopify_session
if current_shop.nil?
redirect_to new_login_url
else
begin
session = ShopifyAPI::Session.new(current_shop.url, current_shop.token)
ShopifyAPI::Base.activate_session(session)
yield
ensure
ShopifyAPI::Base.clear_session
end
end
end
end
In my login controller:
def create
omniauth = request.env['omniauth.auth']
if omniauth && omniauth[:provider] && omniauth[:provider] == "shopify"
shop = Shop.find_or_create_by_url(params[:shop].gsub(/https?\:\/\//, ""))
shop.update_attribute(:token, omniauth['credentials'].token)
shopify_session = ShopifyAPI::Session.new(shop.url, shop.token)
session[:shop_id] = shop.to_param
redirect_to root_url
else
flash[:error] = "Something went wrong"
redirect_to root_url
end
end
config/initializers/omniauth.rb
Rails.application.config.middleware.use OmniAuth::Builder do
provider :shopify, Settings.api_key, Settings.api_secret,
scope: 'write_products,write_script_tags,read_orders',
setup: lambda { |env| params = Rack::Utils.parse_query(env['QUERY_STRING'])
env['omniauth.strategy'].options[:client_options][:site] = "http://#{params['shop']}" }
end
Then in your routes file, map the create action of your session appropriately:
match '/auth/shopify/callback', :to => 'login#create'
From there i use the shopify_session method as an around filter on the appropriate controllers.
Been trying to get fb_graph working so i can get things like someones friendlist and i cannot get rid of this error. The ActiveSupport::Memoizable is included in the facebook class. Trying figure it out from a fb_graph example application here https://github.com/nov/fb_graph_sample
image of error: http://imgur.com/VXSHhJf
facebook model:
class Facebook < ActiveRecord::Base
def profile
#profile ||= FbGraph::User.me(self.access_token).fetch
end
class << self
extend ActiveSupport::Memoizable
def config
#config ||= if ENV['fb_client_id'] && ENV['fb_client_secret'] && ENV['fb_scope']
{
:client_id => ENV['fb_client_id'],
:client_secret => ENV['fb_client_secret'],
:scope => ENV['fb_scope'],
}
else
YAML.load_file("#{Rails.root}/config/facebook.yml")[Rails.env].symbolize_keys
end
rescue Errno::ENOENT => e
raise StandardError.new("config/facebook.yml could not be loaded.")
end
def app
FbGraph::Application.new config[:client_id], :secret => config[:client_secret]
end
def auth(redirect_uri = nil)
FbGraph::Auth.new config[:client_id], config[:client_secret], :redirect_uri => redirect_uri
end
def identify(fb_user)
_fb_user_ = find_or_initialize_by_identifier(fb_user.identifier.try(:to_s))
_fb_user_.access_token = fb_user.access_token.access_token
_fb_user_.save!
_fb_user_
end
end
end
and here is facebooks_controller
require 'rack/oauth2'
class FacebooksController < ApplicationController
before_filter :require_authentication, :only => :destroy
rescue_from Rack:.center.hero-unit
%h1 Welcome to Dropshare
%h2
This is the home page for Dropshare
%p (at least for time being)
= render 'layouts/facebook_signup'
= render 'layouts/drive_signup'
/
<haml:loud> provide(:title, 'Home')</haml:loud>
<h1>Home</h1>
<p>This is the home page (for the time being) for Dropshare</p>
Sign up now!
:OAuth2::Client::Error, :with => :oauth2_error
# handle Facebook Auth Cookie generated by JavaScript SDK
def show
auth = Facebook.auth.from_cookie(cookies)
authenticate Facebook.identify(auth.user)
redirect_to dashboard_url
end
# handle Normal OAuth flow: start
def new
client = Facebook.auth(callback_facebook_url).client
redirect_to client.authorization_uri(
:scope => Facebook.config[:scope]
)
end
# handle Normal OAuth flow: callback
def create
client = Facebook.auth(callback_facebook_url).client
client.authorization_code = params[:code]
access_token = client.access_token! :client_auth_body
user = FbGraph::User.me(access_token).fetch
authenticate Facebook.identify(user)
redirect_to dashboard_url
end
def destroy
unauthenticate
redirect_to root_url
end
private
def oauth2_error(e)
flash[:error] = {
:title => e.response[:error][:type],
:message => e.response[:error][:message]
}
redirect_to root_url
end
end
Solution
replace
ActiveSupport::Memoizable
with memoist and require 'memoist'
I think you may be running into the fact that ActiveSupport::Memoizable was deprecated & removed from Rails.
https://github.com/rails/rails/commit/36253916b0b788d6ded56669d37c96ed05c92c5c
The author of that gem is running this version of Rails in their gemfile, so I would presume it's supported through this:
gem 'rails', '~>3.2.11'
I'm guessing you're running a newer version of Rails.
Currently I have ActionMailer send an email when a user registers, and I generate a random :sign_in_token with the user.
How can a user then click on the link sent to his email and update the users :registration_complete boolean value to TRUE?
Currently, I am able to send the link and generates a random token, but I don't know how to update the boolean value through the email.
MODELS
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation, :sign_in_token,
:registration_complete
###This generates my sign_in_token
def generate_sign_in_token
self.sign_in_token = Digest::SHA1.hexdigest([Time.now, rand].join)
end
end
CONTROLLER
def create
#user = RegularUser.new(params[:regular_user])
if #user.save
###Sends the User an email with sign_in_token
UserMailer.registration_confirmation(#user, login_url+"/#{#user.sign_in_token}").deliver
flash[:success] = "Please Check Your Email to Verify your Registration!"
redirect_to (verifyemail_path)
else
render 'new'
end
end
USER_MAILER
def registration_confirmation(user, login_url)
#login_url = login_url
#user = user
mail(:to => "#{user.name} <#{user.email}>", :subject => "Welcome to APP")
end
VIEWS
###Redirects User to Login Page, But how do i connect it to my activate method?
<%= link_to "Complete Registration", #login_url %>
ROUTES
match '/login/:sign_in_token', :to => 'sessions#new'
When they click a link, it takes them to a controller with an action of set_complete using a GET request, which sets the boolean value.
Something like:
def set_complete
user = User.find(params[:user])
user.update_attribute(registration_complete => true)
redirect_to login_path # or whatever your login url is...
end
For the controller action and something like this for the link:
<a href="example.com/registrations/set_complete?user=1" />
Here is a sample of what might go in the routes file:
get "/users/set_complete", :to => "users#set_complete"
You'd probably need to set the user id to whatever you want using erb, andmake a few other app-specific customizations, but this is the general idea.
Hope this helps!
I'm using Devise invitable for invitation. Typically, in the invitation email there will be a link to redirect the invitee to the sign_in page, some url like this
mywebsite.com/users/invitation/accept?invitation_token=J-azZ8fKtkuAyp2VZWQX
This url comes from invitation_instructions.html:
<p><%= link_to 'Accept invitation', accept_invitation_url(#resource, :invitation_token => #token) %></p>
Now I want to return the invitation url in my controller as json response, something like this:
def invite
invitee = User.invite!({:email => email}, current_user)
accept_invitation_url = ....
render :json => accept_invitation_url
end
any idea how to get the accept_invitation_url in the controller? Thanks!
try to include the url helpers module in your controller:
class MyController < ApplicationController
include DeviseInvitable::Controllers::UrlHelpers
def invite
invitee = User.invite!({:email => email}, current_user)
render :json => accept_invitation_url(invitee, :invitation_token => invitee.token)
end
end
The URL Helper module for the Devise Invitable Gem can be found here on github
Ok the raw invitation token is not accessible by default because it's a instance variable without accessor (source), there are two ways you could solve this.
The ugly way, without modifying your model class:
def invite
invitee = User.invite!({:email => email}, current_user)
raw_token = invitee.instance_variable_get(:#raw_invitation_token)
render :json => accept_invitation_url(invitee, :invitation_token => raw_token)
end
The clean way, by adding an attribute reader to your user model class:
# User Model
class User < ActiveRecord::Base
attr_reader :raw_invitation_token
# rest of the code
end
# In your controller
def invite
invitee = User.invite!({:email => email}, current_user)
raw_token = invitee.raw_invitation_token
render :json => accept_invitation_url(invitee, :invitation_token => raw_token)
end
Update (16th October 2015):
It seems like the UrlHelper module has been removed and the invitation is handled as a normal route, so you can remove the include DeviseInvitable::Controllers::UrlHelpers and replace the accept_invitation_url call with:
Rails.application.routes.url_helpers.accept_invitation_url(invitee, :invitation_token => raw_token)
I found out that to use accept_invitation_url outside the standard invitation mailer view you need to include inside the mailer the following helper:
include Devise::Controllers::UrlHelpers
I tried Rails.application.routes.url_helpers.accept_invitation_url(invitee, :invitation_token => raw_token) but it is not working.
I've set up authentication with the Foursquare API and my Rails app, and now it's time to add functionality. I am not too proficient with Rails - it is not my first language. I want to allow a merchant to connect with Foursquare (Working) and then direct the merchant to a page where they can see unique visitors to their venue, the number of checkins to the venue, and who the mayor of the venue is. I know what endpoints to use, I'm just not sure how to implement them in rails. (Access Token, etc.)
Thanks!
After Answer
I'm trying to implement Turd Ferguson's answer, but I'm not getting anywhere. I keep getting an error saying the method is undefined. I want to try a simple venue search as soon as the user is authenticated. (Create Action)
Also, I'm using OmniAuth for authentication.
sessions_controller.rb
class SessionsController < ApplicationController
require 'foursquare'
def create
auth_hash = request.env['omniauth.auth']
venues = Foursquare::search_venues("starbucks")
render :text => venues
end
def failure
end
def destroy
session[:user_id] = nil
render :text => "Logged out!"
end
def callback
code = params[:code]
#access_token = foursquare.access_token(code, callback_session_url)
session[:access_token] = #access_token
redirect_to examples_path
end
end
foursquare.rb
class Foursquare
def self.search_venues(text)
client.search_venues(:ll => '36.142064,-86.816086', :query => text)
end
def self.client
#client ||= Foursquare2::Client.new(:client_id => '0YO3F0JNZIPVKG1DE01MNPB132D4JZ0QYRQSOWTZQKHHOPKB', :client_secret => 'GMBOGWUNL2GIKZZXQPSLE4BMFNGB5LDHQREH2UKUCK1TJ1L0')
end
end
Have you looked into using a gem such as foursquare2?
Using the gem you could create a Foursquare class like:
class Foursquare
def self.search_venues(text)
client.search_venues(:ll => '36.142064,-86.816086', :query => text)
end
def self.client
#client ||= Foursquare2::Client.new(:client_id => 'your_client_id', :client_secret => 'your_secret')
end
end
You could then call this anywhere you wanted by doing something like:
venues = Foursquare::search_venues "foobar"