I used the examples on the paymill page to get the token with no problem. I'm passing the token using an ajax call. But I cannot make the API to make the final call. I cannot really understand how to use the code from the paymill-ruby or paymill-rails code at github, so I went trough the code and found the API call -which is what I'm missing-, but cannot make it work. All I want to do is a simple payment. No subscription, no user. Just a credit card payment. Here is the code from my controller:
token = params[:token]
params = {'amount' => '#{##amount}', 'currency' => 'EUR', 'token' => '#{token}', 'description' => 'description'}
https = Net::HTTP.new('api.paymill.de', 80)
https.use_ssl = false
https.start do |connection|
url = "/v2/transactions"
https_request = Net::HTTP::Post.new(url)
https_request.basic_auth(Paymill.api_key,"")
https_request.set_form_data(params)
#response = https.request(https_request)
end
puts #response.body
puts #response.header
render :action => "confirm"
The response is
<html>
<head><title>302 Found</title></head>
<body bgcolor="white">
<center><h1>302 Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
#<Net::HTTPFound:0x00000004a98400>
And I cannot figure out what's wrong... a few weeks ago I didn't know what ajax was... can anybody give a hand?
I'm not a rails expert but ...
... you are connecting to our API at port 80 without using SSL. We only allow SSL requests to the api so you'll have to connect using port 443 and having SSL enabled.
Hope that solves your issues.
Related
I'm using Ruby on Rails 5 and I need to execute the following command in my application:
curl -F 'client_id=126581840734567' -F 'client_secret=678ebe1b3b8081231aab27dff738313' -F 'grant_type=authorization_code' -F 'redirect_uri=https://uri.com/' -F 'code=AQBi4L2Ohy3Q_N3V48OygFm0zb3gEsL985x5TIyDTNDJaLs93BwXiT1tyGYWoCg1HlBDU7ZRjUfLL5HVlzw4G-7YkVEjp6Id2WuqOz0Ylt-k2ADwDC5upH3CGVtHgf2udQhLlfDnQz5NPsnmxjg4bW3PJpW5FaQs8fn1ztgYp-ssfAf6IRt2-sI45ZC8cqqr5K_12y0Nq_Joh0H-tTfVyNLKatIxHPCqRDb3tfqgmxim1Q' https://api.instagram.com/oauth/access_token
so that it returns something like:
{"access_token": "IGQVJYS0k8V6ZACRC10WjYxQWtyMVRZAN8VXamh0RVBZAYi34RkFlOUxXZnTJsbjlEfnFJNmprQThmQ4hTckpFUmJEaXZAnQlNYa25aWURnX3hpO12NV1VMWDNMWmdIT3FicnJfZAVowM3VldlVWZAEViN1ZAidHlyU2VDMUNuMm2V", "user_id": 17231445640157812}
Is there a way to make Rails execute those types of commands? I was trying the following:
uri = URI.parse('https://api.instagram.com/oauth/access_token')
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data({
"client_id" => "126581840734567",
"client_secret" => "678ebe1b3b8081231aab27dff738313",
"grant_type" => "authorization_code",
"redirect_uri" => "http://nace.network/",
"code" => params[:code]
})
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(request)
end
but I get the following error:
end of file reached
in this line:
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(request)
end
You're using HTTPS, so you need to add this to your code:
Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
res = http.request(request)
end
But if you don't need persistent connections, you could also use this:
res = Net::HTTP.post_form(uri,
"client_id" => "126581840734567",
"client_secret" => "678ebe1b3b8081231aab27dff738313",
"grant_type" => "authorization_code",
"redirect_uri" => "http://nace.network/",
"code" => params[:code]
)
Also, you could consider using a library like Faraday, which is a lot easier to deal with.
Edit
This is from TinMan's comment below, sound points.
Using cURL from inside Ruby or Rails is extremely valuable. There is an incredible amount of functionality inside cURL that isn't implemented in Rails or Ruby; Even Ruby's HTTP clients have a hard time replicating it, so cURL is very acceptable depending on the needs of the application. And, depending on the application, because cURL is in compiled C, it could easily outrun pure Ruby clients.
Curl is a means of issuing HTTP (or HTTPs) requests from the command line.
You don't want to use CURL in Rails. You want to issue HTTP requests from within Rails. Using curl is okay, it's one way to issue HTTP requests from with Rails.
We can refine that down further to, you want to issue HTTP requests from Ruby. Narrowing/distilling down to the most basic version of the problem is always good to do.
We knew all this already probably - still worth writing down for us all to benefit from!
Use HTTP in Ruby
We want to use a HTTP Client. There are many but, for this I'm going to use Faraday (a gem) 'cause I like it.
You've made a good start with Ruby's built in NET:HTTP but I prefer Faraday's DSL. It results in more readable and extendable code.
So, here is a class! I barely tested this so, use as a starting point. Make sure you write some unit tests for it.
# This is a Plain Old Ruby Object (PORO)
# It will work in Rails but, isn't Rails specific.
require 'faraday' # This require is needed as it's a PORO.
class InstagramOAuth
attr_reader :code
# The code parameter will likely change frequently, so we provide it
# at run time.
def initialize(code)
#code = code
end
def get_token
connection.get('/oauth/access_token') do |request|
request.params[:code] = code
end
end
private
def connection
#connection ||= Faraday.new(
url: instagram_api_url,
params: params,
ssl: { :ca_path => https_certificate_location }
)
end
def instagram_api_url
#url ||= 'https://api.instagram.com'
end
# You need to find out where these are for your self.
def https_certificate_location
'/usr/lib/ssl/certs'
end
def params
# These params likely won't change to often so we set a write time
# in the class like this.
{
client_id: '126581840734567',
client_secret: '678ebe1b3b8081231aab27dff738313',
grant_type: 'authorization_code',
redirect_uri: 'https://uri.com/'
}
end
end
# How do we use it? Like so
# Your big old authorisation code from your question
code = 'AQBi4L2Ohy3Q_N3V48OygFm0zb3gEsL985x5TIyDTNDJaLs93BwXiT1tyGYWoCg1HlBDU'\
'7ZRjUfLL5HVlzw4G-7YkVEjp6Id2WuqOz0Ylt-k2ADwDC5upH3CGVtHgf2udQhLlfDnQz'\
'5NPsnmxjg4bW3PJpW5FaQs8fn1ztgYp-ssfAf6IRt2-sI45ZC8cqqr5K_12y0Nq_Joh0H'\
'-tTfVyNLKatIxHPCqRDb3tfqgmxim1Q'
# This will return a Faraday::Response object but, what is in it?
response = InstagramOAuth.new(code).get_token
# Now we've got a Hash
response_hash = response.to_hash
puts 'Request made'
puts "Request full URL: #{response_hash[:url]}"
puts "HTTP status code: #{response_hash[:status]}"
puts "HTTP response body: #{response_hash[:body]}"
When I ran the snippet above I got the following. The class works, you just need to tweak the request params until you get what you want. Hopefully the class demonstrates how to send HTTP requests in Ruby/Rails.
Request made
Request full URL: https://api.instagram.com/oauth/access_token?client_id=126581840734567&client_secret=678ebe1b3b8081231aab27dff738313&code=AQBi4L2Ohy3Q_N3V48OygFm0zb3gEsL985x5TIyDTNDJaLs93BwXiT1tyGYWoCg1HlBDU7ZRjUfLL5HVlzw4G-7YkVEjp6Id2WuqOz0Ylt-k2ADwDC5upH3CGVtHgf2udQhLlfDnQz5NPsnmxjg4bW3PJpW5FaQs8fn1ztgYp-ssfAf6IRt2-sI45ZC8cqqr5K_12y0Nq_Joh0H-tTfVyNLKatIxHPCqRDb3tfqgmxim1Q&grant_type=authorization_code&redirect_uri=https%3A%2F%2Furi.com%2F
HTTP status code: 405
HTTP response body:
Additional Reading
. https://lostisland.github.io/faraday/usage/
. https://github.com/lostisland/faraday/wiki/Setting-up-SSL-certificates
Currently I am working on rails 4 project, and now I have to link / connect another application (not sso but for accessing API's) say example.com. (Note: example.com uses 3-legged oauth security architecture)
After searching found that I have to implement omniouth strategy.
For this I have refereed this link. As per Strategy-Contribution-Guide I am able to complete setup and request Phase, You can find my sample code here.
require 'multi_json'
require 'omniauth/strategies/oauth2'
require 'uri'
module OmniAuth
module Strategies
class MyAppStrategy < OmniAuth::Strategies::OAuth2
option :name, 'my_app_strategy'
option :client_options, {
site: site_url,
authorize_url: authorize_url,
request_url: request_url,
token_url: token_url,
token_method: :post,
header: { Accept: accept_header }
}
option :headers, { Accept: accept_header }
option :provider_ignores_state, true
def consumer
binding.pry
::OAuth::Consumer.new(options.client_id, options.client_secret, options.client_options)
end
def request_phase # rubocop:disable MethodLength
binding.pry
request_token = consumer.get_request_token({:oauth_callback => callback_url}, options.request_params)
session["oauth"] ||= {}
session["oauth"][name.to_s] = {"callback_confirmed" => request_token.callback_confirmed?, "request_token" => request_token.token, "request_secret" => request_token.secret}
if request_token.callback_confirmed?
redirect request_token.authorize_url(options[:authorize_params])
else
redirect request_token.authorize_url(options[:authorize_params].merge(:oauth_callback => callback_url))
end
rescue ::Timeout::Error => e
fail!(:timeout, e)
rescue ::Net::HTTPFatalError, ::OpenSSL::SSL::SSLError => e
fail!(:service_unavailable, e)
end
def callback_phase # rubocop:disable MethodLength
fail(OmniAuth::NoSessionError, "Session Expired") if session["oauth"].nil?
request_token = ::OAuth::RequestToken.new(consumer, session["oauth"][name.to_s].delete("request_token"), session["oauth"][name.to_s].delete("request_secret"))
opts = {}
if session["oauth"][name.to_s]["callback_confirmed"]
opts[:oauth_verifier] = request["oauth_verifier"]
else
opts[:oauth_callback] = 'http://localhost:3000/auth/callback' #callback_url
end
#access_token = request_token.get_access_token(opts)
super
rescue ::Timeout::Error => e
fail!(:timeout, e)
rescue ::Net::HTTPFatalError, ::OpenSSL::SSL::SSLError => e
fail!(:service_unavailable, e)
rescue ::OAuth::Unauthorized => e
fail!(:invalid_credentials, e)
rescue ::OmniAuth::NoSessionError => e
fail!(:session_expired, e)
end
def custom_build_access_token
binding.pry
verifier = request["oauth_verifier"]
client.auth_code.get_token(verifier, get_token_options(callback_url), deep_symbolize(options.auth_token_params))
end
alias_method :build_access_token, :custom_build_access_token
def raw_info
binding.pry
#raw_info ||= access_token.get('users/me').parsed || {}
end
private
def callback_url
options[:redirect_uri] || (full_host + script_name + callback_path)
end
def get_token_options(redirect_uri)
{ :redirect_uri => redirect_uri }.merge(token_params.to_hash(:symbolize_keys => true))
end
end
end
end
I am able redirect to example.com, also after login I am able to return to my callback_phase (you will ask how did you know, so answer is I have added binding.pry in callback_phase method for checking the flow).
But after executing the strategy I am getting following error
ERROR -- omniauth: (my_app_strategy) Authentication failure! invalid_credentials: OAuth2::Error.
After debugging found that I am getting this error for the super call (from callback_phase method).
First I though may be there are some credentials issue but I am able fetch access token using following (which is executing before the super call)
#access_token = request_token.get_access_token(opts)
Also for more information I am getting error for build_access_token which is the oauth2 method
You can refer this link for more info (just search the build_access_token on the page).
EDIT - 1
After debugging found that getting this issue from the request method.
(While making the faraday request). Here is the code snippet
response = connection.run_request(verb, url, opts[:body], opts[:headers]) do |req|
yield(req) if block_given?
end
Here is my faraday request
#<struct Faraday::Request method=:post, path="example.com/oauth/access_token", params={}, headers={"User-Agent"=>"Faraday v0.9.2", "Content-Type"=>"application/x-www-form-urlencoded"}, body={"grant_type"=>"authorization_code", "code"=>"aPexxxvUg", "client_id"=>"xxxxxur303GXEch7QK9k", "client_secret"=>"xxxxxxcad97b3d252e2bcdd393a", :redirect_uri=>"http://localhost:3000/auth/my_app_strategy/callback"}, options=#<Faraday::RequestOptions (empty)>>
In response I am getting following error message
HTTP Status 400 - Inadequate OAuth consumer credentials.
So can any one help to fix this issue?
Is there any other way to store the access token so that I can utilize this for communication purpose.
Thanks
First of all, I wan to make clear how Oauth2 works:
Oauth2, the protocol says:
You redirect the user to the provider sign in endpoint adding some required parameters (Ejm: PROVIDER/public/oauth?redirect_uri=MYWEB/oauthDemo&
response_type=code&client_id=ABCDE). Sometimes there is also a scope/permission/resource parameter that indicates whats your purpose.
-> Then the users signs in and is redirected to your endpoint MYWEB/public/oauth with a code
Now you have to request the access token doing a POST to the providers endpoint. Example:
POST PROVIDER?code=d5Q3HC7EGNH36SE3N&
client_id=d4HQNPFIXFD255H&
client_secret=1a98b7cb92407cbd8961cd8db778de53&
redirect_uri=https://example.com/oauthDemo&
grant_type=authorization_code
Now you have the access_token and you can use it to get information or decode it using JWT.
Having this clear, and seeing that your call seems corect:
#<struct Faraday::Request method=:post, path="PROVIDER/oauth/access_token", params={}, headers={"User-Agent"=>"Faraday v0.9.2", "Content-Type"=>"application/x-www-form-urlencoded"}, body={"grant_type"=>"authorization_code", "code"=>"aPexxxvUg", "client_id"=>"xxxxxur303GXEch7QK9k", "client_secret"=>"xxxxxxcad97b3d252e2bcdd393a", :redirect_uri=>"MYWEB/auth/my_app_strategy/callback"}, options=#<Faraday::RequestOptions (empty)>>
As the response is "HTTP Status 400 - Inadequate OAuth consumer credentials.", I think maybe you:
a. Your client is not well configured on the Provider. Usually you use to have a basic configuration on the provider site so he can recognise you. So maybe is not well configured.
b. There is a resource/permission/scope parameter missing or wrong configured on the first step (in the redirection to the provider). So when you ask for the token there is a problem.
I've build a listener for my IPN. I can receive the request via IPN Simulator and all the parameters are correct, but when I send it back to link for sending my request to make validations it will ALWAYS return INVALID.
I've tried changing the encoding on my PayPal account to UTF-8 but it didn't work.
I'm not using any gem for this and I'm not looking forward to this.
class PaymentNotificationController [:create] #Otherwise the request from PayPal wouldn't make it to the controller
def create
response = validate_IPN_notification(request.raw_post)
case response
when "VERIFIED"
P 'worked'
when "INVALID"
p 'did not work'
else
end
render :nothing => true, :status => 200, :content_type => 'text/html'
end
protected
def validate_IPN_notification(raw)
uri = URI.parse('https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_notify-validate')
#uri = URI.parse('https://www.paypal.com/cgi-bin/webscr?cmd=_notify-validate')
http = Net::HTTP.new(uri.host, uri.port)
http.open_timeout = 60
http.read_timeout = 60
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
http.use_ssl = true
response = http.post(uri.request_uri, raw,
'Content-Length' => "#{raw.size}",
'User-Agent' => "My custom user agent"
).body
end
end
Please help me, I've been stuck with it all afternoon and can't get it figured out.
I think your problem is not follow exactly what papal requires
From Paypal document
Your listener HTTP POSTs the complete, unaltered message back to PayPal.
Note This message must contain the same fields, in the same order, as the original IPN from PayPal, all preceded by cmd=_notify-validate. Further, this message must use the same encoding as the original.
So you validate function would be:
def validate_IPN_notification(ipn_url)
validate_url = "#{ipn_url}?cmd=_notify-validate"
# Post validate_url
...
end
Note that the ipn_url is the same url of IPN
It turned out to be a Windows problem which I don't know how to fix.
I tried the same thing on my Ubuntu 15 and it worked perfectly.
I'm using stripe connect on my website, and when a user in production tries to connect his stripe account to my web site, I have the following error in the stripe callback :
{
"error": "invalid_redirect_uri",
"error_description": "Invalid redirect URI 'http://www.mywebsite.com/stripe_connections/callback'. Ensure this uri exactly matches one of the uris specified in your application settings",
"state": "4 ยป
}
whereas my redirecti URIS in my stripe application setting is https://www.mywebsite.com/stripe_connections/callback
here is my controller :
require 'oauth2'
class StripeConnectionsController < ApplicationController
skip_after_action :verify_authorized
def new
stripe_auth_url = "https://connect.stripe.com/oauth"
client = OAuth2::Client.new(ENV['STRIPE_CONNECT_CLIENT_ID'], ENV['STRIPE_SECRET_KEY'], :site => stripe_auth_url)
#stripe_url = client.auth_code.authorize_url(:redirect_uri => "#{request.protocol}#{request.host_with_port}/stripe_connections/callback", :scope => 'read_write', state: params[:brief_id])
end
def callback
#brief = Brief.find(params[:state])
stripe_auth_url = "https://connect.stripe.com/oauth"
#user = current_user
client = OAuth2::Client.new(ENV['STRIPE_CONNECT_CLIENT_ID'], ENV['STRIPE_SECRET_KEY'], :site => stripe_auth_url)
access_token = client.auth_code.get_token(params[:code], :redirect_uri => '#{request.protocol}#{request.host_with_port}/oauth2/callback')
stripe_connection = StripeConnection.find_or_create_by(user_id: #user.id)
stripe_connection.update_attributes(access_token: access_token.token,
refresh_token: access_token.refresh_token,
livemode: access_token.params['livemode'],
stripe_user_id: access_token.params['stripe_user_id'],
publishable_key: access_token.params['stripe_publishable_key']
)
#user.profile.projects.where(state: 'pending').update_all(state: "on_sale")
end
end
I'm using heroku and paying the SSL add-ons already.
I don't know why stripe is returning http instead of https. Does anyone have an idea? thx.
ps: this has already worked before in production and works in the beta version of the website
Do you have a button that the user clicks to connect to stripe? I just removed the redirect_uri parameter.
You have to remove the redirect_uri of client.auth_code.authorize_url() in the new method and also the redirect_uri in the callback method and put the right protocol in the dashboard stripe.
I'm trying to authenticate a user with Instagram gem. So I have a page with code param returned by instagram, and I only need to send it back with POST request. According to gem documentation I need to do something like:
get "/oauth/callback" do
response = Instagram.get_access_token(params[:code], :redirect_uri => CALLBACK_URL)
session[:access_token] = response.access_token
redirect "/feed"
end
so I have
def authenticate
response = Instagram.get_access_token(params[:code], :redirect_uri => "http://127.0.0.1:3000")
session[:access_token] = response.access_token
redirect "/feed"
end
And I'm getting
Completed 500 Internal Server Error in 957ms
Instagram::BadRequest (POST https://api.instagram.com/oauth/access_token/: 400):
app/controllers/instagram_controller.rb:25:in `authenticate'
I tried making curl request as per Instagram api documentation, and it works with the same params.
About client_id, I store those keys in instagram.rb in initializers, so it looks like
require "instagram"
Instagram.configure do |config|
config.client_id = "123345"
config.client_secret = "123123"
end
CALLBACK_URL = "http://127.0.0.1:3000"
Thanks a lot in advance.
Found the problem. So, I have devise in my app as well, and I didn't put "redirect_url" in config.omniauth over there. Exactly the same thing people talking about over here https://github.com/Instagram/instagram-ruby-gem/issues/22.
Thanks for help.