Express Checkout Order Total is Missing - ruby-on-rails

I keep getting error #10400 (Order total is missing) but am not sure what I am leaving out. Everything appears to be processing correctly. This is where the payment is setup:
def setcheckout
api = PayPal::SDK::Merchant::API.new
#set_express_checkout = api.build_set_express_checkout(params[:SetExpressCheckoutRequestType])
# Find Item Total and Order Total
details = #set_express_checkout.SetExpressCheckoutRequestDetails
pay = details.PaymentDetails[0]
pay.PaymentDetailsItem[0].Name = 'Item'
pay.PaymentDetailsItem[0].Amount = 1
pay.PaymentDetailsItem[0].Quantity = 1
pay.ItemTotal = pay.PaymentDetailsItem[0].Amount
pay.OrderTotal.currencyID = pay.ItemTotal.currencyID
pay.OrderTotal.value = pay.ItemTotal.value.to_f
# Notify url
#pay.NotifyURL ||= ipn_notify_url
# Return and cancel url
details.ReturnURL ||= 'http://localhost:3000/confirm'
details.CancelURL ||= 'http://localhost:3000/failed'
#set_express_checkout_response = api.set_express_checkout(#set_express_checkout)
if #set_express_checkout_response.success?
redirect_to "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token=#{#set_express_checkout_response.Token}"
end
end
This takes me to paypal, authenticates the user, and returns to the confirmation url as expected. That looks like this:
def confirm
session[:token] = params[:token] if params[:token]
session[:PayerID] = params[:PayerID] if params[:PayerID]
api = PayPal::SDK::Merchant::API.new
#do_express_checkout_payment = api.build_do_express_checkout_payment(params[:DoExpressCheckoutPaymentRequestType])
details = #do_express_checkout_payment.DoExpressCheckoutPaymentRequestDetails
details.Token = session[:token]
details.PayerID = session[:PayerID]
#details.PaymentDetails[0].NotifyURL ||= ipn_notify_url
#do_express_checkout_payment_response = api.do_express_checkout_payment(#do_express_checkout_payment) if request.post?
end
Once the "Confirm and Pay" button is clicked and the above is posted to, the transaction fails with a 10400 Order total is missing. error. It looks to me like I specified the order total above, and the total is displayed when I am taken to paypal. What am I missing?

I don't see the total getting sent in your DoExpressCheckoutPayment request..?? You need to include those same details in DECP that you do in SEC.
As of version 112.0 they introduced the USESESSIONPAYMENTDETAILS parameter, which is supposed to allow to tell DECP to just use what you sent in SEC if you set it to true or 1. There seems to be some discrepancy about whether or not it works, though. I've yet to test it myself.

Related

How to properly use Sidekiq to process background tasks in Rails

So, i've generated a rails app using https://github.com/Shopify/shopify_app - and for the most part have the app working as intended - it's goal is to get product quantities from an external stock management API, and then update the variant quantities in Shopify with the latest quantities from that stock management system.
My problem is that the initial POST request to the external API responds with a large number of products - this takes upwards of 15 seconds sometimes. In addition to this, another portion of my app then takes this response, and for every product in the response that also exists in Shopify, it will make a PUT request to Shopify to update the variant quantities. As with the initial request, this also takes upwards of 10-15 seconds.
My problem is that i'm hosting the app on Heroku, and as a result i've hit their 30 second request timeout limit. As a result I need to use a background worker to offset at least one of the requests above (perhaps both) to a worker queue. I've gone with the widely recommended Sidekiq gem - https://github.com/mperham/sidekiq - which is easy enough to set up.
My problem is that I don't know how to get the results from the finished Sidekiq worker job, and then use that again within the Controller - I also don't know if this is best practice (i'm a little new to Rails/App development).
I've included my controller (prior to breaking it down into workers) that currently runs the app below - I guess I just need some advice - am I doing this correctly - should some of this logic be inside a Model, and if so how would that model then communicate with the Controller, and then how would Sidekiq then fit into all of it.
Appreciate any advice or assistance, thanks.
class StockManagementController < ShopifyApp::AuthenticatedController
require 'uri'
require 'net/http'
require 'json'
require 'nokogiri'
require 'open-uri'
require 'rexml/document'
def new
#token = StockManagementController.new
end
def get_token
url = URI('https://external.api.endpoint/api/v1/AuthToken')
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
#HEROKU_ENV_USERNAME = ENV['HEROKU_ENV_USERNAME']
#HEROKU_ENV_PASSWORD = ENV['HEROKU_ENV_PASSWORD']
request = Net::HTTP::Post.new(url)
request['content-type'] = 'application/x-www-form-urlencoded'
request['cache-control'] = 'no-cache'
request.body = 'username=' + #HEROKU_ENV_USERNAME + '&password=' + #HEROKU_ENV_PASSWORD + '&grant_type=password'
response = http.request(request)
responseJSON = JSON.parse(response.read_body)
session[:accessToken] = responseJSON['access_token']
if session[:accessToken]
flash[:notice] = 'StockManagement token generation was successful.'
redirect_to '/StockManagement/product_quantity'
else
flash[:alert] = 'StockManagement token generation was unsuccessful.'
end
end
def product_quantity
REXML::Document.entity_expansion_text_limit = 1_000_000
#theToken = session[:accessToken]
if #theToken
url = URI('https://external.api.endpoint/api/v1/ProductQuantity')
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Post.new(url)
request['authorization'] = 'bearer ' + #theToken + ''
request['content-type'] = 'application/xml'
request['cache-control'] = 'no-cache'
response = http.request(request)
responseBody = response.read_body
finalResponse = Hash.from_xml(responseBody).to_json
resultQuantity = JSON.parse finalResponse
#connectionType = resultQuantity['AutomatorResponse']['Type']
#successResponse = resultQuantity['AutomatorResponse']['Success']
#errorResponse = resultQuantity['AutomatorResponse']['ErrorMsg']
productQuantityResponse = resultQuantity['AutomatorResponse']['ResponseString']
xmlResponse = Hash.from_xml(productQuantityResponse).to_json
jsonResponse = JSON.parse xmlResponse
#fullResponse = jsonResponse['StockManagement']['Company']['InventoryQuantitiesByLocation']['InventoryQuantity']
# This hash is used to store the final list of items that we need in order to display the item's we've synced, and to show the number of items we've sycned successfully.
#finalList = Hash.new
# This array is used to contain the available products - this is used later on as a way of only rendering
#availableProducts = Array.new
# Here we get all of the variant data from Shopify.
#variants = ShopifyAPI::Variant.find(:all, params: {})
# For each peace of variant data, we push all of the available SKUs in the store to the #availableProducts Array for use later
#variants.each do |variant|
#availableProducts << variant.sku
end
#Our final list of products which will contain details from both the Stock Management company and Shopify - we will use this list to run api calls against each item
#finalProductList = Array.new
puts "Final product list has #{#fullResponse.length} items."
puts #fullResponse.inspect
# We look through every item in the response from Company
#fullResponse.each_with_index do |p, index|
# We get the Quantity and Product Code
#productQTY = p["QtyOnHand"].to_f.round
#productCode = p["Code"].upcase
# If the product code is found in the list of available products in the Shopify store...
if #availableProducts.include? #productCode
#variants.each do |variant|
if #productCode === variant.sku
if #productQTY != 0
#finalProductList << {
"sku" => variant.sku,
"inventory_quantity" => variant.inventory_quantity,
"old_inventory_quantity" => variant.old_inventory_quantity,
"id" => variant.id,
"company_sku" => #productCode,
"company_qty" => #productQTY
}
end
end
end
end
end
# If we get a successful response from StockManagement, proceed...
if #finalProductList
flash[:notice] = 'StockManagement product quantity check was successful.'
puts "Final product list has #{#finalProductList.length} items."
puts #finalProductList
#finalProductList.each do |item|
#productSKU = item["sku"]
#productInventoryQuantity = item["inventory_quantity"]
#productOldInventoryQuantity = item["old_inventory_quantity"]
#productID = item["id"]
#companySKU = item["company_sku"]
#companyQTY = item["company_qty"]
url = URI("https://example.myshopify.com/admin/variants/#{#productID}.json")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
request = Net::HTTP::Put.new(url)
request["content-type"] = 'application/json'
request["authorization"] = 'Basic KJSHDFKJHSDFKJHSDFKJHSDFKJHSDFKJHSDFKJHSDFKJHSDFKJHSDFKJHSDF'
request["cache-control"] = 'no-cache'
request.body = "{\n\t\"variant\": {\n\t\t\"id\": #{#productID},\n\t\t\"inventory_quantity\": #{#companyQTY},\n\t\t\"old_inventory_quantity\": #{#productOldInventoryQuantity}\n\t}\n}"
# This is the line that actually runs the put request to update the quantity.
response = http.request(request)
# Finally, we populate the finalList has with response information.
#finalList[#companySKU] = ["","You had #{#productOldInventoryQuantity} in stock, now you have #{#companyQTY} in stock."]
end
else
# If the overall sync failed, we flash an alert.
flash[:alert] = 'Quantity synchronisation was unsuccessful.'
end
# Lastly we get the final number of items that were synchronised.
#synchronisedItems = #finalList.length
# We flash this notification, letting the user known how many products were successfully synchronised.
flash[:notice] = "#{#synchronisedItems} product quantities were synchronised successfully."
# We then pretty print this to the console for debugging purposes.
pp #finalList
else
flash[:alert] = #errorResponse
end
end
end
First of all, your product_quantity method is way too long. You should break it into smaller parts. 2nd, http.verify_mode = OpenSSL::SSL::VERIFY_NONE should not be done in production. The example you've provide along with your question are too complex and are therefore difficult to answer. It sounds like you need a basic understanding of design patterns and this is not a specific ruby question.
If your app needs to make realtime API calls inside of a controller this is a poor design. You don't want to keep requests of any kind waiting for more than a couple of seconds at most. You should consider WHY you need to make these requests in the first place. If it's data you need quick access to, you should write background jobs to scrape the data on a schedule and store it in your own database.
If a user of your app makes a request which needs to wait for the API's response, you could write a worker to handle fetching the API data and eventually send a response to the user's browser probably using actioncable.
For your constant definitions you probably should do this in an initializer wihich you would keep in my_app_root/config/initializers/constants.rb which get loaded into your app at runtime. You could just call them where need using te ENV[] syntax but if you prefer simpler constants drop the # since that naming convention in ruby is for instance objects.
#app_root/config/initializers/constants.rb
HEROKU_ENV_USERNAME = ENV['HEROKU_ENV_USERNAME']
HEROKU_ENV_PASSWORD = ENV['HEROKU_ENV_PASSWORD']

How to retrieve sixth record from database in rails using Where query

I want to retrieve sixth, seventh and eighth record from database using ruby on rails, but it's possible only till fifth, after that undefined method sixth is coming. Please suggest me if there is any possible way.
Following is my code which I tried:
#reporting_masters_travel_requests4 = ReportingMastersTravelRequest.where(travel_request_id: #travel_request.id,status: nil).fifth
if #reporting_masters_travel_requests2 = ReportingMastersTravelRequest.where(travel_request_id: #travel_request.id,status: nil)[1]
ReportingMastersTravelRequest.where(reporting_master_id: #reporting_masters_travel_requests2.reporting_master_id).update_all(status: "true",daily_bill_comment: #comment)
TravelRequest.where(id: #travel_request.id).update_all(reporting_master_id: #reporting_masters_travel_requests3.reporting_master_id)
flash[:notice] = 'Daily Bill Request Send To Higher Authority For Approval'
elsif #reporting_masters_travel_requests3 = ReportingMastersTravelRequest.where(travel_request_id: #travel_request.id,status: nil)[2]
ReportingMastersTravelRequest.where(reporting_master_id: #reporting_masters_travel_requests3.reporting_master_id).update_all(status: "true",daily_bill_comment: #comment)
TravelRequest.where(id: #travel_request.id).update_all(reporting_master_id: #reporting_masters_travel_requests4.reporting_master_id)
flash[:notice] = 'Daily Bill Request Send To Higher Authority For Approval'
elsif #reporting_masters_travel_requests4 = ReportingMastersTravelRequest.where(travel_request_id: #travel_request.id,status: nil)[3]
ReportingMastersTravelRequest.where(reporting_master_id: #reporting_masters_travel_requests4.reporting_master_id).update_all(status: "true",daily_bill_comment: #comment)
TravelRequest.where(id: #travel_request.id).update_all(reporting_master_id: #reporting_masters_travel_requests5.reporting_master_id)
flash[:notice] = 'Daily Bill Request Send To Higher Authority For Approval'
else
flash[:alert] = 'No Reporting Manager is present'
end
I have used the above code and it works perfectly,but its not so dynamic as i manually need to specify for how many time if want to check in table via array [4].But problem is what if 10 records are present in database then for this the above logic will fail.
You are using where to get records from database and it returns array of objects so you can use something like this:
#reporting_masters_travel_requests4 = ReportingMastersTravelRequest.where(travel_request_id: #travel_request.id,status: nil)[5]
You can do as follows:
#reporting_masters_travel_requests = ReportingMastersTravelRequest.where(travel_request_id: #travel_request.id,status: nil)
#reporting_masters_travel_requests.each do |record|
#Something like record.update record.save
end
#set a msg like no more records after loop
msg="No more records"

wrong content loaded due to browser cache

I used Ruby on Rails for my website. In one web page, it will load a poll information based on the poll id, which set in url like "http://mywebsite/polls/1". The poll information includes the poll owner name, the poll title, the item pictures in the poll and the people's name who voted on the poll.
I found sometimes it loaded wrong information. That is, it loaded the wrong poll owner name, poll title and voted people from the other poll while the item pictures are correct.I checked the back end and found there was nothing wrong in rails controller. All the variables got the right values. But the chrome browser told me the view is wrong.
If I cleared all the cache and reload the page then it would work normally.Anyone knows why does it happen and what should I do? Thanks
The relavant action codes:
def show
#poll=Poll.where("is_deleted = false AND id = #{params[:id]}")[0]
#items=#poll.items.where("is_deleted = false")
#voted_user_ids = #poll.audiences.where('has_voted != 0').map(&:user_id).uniq
#voted_users = User.where('id IN (?)',#voted_user_ids)
#voted_user_names = #voted_users.map(&:user_name)
if current_user.nil?
#poll_vote_url = "/voted_choice"
else
#current_user_name = current_user.user_name
#poll_vote_url = "/audiences/"+#poll.id.to_s
#if_current_user_voted = #voted_users.include?(current_user)
#is_poll_owner = (current_user == #poll.user)
check_item_id_in_cookies
end
end
def check_item_id_in_cookies
if !cookies.signed[:item_id].nil?
item = Item.find(cookies.signed[:item_id].to_i)
#create audience if the voter is not the poll owner
if current_user == item.poll.user
flash.now[:alert] = "You can't vote on your own poll."
cookies.signed[:item_id] = nil
else
create_audience cookies.signed[:item_id]
end
end
end
def create_audience item_id
#item_id = item_id.to_i
#item = Item.find(#item_id)
#poll = #item.poll
#voted_user_ids = #poll.audiences.where('has_voted != 0').map(&:user_id).uniq
if #voted_user_ids.include?(current_user.id)
flash.now[:alert]="You already voted."
else
audience = #item.poll.audiences.find{|audience| audience.user_id == current_user.id} || Audience.create(:poll_id => #poll.id,:user_id => current_user.id)
#update audience
audience.is_following = true
audience.has_voted = #item.id
audience.save
cookies.signed[:item_id]=nil
flash[:alert]="Thank you for your vote"
redirect_to "/polls/#{#poll.id}"
end
end
Please monitor the network while loading and reloading the page. Especially to request to request to http://mywebsite/polls/1. Check the response headers as well. Even if you don't do on application side the web server or a proxy server may be modifying the request.
You can find help on who to use the network panel of chrome here.

How do i pass parameters in Mogli

Using Mogli and facebooker gem, how do i get my wall data with a limit of 1000?
using this i get whole posts
def index
redirect_to new_oauth_path and return unless session[:at]
user = Mogli::User.find("me",Mogli::Client.new(session[:at]))
#user = user
#posts = user.posts
end
but i need to filter it with limit 1000
Seems that the only way is to set limit parameter in the client
client = Mogli::Client.new(session[:at])
client.default_params[:limit] = 1000
user = Mogli::User.find("me", client)

How to create a secure credit card gateway using paypal / ruby on rails / active merchant

I am creating a store using Active Merchant and PayPal sandbox right now. It seems to be working fine, but I don't think it is even remotely secure. I don't really know too much about HTTPS and how to implement secure connections.
I am currently passing the credit card and billing information in the session (probably not the smartest idea). My current code is posted below. I really need help with what direction and steps to take in order to make this a secure, usable store.
def payment
session[:billing_address] = params[:billing_address]
end
def summary
#credit_card = params[:credit_card]
session[:credit_card] = params[:credit_card]
#billing_address = session[:billing_address]
#cart = get_cart
#purchases = #cart.purchases
#total = #cart.total
end
def finish
#cart = get_cart
#total = #cart.total
credit_card = ActiveMerchant::Billing::CreditCard.new( session[:credit_card] )
billing_address = session[:billing_address]
flash[:notice] = credit_card.errors and return unless credit_card.valid?
gateway = ActiveMerchant::Billing::PaypalGateway.new(:login=>$PAYPAL_LOGIN, :password=>$PAYPAL_PASSWORD)
res = gateway.authorize(#total, credit_card, :ip=>request.remote_ip, :billing_address=>billing_address)
if res.success?
gateway.capture(#total, res.authorization)
flash[:notice] = "Authorized"
else
flash[:notice] = "Failure: " + res.message.to_s
end
end
There was a good railscast about how to implement ssl.
http://railscasts.com/episodes/143-paypal-security

Resources