Pay Package (paypkg)
A Ruby Gem designed to simplify connecting to PayPal
Copyright
Copyright (©) 2014, Michael J. Welch, Ph.D. and Contributors. All Rights Reserved.
Email: [email protected]
This project is licensed under the [MIT License] (LICENSE.md).
Features:
- Uses
Net::HTTPto communicate with PayPal servers. - Easy to set up and use.
Install
To install this gem, (and another one I recommend) use:
development:~$ gem install paypkg
development:~$ gem install pretty_inspect
Want to Make a Rails Test Project?
Follow the steps below. First, create a rails project. In this case, I'm using bundler. You probably are too..
development:~$ rails new paypkg-test -d mysql
create
create README.rdoc
...
create vendor/assets/stylesheets/.keep
run bundle install
Fetching gem metadata from https://rubygems.org/...........
Fetching additional metadata from https://rubygems.org/..
Resolving dependencies...
Using rake (10.3.1)
...
Using uglifier (2.5.0)
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
development:~$
Change Directory into Your Project
development:~$ cd paypkg-test
development:~/paypkg-test$
If you're using bundler, you'll need to add paypkg, and pretty-inspect to your Gemfile.
...
# Use this gem to connect to PayPal
gem 'paypkg'
gem 'pretty_inspect'
...
I had problems with the new spring gem. The "rails c" environment would hang, so if you have the same problem, try commenting out that gem.
# Spring speeds up development by keeping your application running in the background.
# gem 'spring', group: :development
Then you'll have to run bundler again.
development:~/paypkg-test$ bundle install
Resolving dependencies...
Using rake (10.3.1)
...
Using paypkg (0.1.2)
Using pretty_inspect (0.9.0)
...
Using uglifier (2.5.0)
Your bundle is complete!
Use `bundle show [gemname]` to see where a bundled gem is installed.
development:~/paypkg-test$
Edit the config/routes.rb file, and add the routes:
Rails.application.routes.draw do
get 'paypkg_test/test1' => 'paypkg_test#test1'
get 'paypkg_test/test2' => 'paypkg_test#test2'
get 'paypkg_test/approved' => 'paypkg_test#approved'
get 'paypkg_test/cancelled' => 'paypkg_test#cancelled'
end
Configure your config/database.yml. If you don't know how to do this, consult your Rails documentation. If you already have a working project, you can copy the database.yml from there. This test program does not access the database, but Rails won't start without one.
Copy the test page and view folder. Your gems might be in a different place, so beware!
development:~/paypkg-test$ cp /var/lib/gems/2.0.0/gems/paypkg-0.1.2/test/paypkg_test_controller.rb app/controllers/paypkg_test_controller.rb
development:~/paypkg-test$ cp -r /var/lib/gems/2.0.0/gems/paypkg-0.1.2/test/paypkg_test app/views/paypkg_test
development:~/paypkg-test$
Configure Pay Package. Create a file named config/paypkg.yml and put this code in it, modified to use your own data.
development:
client_id: '--------your sandbox client ID as assigned by PayPal--------'
secret: '---------your sandbox secret as assigned by PayPal----------'
uri_base: 'https://api.sandbox.paypal.com'
website: 'http://www.example.com:3000'
test:
client_id: '--------your sandbox client ID as assigned by PayPal--------'
secret: '---------your sandbox secret as assigned by PayPal----------'
uri_base: 'https://api.sandbox.paypal.com'
website: 'http://www.example.com:3000'
production:
client_id: '-------your production client ID as assigned by PayPal------'
secret: '--------your production secret as assigned by PayPal--------'
uri_base: 'https://api.paypal.com'
website: 'https://example.com'
Run Webrick to test the paypkg gem.
development:~/paypkg-test$ rails s
=> Booting WEBrick
=> Rails 4.0.4 application starting in development on http://0.0.0.0:3000
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
[2014-04-22 03:37:14] INFO WEBrick 1.3.1
[2014-04-22 03:37:14] INFO ruby 2.0.0 (2013-08-29) [x86_64-linux-gnu]
[2014-04-22 03:37:14] INFO WEBrick::HTTPServer#start: pid=21184 port=3000
In your browser, type www.example.com:3000 and you should get:
Welcome aboard
You’re riding Ruby on Rails!
Getting started
Here’s how to get rolling:
-
Use
rails generateto create your models and controllersTo see all available options, run it without parameters.
-
Set up a root route to replace this page
You're seeing this page because you're running in development mode and you haven't set a root route yet.
Routes are set up in config/routes.rb.
-
Configure your database
If you're not using SQLite (the default), edit config/database.yml with your username and password.
To run the first test, type into your browser http://www.example.com:3000/paypkg_test/test1. This test does a lot of PayPal acesses, so it may take a couple of minutes. If you have a problem in your setup, it'll die immediately. If all is well, you should get the results shown below.
Paypkg Test1
validate_credit_card OK
validate_credit_card Passed
store_credit_card OK
retrieve_credit_card OK
retrieve_credit_card Passed
accept_tendered_cc_payment OK
retrieve_sale_transaction OK
retrieve_sale_transaction Passed
refund_sale OK
refund_sale Passed
accept_stored_cc_payment OK
retrieve_sale_transaction OK
retrieve_sale_transaction Passed
refund_sale OK
refund_sale Passed
retrieve_refund_transaction OK
retrieve_refund_transaction Passed
delete_credit_card OK
Test2 will request a payment from a client through PayPal.
Paypkg -- Buyer Approved
The buyer selected to approve the purchase.
payment ID: PAY-5L177311YG734994KKNK64AY
Sale ID: 2UU00055SD347731A
Amount: 3.00
Sample Responses
A simple way to see what you're going to get back is to run the class inrails c. Here's an example of three transactions taken from the test suite test/paypkg_test_controller.rb.
development:~/paypkg-test$ rails c
Loading development environment (Rails 4.1.0)
irb(main):001:0>
First, you have to create a class instance (which also guarantees that you have a valid access code). The access code will be reused as long as it remains valid (usually 8 hours). In a Rails web app, the codes are kept in the session, but here, in the irb, the codes are reused until the class instance is discarded.
irb(main):001:0> pp = Paypkg.new
"{\"scope\":\"openid https://api.paypal.com/v1/payments/.* https://api.paypal.com/v1/developer/.*
https://api.paypal.com/v1/vault/credit-card/.* https://api.paypal.com/v1/vault/credit-card\",
\"access_token\":\"---------your sandbox access token---------\",\"token_type\":\"Bearer\",
\"app_id\":\"-----your app id-----\",\"expires_in\":28800}"
=> #<Paypkg:0x00000004c58858 @session={:paypal_authorization=>{:expires_after=>2014-04-23 07:36:31 +0000,
:access_token=>"---------your sandbox access token---------"}}, @mode=:development,
@credentials={"client_id"=>"--------your sandbox client ID as assigned by PayPal--------",
"secret"=>"---------your sandbox secret as assigned by PayPal----------",
"uri_base"=>"https://api.sandbox.paypal.com", "website"=>"http://www.example.com:3000"},
@website="http://www.example.com:3000", @uri_base="https://api.sandbox.paypal.com",
@http=#<Net::HTTP api.sandbox.paypal.com:443 open=false>,
@access_token="---------your sandbox access token---------",
@json=[], @hash=[], @status=[], @request=[]>
irb(main):002:0>
Here we store a new credit card. The return code of true tells us that the status code has been checked and is good. We also save the card_id.
irb(main):002:0> @ok = pp.store_credit_card('visa', '4417119669820331', '11',
'2018', '999', 'Betsy', 'Buyer', '111 First Street', nil, 'Saratoga', 'CA', '95070', 'US', 'betsy')
=> true
irb(main):003:0> card_id = card_data[:id]
=> "CARD-4D566808P5063783FKNLP2XI"
irb(main):004:0>
The response from PayPay (in Ruby Hash format) looks like this (notice the use of puts ... pretty_inspect).
Compare with the documentation at Store a Credit Card.
irb(main):004:0> puts pp.hash.last.pretty_inspect
{
:id => "CARD-4D566808P5063783FKNLP2XI",
:state => "ok",
:payer_id => "betsy",
:type => "visa",
:number => "xxxxxxxxxxxx0331",
:expire_month => "11",
:expire_year => "2018",
:first_name => "Betsy",
:last_name => "Buyer",
:billing_address => {
:line1 => "111 First Street",
:city => "Saratoga",
:state => "CA",
:postal_code => "95070",
:country_code => "US"
},
:valid_until => "2017-04-21T00:00:00Z",
:create_time => "2014-04-22T23:38:05Z",
:update_time => "2014-04-22T23:38:05Z",
:links => [
{
:href => "https://api.sandbox.paypal.com/v1/vault/credit-card/CARD-4D566808P5063783FKNLP2XI",
:rel => "self",
:method => "GET"
},
{
:href => "https://api.sandbox.paypal.com/v1/vault/credit-card/CARD-4D566808P5063783FKNLP2XI",
:rel => "delete",
:method => "DELETE"
},
{
:href => "https://api.sandbox.paypal.com/v1/vault/credit-card/CARD-4D566808P5063783FKNLP2XI",
:rel => "patch",
:method => "PATCH"
}
]
}
=> nil
irb(main):005:0>
Now we retrieve the credit card.
irb(main):005:0> @ok = pp.retrieve_credit_card(card_id)
=> true
irb(main):006:0>
And here's the response (it's the same as the response when we stored the card). The response is not always identical to the original response when the transaction was first accomplished, but it will always contain the essential information. Also note that we use the card_id we stored (in the database in a real application).
irb(main):006:0> puts pp.hash.last.pretty_inspect
{
:id => "CARD-4D566808P5063783FKNLP2XI",
:state => "ok",
:payer_id => "betsy",
:type => "visa",
:number => "xxxxxxxxxxxx0331",
:expire_month => "11",
:expire_year => "2018",
:first_name => "Betsy",
:last_name => "Buyer",
:billing_address => {
:line1 => "111 First Street",
:city => "Saratoga",
:state => "CA",
:postal_code => "95070",
:country_code => "US"
},
:valid_until => "2017-04-21T00:00:00Z",
:create_time => "2014-04-22T23:38:05Z",
:update_time => "2014-04-22T23:38:05Z",
:links => [
{
:href => "https://api.sandbox.paypal.com/v1/vault/credit-card/CARD-4D566808P5063783FKNLP2XI",
:rel => "self",
:method => "GET"
},
{
:href => "https://api.sandbox.paypal.com/v1/vault/credit-card/CARD-4D566808P5063783FKNLP2XI",
:rel => "delete",
:method => "DELETE"
},
{
:href => "https://api.sandbox.paypal.com/v1/vault/credit-card/CARD-4D566808P5063783FKNLP2XI",
:rel => "patch",
:method => "PATCH"
}
]
}
=> nil
irb(main):007:0>
Lastly, we delete the credit card. There's no response, other than the status code, which was verified by the true response.
If there's a reason to know whether the card was still on PayPal's servers (Paypal automatically removes expired cards), we can check the status code.
irb(main):007:0> @ok = pp.delete_credit_card(card_id)
=> true
irb(main):008:0>pp.status.last.inspect
=> "\"204\""
irb(main):009:0>
If it's gone, we get this response:
irb(main):009:0> @ok = pp.delete_credit_card(card_id)
=> false
irb(main):010:0> pp.status.last.inspect
=> "\"404\""
irb(main):011:0>
There's no special closing process: the connection will be closed automaticall when pp is released (set to nil or garbage collected.
PaypkgResponse Class
The class PaypkgResponse is used to objectize a response hash (or almost any
hash object). Use it separately in other projects, if it works for you.
To get the PayPal response in an object format, after a successful call, use response = pp.response.
If you want this, for example:
payment_data = pp.hash.last
@amount = payment_data[:transactions][0][:related_resources][0][:sale][:amount][:total]
you would use this:
payment_data = pp.response # this will convert pp.hash.last
@amount = payment_data.transactions[0].[0].sale.amount.total
This is provided to be more compatible with views using ActiveRecord conventions.