sinatra-paypal 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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