I'm working with an Api, so far so good i am able to send requests and receive response, but i want the ruby code to send the json response and message that can be accessible from my rails controller so that i can get simple_form to render the error or success messages and also handle redirection from the rails controller, i also want to save transaction details from the response in a model, here's my lib code
class SimplePay
require 'net/https'
require 'uri'
require 'json'
def self.pay(api_key, token, amount, amount_currency, status)
uri = URI.parse('https://checkout.simplepay.ng/v2/payments/card/charge/')
header = {'Content-Type': 'text/json'}
data = {
'token': token,
'amount': amount,
'amount_currency': amount_currency
}
http = Net::HTTP.new(uri.host, uri.port)
request = Net::HTTP::Post.new(uri.request_uri, header)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(uri.request_uri, header)
request.basic_auth(api_key, '')
request.set_form_data(data)
response = http.request(request)
res = JSON.parse(response.read_body)
response_code = res['response_code']
if response.kind_of? Net::HTTPSuccess
if response_code.eql? 20000
puts 'success'
else
puts 'Failed'
end
elsif status.eql? true
puts 'Success But Not Charged'
elsif status.eql? false
raise error 'Failed To Charge Card'
elsif response.kind_of? Net::HTTPBadRequest
raise error 'Failed 400'
elsif response.kind_of? Net::HTTPError
raise error 'Failed 404'
else
raise error 'Unknown Error'
end
end
end
How do i go about achieving this?
I would say send a Hash to the controller and then when you are sending it just doing a .to_json will make it into a json
I am having weird behavior of my omniauth app. Basically, I have admin panel to whom access it is required to authenticate with Yandex account.
Problem: I did everything as required in multiple guides and everything worked fine since yesterday I tried to authenticate with Yandex account and I received HTTPBadRequest error.
Note: I haven't changed a bit in my code. All my access data client_Id and password hasn't changed either.
Gemfile:
gem "omniauth-yandex"
Routes:
devise_for :users, :controllers => { :omniauth_callbacks => "callbacks" }
CallbacksController:
def yandex
require 'net/http'
require 'json' # => false
#user = User.from_omniauth(request.env["omniauth.auth"])
#client_id = Rails.application.secrets.client_id
#secret = Rails.application.secrets.password
#authorization_code = params[:code]
#user.update_attribute(:code, #authorization_code)
#user.update_attribute(:state, params[:state])
#post_body = "grant_type=authorization_code&code=#{#authorization_code}&client_id=#{#client_id}&client_secret=#{#secret}"
#url = "https://oauth.yandex.ru/token"
url = URI.parse(#url)
req = Net::HTTP::Post.new(url.request_uri)
req['host'] ="oauth.yandex.ru"
req['Content-Length'] = #post_body.length
req['Content-Type'] = 'application/x-www-form-urlencoded'
req.body = #post_body
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = (url.scheme == "https")
#response_mess = http.request(req)
refreshhash = JSON.parse(#response_mess.body)
access_token = refreshhash['access_token']
refresh_token = refreshhash['refresh_token']
access_token_expires_at = DateTime.now + refreshhash["expires_in"].to_i.seconds
if access_token.present? && refresh_token.present? && access_token_expires_at.present?
#user.update_attribute(:access_token, access_token)
#user.update_attribute(:refresh_token, refresh_token)
#user.update_attribute(:expires_in, access_token_expires_at)
sign_in(#user)
redirect_to admin_dashboard_index_path
end
end
User model:
require 'rest-client'
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable,
:omniauthable, :omniauth_providers => [:yandex]
def self.from_omniauth(auth)
where(provider: auth.provider, uid: auth.uid).first_or_create do |user|
user.provider = auth.provider
user.uid = auth.uid
user.email = auth.info.email
user.code = auth.info.code
user.state = auth.info.state
user.password = Devise.friendly_token[0,20]
end
end
def refresh_token_if_expired
if token_expired?
response = RestClient.post "https://oauth.yandex.com/token",
:grant_type => 'refresh_token',
:refresh_token => self.refresh_token
refreshhash = JSON.parse(response.body)
self.access_token = refreshhash['access_token']
self.expires_in = DateTime.now + refreshhash["expires_in"].to_i.seconds
self.save
puts 'Saved'
end
end
def token_expired?
expiry = Time.at(self.expires_in)
logger.debug "#{expiry}"
return true if expiry < Time.now
token_expires_at = expiry
save if changed?
false
end
end
Problem:
HTTPBadRequest error highlights the line in CallbacksController:
#response_mess = http.request(req)
What I have tried:
1) Restarted Rails app;
2) Deleted user from database and tried to sign in again;
3) Deleted registered app in Yandex Oauth section. Then added back again with new client_id and password.
From Yandex Oauth guide:
Exchanging an authorization code for a token
The application sends the code, along with its ID and password, in a POST request.
POST /token HTTP/1.1
Host: oauth.yandex.
Content-type: application/x-www-form-urlencoded
Content-Length: <length of request body>
[Authorization: Basic <encoded client_id:client_secret string>]
grant_type=authorization_code
& code=<authorization code>
[& client_id=<application ID>]
[& client_secret=<application password>]
[& device_id=<device ID>]
[& device_name=<device name>]
UPDATE: I am receiving this error:
{"error_description": "Code has expired", "error": "invalid_grant"}
UPDATE 2 : I tried to contact Yandex Support. I sent them brief description to my problem and link to question. But no response.
UPDATE 3:
I tried the same POST request in CHROME POSTMAN and received the same response as
{"error_description": "Code has expired", "error": "invalid_grant"}
Update 4:
I checked again all my credentials client_id and password for Yandex and they are 100% valid.
From the OAUTH 2.0 RFC:
invalid_grant
The provided authorization grant (e.g., authorization
code, resource owner credentials) or refresh token is
invalid, expired, revoked, does not match the redirection
URI used in the authorization request, or was issued to
another client.
This is further explained on the Yandex site:
The access_token could not be issued. Either the temporary authorization code was not issued by Yandex.Money, or it has expired, or an access_token has already been issued for this temporary authorization code (a duplicate request for an access token using the same temporary authorization code).
You are supplying invalid credentials to Yandex. Forget the code you've written for a moment because the only part that is failing is when it sends the data to Yandex. Yandex is telling you the data is invalid and you have confirmed this with Postman. That means this is sending invalid data:
#post_body = "grant_type=authorization_code&code=#{#authorization_code}&client_id=#{#client_id}&client_secret=#{#secret}"
So double check these things:
Is the grant_type supposed to be authorization_code?
Are you certain #authorization_code is set to a value (and not nil) and that the value is valid?
Same question for #client_id?
Same question #secret?
Are you missing a redirect_uri value?
When I testing the procedure of requesting an access token, which is part of authorize flow, from Doorkeeper gem at localhost side through RSpec with Ruby on Rails, Devise, Grape and Wine_bouncer, RSpec always receives a 401 response from Doorkeeper, whose error description says Invalid-grant: The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.
I want to know how to solve this problem. Please help me, thank you.
The followings are my test environments and code:
Ruby 2.3.0
Rails 4.2.5.1
Doorkeeper 3.1.0
Devise 3.5.5
RSpec-core 3.4.2, RSpec-support 3.4.1, RSpec-mocks 3.4.1, RSpec-rails 3.4.1, RSpec-expectations 3.4.0
Wine_bouncer 0.5.1
Doorkeeper configuration at config/initializers/doorkeeper.rb
Doorkeeper.configure do
orm :active_record
resource_owner_authenticator do
session[:user_return_to] = request.fullpath
current_user || redirect_to(new_user_session_url)
end
authorization_code_expires_in 20.minutes
access_token_expires_in 30.days
enable_application_owner :confirmation => false
default_scopes :public, :description => "Access public data."
optional_scopes :write, :description => "Update your data."
optional_scopes :admin, :description => "Do admin things."
access_token_methods :from_bearer_authorization, :from_access_token_param, :from_bearer_param
grant_flows %w(authorization_code client_credentials implicit)
skip_authorization do
true
end
end
spec/requests/api/v1/specifiedvegetables_spec.rb
describe SpecifiedVegetables do
describe 'OAuth client requests the grant' do
context 'When a REST client sends a request for getting the grant' do
before(:all) do
post "http://localhost:3000/users/sign_in?user[email]=test%40test123%2Ecom&user[password]=12345678" # Log in the devise
#app = Doorkeeper::Application.new :name => "rspectest-107", :redirect_uri => "https://localhost:3000/api/v1/specified_vegetables/", :scopes => "public"
#app.owner = User.last
#app.save! # Create OAuth client into database.
#authorize_code = String.new # Use later for getting authorize grant code
end
it 'should getting response code 302 for requesting authorization code.' do
query_url = "http://localhost:3000/oauth/authorize"
parameters = { "response_type" => "code", "client_id" => #app.owner.oauth_applications.last.uid, "redirect_uri" => "https://localhost:3000/api/v1/specified_vegetables/", "scope" => "public"}
headers = {'Content-Type' => 'application/x-www-form-urlencoded'}
get query_url, parameters, headers # Send request for getting authorize grant code
expect(response.status).to eq(302)
authorize_code_param = Rack::Utils.parse_query(URI.parse(response.location).query)
#authorize_code << authorize_code_param['code'] # Get the authorize grant code
end
it 'should get response code 302 for requesting access token.' do
query_url = "http://localhost:3000/oauth/token"
parameters = {"grant_type" => "authorization_code", "code" => #authorize_code, "client_id" => #app.owner.oauth_applications.last.uid, "redirect_uri" => "https://localhost:3000/api/v1/specified_vegetables/"}
headers = {'Content-Type' => 'application/x-www-form-urlencoded', "Authorization" => "Basic " + Base64.urlsafe_encode64(#app.owner.oauth_applications.last.uid + ":" + #app.owner.oauth_applications.last.secret, :padding => false)}
post query_url, parameters, headers # Send request for getting access token
expect(response).to eq(200) # **Receive the Error response**
end
after(:all) do
#app.destroy # After running all test cases, destroy this OAuth client application.
end
end
end
end
Error response after running RSpec command at root directory of rails app.
rspec spec/requests/api/v1/specifiedvegetables_spec.rb
expected: 200
got: #, #stream=#, #buf=["{\"error\":\"invalid_grant\",\"error_description\":\"The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.\"}"], #closed=false>, #header={"X-Frame-Options"=>"SAMEORIGIN", "X-XSS-Protection"=>"1; mode=block", "X-Content-Type-Options"=>"nosniff", "Cache-Control"=>"no-store", "Pragma"=>"no-cache", "Content-Type"=>"application/json; charset=utf-8", "WWW-Authenticate"=>"Bearer realm=\"Doorkeeper\", error=\"invalid_grant\", error_description=\"The provided authorization grant is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client.\"", "X-Request-Id"=>"97099b43-3456-4396-b9d9-cf744ec38ea6", "X-Runtime"=>"0.006156", "Content-Length"=>"213"}, #status=401, #sending_file=false, #blank=false, #cv=#, #cond=#>, #committed=false, #sending=false, #sent=false, #content_type=#, #charset="utf-8", #cache_control={:extras=>["no-store"]}, #etag=nil>
20160306 PM10:39 UTC+8 update:
If I try to get access token through Postman software(get it from Chrome store), I can get it as I expect. But trying to get it through rspec, I can't acquire it.
I have used the modified ruby-type code in RSpec, which are from Postman generate function, I still can't get access token successfully. I think this difference is weird.
20160307 PM03:10 Update:
The related file has been put at github.com . I've gotten an access token through Postman software but I still don't understand why I can acquire access token through RSpec although have been tried many other codes to get. It's still unsolved by RSpec.
I've never used Doorkeeper gem, I use Devise Token Auth. I checked the documentation though and found this. You can stub the :doorkeeper_token method to test protected methods.
let(:token) { double :acceptable? => true }
before do
controller.stub(:doorkeeper_token) { token }
# allow(controller).to receive(:doorkeeper_token) {token} # => RSpec 3
end
https://github.com/doorkeeper-gem/doorkeeper/wiki/Testing-protected-controllers
Sorry, I think I have misunderstood about it-statement usage of RSpec test cases after trying many things.
Right meaning of it-statement in RSpec: Every it-statement is independent between each other even if they're under same context- or describe-statement.
So if I combine two it-statements of requesting authorization code and requesting access token together, I will be at halfway towards passing test with RSpec.
The other thing I need to fix is to use right encoding of Authorization at HTTP header for acquiring access token. Change from Base64.urlsafe_encode64 to Base64.strict_encode64, please.
Following are the right code of spec/requests/api/v1/specifiedvegetables_spec.rb:
require 'rails_helper'
describe SpecifiedVegetables do
describe 'OAuth client requests the grant' do
context 'When a REST client sends a request for getting the grant' do
before(:all) do
post "http://localhost:3000/users/sign_in?user[email]=test%40test123%2Ecom&user[password]=12345678"
expect(response.status).to eq(302)
#app = Doorkeeper::Application.new :name => 'rspectest-107', :redirect_uri => 'https://localhost:3000/api/v1/specified_vegetables/', :scopes => 'public'
#app.owner = User.last
#app.save!
#authorize_code = String.new
end
it 'should getting response code 302 for requesting authorization code and access token' do
query_url = "http://localhost:3000/oauth/authorize"
parameters = { "response_type" => "code", "client_id" => #app.owner.oauth_applications.last.uid, "redirect_uri" => "https://localhost:3000/api/v1/specified_vegetables/", "scope" => "public"}
headers = {"content-type" => "application/x-www-form-urlencoded"}
get query_url, parameters, headers
expect(response.status).to eq(302)
authorize_code_param = Rack::Utils.parse_query(URI.parse(response.location).query)
#authorize_code << authorize_code_param['code']
# above are acquiring authorize code
# The following are acquiring access token
query_url = "http://localhost:3000/oauth/token"
parameters = { "grant_type" => "authorization_code", "code" => #authorize_code , "client_id" => #app.owner.oauth_applications.last.uid, "redirect_uri" => "https://localhost:3000/api/v1/specified_vegetables/"}
headers = {"content-type" => "application/x-www-form-urlencoded", "authorization" => "Basic " + Base64.strict_encode64(#app.owner.oauth_applications.last.uid + ":" + #app.owner.oauth_applications.last.secret), "cache-control" => "no-cache"}
post query_url, parameters, headers
expect(response.status).to eq(200) # Here, we get status 200 because the response has access token.
end
after(:all) do
#app.destroy # Destroy the oauth client, but doesn't purge related access token for this rspec request.
end
end
end
end
I'm trying to subscribe a user to my MailChimp list from Ruby 1.9.3. I've got the following code:
require 'net/http'
require 'digest/md5'
class Email < ActiveRecord::Base
attr_accessible :email
validates :email, :confirmation => true
before_create :chimp_subscribe
def chimp_subscribe
api_key = "my api key"
list_id = "my list id"
dc = "my data center"
member_id = Digest::MD5.hexdigest(self.email)
uri = URI.parse("https://" + dc + ".api.mailchimp.com/3.0/lists/" + list_id + "/members/" + member_id)
req = Net::HTTP::Put.new(uri.path)
req.body = { "email" => self.email, "status" => 'subscribed' }.to_json
req.content_type = "application/json"
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
http.request(req)
end
end
end
Before creating a new Email, it runs chimp_subscribe. Unfortunately, it breaks in the request block, with Errno::ECONNRESET, meaning that MailChimp is resetting my connection.
Am I missing some MailChimp API parameter in here? I'm struggling to find it.
The server you are requesting is HTTPS, so you should use SSL.
You should first require it:
require 'openssl'
Then make your request like this:
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE # You should use VERIFY_PEER in production
res = http.request(req)
I'm trying to access the Basecamp API through Rails, but it responds with a SocketError. My code is like this:
require 'rubygems'
require 'net/https'
http = Net::HTTP.new('https://webonise.basecamphq.com')
http.use_ssl = true
http.start do |http|
req = Net::HTTP::GET.new('/projects.xml')
req.basic_auth 'username' , 'password'
resp, data = http.request(req)
end
The response is:
SocketError: getaddrinfo: Name or service not known
Net::HTTP.new takes a hostname, not a URI, as its first argument. Try calling URI.parse to break up the URI into the parts you want first:
require 'rubygems'
require 'net/http'
uri = URI.parse("https://webonise.basecamphq.com/")
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
req = Net::HTTP::Get.new(uri.request_uri)
req.basic_auth 'username', 'password'
resp = http.request(req)
body = resp.body
You'll also have to get the body in the response from the body method.