ruby-paypal 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README CHANGED
@@ -42,7 +42,11 @@ DoDirectPayment APIs:
42
42
  # do your thing
43
43
  end
44
44
 
45
- The above code is for a final sale only
45
+ The above code is for a final sale only.
46
+
47
+ Note that the credit card number is checked against a modulo-10 algorithm (Luhn check) as well as a simple credit card
48
+ type check. For more information please refer to http://en.wikipedia.org/wiki/Luhn_algorithm and
49
+ http://en.wikipedia.org/wiki/Credit_card_number
46
50
 
47
51
  2. For the Express Checkout using the customer's PayPal account for payment, you will need to use the ExpressCheckout APIs:
48
52
 
data/lib/ruby-paypal.rb CHANGED
@@ -2,4 +2,5 @@
2
2
  #
3
3
  # Ruby-PayPal is a lightweight wrapper around the PayPal NVP APIs
4
4
 
5
- require 'ruby-paypal/paypal'
5
+ require 'ruby-paypal/paypal'
6
+ require 'ruby-paypal/credit_card_checks'
@@ -0,0 +1,58 @@
1
+ #
2
+ # Performs a series of credit card number checks.
3
+ #
4
+ module CreditCardChecks
5
+ # Perform Luhn check on a credit card number. Refer to http://en.wikipedia.org/wiki/Luhn_algorithm
6
+ #
7
+ def luhn_check(number)
8
+ num = 0
9
+ number.length.times { |p|
10
+ position = number.length - p - 1
11
+ if position % 2 == 0 then
12
+ doubled = number[position,1].to_i * 2
13
+ if doubled > 9 then
14
+ num = num + doubled.to_s[0,1].to_i + doubled.to_s[1,1].to_i
15
+ else
16
+ num = num + doubled.to_i
17
+ end
18
+ else
19
+ num = num + number[position,1].to_i
20
+ end
21
+ }
22
+ if num.to_i % 10 == 0 then
23
+ return true
24
+ else
25
+ return false
26
+ end
27
+ end
28
+
29
+ # Perform checks on credit card numbers with reference to the length of the credit card number
30
+ # and the type of credit card. Refer to http://en.wikipedia.org/wiki/Credit_card_number
31
+ #
32
+ def card_type_check(type, number)
33
+ validity = false
34
+ case type.upcase
35
+ when "VISA"
36
+ validity = true if (number.length == 16 or number.length = 13) and number[0,1] == "4"
37
+ when "MASTERCARD"
38
+ validity = true if (number.length == 16) and (52..55).include?(number[0,2].to_i)
39
+ when "AMEX"
40
+ validity = true if (number.length == 16) and ['34', '37'].include?(number[0,2])
41
+ when "DISCOVER"
42
+ validity = true if (number.length == 16) and (number[0,2] == "65" or number[0,4] == "6011")
43
+ when "SWITCH"
44
+ # TODO checks for Switch cards
45
+ validity = true
46
+ when "SOLO"
47
+ # TODO checks for Solo cards
48
+ validity = true
49
+ else
50
+ raise "Invalid card type entered"
51
+ end
52
+
53
+ return validity
54
+ end
55
+
56
+
57
+
58
+ end
@@ -2,12 +2,16 @@ require 'pp'
2
2
  require 'net/http'
3
3
  require 'net/https'
4
4
  require 'uri'
5
+ require 'lib/ruby-paypal/credit_card_checks'
5
6
 
6
7
  SANDBOX_SERVER = 'api-3t.sandbox.paypal.com/nvp'
7
- LIVE_SERVER = 'api-3t.paypal.com/nvp'
8
+ PRODUCTION_SERVER = 'api-3t.paypal.com/nvp'
8
9
  API_VERSION = '3.2'
9
10
 
10
11
  module Net
12
+ #
13
+ # A convenience class to enable this library to call PayPal's HTTPS NVP APIs
14
+ #
11
15
  class HTTPS < HTTP
12
16
  def initialize(address, port = nil, verify = :no_verify)
13
17
  super(address, port)
@@ -17,6 +21,23 @@ module Net
17
21
  end
18
22
  end
19
23
 
24
+ #
25
+ # A container for the response from PayPal. Each call to PayPal returns a generic set
26
+ # of information as well as a specific set for the call. For more information please refer
27
+ # to PayPal NVP API Developer Guide and Reference.
28
+ #
29
+ # To use retrieve information in the response, call the corresponding name of the object. For
30
+ # example, all responses from PayPal includes the field <tt>ACK</tt>. To get the data for this
31
+ # field:
32
+ #
33
+ #
34
+ # if response.ack == 'Success' then
35
+ # # do your stuff
36
+ # end
37
+ #
38
+ # This is because this class uses a meta-programming trick with method_missing to redirect all
39
+ # known method calls to its internal hash data structure.
40
+ #
20
41
  class PayPalResponse < Hash
21
42
  def method_missing(m,*a)
22
43
  if m.to_s.upcase =~ /=$/
@@ -29,27 +50,117 @@ class PayPalResponse < Hash
29
50
  end
30
51
  end
31
52
 
53
+
54
+ =begin rdoc
55
+ Author:: Chang Sau Sheong (mailto:sausheong.chang@gmail.com)
56
+ Copyright:: Copyright (c) 2007 Chang Sau Sheong
57
+ License:: Distributes under the same terms as Ruby
58
+ Version:: 0.0.2
59
+
60
+ =Installing Ruby-PayPal
61
+ A lightweight ruby wrapper for PayPal NVP APIs. To install type the following
62
+ at the command line:
63
+
64
+ $ gem install ruby-paypal
65
+
66
+
67
+ =Using Ruby-PayPal
68
+ It's critical that you understand how PayPal works and how the PayPal NVP API
69
+ works. You should be relatively well-versed in the NVP API Developer Guide and
70
+ Reference (https://www.paypal.com/en_US/ebook/PP_NVPAPI_DeveloperGuide/index.html).
71
+ You should also visit and register yourself with the PayPal Developer Network
72
+ and get a Sandbox account with in the PayPal Development Central
73
+ (https://developer.paypal.com/).
74
+
75
+ Note that this library only supports the API signature method of securing the API credentials.
76
+
77
+ ==Direct Payment
78
+ To use credit card payment through PayPal, you need to use the DoDirectPayment APIs:
79
+
80
+ username = <PayPal API username>
81
+ password = <PayPal API password>
82
+ signature = <PayPal API signature>
83
+
84
+ ipaddress = '192.168.1.1' # can be any IP address
85
+ amount = '100.00' # amount paid
86
+ card_type = 'VISA' # can be Visa, Mastercard, Amex etc
87
+ card_no = '4512345678901234' # credit card number
88
+ exp_date = '022010' # expiry date of the credit card
89
+ first_name = 'Sau Sheong'
90
+ last_name = 'Chang'
91
+
92
+ paypal = Paypal.new(username, password, signature) # uses the PayPal sandbox
93
+ response = paypal.do_direct_payment_sale(ipaddress, amount, card_type,
94
+ card_no, exp_date, first_name, last_name)
95
+ if response.ack == 'Success' then
96
+ # do your thing
97
+ end
98
+
99
+ The above code is for a final sale only.
100
+
101
+ Note that the credit card number is checked against a modulo-10 algorithm (Luhn check) as well as a simple credit card
102
+ type check. For more information please refer to http://en.wikipedia.org/wiki/Luhn_algorithm and
103
+ http://en.wikipedia.org/wiki/Credit_card_number
104
+
105
+ ==Express Checkout
106
+ To use the customer's PayPal account for payment, you will need to use the ExpressCheckout APIs:
107
+
108
+ <to be documented>
109
+
110
+ =More information
111
+ I will be continually providing updates in my blog at http://blog.saush.com
112
+
113
+ =end
114
+
32
115
  class Paypal
33
- def initialize(user, password, signature, url=SANDBOX_SERVER, subject=nil)
116
+ include CreditCardChecks
117
+
118
+ # Create a new object with the given user name, password and signature. To enable production
119
+ # access to PayPal change the url to the live PayPal server. Set url to <tt>:production</tt> to change
120
+ # access to PayPal production servers.
121
+ #
122
+ def initialize(user, password, signature, url=:sandbox, subject=nil)
34
123
  @api_parameters = {'USER' => user,
35
124
  'PWD' => password,
36
125
  'VERSION' => API_VERSION,
37
126
  'SIGNATURE' => signature }
38
- @paypal_url = url
127
+ if url == :sandbox
128
+ @paypal_url = SANDBOX_SERVER
129
+ elsif url == :production
130
+ @paypal_url = PRODUCTION_SERVER
131
+ else
132
+ raise 'Invalid url specified'
133
+ end
39
134
  end
40
135
 
136
+ # Performs credit card payment with PayPal, but only requesting for authorization. You need
137
+ # to capture the funds later. Calls do_direct_payment.
138
+ #
139
+ # Equivalent of DoDirectPayment with the PAYMENTACTION of 'authorization'
140
+ #
41
141
  def do_direct_payment_authorization(ipaddress, amount, credit_card_type, credit_card_no, expiry_date,
42
142
  first_name, last_name, cvv2=nil, other_params={})
43
143
  do_direct_payment('Authorization', ipaddress, amount, credit_card_type, credit_card_no,
44
144
  expiry_date, first_name, last_name, cvv2, other_params)
45
145
  end
46
146
 
147
+ # Performs credit card payment with PayPal, finalizing the sale. Funds are captured immediately.
148
+ # Calls do_direct_payment.
149
+ #
150
+ # Equivalent of DoDirectPayment with the PAYMENTACTION of 'sale'
151
+ #
47
152
  def do_direct_payment_sale(ipaddress, amount, credit_card_type, credit_card_no, expiry_date,
48
153
  first_name, last_name, cvv2=nil, other_params={})
49
154
  do_direct_payment('Sale', ipaddress, amount, credit_card_type, credit_card_no,
50
155
  expiry_date, first_name, last_name, cvv2, other_params)
51
156
  end
52
157
 
158
+ # Performs credit card payment with PayPal.
159
+ #
160
+ # Equivalent of DoDirectPayment.
161
+ #
162
+ # Performs Luhn check and a simple credit card type check based on the card number.
163
+ #
53
164
  def do_direct_payment(payment_action, ipaddress, amount, credit_card_type,
54
165
  credit_card_no, expiry_date, first_name, last_name, cvv2=nil, other_params={})
55
166
  params = {
@@ -62,12 +173,19 @@ class Paypal
62
173
  'FIRSTNAME' => first_name,
63
174
  'LASTNAME' => last_name,
64
175
  'IPADDRESS' => ipaddress }
65
- params['CVV2'] = cvv2 unless cvv2.nil?
66
- params.merge! other_params
67
- make_nvp_call(params)
68
- end
176
+ params['CVV2'] = cvv2 unless cvv2.nil?
177
+ params.merge! other_params
69
178
 
179
+ raise 'Invalid credit card number' if not luhn_check(params['ACCT'])
180
+ raise 'Invalid credit card type' if not card_type_check(params['CREDITCARDTYPE'], params['ACCT'])
70
181
 
182
+ make_nvp_call(params)
183
+ end
184
+
185
+ # Performs payment through PayPal.
186
+ #
187
+ # Equivalent of SetExpressCheckout.
188
+ #
71
189
  def set_express_checkout(return_url, cancel_url, amount)
72
190
  params = {
73
191
  'METHOD' => 'SetExpressCheckout',
@@ -78,6 +196,10 @@ class Paypal
78
196
  make_nvp_call(params)
79
197
  end
80
198
 
199
+ # Gets the details of the request started through set_express_checkout.
200
+ #
201
+ # Equivalent of GetExpressCheckoutDetails.
202
+ #
81
203
  def get_express_checkout_details(token)
82
204
  params = {
83
205
  'METHOD' => 'GetExpressCheckoutDetails',
@@ -86,6 +208,11 @@ class Paypal
86
208
  make_nvp_call(params)
87
209
  end
88
210
 
211
+ #
212
+ # Gets payment through PayPal for Express Checkout.
213
+ #
214
+ # Equivalent of DoExpressCheckoutPayment
215
+ #
89
216
  def do_express_checkout_payment(token, payment_action,payer_id, amount)
90
217
  params = {
91
218
  'METHOD' => 'DoExpressCheckoutPayment',
@@ -97,16 +224,63 @@ class Paypal
97
224
  make_nvp_call(params)
98
225
  end
99
226
 
100
- def do_authorization(transaction_id, amount)
227
+ #
228
+ # Does authorization of a request.
229
+ #
230
+ # Equivalent of DoAuthorization.
231
+ #
232
+ def do_authorization(transaction_id, amount, currency_code = 'USD')
101
233
  params = {
102
234
  'METHOD' => 'DoAuthorization',
103
235
  'TRANSACTIONID' => transaction_id,
104
236
  'AMT' => amount.to_s,
105
- 'TRANSACTIONENTITY' => 'Order'
237
+ 'TRANSACTIONENTITY' => 'Order',
238
+ 'CURRENCYCODE' => currency_code
106
239
  }
107
240
  make_nvp_call(params)
108
241
  end
109
242
 
243
+ #
244
+ # Captures payment for a transaction.
245
+ #
246
+ # Equivalent of DoCapture.
247
+ #
248
+ def do_capture(authorization_id, amount, complete=true, currency_code='USD', invoice_no=nil, note=nil, soft_descriptor=nil)
249
+ params = {
250
+ 'METHOD' => 'DoCapture',
251
+ 'AUTHORIZATIONID' => authorization_id,
252
+ 'AMT' => amount.to_s,
253
+ 'CURRENCYCODE' => currency_code
254
+ }
255
+ if complete then
256
+ params['COMPLETETYPE'] = 'Complete'
257
+ else
258
+ params['COMPLETETYPE'] = 'NotComplete'
259
+ end
260
+ params['INVNUM'] = invoice_no unless invoice_no.nil?
261
+ params['NOTE'] = note unless note.nil?
262
+ params['SOFTDESCRIPTOR'] = soft_descriptor unless soft_descriptor.nil?
263
+ make_nvp_call(params)
264
+ end
265
+
266
+ #
267
+ # Re-authorizes an authorized transaction.
268
+ #
269
+ # Equivalent of DoReauthorization.
270
+ #
271
+ def do_reauthorization(authorization_id, amount, currency_code = 'USD')
272
+ params = {
273
+ 'METHOD' => 'DoReauthorization',
274
+ 'AUTHORIZATIONID' => authorization_id,
275
+ 'AMT' => amount.to_s,
276
+ 'CURRENCYCODE' => currency_code
277
+ }
278
+ make_nvp_call(params)
279
+ end
280
+
281
+ #
282
+ # Makes the call to the PayPal NVP API. This is the workhorse method for the other method calls.
283
+ #
110
284
  def make_nvp_call(params)
111
285
  @api_parameters.merge! params
112
286
  parameters = URI.escape(@api_parameters.to_a.collect {|pair| pair.join('=')}.join('&'))
@@ -115,9 +289,20 @@ class Paypal
115
289
  PayPalResponse.new.merge get_hash(response.body)
116
290
  end
117
291
 
118
- private
292
+ #
293
+ # Checks and returns information on the card based on the given BIN (Bank Identification Number).
294
+ # Currently inactive since bindatabase.com is down.
295
+ #
296
+ def bin_check(bin)
297
+ # stub for check to bindatabase.com, currently down
298
+ end
119
299
 
300
+ private
120
301
 
302
+ #
303
+ # Gets a hash from a string, with a set of name value pairs joined by '='
304
+ # and concatenated with '&'
305
+ #
121
306
  def get_hash(string)
122
307
  hash = {}
123
308
  string.split('&').collect { |pair| pair.split('=') }.each { |a|
@@ -1,20 +1,34 @@
1
- require 'rubygems'
2
1
  require 'pp'
3
- require 'ruby-paypal'
4
2
  require 'test/unit'
3
+ require 'lib/ruby-paypal/paypal'
4
+ require 'lib/ruby-paypal/credit_card_checks'
5
+
5
6
 
6
7
  class RubyPayPal < Test::Unit::TestCase
8
+ def setup
9
+ @test_data = {"visa" => "4154727623381301", "visa" => "4015262522324137", "visa" => "4574806205610007"}
10
+ @paypal = Paypal.new('css.pa_1195828529_biz_api1.gmail.com', '1195828547', 'AJsoTDMhNESFz43L9WKtkP2tVCZZAZ3Wl.IxVLj8IfJQsjg8WZYMENpS')
11
+ end
7
12
  def test_do_direct_payment_sale
8
- paypal = Paypal.new('css.pa_1195828529_biz_api1.gmail.com', '1195828547',
9
- 'AJsoTDMhNESFz43L9WKtkP2tVCZZAZ3Wl.IxVLj8IfJQsjg8WZYMENpS')
10
- resp = paypal.do_direct_payment_sale('202.156.13.1', '14.34', 'VISA',
11
- '4574806205610007', '022017', 'Test', 'User')
13
+ resp = @paypal.do_direct_payment_sale('202.156.13.1', '14.34', 'visa', '4574806205610007', '022017', 'Test', 'User')
12
14
  assert_equal("Success", resp.ack)
13
15
  end
16
+
17
+ def test_luhn_check
18
+ @test_data.each { |type, number|
19
+ assert_equal(@paypal.luhn_check(number), true)
20
+ }
21
+ end
14
22
 
23
+ def test_card_type_check
24
+ @test_data.each { |type, number|
25
+ assert_equal(@paypal.card_type_check(type,number), true)
26
+ }
27
+ end
28
+
15
29
  def test_do_direct_payment_authorization
16
-
30
+
17
31
  end
18
-
32
+
19
33
  end
20
34
 
metadata CHANGED
@@ -3,9 +3,9 @@ rubygems_version: 0.9.4
3
3
  specification_version: 1
4
4
  name: ruby-paypal
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.1
7
- date: 2007-12-23 00:00:00 +08:00
8
- summary: An lightweight Ruby wrapper for PayPal NVP API
6
+ version: 0.0.2
7
+ date: 2008-01-02 00:00:00 +08:00
8
+ summary: A lightweight Ruby wrapper for PayPal NVP API
9
9
  require_paths:
10
10
  - lib
11
11
  email: sausheong.chang@gmail.com
@@ -30,6 +30,7 @@ authors:
30
30
  - Chang Sau Sheong
31
31
  files:
32
32
  - lib/ruby-paypal
33
+ - lib/ruby-paypal/credit_card_checks.rb
33
34
  - lib/ruby-paypal/paypal.rb
34
35
  - lib/ruby-paypal.rb
35
36
  - test/ts_ruby-paypal.rb