Rails 4 - ActiveMerchant Paypal Express Checkout Issue with Passing Item Quantity - ruby-on-rails

I have a weird issue with the PayPal express checkout. When I pass in an options[:item][:quantity], I get an error code from PayPal that the transaction in invalid.
#controllers/orders_controller.rb
def express
options = {
:ip => request.remote_ip,
:return_url => new_order_url,
:cancel_return_url => products_url,
:items => current_cart.line_items_hash
}
response = EXPRESS_GATEWAY.setup_purchase(current_cart.build_order.price_in_cents,
options)
redirect_to EXPRESS_GATEWAY.redirect_url_for(response.token)
end
# models/cart.rb
def line_items_hash
self.line_items.map do |line_item|
{
:name => Product.find(line_item.product_id).name
# :quantity => line_item.quantity,
:description => line_item.product.description,
:amount => line_item.gross_price_in_cents}
end
end
So this works fine. The problem is that the correct quantity is not shown on the PayPal order confirmation page. However, if I uncomment the quantity variable in the line_items_hash function the whole thing breaks and I get "invalid transaction from paypal". Any ideas?

Silly old me. Paypal keeps invalidating my transaction because I'm passing bad information. I was setting the amount to line_item total which is already the total of quantity * product.price. So the information that Paypal was receiving is quantity * (quantity * product.price).
Silly little mistake but I managed to catch it in the end.
# models/cart.rb
def line_items_hash
self.line_items.map do |line_item|
{
:name => Product.find(line_item.product_id).name
:quantity => line_item.quantity,
:description => line_item.product.description,
:amount => line_item.price.in_cents}
end
end

Related

PayPal-Ruby-SDK web-experience-profile creation

I am a bit of a new rails developer and I am not making sense of Paypal's documentation to create a web experience profile before processing a payment with the REST API:
I can make payments ok with code below as long as I do not try to use the experience profile.
When debugging, I get a value for #webprofile similar to:
#<PayPal::SDK::REST::DataTypes::WebProfile:0x007fe0f9344e50 #error=nil,
#name="YeowZa! T-Shirt Shop", #presentation=#<PayPal::SDK::REST::DataTypes::Presentation:0x007fe0f8ec51b8 #error=nil, #brand_name="YeowZa! Paypa
l", #logo_image="http://www.yeowza.com", #locale_code="US">, #input_fields=#<PayPal::SDK::REST::DataTypes::InputFields:0x007fe0f927b0f0 #error=nil, #allow_note=true, #no_shipping=0, #address_override=1>, #flow_config
=#<PayPal::SDK::REST::DataTypes::FlowConfig:0x007fe0f90808e0 #error=nil, #landing_page_type="billing", #bank_txn_pending_url="http://www.yeowza.com">, #request_id="bcc4bc41-b61c-4d28-94f1-a0912121d8e8", #header={}>
On my console I see:
Request[post]: https://api.sandbox.paypal.com/v1/payment-experience/web-profiles/
Response[400]: Bad Request,
My code so far is:
class PaypalController < ApplicationController
require 'paypal-sdk-rest'
include PayPal::SDK::REST
include PayPal::SDK::Core::Logging
def create
PayPal::SDK::REST.set_config(
:mode => "sandbox", # "sandbox" or "live"
:client_id => "my-id",
:client_secret => "my-secret")
# Build Payment object
#payment = Payment.new({
:intent => "sale",
:payer => {
:payment_method => "paypal"},
:experience_profile_id => self.web_experience,
:redirect_urls => {
:return_url => "http://me.com/paypal_complete",
:cancel_url => "http://me.com/paypal_cancel"},
:transactions => [{
:item_list => {
:items => [{
:name => "me.com Thing",
:sku => "the-specific-horse",
:price => "2",
:currency => "USD",
:quantity => "1" }]},
:amount => {
:total => "2.00",
:currency => "USD" },
:description => "Payment for the-specific-thing" }]
})
# Create Payment and return the status(true or false)
if #payment.create
# Redirect the user to given approval url
#redirect_url = #payment.links.find{|v| v.method == "REDIRECT" }.href
logger.info "Payment[#{#payment.id}]"
logger.info "Redirect: #{#redirect_url}"
redirect_to #redirect_url
else
logger.error #payment.error.inspect
end
end
def paypal_complete
begin
# paymentId: PAY-8L3183743T450642VKWDPH7I
# token: EC-57E34614K6825515M
# PayerID: RBWLMFNFF4ZUC
payment_id = params[:paymentId]
# Retrieve the payment object by calling the
# `find` method
# on the Payment class by passing Payment ID
#payment = Payment.find(payment_id)
logger.info "Got Payment Details for Payment[#{#payment.id}]"
rescue ResourceNotFound => err
# It will throw ResourceNotFound exception if the payment not found
logger.error "Payment Not Found"
end
end
def web_experience
#this is not used right now...don't know how
#webprofile = WebProfile.new(
{
:name => "YeowZa! T-Shirt Shop",
:presentation => {
:brand_name => "YeowZa! Paypal",
:logo_image => "http://www.yeowza.com",
:locale_code => "US"
},
:input_fields => {
:allow_note => true,
:no_shipping => 0,
:address_override => 1
},
:flow_config => {
:landing_page_type => "billing",
:bank_txn_pending_url => "http://www.yeowza.com"
}
})
if #webprofile.create
# Redirect the user to given approval url
logger.info "#webprofile[#{#webprofile}]"
debugger
else
logger.error #payment.error.inspect
debugger
end
end
end
This is old but I thought others might end up here like I did.
I had a similar issue with the .NET SDK. Resolved by removing fields from the request object that I shouldn't have been using i.e. the field bank_txn_pending_url I had set to an empty string, when I didn't define this field at all (which would equate to null), the request returned 200. It clearly states in the documentation that this field should only be used in Germany in certain circumstances.
Once created though, subsequent requests fail, because you cannot have two profiles with the same name. You need to get the list of existing profiles and use the existing ID if found. No need to create over and over.
Bummer the dashboard doesn't capture all requests in the transaction section, would make life much easier if it did.

Rails 4 - Import CSV and blank values

I have an ecommerce app in Rail 4. There is an import csv function to allow me to bulk upload product details. Each product has a price and a saleprice. The price is a required field. The saleprice is optional.
When I import a file with saleprice populated or not populated for new products (not on site yet), it uploads as intended. Since the saleprice field is in the hash, when it's blank, it assumes there is no saleprice. This is fine and as intended.
However, when I want to update a product that has a saleprice to remove the saleprice, then it doesn't remove the sale price. If I put the saleprice outside the hash like :saleprice => row['SalePrice'].to_i, then it shows saleprice as $0 (when it's blank)
When saleprice is blank, I want it to assume there is no saleprice so ignore it.
Same behavior for other fields in the list_hash
Appreciate the help.
here is the code:
# model
validates :saleprice, numericality: {greater_than: 0}, :allow_blank => true
def importcsv(file_path, user_id)
open(file_path) do |f|
CSV.parse(f.read , headers: true, skip_blanks: true) do |row|
listing_hash = {:name => row['Product_title'],
:made_to_order => row['Made_to_order'],
:description => row['Description'],
:sku => row['Product_ID'],
:price => row['Price'].to_i,
:category => row['Category'].titleize,
:inventory => row['Quantity_in_stock'].to_i,
:image => URI.parse(row['Image']),
:user_id => user_id}.tap do |list_hash|
list_hash[:designer_or_brand] = row['Designer_or_Brand'] if row['Designer_or_Brand']
list_hash[:saleprice] = row['SalePrice'].to_i if row['SalePrice']
list_hash[:image2] = URI.parse(row['Image2']) if row['Image2']
list_hash[:image3] = URI.parse(row['Image3']) if row['Image3']
list_hash[:image4] = URI.parse(row['Image4']) if row['Image4']
end
listing = Listing.where(sku: listing_hash[:sku], user_id: listing_hash[:user_id])
if listing.count == 1
listing.first.update_attributes(listing_hash)
else
Listing.create!(listing_hash)
end
end

Rails & Stripe - Stock shipping address in a var, in the ChargesController

with Stripe on Rails 4, I want to stock the shipping address of my customer in a variable #testvar, in my ChargesController, so I can :
- Display it in the charges#create page, as a "Here is where we will deliver"
- Send an email to the admin with this info.
Here is my ChargesController : (UPDATED 3rd April)
class ChargesController < ApplicationController
def new
end
def create
admin = Admin.last
customer = Stripe::Customer.create(
email: params[:stripeEmail],
card: params[:stripeToken],
)
charge = Stripe::Charge.create(
:customer => customer.id,
:amount => #amount,
:currency => 'eur',
:receipt_email => params[:stripeEmail],
)
customer_name = charge["name"]
AdminMailer.outfit_ordered(admin, customer_name).deliver_now
rescue Stripe::CardError => e
flash[:error] = e.message
redirect_to :back
end
end
UPDATE 3rd April
I've added a mailer, hence the two following line
customer_name = charge["name"]
AdminMailer.outfit_ordered(admin, customer_name).deliver_now
I've found the following part in another aswer, but I can't get it to work.
stripe_customer_params = JSON.parse customer.to_s
#testvar = stripe_customer_params['cards']['data'].first['shipping_address']
Furthermore, when I assign a random value to my #testvar (like #testvar = "test") and display it in my charges#create view, it works... But #testvar keeps this value with the other tests I run after that.
I am stuck with this problem and can't find anything that helps me in the Stripe's API documentation.
Thanks for your help.

Memory leak in rake task wtith ruby 2.0.0p353 and rails 4.0.4

I've both googled and read other threads on the topic, and implemented all advice I could gather. But, I'm still having issues with too much memory being used (600MB+).
My users.rake file:
namespace :users do
desc "TODO"
task sync_events: :environment do
User.find_in_batches(batch_size: 5) do |users|
users.each(&:sync_events)
end
end
end
The sync_events method in user.rb model:
def sync_events
unless self.username.empty?
# Make the request to swagger
request = RestClient.get 'https://****************/event/findByUser/' + self.username + '?fromDate=' + 1.days.ago.strftime('%Y-%m-%d') + '&toDate=' + 10.days.from_now.strftime('%Y-%m-%d')
json = JSON.parse request
events = json['events']
# We need to fetch the events from the DB or create them if they don't exists
events.each do |event|
# Find existing semester or create new
semester_in_db = Semester.where(:name => event['semester'][0]['name']).first_or_create!
# Find exisiting event, or create new one.
event_in_db = Event.where(:ext_id => event['id']).first_or_create!(
:title => event['title'],
:description => event['description'],
:begin => Time.parse(event['begin']),
:end => Time.parse(event['end']),
:semester_id => semester_in_db.id
)
# Check if lectureres/users exist, or create new, and add them to the event
event['lecturer'].each do |lecturer|
# We only want to do this for users with emails - valid users
if lecturer["email"].length > 5
user = User.where(:username => lecturer['username']).first_or_create!(
:email => lecturer['username'] + '#feil.no',
:altEmail => lecturer['email'],
:password => 'password',
:password_confirmation => 'password',
:first_name => lecturer['firstname'],
:last_name => lecturer['lastname'],
:identity => lecturer['firstname'] + ' ' + lecturer['lastname']
)
#connect lecturer to event
event_in_db.users << user unless event_in_db.user_ids.include?(user.id)
#Add role
user.add_role :lecturer, event_in_db
#try to sync email, because sometimes email is not formatted correctly
begin
user.email = lecturer['email']
user.save!
rescue
e = Error.new
e.body = lecturer['email']
e.save
end
end
end
# Added student user
event_in_db.users << self unless event_in_db.user_ids.include?(self.id)
# add role student, unless lecturer
self.add_role :student, event_in_db unless self.has_role? :lecturer, event_in_db
end
end
end
You can probably see that I'm not a trained ruby developer. This code is headed for some major refactoring. But, in the mean time, I can't see why it's hogging all my memory. I've got about 500+ users in the DB. Eventually, my rake task gets killed.
As you can see, I'm already using find_in_batches instead of all. I've also converted all instance variables to local variables.
What more can I do?

Testing association methods with Mocha

I have a Rails app with an Order and a Refund model. Order has_many :refunds. All well and good. I'm trying to write a functional test for refund logic in a controller. Here's what I have right now:
test "should not process partial paypal refund for partially refunded order when refund total plus refund amount is greater than order total" do
set_super_admin_login_credentials
o = Order.new
o.stubs({:id => 1234567, :source => "PayPal", :total => 39.95, :user => users(:dave)})
Order.stubs(:find).with(1234567).returns(o)
get :refund, {:order_id => 1234567}
assert_equal o, assigns(:order)
o.refunds.build.stubs({:amount => 1.0})
o.refunds.build.stubs({:amount => 30.00})
assert_raise do
post :refund, {:order_id => 1234567, :refund_amount => 10.00}
end
end
And in the controller, the refund method looks like this:
def refund
#order = Order.find(params[:order_id])
return if request.get?
amount = params[:refund_amount].to_f
raise "Cannot refund order for more than total" if (#order.refunds.sum(&:amount) + amount)
# Do refund stuff
end
Some notes:
I'm basing the o.refunds.build bit on Ryan Bates' Railscast. If this is not right or no longer relevant, that's helpful information.
I've seen a lot of conflicting information about how to actually do the sum method, some with the & and some without. In script/console, the & blows up but without it, I get an actual sum. In my controller, however, if I switch from &:amount to :amount, I get this message: NoMethodError: undefined method+' for :amount:Symbol`
I feel like there's some conceptual information missing rather than a bug somewhere, so I'll appreciate some pointers.
Finally figured out the issue. I was stubbing an empty association as [] rather than leaving it nil for Rails to handle on some other methods. So, when I would change one, the other would fail. Word to the wise: Enumerable#sum and ActiveRecord::Associations::AssociationCollection#sum take entirely different parameters. :)
So, by changing the stubs to leave off :refunds => [] and using a string for the field name in sum I got things back to normal. So, here's the functional version of the above code:
test "should not process partial paypal refund for partially refunded order when refund total plus refund amount is greater than order total" do
set_super_admin_login_credentials
o = Order.new
o.stubs({:id => 1234567, :source => "PayPal", :total => 39.95, :user => users(:dave)})
Order.stubs(:find).with(1234567).returns(o)
get :refund, {:order_id => 1234567}
assert_equal o, assigns(:order)
o.refunds.build.stubs({:amount => 1.0})
o.refunds.build.stubs({:amount => 30.00})
assert_raise do
post :refund, {:order_id => 1234567, :refund_amount => 10.00}
end
end
def refund
#order = Order.find(params[:order_id])
return if request.get?
amount = params[:refund_amount].to_f
raise "Cannot refund order for more than total" if (#order.refunds.sum('amount') + amount)
# Do refund stuff
end

Resources