I'm working on setting up a webhook from Stripe. Essentially because it's a subscription model and I need to know if I shall keep on renewing the month or not. I'd like to do that through the event 'invoice.payment_succeeded'.
When I test my webhook url in stripe I get this:
host localhost:3000 resolves to illegal IP 127.0.0.1
my full endpoint is:
localhost:3000/hooks/receiver
route:
get 'hooks/receiver' => "hooks#receiver"
and the hooks controller looks like this:
class HooksController < ApplicationController
require 'json'
Stripe.api_key
def receiver
data_json = JSON.parse request.body.read
p data_json['data']['object']['customer']
if data_json[:type] == 'invoice.payment_succeded'
make_active(data_event)
end
if data_json[:type] == 'invoice.payment_failed'
# something make_inactive(data_event)
end
end
def make_active(data_event)
#user = User.find_by_customer_token(data['data']['object']['customer'])
#status = #user.statuses.pluck(:list_id).presence || -1
Resque.enqueue(ScheduleTweets, #user.token, #user.id, #status)
end
def make_inactive(data_event)
end
end
Does anybody know how to fix this?
You cannot use 127.0.0.1 or localhost as a webhook in Stripe. A webhook involves Stripe sending data from their serves to yours, but your 127.0.0.1 isn't available to Stripe since only you can access localhost.
You could use something like ngrok to open your localhost to the interwebs
https://ngrok.com/
Related
I want to log orders data when an order is created in Shopify, my shopify rails app receives web hook data in the webhook controller but the job is not performed. I followed the documentation for the shopify_app gem (8.2.6) but it is not working.
Currently I am working on development environment with ngrok tunneling.
This issue is with development & production environment.
Event Version : 2020-04
This is the config
config.webhooks = [
{topic: 'customers/create', address: Rails.configuration.business_script_endpoint+'/custom/webhooks/customers_create', format: 'json'},
{topic: 'checkouts/create', address: Rails.configuration.business_script_endpoint+'/custom/webhooks/checkouts_create', format: 'json'},
{topic: 'orders/create', address: Rails.configuration.business_script_endpoint+'/custom/webhooks/orders_create', format: 'json'}
]
This is the code for web hook controller
module ShopifyApp
class WebhooksController < ActionController::Base
include ShopifyApp::WebhookVerification
class ShopifyApp::MissingWebhookJobError < StandardError; end
def receive
puts 'Here in webhook receive'
params.permit!
job_args = {shop_domain: shop_domain, webhook: webhook_params.to_h}
webhook_job_klass.perform_later(job_args)
head :no_content
end
private
def webhook_params
params.except(:controller, :action, :type)
end
def webhook_job_klass
webhook_job_klass_name.safe_constantize or raise ShopifyApp::MissingWebhookJobError
end
def webhook_job_klass_name(type = webhook_type)
puts [webhook_namespace, "#{type}_job"].compact.join('/')
[webhook_namespace, "#{type}_job"].compact.join('/').classify
end
def webhook_type
params[:type]
end
def webhook_namespace
ShopifyApp.configuration.webhook_jobs_namespace
end
end
end
I can see the puts message Here in webhook receive
This is the order create job code
class OrdersCreateJob < ActiveJob::Base
def perform(shop_domain:, webhook:)
shop = Shop.find_by(shopify_domain: shop_domain)
puts 'Order create job started'
end
end
I am unable to get the puts message Order create job started as it is not performing the job.
The checkout create web hook is working fine in this case. Other two orders & customers create is not performing.
What do I need to do?
Got the solution for this issue.
When I have change web hook controllers receive functions
webhook_job_klass.perform_later(job_args)
with
webhook_job_klass.perform_now(job_args)
It is working fine.
I have Ember.js client and RoR server side with gem "websocket-rails".
I need to get private channel with authorization to send messages only for one user.
This is how I try:
class AuthorizationController < WebsocketRails::BaseController
before_action :require_login
def authorize_channels
# The channel name will be passed inside the message Hash
puts '*'*400
ap current_user//Here I see the user
channel = WebsocketRails[message[:channel]]
accept_channel current_user
end
def require_login
sign_in(User.find(4))
end
end
My events.rb is:
WebsocketRails::EventMap.describe do
namespace :websocket_rails do
subscribe :subscribe, :to => AuthorizationController, :with_method => :authorize_channels
end
end
But when I try to send message with:
WebsocketRails.users[4].trigger(:test_event, { message: 'This is a private message from WebSocket Rails' } )
I get error #<NoMethodError: undefined method ``trigger' for #<WebsocketRails::UserManager::MissingConnection:0x007ff5bb50dc88>>
And if I try to print WebsocketRails.users I see:
#<WebsocketRails::UserManager:0x007ff5bb617d68 #users={}>
What is wrong?
Thx a lot!
If u use rails-api gem, it disables a session, that was the problem.
My app creates a unique email for each user, and users send email to that address for processing. Using Sendgrid, I've piped incoming emails to my domain (hosted on Heroku) to an address:
site.com/receive_email
I use the TO field to determine the user, since the email address is randomly generated.
I've experimented using an external script like Mailman, but since I'm hosted on Heroku I'd need to have a worker running full time to keep this process going. Not really looking for that at the moment for this test app.
That leaves processing it as a POST request. I have access to POST hash (params["subject"], etc.) at receive_emails.
This is where I get stuck
Would you suggest to deal with raw data from the POST params, or can I use something like Mailman or ActionMailer to process the email for me?
I haven't used Sendgrid to turn emails into post requests, but it works fine with cloudmailin, a heroku addon. Here is an example where someone sends an email to your application, it is processed by cloudmailin/sendgrid and turned into a post, and then sends it to your controller, and then the controller looks through the message params, finds the sender from the email address, and if the sender doesn't already exist, creates an account for her:
class CreateUserFromIncomingEmailController < ApplicationController
require 'mail'
skip_before_filter :verify_authenticity_token
parse_message(params[:message])
def create
User.find_or_create_by_email(#sender)
end
private
def parse_message(message_params)
#message = Mail.new(message_params)
#recipients = #message.to
#sender = #message.from.first
end
end
Good luck.
ActionMailer already depends on the Mail gem, you could use it to parse the incoming email and extract the parts that you want. It is specially useful to deal with multipart emails.
require 'mail'
class IncomingEmails < ApplicationController
skip_before_filter :verify_authenticity_token
def receive_email
comment = Comment.new(find_user, message_body)
comment.save
rescue
# Reject the message
logger.error { "Incoming email with invalid data." }
end
private
def email_message
#email_message ||= Mail.new(params[:message])
# Alternatively, if you don't have all the info wrapped in a
# params[:message] parameter:
#
# Mail.new do
# to params[:to]
# from params[:from]
# subject params[:subject]
# body params[:body]
# end
end
def find_user
# Find the user by the randomly generated secret email address, using
# the email found in the TO header of the email.
User.find_by_secret_email(email_message.to.first) or raise "Unknown User"
end
def message_body
# The message body could contain more than one part, for example when
# the user sends an html and a text version of the message. In that case
# the text version will come in the `#text_part` of the mail object.
text_part = email_message.multipart? ? email_message.text_part : email_message.body
text_part.decoded
end
end
So currently I am manually directing from a naked domain due to restrictions with my hosting provider (Heroku). Everything works just fine. The problem is that if a users visits mydomain.com/route, a redirect will be issued back to www.mydomain.com without the /route. How would I go about re-appending the route, but still redirecting to www. ?
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :ensure_domain
APP_DOMAIN = 'www.domain.com'
def index
end
def ensure_domain
if Rails.env.production?
if request.env['HTTP_HOST'] != APP_DOMAIN
redirect_to "http://#{APP_DOMAIN}", :status => 301
end
end
end
end
EDIT
I removed my code above from my ApplicationController, and opted for using the refraction gem as suggested by hurikhan77, which solved my problem.
Here is refraction_rules.rb I used.
Refraction.configure do |req|
if req.host == "domain.com"
req.permanent! :host => "www.domain.com"
end
end
I suggest using the refraction gem for this: http://rubygems.org/gems/refraction
Ideally, you would set up rules like that in your web server configuration. Requests would become faster, because they would not even reach the rails stack. There would be no need to add any code to your app either.
However, if you are running in some restricted environment, like heroku, I'd advise adding a rack middleware. (Just for guidelines, can't guarantee if this particular code is bug free)
class Redirector
SUBDOMAIN = 'www'
def initialize(app)
#app = app
end
def call(env)
#env = env
if redirect?
redirect
else
#app.call(env)
end
end
private
def redirect?
# do some regex to figure out if you want to redirect
end
def redirect
headers = {
"location" => redirect_url
}
[302, headers, ["You are being redirected..."]] # 302 for temp, 301 for permanent
end
def redirect_url
scheme = #env["rack.url_scheme"]
if #env['SERVER_PORT'] == '80'
port = ''
else
port = ":#{#env['SERVER_PORT']}"
end
path = #env["PATH_INFO"]
query_string = ""
if !#env["QUERY_STRING"].empty?
query_string = "?" + #env["QUERY_STRING"]
end
host = "://#{SUBDOMAIN}." + domain # this is where we add the subdomain
"#{scheme}#{host}#{path}#{query_string}"
end
def domain
# extract domain from request or get it from an environment variable etc.
end
end
You can also test the whole thing in isolation
describe Redirector do
include Rack::Test::Methods
def default_app
lambda { |env|
headers = {'Content-Type' => "text/html"}
headers['Set-Cookie'] = "id=1; path=/\ntoken=abc; path=/; secure; HttpOnly"
[200, headers, ["default body"]]
}
end
def app()
#app ||= Rack::Lint.new(Redirector.new(default_app))
end
it "redirects unsupported subdomains" do
get "http://example.com/zomg?a=1"
last_response.status.should eq 301
last_response.header['location'].should eq "http://www.example.com/zomg?a=1"
end
# and so on
end
Then you can add it to production (or any preferred environments) only
# production.rb
# ...
config.middleware.insert_after 'ActionDispatch::Static', 'Redirector'
If you want to test it in development, add the same line to development.rb and add a record to your hosts file (usually /etc/hosts) to treat yoursubdomain.localhost as 127.0.0.1
Not sure if this is the best solution but you could regex the request.referrer and pull out anything after .com and append it to the APP_DOMAIN
Or I guess you could just take out everything before the first . in request.env['HTTP_HOST'] add replace with http://www. assuming you don't plan on using subdomains.
I have a RESTful Rails application with a resource called "Foo". I'm trying to use REST Client to do a put:
resource = RestClient::Resource.new 'http://localhost:3000/foos/1', :user => 'me', :password => 'secret'
resource.put :name => 'somethingwitty', :content_type => 'application/xml'
But my app raises:
ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken):
/usr/lib/ruby/gems/1.8/gems/actionpack-2.2.2/lib/action_controller/request_forgery_protection.rb:86:in `verify_authenticity_token'
It seems like my app isn't getting the message that this is an XML request and that the AuthenticityToken should be ignored. Maybe I'm not using REST Client correctly. Any ideas on why I'm getting the exception?
Try putting an :only => [:update, :delete, :create] on the protect_from_forgery line in your application controller.
More info: http://ryandaigle.com/articles/2007/9/24/what-s-new-in-edge-rails-better-cross-site-request-forging-prevention
Use something like:
resource.put '<foo><name>somethingwitty</name></foo>', :content_type => 'application/xml'
I think you need to make two changes;
(a) Use the rails routing to tag this as an XML request
(b) Use HTTP Basic Authentication to authenticate the request.
This means changing your URL above to include the username, password like this
me:secret#localhost:3000/foos/1.xml
also note .xml bit
I guess that somewhere on your server-side you have code that authenticates in-bound requests via a before filter. This needs to work something like this ...
#
# If you haven't authenticated already then you are either
# reqirected to the logon screen (for HTML formats) or
# the browser prompts you. You are always allowed to pass
# the username/password in the URL
#
def login_required
#current_user = valid_session?
unless #current_user
if params["format"]
#
# If you specify a format you must authenticate now
#
do_basic_authentication
else
display_logon_screen
end
end
end
#
# Ask Rails for the login and password then authenticate as if this
# were a new login.
#
def do_basic_authentication
user = authenticate_with_http_basic do |login, password|
User.authenticate(login, password)
end
if user
current_user(#current_user = user)
else
request_http_basic_authentication
end
end
That's from our own app and is triggered by a before_filter in ApplicationController.
Also, I don't think you need the :content_type => 'application/xml'. What I normally do is just call post or put directly like this ..
response = RestClient.post URI.encode(url), :record => args
where the url contains the basic authentication and the ".xml"
Happy coding
Chris
Since your application is a Rails app, it might be easier to use ActiveResource for the client.
Something like:
require 'active_resource'
class Foo < ActiveResource::Base
self.site = 'http://localhost:3000/'
end
foo = Foo.new(:name => 'somethingwitty')
foo.save
You can read up on how to do the authentication on the rdoc site.