confused and disoriented with paypal ipn - ruby-on-rails

I am using this gem for payments in paypal https://github.com/tc/paypal_adaptive
I am very confused and disoriented with this gem. It has a poorly documented and is difficult for me to understand how to get the data from paypal on ipn response.
I hope this question will help more people having the same problem.
My steps are:
1º I send request to paypal from my orders_controller.rb with method preaproval_payment.
def preapproval_payment
preapproval_request = PaypalAdaptive::Request.new
data = {
"returnUrl" => response_paypal_user_orders_url(current_user),
"cancelUrl"=> cancel_payment_gift_url(#gift),
"requestEnvelope" => {"errorLanguage" => "en_US"},
"senderEmail" => "gift_1342711309_per#gmail.com",
"startingDate" => Time.now,
"endingDate" => Time.now + (60*60*24) * 30,
"currencyCode"=>"USD",
"maxAmountPerPayment" => "#gift.price",
"ipnNotificationUrl" => ipn_notification_url,
"ip" => request.remote_ip
}
preapproval_response = preapproval_request.preapproval(data)
puts data
if preapproval_response.success?
redirect_to preapproval_response.preapproval_paypal_payment_url
else
redirect_to gift_url(#gift), alert: t(".something_was_wrong")
end
end
2º These are the data of my request in my log console from command puts data :
{"returnUrl"=>"http://localhost:3000/en/u/maserranocaceres/orders/response_paypal", "cancelUrl"=>"http://localhost:3000/en/gifts/gift-1/cancel_payment", "requestEnvelope"=>{"errorLanguage"=>"en_US"}, "senderEmail"=>"gift_1342711309_per#gmail.com", "startingDate"=>2012-07-29 13:05:49 +0200, "endingDate"=>2012-08-28 13:05:49 +0200, "currencyCode"=>"USD", "maxAmountPerPayment"=>9, "ipnNotificationUrl"=>"http://localhost:3000/ipn_notification?locale=en", "ip"=>"127.0.0.1"}
3º I redirect to paypal page, and I make the payment on paypal successfully :D.
4º When payment is completed successfully, I am directed to:
http://localhost:3000/en/u/maserranocaceres/orders/response_paypal
I have response_paypal action in orders_controller.rb. It is GET action and my code for this action is:
def response_paypal
respond_to do |format|
format.html { redirect_to user_orders_url(current_user), :alert => "works fine return url"}
end
end
Up to this point everything works fine.
Now what I need is to get the data I received from paypal and save my database a new order if payment is successfully processed.
5º For this purpose I make a file in lib/paypal_ipn.rb and I add to this file the content from https://github.com/tc/paypal_adaptive/blob/master/templates/paypal_ipn.rb
# Allow the metal piece to run in isolation
require(File.dirname(__FILE__) + "/../../config/environment") unless defined?(Rails)
class PaypalIpn
def self.call(env)
if env["PATH_INFO"] =~ /^\/paypal_ipn/
request = Rack::Request.new(env)
params = request.params
ipn = PaypalAdaptive::IpnNotification.new
ipn.send_back(env['rack.request.form_vars'])
if ipn.verified?
#mark transaction as completed in your DB
output = "Verified."
else
output = "Not Verified."
end
[200, {"Content-Type" => "text/html"}, [output]]
else
[404, {"Content-Type" => "text/html"}, ["Not Found"]]
end
end
end
In my routes.rb I add:
match "/ipn_notification" => PaypalIpn
My 2 problems are:
a) I do not see that after making the payment this file to be fired and I can not see in my console data I get from paypal.
b) I want to send to paypal in my request, the id of object #gift for being able to recover later in paypal_ipn.rb and to save my database.
What am I doing wrong and how I can solve these problems?
Thank you

I haven't used that gem, but I've used PayPal IPN before. Here are some things you should check:
Do you have your PayPal account set up to use IPN? You must enable this setting on the account for this to work.
Have you verified that when you pass ipn_notification_url during the payment process, that it matches your "/ipn_notification" route?
For this to work, PayPal must be able to communicate directly with the server that is running this app. This means that typically, unless you have a custom setup on your local machine with dynamic DNS or something, that you will need to actually deploy this code to a server in order for PayPal to be able to communicate with your app. In other words, if this is running on http://localhost:3000, this will not work.
To answer your second question, how to recover #gift in order to record the fact it was paid in your database, I'm not entirely sure how to do it with this gem, but I'll tell you how I do it using ActiveMerchant - it is probably quite similar.
In your payment request to PayPal, you can pass in an invoice number. I believe the field is just called "invoice". Here you would pass the ID of the gift.
When PayPal notifies your app via IPN that the order was paid for, it will pass the invoice number back to you. Retrieve the #gift using this invoice number and then you can do what you need with it.
Here are the relevant parts of my working PayPal code, using the ActiveMerchant gem: https://gist.github.com/3198178
Good luck!

Related

Stripe::InvalidRequestError Must provide source or customer

order controller page
begin
Stripe.api_key = ENV["STRIPE_API_KEY"]
token = params[:stripeToken]
charge = Stripe::Charge.create(
:amount => (#listing.price * 100).floor,
:currency => "usd",
:source => params[:stripeToken],
:destination => #seller.recipient
)
flash[:notice] = "Thanks for ordering!"
rescue Stripe::CardError => e
flash[:danger] = e.message
end
order.js.coffee
jQuery ->
Stripe.setPublishableKey($('meta[name="stripe-key"]').attr('content'))
payment.setupForm()
payment =
setupForm: ->
$('#new_order').submit ->
$('input[type=submit]').attr('disabled', true)
Stripe.card.createToken($('#new_order'), payment.handleStripeResponse)
false
handleStripeResponse: (status, response) ->
if status == 200
$('#new_order').append($('<input type="hidden" name="stripeToken" />').val(response.id))
$('#new_order')[0].submit()
else
$('#stripe_error').text(response.error.message).show()
$('input[type=submit]').attr('disabled', false)
I am not sure why the source => params[:stripeToken] is not passing thru when I check the log in my stripe account. Is the token null?
Usually when you receive this error is because the publishable key is not in your form so when you send the form to stripe it doesn´t recognize you and it doesn´t send you back the token.
The process of charging a client goes this way.
1.- In your site you tell the user to type his card information in a form (hosted locally or just created by stripe script).
2.- Once the user submit the form. The information is submitted to stripe server with your publishable key. You receive a response from the server with a token. This token is forwarded to the action in your controller and since is linked to your account you can create a customer, a charge or join the user to a plan. This is the 'params[:stripeToken]'. The card information never reaches your controller, you just receive this token.
You can do two things to charge your clients, the first is create the form your self and the second just delegate it to the stripe script.
The first one is more tricky since stripe updates their api from time to time. It requires some knowledge of js and their api. For example Stripe v3 is different from v2. Allows more rationalization but requires more knowledge of js. The information and examples of how to configure can be found in stripe elements.
The second one is pretty easy. I recommend it to not advanced users. You can implement it just following the instructions in this link using checkout and rails.
I usually use the second option since it just takes 5 minutes and you delegate the hard work to stripe.

How to troubleshoot quickbooks-ruby

I have a Rails app set up with the quickbooks-ruby gem.
Oauth seems to be working—there are no errors, I follow the Quickbooks login in a pop-up, and I get a confirmation that the account is connected. No errors.
However, in my audit log on Quickbooks Online, there is not indication that I have logged in.
And when I follow the steps to try to get a list of customers, I get this error:
undefined method 'get' for "qyprd...":String
Which may or may not be a different problem.
This is the code:
def index
#customers = Customer.all
service = Quickbooks::Service::Customer.new
service.company_id = session[:realm_id]
service.access_token = session[:token]
customers = service.query()
end
I don't understand why it isn't working, or how to troubleshoot the issue.
From the above code I see you are not passing the query in last but one line. Query should look something like this i.e customers.query("Select Id, GivenName From Customer")
To understand why it isn't working and to troubleshoot the issue you can use begin and rescue.
begin
oauth_client = OAuth::AccessToken.new($qb_oauth_consumer, token, secret)
service = Quickbooks::Service::Customer.new(:access_token => oauth_client, :company_id => realm_id)
customers = customers.query("Select Id, GivenName From Customer")
rescue Exception => e
# e.message will show response of quickbooks for you request
puts e.message
# e.message.inspect will show logs
puts e.backtrace.inspect
end
You can also try your queries on API explorer and see if get the result set you want.

Unable to get facebook Open Graph action approved using rails and the Koala gem

I keep getting this message after submitting my application
Your Open Graph action failed to publish on any of the Platforms you submitted. Make sure the action is working properly by publishing the action with a test user before resubmitting.
I have testers with test users, my own account, testers and it works all the time..
background.
users has_many :authorization_providers, e.g. facebook, twitter, gplus e.t.c
in the facebook action I'm fetching the oauth_token
def facebook
begin provider = authorization_providers.where(provider: 'facebook').first
#facebook ||= Koala::Facebook::API.new(provider.oauth_token)
block_given? ? yield(#facebook) : #facebook
rescue Koala::Facebook::APIError => e #Koala::Facebook::APIError
return nil
end
#facebook
end
In this action I'm getting permission
def facebook_publish_actions
if facebook
begin
permissions = facebook.get_connection("me", "permissions")
publish_actions_permission = permissions.find { |permission| permission["permission"] == "publish_actions" }
publish_actions_permission_granted = publish_actions_permission && publish_actions_permission["status"] == "granted"
return publish_actions_permission_granted
rescue
return false
end
else
return false
end
end
The actual posting is done from a sidekiq worker where 'share_on_facebook' and 'recording' is records from the db
user.facebook.put_wall_post(share_on_facebook.message,
{
"name" => "#{recording.title}",
"link" => "http://www.digiramp.com/users/#{recording.user.slug}/recordings/#{recording.id}",
"caption" => "#{user.name} recomended a recording",
"description" => "#{recording.comment}",
"picture" => "#{recording.get_artwork}"
})
On the facebook developer page I have created one story for the app 'Recommend a Song'
All the above works.
Anyone willing to help me I will grant all the required permissions.
Right now you can go to http://digiramp.com and sign up with facebook.
I will add you as a tester to my project and you should be able to post.
Edit:
I Do pass the id: FbRecordingCommentWorker.perform_async(#share_on_facebook.id)
Can you post the line of the code, where you initiate sidekiq job? Using objects in sidekiq call may not work as expected as objects are stored in hash representation in Redis. So better idea is replacing the object parameters to values.
Replacing some thing like this
User.delay.do_some_stuff(current_user, 20)
with
User.delay.do_some_stuff(current_user.id, 20)
and finding user object in the actual method may fix the issue.

How can I make the transaction rollback if the API call fails and vice versa?

Two things I want:
a) I want to be able to save a record in the db only if the API call succeeds
b) I want to execute the API call only if the db record saves successfully
The goal, is to keep data stored locally (in the DB) consistent with that of the data on Stripe.
#payment = Payment.new(...)
begin
Payment.transaction do
#payment.save!
stripe_customer = Stripe::Customer.retrieve(manager.customer_id)
charge = Stripe::Charge.create(
amount: #plan.amount_in_cents,
currency: 'usd',
customer: stripe_customer.id
)
end
# https://stripe.com/docs/api#errors
rescue Stripe::CardError, Stripe::InvalidRequestError, Stripe::APIError => error
#payment.errors.add :base, 'There was a problem processing your credit card. Please try again.'
render :new
rescue => error
render :new
else
redirect_to dashboard_root_path, notice: 'Thank you. Your payment is being processed.'
end
The above following will work, because if the record (on line 5) doesn't save, the rest of the code doesn't execute.
But what if I needed the #payment object saved after the API call, because I need to assign the #payment object with values from the API results. Take for example:
#payment = Payment.new(...)
begin
Payment.transaction do
stripe_customer = Stripe::Customer.retrieve(manager.customer_id)
charge = Stripe::Charge.create(
amount: #plan.amount_in_cents,
currency: 'usd',
customer: stripe_customer.id
)
#payment.payment_id = charge[:id]
#payment.activated_at = Time.now.utc
#payment.save!
end
# https://stripe.com/docs/api#errors
rescue Stripe::CardError, Stripe::InvalidRequestError, Stripe::APIError => error
#payment.errors.add :base, 'There was a problem processing your credit card. Please try again.'
render :new
rescue => error
render :new
else
redirect_to dashboard_root_path, notice: 'Thank you. Your payment is being processed.'
end
You notice #payment.save! happens after the API call. This could be a problem, because the API call ran, before the DB tried to save the record. Which could mean, a successful API call, but a failed DB commit.
Any ideas / suggestions for this scenario?
You can't execute API => DB and DB => API at the same time (sounds like an infinite execution conditions), at least I can't image how you can achieve this workflow. I understand your data consistency needs, so I propose:
Check if record is valid #payment.valid? (probably with a custom method like valid_without_payment?)
Run api call
Save record (with payment_id) only if api call succeds
Alternatively:
Save record without payment_id
Run api call
Update record with payment_id (api response) if call succeds
Run a task (script) periodically (cron) to check inconsistent instances (where(payment_id: nil)) and delete it
I think both options are acceptable and your data will remain consistent.

What's the most efficient way to keep a user database in sync with an external mailing list service?

I'd like some advice on how I should synchronize a list of email addresses on 11k users against an external mailing list program, in this case Mailchimp.
Normally the way I'd do this is simply to have an :after_save callback, to send a single update to the external api.
But already each hour, a rake task is run to update a property on every user in the database. If I simply did that, every hour, the the poor mailchimp API would get be hit 11,000 times.
What's the most efficient, simple way to do this, to check only if a single attribute you're watching has changed from what it was before the save?
If there's a variable that persists across the transaction lifecycle I would simply do something like this, where I check if the value has changed, and if it's different execute come other code.
class User
:before_save :store_old_email
:after_save :sync_with_chimp
def store_old_email
$ugly_of_global_variable_to_store_email = user.email
end
:sync_with_chimp
if $ugly_of_global_variable_to_store_email != user.email
//update_mail_chimp_api
end
end
end
I've checked the rails api here, and I'm still slightly unclear on how I should be doing this.
Would you use the dirty? class here to do this?
This is the way I went with in the end.
It turns out Rails gives you loads of handy callbacks in the dirty to do this.
Any suggestions on how to make this code less repetitive wold be gratefully received.
def update_mailchimp(optin)
# Create a Hominid object (A wrapper to the mailchimp api), and pass in a hash from the yaml file
# telling which mailing list id to update with subscribe/unsubscribe notifications)
#hominid = Hominid.new
client_site_list_id = YAML.load(File.read(RAILS_ROOT + "/config/mailchimp.yml"))
case optin
when 'subscribe_newsletter'
logger.debug("subscribing to newsletter...")
"success!" if #hominid.subscribe(client_site_list_id['client_site_to_mailchimp_API_link'], email, {:FNAME => first_name, :LNAME => last_name}, 'html')
when 'unsubscribe_newsletter'
logger.debug("unsubscribing from newsletter...")
"success!" if #hominid.subscribe(client_site_list_id['client_site_to_mailchimp_API_link'], email, {:FNAME => first_name, :LNAME => last_name}, 'html')
when 'subscribe_monthly_update'
logger.debug("subscribing to monthly update...")
"success!" if #hominid.subscribe(client_site_list_id['monthly_update'], email, {:FNAME => first_name, :LNAME => last_name}, 'html')
when 'unsubscribe_monthly_update'
logger.debug("unsubscribing from monthly update...")
"success!" if #hominid.unsubscribe(client_site_list_id['monthly_update'], email, {:FNAME => first_name, :LNAME => last_name}, 'html')
end
end
# Keep the users in sync with mailchimp's own records - by only firing requests to the API if details on a user have changed after saving.
def check_against_mailchimp
logger.info("Checking if changes need to be sent to mailchimp...")
if newsletter_changed?
logger.info("Newsletter changed...")
newsletter ? update_mailchimp('subscribe_newsletter') : update_mailchimp('unsubscribe_newsletter')
end
if monthly_update_changed?
logger.info("update preferences changed...")
monthly_update ? update_mailchimp('subscribe_monthly_update') : update_mailchimp('unsubscribe_monthly_update')
end
end
you could change your users model to an active resource instead of active record and just use mailchimps api as your db for users
this is an older post about active resource but might get you started down the right path
http://www.therailsway.com/2007/9/3/using-activeresource-to-consume-web-services

Resources