Net::HTTP POST with XML Payload and some URL Parameters - ruby-on-rails

Just a quick question,
when I do
request = Net::HTTP::Post.new(path)
Can I do
path = '/api/v1/baskets?apiKey=' + api_key + '&sig=' + sig + '&time=' + time
Where api_key sig and time are some string
or do I need to do
path = '/api/v1/baskets'
request.set_form_data({'apiKey' => api_key, 'sig' => sig, 'time' => time})
is there any difference or are they pretty much the same?

They are pretty much the same, set_form_data does a urlencode.
Here is what it does in set_form_data link
def set_form_data(params, sep = '&')
self.body = params.map {|k,v| "#{urlencode(k.to_s)}=#{urlencode(v.to_s)}" }.join(sep)
self.content_type = 'application/x-www-form-urlencoded'
end
When in doubt always refer ruby-doc.org.

Related

Integrating Payfort Payment with Ruby On Rails App

I am trying to implement the Payfort payment gateway with rails app.
But i am getting following response message:
"response_message":"Signature mismatch"
Following is my try:
params = {command: "AUTHORIZATION",
currency: "USD",
access_code: "z7TfXF2xxxxxxxxxxxx",
merchant_identifier: "xoNbjDoq",
merchant_reference: "405",
language: "en",
amount: 250,
token_name: "token_is_here",
expiry_date: "07/2023",
card_number: "5200421234563432",
card_security_code: "417",
card_holder_name: "Abc Xyz",
remember_me: "YES",
return_url: "http://lvh.me:3000/payments/test"}
params = params.except(:card_security_code, :card_number, :expiry_date, :card_holder_name, :remember_me)
params = params.sort.to_h
string = params.to_query(nil)
string = string.gsub! '&', ''
string = ##sha_request + string + ##sha_request
string = Digest::SHA256.hexdigest string
uri = URI.parse("https://sbpaymentservices.payfort.com/FortAPI/paymentApi")
header = {'Content-Type': 'application/json'}
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = true
request = Net::HTTP::Post.new(uri.request_uri, header)
request.body = params.to_json
response = http.request(request)
Check sequence of parameters while generating signature. and check for algorithm which u have setup in account and use same algorithm while generating signature
Or else try using their gem
https://github.com/payfort/start-ruby
there may be many reasons for such issue one of them is rails form params and also hashing algorithm, here's my implementation for it
def sign_with_key(params, key)
string_to_digest = params.sort { |a, b| a[0].upcase <=> b[0].upcase }.map { |k, v| "#{k}=#{v}" }.join()
string_to_digest.prepend(key)
string_to_digest << key
"Digest::#{#options[:sha].upcase}".constantize.hexdigest(string_to_digest)
end
Rest of the code seems good but the issue I faced and I see here is that you are using string = params.to_query(nil) which will used escaped characters %20 instead of space in card_holder_name
So I used CGI.unescape and fixed the issue -
def signature(string)
Digest::SHA256.hexdigest(CGI.unescape("#{SHA_REQUEST_PHRASE}#{string.gsub(/&/, "")}#{SHA_REQUEST_PHRASE}"))
end
Hope it helps :)

Rails String Replace URL Parameters

Let's say I have a string like:
url = "https://example.com/user/tr_auth.php?key=34432&cmp_id=344&tr_id={user_id}"
I want to update the cmp_id=344 to be say cmp_id=44553. What's the best way to accomplish this? I can't gsub per say because I don't know what cmp_id might be equal, only that it will be a URL parameter in the string.
It seems like I want to do something like
uri = URI.parse(url)
params = CGI.parse(uri.query)
But then, how do I re-build the string swapping out the cmp_id parameter to be 44553?
Thanks!
If you are dealing with a web application (and/or Rails as the tag seems to indicate), then you certainly have Rack available. Rack::Utils has methods to parse and build a query.
url = "https://example.com/user/tr_auth.php?key=34432&cmp_id=344&tr_id={user_id}"
uri = URI.parse(url)
query = Rack::Utils.parse_query(uri.query)
# => {"key"=>"34432", "cmp_id"=>"344", "tr_id"=>"{user_id}"}
# Replace the value
query["cmp_id"] = 44553
uri.query = Rack::Utils.build_query(query)
uri.to_s
# => "https://example.com/user/tr_auth.php?key=34432&cmp_id=44553&tr_id=%7Buser_id%7D"
Also note that Rack, by default, escapes the query.
url = "https://example.com/user/tr_auth.php?key=34432&cmp_id=344&tr_id={user_id}"
uri = URI.parse(url)
params = CGI.parse(uri.query)
params['cmp_id'] = 44553
new_str = uri.host + uri.path + '?' + params.to_query
First, you can parse the url for params:
require 'cgi'
url = 'https://example.com/user/tr_auth.php?key=34432&cmp_id=344&tr_id={user_id}'
string_params = url.split('?')[1]
hash = CGI::parse(string_params)
Then you can iterate the hash by keys and change values:
hash.keys.each {|key| hash[key]='new value'}
url_params = hash.to_param

Using HMAC SHA256 in Ruby

I'm trying to apply HMAC-SHA256 for generate a key for an Rest API.
I'm doing something like this:
def generateTransactionHash(stringToHash)
key = '123'
data = 'stringToHash'
digest = OpenSSL::Digest.new('sha256')
hmac = OpenSSL::HMAC.digest(digest, key, data)
puts hmac
end
The output of this is always this: (if I put '12345' as parameter or 'HUSYED815X', I do get the same)
ۯw/{o���p�T����:��a�h��E|q
The API is not working because of this... Can some one help me with that?
According to the documentation OpenSSL::HMAC.digest
Returns the authentication code an instance represents as a binary string.
If you have a problem using that maybe you need a hex encoded form provided by OpenSSL::HMAC.hexdigest
Example
key = 'key'
data = 'The quick brown fox jumps over the lazy dog'
digest = OpenSSL::Digest.new('sha256')
OpenSSL::HMAC.digest(digest, key, data)
#=> "\xF7\xBC\x83\xF40S\x84$\xB12\x98\xE6\xAAo\xB1C\xEFMY\xA1IF\x17Y\x97G\x9D\xBC-\x1A<\xD8"
OpenSSL::HMAC.hexdigest(digest, key, data)
#=> "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8"
Try This:
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), key, data)
def make_payment(user)
#key= SecureRandom.hex(10)
#puts #key
#secret_key = #key
puts " this is the public key #{#secret_key}"
#access_key= generate_key
puts " this is the access key #{#access_key}"
#name= #user.name
puts "#{#name}"
#time= Time.now.in_time_zone("Nairobi")
puts "This is the time request sent #{#time}"
#server_key = SecureRandom.base64
puts "This is the server key #{#server_key}"
#data = 'This request is being made from Learnida for users to make a payment'
#digest = OpenSSL::Digest.new('sha256')
uri = URI.parse("https://learnida.com")
#hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), #secret_key, #access_key)
puts "This is the HMAC #{#hmac}"
req = Net::HTTP::Get.new(uri)
req['Authorization'] = "TM-HMAC-SHA256 key=#{#access_key} ts=#{#time} sign=#{#hmac}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
#hmacdigest= OpenSSL::HMAC.digest(#digest, #server_key, #data)
puts" This is the HMAC:SHA-256: #{#hmacdigest}"
#puts res.body
#=> "\xF7\xBC\x83\xF40S\x84$\xB12\x98\xE6\xAAo\xB1C\xEFMY\xA1IF\x17Y\x97G\x9D\xBC-\x1A<\xD8"
#sslkey= OpenSSL::HMAC.hexdigest(#digest, #server_key, #data)
puts #sslkey
In my case (Ticketmatic) I had to create the HMAC like above and add an Authorization header to the request with the HMAC in it.
hmac = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), secret_key, access_key + name + time)
req = Net::HTTP::Get.new(uri)
req['Authorization'] = "TM-HMAC-SHA256 key=#{access_key} ts=#{time} sign=#{hmac}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
You can find a full gist here
And a blogpost with more explantion here

How to optimize this Ruby code that fetches web content?

We have a Rails 3.2.12 app that fetches data from a partner API by making POST requests. However, we're finding that our code seems to be a bottleneck, as it takes longer to process requests than expected. Can we do anything to speed up the code?
Should we use another XML parser? Is there a faster way to post SSL requests in Ruby?
Thanks!
def ced( ad )
# Set vars
u = 'e'
pw = 'p'
ad = ad.join ','
url = 'https://r.e.com/interface.asp?ResponseType=XML&Command=BC' + '&UID=' + u + '&PW=' + pw + '&DL=' + ad
results = []
# Invoke API command
uri = URI.parse url
http = Net::HTTP.new uri.host, uri.port
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
req = Net::HTTP::Get.new uri.request_uri
resp = http.request req
# Parse API response
resp_xml = Nokogiri.XML resp.body
resp_xml.remove_namespaces!
num_errors = resp_xml.at_xpath('//ErrCount').content
# Any errors?
if num_errors != '0'
error = true
# No errors, process domains
else
resp_xml.xpath('//CheckResult').each do |elem|
results << {
:domain => elem.xpath('D').text.downcase,
:status => case elem.xpath('RRPCode').text
when '210' then 'a'
when '211' then 't'
else
error = true
'error'
end
}
end
end
<truncated>
end

Facebook FQL Query with Ruby

I'm trying to do a simple GET with ruby to the Facebook fql.query method without success.
The url is basically structured like this:
https://api.facebook.com/method/fql.query?query=SELECT total_count FROM link_stat WHERE url = "http://twitter.com/"&format=json
I've read in a few posts here on StackOverflow about how to make those requests, but even tho I keep getting:
/usr/lib/ruby/1.8/net/http.rb:560:in `initialize': getaddrinfo: Name or service not known (SocketError)
On the first line of http_get function.
def http_get(domain,path,params)
return Net::HTTP.get(domain, "#{path}?".concat(params.collect { |k,v| "#{k}=#{CGI::escape(v.to_s)}" }.join('&'))) if not params.nil?
return Net::HTTP.get(domain, path)
end
def getFacebookStats(url)
params = {
:query => 'SELECT total_count FROM link_stat WHERE url = "' + url + '"',
:format => 'json'
}
http = http_get('https://api.facebook.com', '/method/fql.query', params)
puts http
end
The http call accepts a host, not a URL:
def http_get(domain,path,params)
path = unless params.blank
path + "?" + params.collect { |k,v| "#{k}=#{CGI::escape(v.to_s)}" }.join('&')
else
path
end
request = Net::HTTP.get(domain, path)
end
def get_facebook_stats(url)
params = {
:query => 'SELECT total_count FROM link_stat WHERE url = "' + url + '"',
:format => 'json'
}
http = http_get('api.facebook.com', '/method/fql.query', params)
puts http
end
Please do not use camel case on method names on Ruby.
If you want to make HTTPS calls, you will have to use a different call:
require 'net/http'
require 'net/https'
http = Net::HTTP.new('somehost.com', 443)
http.use_ssl = true
path = '/login.html'
resp, data = http.get(path, nil)

Resources