sinatra-paypal 0.1.1 → 0.2.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b91fdfe678e28fe0fad663c05afb3fa259ca40ad
4
- data.tar.gz: 5f899b739d302480dbfb512f2f3dbae4a7a6c213
3
+ metadata.gz: dbbe8b8b2351d5cf7d643aabff1146ea783b4729
4
+ data.tar.gz: 6095033aa3049ec0307a63960c936b025bc91b11
5
5
  SHA512:
6
- metadata.gz: c37ec074ce4d8a115ed91808fee99109da1effc7003fce4ed43ecc868a350cecf34d8515fef0ca9a20365aab2fce98028b8dc5378d61de5fca7f55d3eb1605f0
7
- data.tar.gz: 951e171455b7369bc5324fb29a4974f1602c2ce2bfb7b60ac3c98c98568ac14cc6ba82a3fc9d20fed76aa53947e9a5b63dca4d5d7933a0c2f60a057f8196e1d0
6
+ metadata.gz: e6bba20dbb1049f2ccd157a7331bae400bcb495ef316eb18cadac06876d6dc1ca717982bc3a6e25aa1501448a5f05777fb1a766b21959efb7977fb9063cca87a
7
+ data.tar.gz: cdc5ac4baf40951a567cdeccdaec36abdab8ef7a16ab03d38c51088054b0ab573b88603381e57f59a3a94a53b4bdbd9c5487b22d0fb6f4dda6eb7d7dd7128d35
data/Rakefile CHANGED
@@ -1,8 +1,7 @@
1
1
  require "bundler/gem_tasks"
2
- require "rake/testtask"
2
+ require 'rake/testtask'
3
3
 
4
4
  Rake::TestTask.new do |t|
5
- ENV['RUBYOPT'] = '-W0'
6
- t.warning = false
7
- t.test_files = FileList['test/*_test.rb']
5
+ t.libs << 'test'
6
+ t.pattern = "test/*_test.rb"
8
7
  end
@@ -9,7 +9,7 @@ PAYPAL_BLOCKS = {}
9
9
  module PayPal
10
10
 
11
11
  module Helpers
12
- def paypal_block(name)
12
+ def _paypal_block(name)
13
13
  return Proc.new{} if !PAYPAL_BLOCKS.key? name
14
14
  PAYPAL_BLOCKS[name]
15
15
  end
@@ -18,16 +18,21 @@ module PayPal
18
18
  PaypalHelper.form_url(settings.paypal.sandbox?)
19
19
  end
20
20
 
21
- def html_payment_form(offer_data, data = nil)
22
- data ||= {}
23
- data[:username] = session[:reddit_user] if !data.nil? && data[:username].nil?
24
- data[:offer_code] = offer_data.code if !offer_data.nil?
21
+ def html_payment_form(item, data = {})
22
+ # it is valid to send a nil item through - we might be going to set the fields in
23
+ # javascript - but if it isn't null then there are certain fields that need to be
24
+ # set
25
+ if !item.nil?
26
+ raise 'item.code required' if !item.respond_to?(:code) || item.code.nil?
27
+ raise 'item.price required' if !item.respond_to?(:price) || item.price.nil?
28
+ raise 'item.price must be a number above zero' if item.price.to_f <= 0
29
+ end
25
30
 
26
31
  raise 'cannot generate a payment form without settings.paypal.email' if settings.paypal.email.nil?
27
32
 
28
33
  erb :_payment, :views => File.join(File.dirname(__FILE__), '/paypal'), :locals => {
29
34
  :custom_data => data,
30
- :offer_data => offer_data
35
+ :item => item
31
36
  }
32
37
  end
33
38
  end
@@ -54,30 +59,35 @@ module PayPal
54
59
  halt 400, 'no username provided' if paypal_request.username.nil?
55
60
 
56
61
  # check transaction log to make sure this not a replay attack
57
- if instance_exec(paypal_request, &paypal_block(:repeated?))
58
- # we want to log this, so we know about it, but we also want to return 200, because
59
- # if it is paypal sending this, it will send it again and again until it gets a 200
60
- # back
61
- log_error 'already processed' if respond_to? :log_error
62
+ if instance_exec(paypal_request, &_paypal_block(:repeated?))
63
+ # we also want to return 200, because if it is paypal sending this, it will send
64
+ # it again and again until it gets a 200 back
62
65
  halt 200, 'already processed'
63
- else
64
- instance_exec(paypal_request, &paypal_block(:save))
65
66
  end
66
67
 
67
- instance_exec(paypal_request, &paypal_block(:validate!))
68
+ instance_exec(paypal_request, &_paypal_block(:validate!))
68
69
 
69
70
  # check that the payment is complete. we still return 200 if not, but
70
71
  # we don't need to do anymore processing (except for marking it as accountable, if it is)
71
72
  if paypal_request.complete?
72
- instance_exec(paypal_request, &paypal_block(:complete))
73
+ instance_exec(paypal_request, &_paypal_block(:complete))
73
74
  end
74
75
 
75
- instance_exec(paypal_request, &paypal_block(:finish))
76
+ instance_exec(paypal_request, &_paypal_block(:finish))
76
77
 
77
78
  return 200
78
79
  end
79
80
  end
80
81
 
82
+ # Register a payment callback. All callbacks are called
83
+ # with a single argument of the type +PaypalRequest+ containing all the
84
+ # data for the notification.
85
+ #
86
+ # payment :complete do |p|
87
+ # # process the payment here
88
+ # # don't forget to check that the price is correct!
89
+ # end
90
+ #
81
91
  def payment(name, &block)
82
92
  PAYPAL_BLOCKS[name] = block
83
93
  end
@@ -86,4 +96,4 @@ end
86
96
 
87
97
  module Sinatra
88
98
  register PayPal
89
- end
99
+ end
@@ -1,5 +1,5 @@
1
1
 
2
- <form id='paypal-form' action="<%= paypal_form_url %>" method="post" style='display:none'>
2
+ <form class='sinatra-paypal-form' action="<%= paypal_form_url %>" method="post">
3
3
  <input type="hidden" name="cmd" value="_xclick">
4
4
 
5
5
  <input type="hidden" name="business" value="<%= settings.paypal.email %>">
@@ -7,14 +7,14 @@
7
7
  <input type="hidden" name="return" value="<%= url(settings.paypal.return_url) %>">
8
8
  <input type="hidden" name="notify_url" value="<%= url(settings.paypal.notify_url) %>">
9
9
 
10
- <% if offer_data.nil? %>
11
- <input type="hidden" name="item_number" id='paypal-item-number' value="0">
12
- <input type="hidden" name="item_name" id='paypal-item-name' value="0">
10
+ <% if item.nil? %>
11
+ <input type="hidden" name="item_number" id='paypal-item-number' value="NO_ITEM">
12
+ <input type="hidden" name="item_name" id='paypal-item-name' value="NO_ITEM">
13
13
  <input type="hidden" name="amount" id='paypal-amount' value="1.00">
14
14
  <% else %>
15
- <input type="hidden" name="item_number" id='paypal-item-number' value="<%= offer_data.offer.item_code %>">
16
- <input type="hidden" name="item_name" id='paypal-item-name' value="<%= offer_data.item.name %>">
17
- <input type="hidden" name="amount" id='paypal-amount' value="<%= offer_data.offer.amount %>">
15
+ <input type="hidden" name="item_number" id='paypal-item-number' value="<%= item.code %>">
16
+ <input type="hidden" name="item_name" id='paypal-item-name' value="<%= item.name || item.code %>">
17
+ <input type="hidden" name="amount" id='paypal-amount' value="<%= item.price %>">
18
18
  <% end %>
19
19
 
20
20
  <input type="hidden" name="no_shipping" value="1">
@@ -5,6 +5,12 @@ class PaypalHelper
5
5
  @use_sandbox = use_sandbox
6
6
  end
7
7
 
8
+ # returns the url that the payment forms must be submitted to so they can be
9
+ # processed by paypal. If the sandbox attribute is set, then it will return the
10
+ # url for the sandbox
11
+ #
12
+ # form_url # => https://www.paypal.com/cgi-bin/webscr
13
+ #
8
14
  def form_url
9
15
  @use_sandbox ? 'https://www.sandbox.paypal.com/cgi-bin/webscr' : 'https://www.paypal.com/cgi-bin/webscr'
10
16
  end
@@ -13,6 +19,8 @@ class PaypalHelper
13
19
  new(use_sandbox).form_url
14
20
  end
15
21
 
22
+ # validates the ipn request with paypal to make sure it is genuine. +params+ should contain
23
+ # the exact params object that was sent as part of the IPN POST
16
24
  def ipn_valid?(params)
17
25
  return false if params.nil?
18
26
 
@@ -1,3 +1,4 @@
1
+ require 'json'
1
2
 
2
3
  class PaypalRequest
3
4
  def initialize(params)
@@ -9,17 +10,26 @@ class PaypalRequest
9
10
  @custom_data = nil
10
11
  end
11
12
 
12
-
13
+ # a unique id for the transaction (event if the paypal transaction_id is the
14
+ # same)
15
+ #
16
+ # payment.id # => 661295c9cbf9d6b2f6428414504a8deed3020641
17
+ #
13
18
  def id
14
19
  self.transaction_hash
15
20
  end
16
21
 
17
- # the same transaction id can come in mulitple times with different statuses
18
- # so we need to check both of them in order to see if the txn is unquie
22
+ # alias for +id+
19
23
  def transaction_hash
24
+ # the same transaction id can come in mulitple times with different statuses
25
+ # so we need to check both of them in order to see if the txn is unquie
20
26
  "#{self.transaction_id}-#{@fields[:payment_status]}".sha1
21
27
  end
22
28
 
29
+ # the paypal transaction_id
30
+ #
31
+ # payment.transaction_id # => 6FH51066BB6306017
32
+ #
23
33
  def transaction_id
24
34
  @fields[:txn_id]
25
35
  end
@@ -44,7 +54,7 @@ class PaypalRequest
44
54
  end
45
55
 
46
56
  def item_number
47
- @fields[:item_number] || @fields[:item_number1]
57
+ @fields[:item_number]
48
58
  end
49
59
 
50
60
  def amount
@@ -55,17 +65,39 @@ class PaypalRequest
55
65
  Float(@fields[:mc_fee] || 0)
56
66
  end
57
67
 
58
- # defined as the gross amount, minus transaction fees
59
- # could also subtract shipping and tax here as well, but we don't have to deal with
60
- # any of that yet
68
+ # The payment amount minus any fees, giving the net profit
69
+ #
70
+ # payment.profit # => 1.63
71
+ #
61
72
  def profit
73
+ # defined as the gross amount, minus transaction fees
74
+ # could also subtract shipping and tax here as well, but we don't have to deal with
75
+ # any of that yet
62
76
  self.amount - self.payment_fee
63
77
  end
64
78
 
79
+ # One of the most common peices of data to send through with the payment is the
80
+ # username that the payment applies to. This method will return the username from
81
+ # the custom_data field, if it contains one
82
+ #
83
+ # payment.username # => reednj
84
+ #
65
85
  def username
66
86
  self.custom_data[:username]
67
87
  end
68
88
 
89
+ # Whatever is put in the custom_data field on the payment form will be sent back
90
+ # in the payment notification. This is useful for tracking the username and other
91
+ # information that is required to process the payment.
92
+ #
93
+ # The sinatra-paypal module expects this to be either a plain string containing
94
+ # the username or a json string that can be parse into a ruby object.
95
+ #
96
+ # Note that this field can be modified by the user before submitting the form,
97
+ # so you should verify the contents of it before using it.
98
+ #
99
+ # payment.custom_data # => { :username => 'dave' }
100
+ #
69
101
  def custom_data
70
102
  if @custom_data.nil?
71
103
  if @fields[:custom].strip.start_with? '{'
@@ -81,29 +113,97 @@ class PaypalRequest
81
113
  return @custom_data
82
114
  end
83
115
 
116
+ # an alias for +custom_data+
84
117
  def data
85
118
  custom_data
86
119
  end
87
120
 
121
+ # The payment status. The most common is Completed, but you might also see
122
+ #
123
+ # - Refunded
124
+ # - Pending
125
+ # - Reversed
126
+ # - Canceled_Reversal
127
+ # - Completed
128
+ #
129
+ # payment.status # => Pending
130
+ #
88
131
  def status
89
132
  @fields[:payment_status]
90
133
  end
91
134
 
135
+ # Returns true if +status+ is Completed
92
136
  def complete?
93
137
  self.status == 'Completed'
94
138
  end
95
139
 
96
- # these are payment statues that actually result in the paypal balance changing, so we should set them as
97
- # accountable in the payment_log
140
+ # Returns true if the transaction results in a change of the merchant account balance. This
141
+ # doesn't apply to all IPN notifications - some (such as those with status Pending) are simply
142
+ # notifications that do not actually result in money chaning hands
98
143
  def is_accountable?
144
+ # these are payment statues that actually result in the paypal balance changing, so we should set them as
145
+ # accountable in the payment_log
99
146
  (self.complete? || self.status == 'Refunded' || self.status == 'Reversed' || self.status == 'Canceled_Reversal')
100
147
  end
101
148
 
149
+ # returns the reason code for noticiations that have one (usually Pending or Refunded transactions)
150
+ # if a reason code is not applicable, this will return nil
102
151
  def reason_code
103
152
  @fields[:reason_code] || @fields[:pending_reason]
104
153
  end
105
154
 
155
+ # A Hash with the raw data for the paypal transaction, this contains many less useful
156
+ # fields not listed above
157
+ #
158
+ # Here is a list of the fields for a Completed transaction. These fields differ slightly
159
+ # between the main transaction types.
160
+ #
161
+ # :business: you@yourcompany.com
162
+ # :charset: UTF-8
163
+ # :cmd: _notify-validate
164
+ # :custom: ! '{"thread_id":"3f3fc5","username":"customer_user_name"}'
165
+ # :first_name: Bill
166
+ # :handling_amount: '0.00'
167
+ # :ipn_track_id: a5d596fffd057
168
+ # :item_name: Describe your item
169
+ # :item_number: I01
170
+ # :last_name: Davies
171
+ # :mc_currency: USD
172
+ # :mc_fee: '0.37'
173
+ # :mc_gross: '2.00'
174
+ # :notify_version: '3.8'
175
+ # :payer_email: customer@gmail.com
176
+ # :payer_id: 9AFYVBV9TTT5L
177
+ # :payer_status: verified
178
+ # :payment_date: 18:01:26 Jul 29, 2015 PDT
179
+ # :payment_fee: '0.37'
180
+ # :payment_gross: '2.00'
181
+ # :payment_status: Completed
182
+ # :payment_type: instant
183
+ # :protection_eligibility: Ineligible
184
+ # :quantity: '1'
185
+ # :receiver_email: you@yourcompany.com
186
+ # :receiver_id: 4SUZXXXXFMC28
187
+ # :residence_country: US
188
+ # :shipping: '0.00'
189
+ # :tax: '0.00'
190
+ # :transaction_subject: ! '{"thread_id":"3f3fc5","username":"customer_user_name"}'
191
+ # :txn_id: 6FH51036BB6776017
192
+ # :txn_type: web_accept
193
+ # :verify_sign: AmydMwaMHzmxRimnFMnKy3o9n-ElAWWRtiJ9TEixE0iGouC6EaMS0mWI
194
+ #
195
+ # See https://developer.paypal.com/docs/classic/ipn/integration-guide/IPNandPDTVariables/ for
196
+ # a full description of the notification fields
197
+ #
106
198
  def fields
107
199
  @fields
108
200
  end
109
201
  end
202
+
203
+ # extensions needed for the paypal request to work
204
+ class String
205
+ def sha1
206
+ Digest::SHA1.hexdigest self
207
+ end
208
+ end
209
+
@@ -1,5 +1,5 @@
1
1
  module Sinatra
2
2
  module Paypal
3
- VERSION = "0.1.1"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
@@ -14,14 +14,17 @@ Gem::Specification.new do |spec|
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
18
  spec.bindir = "exe"
18
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
20
  spec.require_paths = ["lib"]
20
21
 
21
22
  spec.add_development_dependency "bundler", "~> 1.9"
22
- spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rake", "~> 10.0"
23
24
  spec.add_development_dependency "test-unit"
24
25
  spec.add_development_dependency "rack-test"
26
+ spec.add_development_dependency "minitest"
27
+
25
28
  spec.add_runtime_dependency "sinatra"
26
29
  spec.add_runtime_dependency "rest-client"
27
30
  end
@@ -0,0 +1,57 @@
1
+
2
+ require 'bundler'
3
+ Bundler.require
4
+
5
+ require 'sinatra'
6
+ require 'sinatra/paypal'
7
+
8
+ # this allows us to handle errors in test without the default page
9
+ # getting generated (which isn't very useful inside unit-tests)
10
+ set :raise_errors, false
11
+ set :show_exceptions, false
12
+
13
+ # dump the error description - this will make it appear nicely in the
14
+ # unit test description
15
+ error do
16
+ e = request.env['sinatra.error']
17
+ return "#{e.class}: #{e.message}" unless e.nil?
18
+ return "unknown error"
19
+ end
20
+
21
+ payment :repeated? do |p|
22
+ path = '/tmp/test.sinatra-payment.log'
23
+ data = File.read path
24
+ id = "#{p.id}\n"
25
+
26
+ if data.include? id
27
+ true
28
+ else
29
+ File.append path, "#{p.id}\n"
30
+ false
31
+ end
32
+ end
33
+
34
+ get '/payment/form/empty' do
35
+ return html_payment_form nil
36
+ end
37
+
38
+ get '/payment/form' do
39
+ item = {}
40
+ item[:code] = params[:item_code]
41
+ item[:price] = params[:item_price]
42
+ item[:name] = params[:item_name]
43
+ item = OpenStruct.new item
44
+
45
+ return html_payment_form(item)
46
+ end
47
+
48
+ #
49
+ # Extensions to make the the test app simpler
50
+ #
51
+ class File
52
+ def self.append(path, data)
53
+ File.open(path, 'a:UTF-8') do |file|
54
+ file.write data
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,93 @@
1
+ ENV['RACK_ENV'] = 'development'
2
+
3
+ require 'rubygems'
4
+ require 'minitest/autorun'
5
+ require 'rack/test'
6
+ require 'test/unit'
7
+
8
+ require_relative './app'
9
+
10
+ class RedditStreamTest < Test::Unit::TestCase
11
+ include Rack::Test::Methods
12
+
13
+ def app
14
+ Sinatra::Application
15
+ end
16
+
17
+ def page_error(desc)
18
+ "#{desc} (HTTP #{last_response.status})\n" +
19
+ "#{last_response.body.truncate 256}"
20
+ end
21
+
22
+ def test_form_helper_no_email
23
+ app.paypal.email = nil
24
+
25
+ begin
26
+ get '/payment/form/empty'
27
+ assert false, 'no email payment form didn\'t raise an exception'
28
+ rescue => e
29
+ raise if !e.message.include? 'email'
30
+ end
31
+ end
32
+
33
+ def test_form_helper_no_item_code
34
+ app.paypal.email = 'reednj@gmail.com'
35
+
36
+ get '/payment/form'
37
+ assert !last_response.ok?, 'form created without item code'
38
+ assert last_response.body.include?('item.code'), 'no error about missing item.code'
39
+ end
40
+
41
+ def test_form_helper_invalid_price
42
+ app.paypal.email = 'reednj@gmail.com'
43
+
44
+ get '/payment/form', { :item_code => 'ITEM-0', :item_price => 'xxx'}
45
+ assert !last_response.ok?, 'form created with invalid price'
46
+ assert last_response.body.include?('item.price'), 'no error about invalid price'
47
+ end
48
+
49
+ def test_empty_form_helper
50
+ app.paypal.email = 'reednj@gmail.com'
51
+
52
+ get '/payment/form/empty'
53
+ assert last_response.ok?, page_error('could not generate payment form')
54
+ assert last_response.body.include?(app.paypal.email), 'account email not found in form'
55
+ end
56
+
57
+ def test_form_helper_with_item
58
+ app.paypal.email = 'reednj@gmail.com'
59
+
60
+ code = 'ITEM-0'
61
+ get '/payment/form', { :item_code => code, :item_price => '5.00'}
62
+ assert last_response.ok?, page_error('could not generate payment form')
63
+ assert last_response.body.include?(app.paypal.email), 'account email not found in form'
64
+ assert last_response.body.include?(code), 'item_code not found in form'
65
+ end
66
+
67
+ def test_form_helper_with_item_description
68
+ app.paypal.email = 'reednj@gmail.com'
69
+
70
+ code = 'ITEM-0'
71
+ desc = 'item description'
72
+ get '/payment/form', { :item_code => code, :item_price => '5.00', :item_name => desc}
73
+ assert last_response.ok?, page_error('could not generate payment form')
74
+ assert last_response.body.include?(app.paypal.email), 'account email not found in form'
75
+ assert last_response.body.include?(code), 'item_code not found in form'
76
+ assert last_response.body.include?(desc), 'item_name not found in form'
77
+ end
78
+
79
+ end
80
+
81
+ class Array
82
+ def rand
83
+ self[Object.send(:rand, self.length)]
84
+ end
85
+ end
86
+
87
+ class String
88
+ def truncate(max_len = 32)
89
+ append = '...'
90
+ return self if self.length < max_len
91
+ return self[0...(max_len - append.length)] + append
92
+ end
93
+ end
@@ -0,0 +1,135 @@
1
+ ENV['RACK_ENV'] = 'development'
2
+
3
+ require 'rubygems'
4
+ require 'minitest/autorun'
5
+ require 'rack/test'
6
+ require 'test/unit'
7
+
8
+ require_relative './app'
9
+
10
+ class RedditStreamTest < Test::Unit::TestCase
11
+ include Rack::Test::Methods
12
+
13
+ def app
14
+ Sinatra::Application
15
+ end
16
+
17
+ def standard_payment_data
18
+ {
19
+ :tax=>"0.00",
20
+ :receiver_email=>"accounts@reddit-stream.com",
21
+ :payment_gross=>"1.49",
22
+ :transaction_subject=>"rs-test",
23
+ :receiver_id=>"TDCZRKH9NXETE",
24
+ :quantity=>"1",
25
+ :business=>"accounts@reddit-stream.com",
26
+ :mc_currency=>"USD",
27
+ :payment_fee=>"0.44",
28
+ :notify_version=>"3.7",
29
+ :shipping=>"0.00",
30
+ :verify_sign=>"AVQ7PVd2w7LAwpsg5Yh7hFxe9SywAuiBQIp9fScf77n48fA8WF21KG2i",
31
+ :cmd=>"_notify-validate",
32
+ :test_ipn=>"1",
33
+ :txn_type=>"web_accept",
34
+ :charset=>"UTF-8",
35
+ :payer_id=>"649KQR22GAU4W",
36
+ :payer_status=>"verified",
37
+ :ipn_track_id=>"d1992aaea45a",
38
+ :handling_amount=>"0.00",
39
+ :residence_country=>"US",
40
+ :payer_email=>"user1@reddit-stream.com",
41
+ :payment_date=>"09:26:13 Sep 23, 2013 PDT",
42
+ :protection_eligibility=>"Ineligible",
43
+ :payment_type=>"instant",
44
+
45
+ :first_name=>"Nathan",
46
+ :last_name=>"Reed",
47
+ :custom=>"rs-test",
48
+
49
+ :txn_id=>random_string(),
50
+ :payment_status=>"Completed",
51
+ :item_number=>"RS1",
52
+ :item_name=>"reddit-stream.com Unlimited Account",
53
+ :mc_gross=>"1.49",
54
+ :mc_fee=>"0.44"
55
+ }
56
+ end
57
+
58
+ def random_string(len = 5)
59
+ space = ['a', 'b', 'c', 'd', 'e','f', '1', '2', '3', '4', '5', '6', '7', '8', '9']
60
+ return (0..5).map {|a| space.rand }.join
61
+ end
62
+
63
+ def page_error(desc)
64
+ "#{desc} (HTTP #{last_response.status})\n" +
65
+ "#{last_response.body.truncate 256}"
66
+ end
67
+
68
+ #
69
+ # Now we start the testing of the payment processing
70
+ #
71
+ def test_payment_rejects_double_processing
72
+ data = standard_payment_data
73
+
74
+ post '/payment/validate', data
75
+ assert last_response.ok?, page_error("Payment rejected")
76
+ assert !last_response.body.include?('already processed')
77
+
78
+ post '/payment/validate', data
79
+ assert last_response.ok?
80
+ assert last_response.body.include?('already processed'), "duplicate payment accepted!"
81
+ end
82
+
83
+ def test_payment_thread_id
84
+ data = standard_payment_data
85
+ data[:custom] = {
86
+ :username => 'njr123',
87
+ :thread_id => '2hqk1o'
88
+ }.to_json
89
+
90
+ header 'Accept', 'text/plain'
91
+ post '/payment/validate', data
92
+ assert last_response.ok?, page_error("Payment not accepted")
93
+ end
94
+
95
+ def test_payment_thread_id_only
96
+ data = standard_payment_data
97
+ data[:custom] = {
98
+ :thread_id => '2hqk1o'
99
+ }.to_json
100
+
101
+ post '/payment/validate', data
102
+ assert_equal last_response.status, 400
103
+ end
104
+
105
+ def test_payment_accepted_json_custom_data
106
+ data = standard_payment_data
107
+ data[:custom] = {:username => data[:custom]}.to_json
108
+
109
+ post '/payment/validate', data
110
+ assert last_response.ok?, page_error("Payment not accepted")
111
+ end
112
+
113
+ def test_payment_accepted
114
+ data = standard_payment_data
115
+ username = data[:custom]
116
+
117
+ post '/payment/validate', data
118
+ assert last_response.ok?, page_error("Payment not accepted")
119
+ end
120
+
121
+ end
122
+
123
+ class Array
124
+ def rand
125
+ self[Object.send(:rand, self.length)]
126
+ end
127
+ end
128
+
129
+ class String
130
+ def truncate(max_len = 32)
131
+ append = '...'
132
+ return self if self.length < max_len
133
+ return self[0...(max_len - append.length)] + append
134
+ end
135
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sinatra-paypal
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Reed
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-07-12 00:00:00.000000000 Z
11
+ date: 2015-10-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -26,6 +26,20 @@ dependencies:
26
26
  version: '1.9'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: test-unit
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - ">="
@@ -39,7 +53,7 @@ dependencies:
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
- name: test-unit
56
+ name: rack-test
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
59
  - - ">="
@@ -53,7 +67,7 @@ dependencies:
53
67
  - !ruby/object:Gem::Version
54
68
  version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
- name: rack-test
70
+ name: minitest
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - ">="
@@ -115,6 +129,9 @@ files:
115
129
  - lib/sinatra/paypal/paypal-request.rb
116
130
  - lib/sinatra/paypal/version.rb
117
131
  - sinatra-paypal.gemspec
132
+ - test/app.rb
133
+ - test/form_test.rb
134
+ - test/paypal_test.rb
118
135
  homepage: http://github.com/reednj/sinatra-paypal
119
136
  licenses:
120
137
  - MIT
@@ -135,8 +152,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
152
  version: '0'
136
153
  requirements: []
137
154
  rubyforge_project:
138
- rubygems_version: 2.4.5.2
155
+ rubygems_version: 2.4.5
139
156
  signing_key:
140
157
  specification_version: 4
141
158
  summary: Easy validation and processing of Paypal IPN payments
142
- test_files: []
159
+ test_files:
160
+ - test/app.rb
161
+ - test/form_test.rb
162
+ - test/paypal_test.rb