I have been tinkering with the new Paypal Adaptive Payments API and created a simple ruby gem to interface with it. Still pretty new but I’m using it with little problems so far. Submit bug reports if found. See the code at github
Paypal Adaptive Payments API
The adaptive payments api gives you the opportunity to make preapproved payments, chained payments and parallel payments. The chained/parallel payments are great for commission-based apps or if you are trying to connect a buyer to multiple sellers with a single interface.
How to use with Rails:
Install:
sudo gem install paypal_adaptive
Setup your API info by adding a paypal_adaptive.yml to your config folder:
development:
environment: "sandbox"
username: "sandbox_username"
password: "sandbox_password"
signature: "sandbox_signature"
application_id: "sandbox_app_id"
test:
environment: "sandbox"
username: "sandbox_username"
password: "sandbox_password"
signature: "sandbox_signature"
application_id: "sandbox_app_id"
production:
environment: "production"
username: "my_production_username"
password: "my_production_password"
signature: "my_production_signature"
application_id: "my_production_app_id"
Make the payment request:
pay_request = PaypalAdaptive::Request.new
data = {
"returnUrl" => "http://testserver.com/payments/completed_payment_request",
"requestEnvelope" => {"errorLanguage" => "en_US"},
"currencyCode"=>"USD",
"receiverList"=>{"receiver"=>
[{"email"=>"testpp_1261697850_per@nextsprocket.com", "amount"=>"10.00"}]},
"cancelUrl"=>"http://testserver.com/payments/canceled_payment_request",
"actionType"=>"PAY",
"ipnNotificationUrl"=>"http://testserver.com/payments/ipn_notification"
}
pay_response = pay_request.pay(data)
if pay_response.success?
redirect_to pay_response.approve_paypal_payment_url
else
puts pay_response.errors.first['message']
redirect_to failed_payment_url
end
Once the user goes to pay_response.approve_paypal_payment_url, they will be prompted to login to Paypal for payment.
Upon payment completion page, they will be redirected to http://testserver.com/payments/completed_payment_request.
They can also click cancel to go to http://testserver.com/payments/canceled_payment_request
The actual payment details will be sent to your server via “ipnNotificationUrl” You have to create a listener to receive POST messages from paypal. I added a Rails metal template in the templates folder which handles the callback.
Additionally, you can make calls to Paypal Adaptive’s other APIs:
payment_details, preapproval, preapproval_details,
cancel_preapproval, convert_currency, refund
Input is just a Hash just like the pay method. Refer to the Paypal Adaptive manual for more details.
Coll !!!
Do you have an example of how your gem does Parallel payments?
Hey Matt,Just add two or more people to the receiverList to make it a parallel payment like:”receiverList”=>{“receiver”=> [{"email"=>"person1@chheng.com", "amount"=>"10.00"}, {"email"=>"person2@chheng.com", "amount"=>"20.00"}]},
testing if posted in right area
Hi Tommy,Above format is not working for me, getting an error message “This kind of unilateral payment is not allowed”. I had provided API username, pass, signature. Do I missing something?Thanks
hey, if you are getting a 'unilateral payment is not allowed', make sure the test email is a registered email in your sandbox account.
Hi. Looks good! Thx. Just trying it out.What do I need to put in application_id: “sandbox_app_id”?I didn't get one with my sandbox account and even googling doesn't reveal the answer…Nico
Works fine now. But I'd require users to also be able to pay with credit card, without having a PP account, like it is possible with website payments standard checkout. Also I don't like the user to see how much money goes to which email address.So ideally I'd like PP Website Payments Standard checkout capability but diverting the payment to different paypal accounts in the background. I think this must be somehow possible.Any ideas are greatly appreciated.
hi nico, you are right that paypal adaptive payments don't support credit card payments yet. It is scheduled in their roadmap. You can get a date from paypal to see how soon this is.
Thanks for the info. Any references?Is your gem also supporting chained payments? That is more interesting for me, as I don't like payers to see all the receivers of the payment.
There were a few posts on the http://x.com regarding CC payments coming in the near future.To do chained payments, just add a primary boolean flag:{“receiver”=> [{"email"=>"PRIMARY", "amount"=>"100.00", "primary" => true}, {"email"=>"OTHER", "amount"=>"75.00", "primary" => false}]}
Thanks!
Hey Tommy… newbie question but I see the username/password/signature fields on my developer paypal screen but where do I find the application_id? Is that just an arbitrary name that I can pick or is it somewhere on developer.paypal.com?
yes, sign up for an id at http://x.com
@Kevin you have to log in https://www.x.com/ and somewhere in profile you will find application_id. For production application_id you have to contact to paypal.
looks great! good work!
I am getting this error and used as the above example as stated above:undefined local variable or method `pp_response'please advice!Thank you for your time
looks like a typo, it should be pay_response
Also can you fix it in github too …http://github.com/tc/paypal_adaptive
yes, i'll do it right now, thanks for catching it
This looks great. I am having one problem getting it to work though… I'm getting this error:/home/user/.rvm/rubies/ruby-1.9.2-head/lib/ruby/1.9.1/net/http.rb:677:in `connect': SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed (OpenSSL::SSL::SSLError) from /home/user/.rvm/rubies/ruby-1.9.2-head/lib/ruby/1.9.1/net/http.rb:677:in `connect' from /home/user/.rvm/rubies/ruby-1.9.2-head/lib/ruby/1.9.1/net/http.rb:637:in `do_start' from /home/user/.rvm/rubies/ruby-1.9.2-head/lib/ruby/1.9.1/net/http.rb:626:in `start' from /home/user/.rvm/rubies/ruby-1.9.2-head/lib/ruby/1.9.1/net/http.rb:1160:in `request' from /home/user/.rvm/rubies/ruby-1.9.2-head/lib/ruby/1.9.1/net/http.rb:970:in `post' from /home/user/.rvm/gems/ruby-1.9.2-head/gems/paypal_adaptive-0.1.0/lib/request.rb:79:in `call_api' from /home/user/.rvm/gems/ruby-1.9.2-head/gems/paypal_adaptive-0.1.0/lib/request.rb:31:in `pay' from ./test.rb:18:in `<main>'When rolling this stuff by hand without your gem the solution was to add this line:http.verify_mode = OpenSSL::SSL::VERIFY_NONEWhere http is the Net::HTTP object. Is there a way to do the same thing using your gem, or is this something I've got misconfigured?Thanks.
This looks like a net::http error?If you are using Rails, you can try sticking http.verify_mode = OpenSSL::SSL::VERIFY_NONE into ssl.rb of config/initializers
Thanks for the reply. Actually, it looks like I can fix this by reopening the Net::HTTP class and tweaking the initialize method to it sets a ca_path for all new objects so it can find the right certificates. Here's what I did:class Net::HTTP alias
rig_initialize :initialize def initialize(*args) # Call the original init orig_initialize(*args) # Setup ssl cert path @ca_path = '/etc/ssl/certs' endend/etc/ssl/certs is where the certs live on Ubuntu. Don't know how common that is across platforms.
Nice little gem! I'm having trouble figuring out how to use the IPN. I'm not sure how to use the metal template. I should make an action to handle it, right? I made an “ipn” method in one of my controllers and set that to the ipnNotificationUrl, but after that, I haven't the foggiest idea what to do next. I'd appreciate any help!
Hi, you can also use a Rails action as well. See https://github.com/tc/paypal_a… for an example.
Hi There,i tried your example as it is, but when i use your metal template the “ipn.verified?” call always returns false (send_back sets it according your source). anything special i should be paying attention to?
Hi There,i tried your example as it is, but when i use your metal template the “ipn.verified?” call always returns false (send_back sets it according your source). anything special i should be paying attention to?
I think it’s great that you’re tniakg the time to explain the answers! I really like this series of articles! Keep up the great work!
when you are doing the send back: ipn.send_back(env['rack.request.form_vars'])check the env['rack.request.form_vars'] value before you send it outMake sure that's not garage.
turns out the problem was an authentication filter was blocking the post-back from Paypal.Now i am banging my head with trying to understand the incoming parameters.when i create the inital payment request, i keep a record with the payKey (something i need to find back and activate when the payment callback is processes) but it seems that the incoming parameters (rack.env) don't contain any reference to the initial request (payKey is different).ideas on how am i supposed to link between the incoming IPN to my pre-existing payment record?
nevermind that, i shouldn't be coding drunk
thanks for an awesome gem.
In my development environment the Paypal sandbox is working but it’s not working on production mode.
I’ve changed the paypal_adaptive.yml to ‘sandbox’ in the production, but still it doesn’t work. Is there anything else that I should change to see the production using sandbox?
Ignore my last post. I didn’t uploaded the file.
Hi ,
I am using papal_adaptive gem. I provided my paypal sandbox account details in papal_adaptive.yml file. When I call pay_request = PaypalAdaptive::Request.new, it throws the error below:
You have a nil object when you didn’t expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]
I am using ruby 1.9.2 and rails 3.0.7 in windows system.
Can you provide a slightly longer stacktrace? especially the line where it references the application and gem lines.
HI Tommychheng ,
I am still waiting for your reply .
Hi , tommychheng
My application error is :
———————————
You have a nil object when you didn’t expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]
paypal_adaptive (0.2.1) lib/config.rb:34:in `load’
paypal_adaptive (0.2.1) lib/config.rb:26:in `initialize’
paypal_adaptive (0.2.1) lib/request.rb:14:in `new’
paypal_adaptive (0.2.1) lib/request.rb:14:in `initialize’
app/controllers/ven_controller.rb:8:in `new’
app/controllers/ven_controller.rb:8:in `index’
actionpack (3.0.7) lib/action_controller/metal/implicit_render.rb:5:in `send_action’
actionpack (3.0.7) lib/abstract_controller/base.rb:150:in `process_action’
actionpack (3.0.7) lib/action_controller/metal/rendering.rb:11:in `process_action’
actionpack (3.0.7) lib/abstract_controller/callbacks.rb:18:in `block in process_action’
activesupport (3.0.7) lib/active_support/callbacks.rb:436:in `_run__954984038__process_action__457429307__callbacks’
activesupport (3.0.7) lib/active_support/callbacks.rb:410:in `_run_process_action_callbacks’
activesupport (3.0.7) lib/active_support/callbacks.rb:94:in `run_callbacks’
actionpack (3.0.7) lib/abstract_controller/callbacks.rb:17:in `process_action’
actionpack (3.0.7) lib/action_controller/metal/instrumentation.rb:30:in `block in process_action’
activesupport (3.0.7) lib/active_support/notifications.rb:52:in `block in instrument’
activesupport (3.0.7) lib/active_support/notifications/instrumenter.rb:21:in `instrument’
activesupport (3.0.7) lib/active_support/notifications.rb:52:in `instrument’
actionpack (3.0.7) lib/action_controller/metal/instrumentation.rb:29:in `process_action’
actionpack (3.0.7) lib/action_controller/metal/rescue.rb:17:in `process_action’
actionpack (3.0.7) lib/abstract_controller/base.rb:119:in `process’
actionpack (3.0.7) lib/abstract_controller/rendering.rb:41:in `process’
actionpack (3.0.7) lib/action_controller/metal.rb:138:in `dispatch’
actionpack (3.0.7) lib/action_controller/metal/rack_delegation.rb:14:in `dispatch’
actionpack (3.0.7) lib/action_controller/metal.rb:178:in `block in action’
actionpack (3.0.7) lib/action_dispatch/routing/route_set.rb:62:in `call’
actionpack (3.0.7) lib/action_dispatch/routing/route_set.rb:62:in `dispatch’
actionpack (3.0.7) lib/action_dispatch/routing/route_set.rb:27:in `call’
rack-mount (0.6.14) lib/rack/mount/route_set.rb:148:in `block in call’
rack-mount (0.6.14) lib/rack/mount/code_generation.rb:93:in `block in recognize’
rack-mount (0.6.14) lib/rack/mount/code_generation.rb:68:in `optimized_each’
rack-mount (0.6.14) lib/rack/mount/code_generation.rb:92:in `recognize’
rack-mount (0.6.14) lib/rack/mount/route_set.rb:139:in `call’
actionpack (3.0.7) lib/action_dispatch/routing/route_set.rb:493:in `call’
actionpack (3.0.7) lib/action_dispatch/middleware/best_standards_support.rb:17:in `call’
actionpack (3.0.7) lib/action_dispatch/middleware/head.rb:14:in `call’
rack (1.2.3) lib/rack/methodoverride.rb:24:in `call’
actionpack (3.0.7) lib/action_dispatch/middleware/params_parser.rb:21:in `call’
actionpack (3.0.7) lib/action_dispatch/middleware/flash.rb:182:in `call’
actionpack (3.0.7) lib/action_dispatch/middleware/session/abstract_store.rb:149:in `call’
actionpack (3.0.7) lib/action_dispatch/middleware/cookies.rb:302:in `call’
activerecord (3.0.7) lib/active_record/query_cache.rb:32:in `block in call’
activerecord (3.0.7) lib/active_record/connection_adapters/abstract/query_cache.rb:28:in `cache’
activerecord (3.0.7) lib/active_record/query_cache.rb:12:in `cache’
activerecord (3.0.7) lib/active_record/query_cache.rb:31:in `call’
activerecord (3.0.7) lib/active_record/connection_adapters/abstract/connection_pool.rb:354:in `call’
actionpack (3.0.7) lib/action_dispatch/middleware/callbacks.rb:46:in `block in call’
activesupport (3.0.7) lib/active_support/callbacks.rb:416:in `_run_call_callbacks’
actionpack (3.0.7) lib/action_dispatch/middleware/callbacks.rb:44:in `call’
rack (1.2.3) lib/rack/sendfile.rb:107:in `call’
actionpack (3.0.7) lib/action_dispatch/middleware/remote_ip.rb:48:in `call’
actionpack (3.0.7) lib/action_dispatch/middleware/show_exceptions.rb:47:in `call’
railties (3.0.7) lib/rails/rack/logger.rb:13:in `call’
rack (1.2.3) lib/rack/runtime.rb:17:in `call’
activesupport (3.0.7) lib/active_support/cache/strategy/local_cache.rb:72:in `call’
rack (1.2.3) lib/rack/lock.rb:11:in `block in call’
:10:in `synchronize’
rack (1.2.3) lib/rack/lock.rb:11:in `call’
actionpack (3.0.7) lib/action_dispatch/middleware/static.rb:30:in `call’
railties (3.0.7) lib/rails/application.rb:168:in `call’
railties (3.0.7) lib/rails/application.rb:77:in `method_missing’
railties (3.0.7) lib/rails/rack/log_tailer.rb:14:in `call’
rack (1.2.3) lib/rack/content_length.rb:13:in `call’
rack (1.2.3) lib/rack/handler/webrick.rb:52:in `service’
/home/mahesh/.rvm/rubies/ruby-1.9.2-p0/lib/ruby/1.9.1/webrick/httpserver.rb:111:in `service’
/home/mahesh/.rvm/rubies/ruby-1.9.2-p0/lib/ruby/1.9.1/webrick/httpserver.rb:70:in `run’
/home/mahesh/.rvm/rubies/ruby-1.9.2-p0/lib/ruby/1.9.1/webrick/server.rb:183:in `block in start_thread’
Hi , Tommychheng
Please reply to me……
PRASAD
Prasad it’s easy, just add paypal_adaptive.yml to your config path.
In the future, try to read code before to post stacktraces.
Hello, newbie question here. I’m trying to create a payment preapproval test but don’t know where to get this information:
username: “sandbox_username”
password: “sandbox_password”
signature: “sandbox_signature”
application_id: “sandbox_app_id”
From my x.com account? From the enterprise sandbox account I created for the preapproval?
Thanks for the help.
@ciriusmex
you got solution ? I also wanna test payment preapproval .. but i don’t know how to start work. could you please share with me example code if u got it?
Hi there,
I’ve been trying to make this gem work with no luck: I always get the error “undefined method ‘strip’ for nil:NilClass”. The last lines of my framework trace are the next ones:
activesupport (3.0.3) lib/active_support/whiny_nil.rb:48:in `method_missing’
C:/Ruby192/lib/ruby/1.9.1/net/http.rb:1296:in `block in initialize_http_header’
C:/Ruby192/lib/ruby/1.9.1/net/http.rb:1294:in `each’
C:/Ruby192/lib/ruby/1.9.1/net/http.rb:1294:in `initialize_http_header’
C:/Ruby192/lib/ruby/1.9.1/net/http.rb:1662:in `initialize’
C:/Ruby192/lib/ruby/1.9.1/net/http.rb:1779:in `initialize’
C:/Ruby192/lib/ruby/1.9.1/net/http.rb:970:in `new’
C:/Ruby192/lib/ruby/1.9.1/net/http.rb:970:in `post’
paypal_adaptive (0.2.2) lib/request.rb:91:in `call_api’
paypal_adaptive (0.2.2) lib/request.rb:33:in `pay’
I think this could be due to my ruby or rails version. I’m working under rails 3.0.3 and ruby 1.9.2. Another possible problem is that I commented “application_id” field in paypal_adaptive.yml config file because I couldn’t find this info anywhere inside x.com (i created an account) neither paypal.com (inside my business acount nor my sandbox account). I also tested this field uncommented but with a blank value without any luck.
Any help would be really apreciated.
any chanbe this gem would work with activemerchant? or any other suggestions for same?
Hi ,
I have seen refund method , For a shopping cart application refund method functionality required , but In paypal adaptive payments pdf refund option requires third party access . Shall I know how to check sellers account third party access option , it means for a refund option , sellers have to give third party access to my api , so I need to check every time , is seller given third party access or not . How it is possible .
Thank in Advance,
PRASAD
Hey Tommy,
Thanks for the great gem, it saved me a lot of time. I was just curious if it’s possible to use the delayed chained payment features that paypal offers with this gem. If so do you think you could give me some tips? Specifically I want to instantly transfer the money to the primary user and at a later specified date, transfer part of the money from the primary receiver to the secondary receiver.
Thanks,
Zach
Hey Zach,
I’m looking for this same feature.
Are you working on this now?
-Vivek
Hi Tommy,
Great gem! Thank you.
Could you please provide an example of ssl certificate usage?
Which file path should I put in the ssl_cert_file: ?
My certificate or paypal certificate?
And my private key?
And more generally, since all the payment interactions will be in the paypal website, is it less secure not to use ssl? What would be the differences?
Thank you very much
Valerio
Hi tommychheng,
Do you know how long take to achieve the api key to use the adaptive payments out of sandbox?
Thanks
Hi Tommy,
Could you please take look the error?
I got error while I call pay method as follow:
returnUrl = “http://localhost:3000/payment/payment_success/”+params[:id]
cancelUrl = “http://localhost:3000/payment/payment_cancel”
##### Payment after preapproved
pay_request = PaypalAdaptive::Request.new
data = {
“returnUrl” => returnUrl,
“requestEnvelope” => {“errorLanguage” => “en_US”},
“currencyCode”=>”SGD”,
“receiverList”=>{“receiver”=>
[{"email"=>"htay_1319791352_biz@gajah.com.sg", "amount"=>"1.00"}]},
“cancelUrl”=> cancelUrl,
“actionType”=>”PAY”,
“preapprovalKey” => @user.preapprovalKey #”PA-5WN75327B39732150″
}
pay_response = pay_request.pay(data)
if pay_response.success?
session[:pay_response] = pay_response['payKey']
redirect_to pay_response.approve_paypal_payment_url
else
puts pay_response.errors.first['message']
#redirect_to failed_payment_url
respond_to do |format|
format.html { redirect_to inventories_url, :notice => pay_response.errors.first['message'] }
end
Error :
This transaction has already been approved. Please visit your PayPal Account Overview to see the details.
However, payment is successul but not redirect back to specified “returnUrl”.
Could you pelase advice?
Thank
Hi Tommy
I’d require users to also be able to pay with credit card, without having a Paypal account, like it is possible with website payments standard.
Hi, Tommy.
We have a pretty simple requirement.
We want to allow our users to request money from us, and for us to send them money via PayPal automatically (no manual login required from either the requester or from us).
So the user would enter the following:
* PayPal email address to receive the money
* Amount (amount and currency)
Is this something Adaptive Payment can support?
Looking at your example above, we won’t need returnUrl or cancelUrl.
As the sender of money is us (the website, the merchant), and it should be completely
automated. No one is going to manually enter a password to PayPal to indicate that
we own the merchant account. Is this possible?
Thanks alot!
Zack
Hi,
I’m sorry but I’m not sure how to implement this reverse payment system. Maybe ask on the paypal forums? And you do have an answer which uses the paypal adaptive gem, please share it here as well.
Just wondering if it’s possible to include the line item details? i.e. product name, product description, price etc?
Thank for your work and this is great gem.
I am working on developing web mobile app with JQuery Mobile. Is it possible to request Paypal login for mobile to Paypal through this gem?
Thank in advance
If you are developing your app with jquery mobile and ruby backend, this gem can work just the same.
I appreciate your quick reply. But I mean it works fine with the gem but it redirects to Paypal login page for Web. Is it possible for me to configure in order to redirect to Paypal log page for Mobile?
I’m sorry, but i’m not sure if there is a paypal mobile login screen. Maybe try asking on the paypal adaptive forums. If you find an answer, feel free to share it here too
Hey tommy; I’ve just read lots of documentation, and your gem looks really great. I’ve been wondering how to receive payments before making the parallel payment to the recievers. I am developing an ecommerce site, where you can buy and sell things, so I need also receive payments. Does this gem supprt this? or do I have to use Active Merchant and PayPal Adaptive gems?
Thanks for your response
Yes, this gem supports what you suggested. Maybe you want to do a chained payment system?
https://www.x.com/devzone/articles/creating-online-store-chained-payments-apis
Yes, kind of; but I need to control those payments from the rails app. My payment logical is this one:
User pays for an item,
Site owner receives 30% of the payment,
Item owner receives 70% of the payment.
How do I send the transaction when the first user pays the total in the site?
Thanks
Take a look at the README code example: https://github.com/tc/paypal_adaptive
You need to adjust the request hash which you can learn more from the paypal adaptive docs.