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::HTTP to 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: Ruby on Rails: Welcome aboard

Getting started

Here’s how to get rolling:

  1. Use rails generate to create your models and controllers

    To see all available options, run it without parameters.

  2. 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.

  3. 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].related_resources[0].sale.amount.total

This is provided to be more compatible with views using ActiveRecord conventions.