paypal-recurring 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- paypal-recurring (0.1.0)
4
+ paypal-recurring (0.1.1)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
@@ -1,13 +1,13 @@
1
1
  require "net/https"
2
2
  require "cgi"
3
3
  require "uri"
4
- require "logger"
5
4
  require "ostruct"
6
5
  require "time"
7
6
 
8
7
  module PayPal
9
8
  module Recurring
10
9
  autoload :Base, "paypal/recurring/base"
10
+ autoload :Notification, "paypal/recurring/notification"
11
11
  autoload :Request, "paypal/recurring/request"
12
12
  autoload :Response, "paypal/recurring/response"
13
13
  autoload :Version, "paypal/recurring/version"
@@ -44,12 +44,15 @@ module PayPal
44
44
  #
45
45
  attr_accessor :signature
46
46
 
47
- # Set application logger. By default, will send output to +STDOUT+.
47
+ # Set seller id. Will be used to verify IPN.
48
48
  #
49
- attr_accessor :logger
50
- end
49
+ #
50
+ attr_accessor :seller_id
51
51
 
52
- self.logger = Logger.new(STDOUT)
52
+ # The seller e-mail. Will be used to verify IPN.
53
+ #
54
+ attr_accessor :email
55
+ end
53
56
 
54
57
  # Just a shortcut for <tt>PayPal::Recurring::Base.new</tt>.
55
58
  #
@@ -44,7 +44,7 @@ module PayPal
44
44
  #
45
45
  def checkout
46
46
  params = collect(:amount, :return_url, :cancel_url, :currency, :description, :ipn_url).merge(:payment_action => "Authorization", :no_shipping => 1, :L_BILLINGTYPE0 => "RecurringPayments")
47
- request.post(:checkout, params)
47
+ request.run(:checkout, params)
48
48
  end
49
49
 
50
50
  # Suspend a recurring profile.
@@ -54,7 +54,7 @@ module PayPal
54
54
  # response = ppr.suspend
55
55
  #
56
56
  def suspend
57
- request.post(:manage_profile, :action => :suspend, :profile_id => profile_id)
57
+ request.run(:manage_profile, :action => :suspend, :profile_id => profile_id)
58
58
  end
59
59
 
60
60
  # Reactivate a suspended recurring profile.
@@ -63,7 +63,7 @@ module PayPal
63
63
  # response = ppr.reactivate
64
64
  #
65
65
  def reactivate
66
- request.post(:manage_profile, :action => :reactivate, :profile_id => profile_id)
66
+ request.run(:manage_profile, :action => :reactivate, :profile_id => profile_id)
67
67
  end
68
68
 
69
69
  # Cancel a recurring profile.
@@ -73,7 +73,7 @@ module PayPal
73
73
  # response = ppr.cancel
74
74
  #
75
75
  def cancel
76
- request.post(:manage_profile, :action => :cancel, :profile_id => profile_id)
76
+ request.run(:manage_profile, :action => :cancel, :profile_id => profile_id)
77
77
  end
78
78
 
79
79
  # Return checkout details.
@@ -82,7 +82,7 @@ module PayPal
82
82
  # response = ppr.checkout_details
83
83
  #
84
84
  def checkout_details
85
- request.post(:details, :token => token)
85
+ request.run(:details, :token => token)
86
86
  end
87
87
 
88
88
  # Request payment.
@@ -98,7 +98,7 @@ module PayPal
98
98
  #
99
99
  def request_payment
100
100
  params = collect(:amount, :return_url, :cancel_url, :ipn_url, :currency, :description, :payer_id, :token).merge(:payment_action => "Sale")
101
- request.post(:payment, params)
101
+ request.run(:payment, params)
102
102
  end
103
103
 
104
104
  # Create a recurring billing profile.
@@ -122,7 +122,7 @@ module PayPal
122
122
  #
123
123
  def create_recurring_profile
124
124
  params = collect(:amount, :currency, :description, :payer_id, :token, :reference, :start_at, :failed, :outstanding, :ipn_url, :frequency, :period, :email)
125
- request.post(:create_profile, params)
125
+ request.run(:create_profile, params)
126
126
  end
127
127
 
128
128
  # Retrieve information about existing recurring profile.
@@ -131,7 +131,7 @@ module PayPal
131
131
  # response = ppr.profile
132
132
  #
133
133
  def profile
134
- request.post(:profile, :profile_id => profile_id)
134
+ request.run(:profile, :profile_id => profile_id)
135
135
  end
136
136
 
137
137
  private
@@ -0,0 +1,75 @@
1
+ module PayPal
2
+ module Recurring
3
+ class Notification
4
+ attr_reader :params
5
+
6
+ def initialize(params = {})
7
+ self.params = params
8
+ end
9
+
10
+ def params=(params)
11
+ @params = params.inject({}) do |buffer, (name,value)|
12
+ buffer.merge(name.to_sym => value)
13
+ end
14
+ end
15
+
16
+ def request
17
+ @request ||= PayPal::Recurring::Request.new.tap do |request|
18
+ request.uri = URI.parse("#{PayPal::Recurring.site_endpoint}?cmd=_notify-validate")
19
+ end
20
+ end
21
+
22
+ def response
23
+ @response ||= request.post(params.merge(:cmd => "_notify-validate"))
24
+ end
25
+
26
+ def valid?
27
+ completed? && verified? && params[:receiver_email] == PayPal::Recurring.email && params[:receiver_id] == PayPal::Recurring.seller_id && params[:txn_type] == "recurring_payment"
28
+ end
29
+
30
+ def completed?
31
+ status == "Completed"
32
+ end
33
+
34
+ def transaction_id
35
+ params[:txn_id]
36
+ end
37
+
38
+ def fee
39
+ params[:mc_currency]
40
+ end
41
+
42
+ def reference
43
+ params[:rp_invoice_id]
44
+ end
45
+
46
+ def payment_id
47
+ params[:recurring_payment_id]
48
+ end
49
+
50
+ def next_payment_date
51
+ Time.parse(params[:next_payment_date]) if params[:next_payment_date]
52
+ end
53
+
54
+ def paid_at
55
+ Time.parse params[:time_created] if params[:time_created]
56
+ end
57
+
58
+ def amount
59
+ params[:amount]
60
+ end
61
+
62
+ def currency
63
+ params[:mc_currency]
64
+ end
65
+
66
+ def status
67
+ params[:payment_status]
68
+ end
69
+
70
+ def verified?
71
+ response.body == "VERIFIED"
72
+ end
73
+ end
74
+ end
75
+ end
@@ -57,12 +57,14 @@ module PayPal
57
57
 
58
58
  CA_FILE = File.dirname(__FILE__) + "/cacert.pem"
59
59
 
60
+ attr_accessor :uri
61
+
60
62
  # Do a POST request to PayPal API.
61
63
  # The +method+ argument is the name of the API method you want to invoke.
62
64
  # For instance, if you want to request a new checkout token, you may want
63
65
  # to do something like:
64
66
  #
65
- # response = request.post(:express_checkout)
67
+ # response = request.run(:express_checkout)
66
68
  #
67
69
  # We normalize the methods name. For a list of what's being covered, refer to
68
70
  # PayPal::Recurring::Request::METHODS constant.
@@ -70,15 +72,27 @@ module PayPal
70
72
  # The params hash can use normalized names. For a list, check the
71
73
  # PayPal::Recurring::Request::ATTRIBUTES constant.
72
74
  #
73
- def post(method, params = {})
75
+ def run(method, params = {})
74
76
  params = prepare_params(params.merge(:method => METHODS.fetch(method, method.to_s)))
75
- request = Net::HTTP::Post.new(uri.request_uri)
76
- request["User-Agent"] = "PayPal::Recurring/#{PayPal::Recurring::Version::STRING}"
77
- request.form_data = params
78
- response = client.request(request)
77
+ response = post(params)
79
78
  Response.process(method, response)
80
79
  end
81
80
 
81
+ #
82
+ #
83
+ def request
84
+ @request ||= Net::HTTP::Post.new(uri.request_uri).tap do |http|
85
+ http["User-Agent"] = "PayPal::Recurring/#{PayPal::Recurring::Version::STRING}"
86
+ end
87
+ end
88
+
89
+ #
90
+ #
91
+ def post(params = {})
92
+ request.form_data = params
93
+ client.request(request)
94
+ end
95
+
82
96
  # Join params and normalize attribute names.
83
97
  #
84
98
  def prepare_params(params) # :nodoc:
@@ -3,7 +3,7 @@ module PayPal
3
3
  module Version
4
4
  MAJOR = 0
5
5
  MINOR = 1
6
- PATCH = 0
6
+ PATCH = 1
7
7
  STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
8
8
  end
9
9
  end
@@ -0,0 +1,32 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :post
5
+ uri: https://www.sandbox.paypal.com:443/cgi-bin/webscr?cmd=_notify-validate
6
+ body: cmd=_notify-validate
7
+ headers:
8
+ user-agent:
9
+ - PayPal::Recurring/0.1.0
10
+ content-type:
11
+ - application/x-www-form-urlencoded
12
+ response: !ruby/struct:VCR::Response
13
+ status: !ruby/struct:VCR::ResponseStatus
14
+ code: 200
15
+ message: OK
16
+ headers:
17
+ date:
18
+ - Mon, 27 Jun 2011 17:39:42 GMT
19
+ server:
20
+ - Apache
21
+ set-cookie:
22
+ - c9MWDuvPtT9GIMyPc3jwol1VSlO=FYMA2gxaecqXl8Q9SSeevuaQcqfcQcwNGw_shFaqV4wYiWT3ZUcQ4O8NLvXqevPJfliR0yse8Z4bzZ8A37MQKM6Q_ci6wnYOkfGZSxZYa_hNLMvR6QHnGyW6u94YxhYQpgu0VG%7cySXso7WZVjU8pXiTJS488DAO3YqW5S6DsAZMY8TN22H5J8gW1As5DlqX1pm60R5dWee5wm%7cdMLFsFZz_xjN2U4nbQ71pdvMUEch5_rqq4NReBSNMz7j3ry9JJhQ1J0X6pqjinKs-kO5NG%7c1309196387; domain=.paypal.com; path=/; Secure; HttpOnly
23
+ - cookie_check=yes; expires=Thu, 24-Jun-2021 17:39:47 GMT; domain=.paypal.com; path=/; Secure; HttpOnly
24
+ - navcmd=_notify-validate; domain=.paypal.com; path=/; Secure; HttpOnly
25
+ - navlns=0.0; expires=Sun, 22-Jun-2031 17:39:47 GMT; domain=.paypal.com; path=/; Secure; HttpOnly
26
+ - Apache=10.72.109.11.1309196382543853; path=/; expires=Wed, 19-Jun-41 17:39:42 GMT
27
+ transfer-encoding:
28
+ - chunked
29
+ content-type:
30
+ - text/html; charset=UTF-8
31
+ body: INVALID
32
+ http_version: "1.1"
@@ -0,0 +1,32 @@
1
+ ---
2
+ - !ruby/struct:VCR::HTTPInteraction
3
+ request: !ruby/struct:VCR::Request
4
+ method: :post
5
+ uri: https://www.sandbox.paypal.com:443/cgi-bin/webscr?cmd=_notify-validate
6
+ body: test_ipn=1&payment_type=echeck&payment_date=06%3a16%3a11%20Jun%2027%2c%202011%20PDT&payment_status=Completed&pending_reason=echeck&address_status=confirmed&payer_status=verified&first_name=John&last_name=Smith&payer_email=buyer%40paypalsandbox.com&payer_id=TESTBUYERID01&address_name=John%20Smith&address_country=United%20States&address_country_code=US&address_zip=95131&address_state=CA&address_city=San%20Jose&address_street=123%2c%20any%20street&business=seller%40paypalsandbox.com&receiver_email=seller%40paypalsandbox.com&receiver_id=TESTSELLERID1&residence_country=US&item_name=something&item_number=AK-1234&quantity=1&shipping=3.04&tax=2.02&mc_currency=USD&mc_fee=0.44&mc_gross=12.34&txn_type=web_accept&txn_id=116271316&notify_version=2.1&custom=xyz123&invoice=abc1234&charset=windows-1252&verify_sign=AvgHA1TpBJYQMa-x3XsZYuQd7MkKAMUFy7f2a9G4UNFmNHHXGnfAFi3l&cmd=_notify-validate
7
+ headers:
8
+ user-agent:
9
+ - PayPal::Recurring/0.1.0
10
+ content-type:
11
+ - application/x-www-form-urlencoded
12
+ response: !ruby/struct:VCR::Response
13
+ status: !ruby/struct:VCR::ResponseStatus
14
+ code: 200
15
+ message: OK
16
+ headers:
17
+ date:
18
+ - Mon, 27 Jun 2011 17:37:09 GMT
19
+ server:
20
+ - Apache
21
+ set-cookie:
22
+ - c9MWDuvPtT9GIMyPc3jwol1VSlO=EShGvdSJTkQTmhIi4jIAvW9REPYfLjRSo3BqPdRvBymVBvHIai4jnMHE9UzQDrz7FgAtLCUw0V0VroudhtcfoaNLca2TZq61mMSzEHPHgwAj5ON8rmHCr_IjY_AkKI885AFFLW%7c-PiLEQg9LK_E6xg7biKrMLHv97B5cP5yT2R1l6g_ZNq-3SDSCAm-9VQdvpJSnrGNtfsrrG%7cvdBU3UXz8sDrfFIOKP_zwmNkSm8nNcKCATxNvMpGb4VOdGI4Sjia0YCorXMBWxpjKuAip0%7c1309196230; domain=.paypal.com; path=/; Secure; HttpOnly
23
+ - cookie_check=yes; expires=Thu, 24-Jun-2021 17:37:10 GMT; domain=.paypal.com; path=/; Secure; HttpOnly
24
+ - navcmd=_notify-validate; domain=.paypal.com; path=/; Secure; HttpOnly
25
+ - navlns=0.0; expires=Sun, 22-Jun-2031 17:37:10 GMT; domain=.paypal.com; path=/; Secure; HttpOnly
26
+ - Apache=10.72.109.11.1309196229102128; path=/; expires=Wed, 19-Jun-41 17:37:09 GMT
27
+ transfer-encoding:
28
+ - chunked
29
+ content-type:
30
+ - text/html; charset=UTF-8
31
+ body: VERIFIED
32
+ http_version: "1.1"
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+
3
+ describe PayPal::Recurring::Notification do
4
+ context "when successful" do
5
+ use_vcr_cassette "notification/success"
6
+
7
+ it "verifies notification" do
8
+ subject.params = {"test_ipn"=>"1", "payment_type"=>"echeck", "payment_date"=>"06:16:11 Jun 27, 2011 PDT", "payment_status"=>"Completed", "pending_reason"=>"echeck", "address_status"=>"confirmed", "payer_status"=>"verified", "first_name"=>"John", "last_name"=>"Smith", "payer_email"=>"buyer@paypalsandbox.com", "payer_id"=>"TESTBUYERID01", "address_name"=>"John Smith", "address_country"=>"United States", "address_country_code"=>"US", "address_zip"=>"95131", "address_state"=>"CA", "address_city"=>"San Jose", "address_street"=>"123, any street", "business"=>"seller@paypalsandbox.com", "receiver_email"=>"seller@paypalsandbox.com", "receiver_id"=>"TESTSELLERID1", "residence_country"=>"US", "item_name"=>"something", "item_number"=>"AK-1234", "quantity"=>"1", "shipping"=>"3.04", "tax"=>"2.02", "mc_currency"=>"USD", "mc_fee"=>"0.44", "mc_gross"=>"12.34", "txn_type"=>"web_accept", "txn_id"=>"116271316", "notify_version"=>"2.1", "custom"=>"xyz123", "invoice"=>"abc1234", "charset"=>"windows-1252", "verify_sign"=>"A12AYqq6LElPcpXaQx48GiroLHnMAoQPnK0Z7aRwXUOpg1GfDaE15mFN"}
9
+ subject.should be_verified
10
+ subject.should be_completed
11
+ end
12
+ end
13
+
14
+ context "when failure" do
15
+ use_vcr_cassette "notification/failure"
16
+
17
+ it "verifies notification" do
18
+ subject.params = {}
19
+ subject.should_not be_verified
20
+ subject.should_not be_completed
21
+ end
22
+ end
23
+ end
@@ -1,12 +1,6 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe PayPal::Recurring do
4
- describe ".logger" do
5
- it "has a default logging" do
6
- PayPal::Recurring.logger.should be_a(Logger)
7
- end
8
- end
9
-
10
4
  describe ".new" do
11
5
  it "instantiates PayPal::Recurring::Base" do
12
6
  PayPal::Recurring.new.should be_a(PayPal::Recurring::Base)
@@ -19,7 +19,7 @@ describe PayPal::Recurring::Request do
19
19
  describe "#post" do
20
20
  let(:request) {
21
21
  FakeWeb.register_uri :post, "https://api-3t.sandbox.paypal.com/nvp", :status => 200
22
- subject.post(:checkout)
22
+ subject.run(:checkout)
23
23
  CGI.parse(FakeWeb.last_request.body)
24
24
  }
25
25
 
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: paypal-recurring
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.1.0
5
+ version: 0.1.1
6
6
  platform: ruby
7
7
  authors:
8
8
  - Nando Vieira
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-06-27 00:00:00 Z
13
+ date: 2011-06-28 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rspec
@@ -89,6 +89,7 @@ files:
89
89
  - lib/paypal/recurring.rb
90
90
  - lib/paypal/recurring/base.rb
91
91
  - lib/paypal/recurring/cacert.pem
92
+ - lib/paypal/recurring/notification.rb
92
93
  - lib/paypal/recurring/request.rb
93
94
  - lib/paypal/recurring/response.rb
94
95
  - lib/paypal/recurring/response/base.rb
@@ -106,6 +107,8 @@ files:
106
107
  - spec/fixtures/details/cancelled.yml
107
108
  - spec/fixtures/details/failure.yml
108
109
  - spec/fixtures/details/success.yml
110
+ - spec/fixtures/notification/failure.yml
111
+ - spec/fixtures/notification/success.yml
109
112
  - spec/fixtures/payment/failure.yml
110
113
  - spec/fixtures/payment/success.yml
111
114
  - spec/fixtures/profile/cancel/failure.yml
@@ -116,6 +119,7 @@ files:
116
119
  - spec/fixtures/profile/success.yml
117
120
  - spec/fixtures/profile/suspend/failure.yml
118
121
  - spec/fixtures/profile/suspend/success.yml
122
+ - spec/paypal/notification_spec.rb
119
123
  - spec/paypal/recurring_spec.rb
120
124
  - spec/paypal/request_spec.rb
121
125
  - spec/paypal/response/checkout_details_spec.rb
@@ -138,7 +142,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
138
142
  requirements:
139
143
  - - ">="
140
144
  - !ruby/object:Gem::Version
141
- hash: -1993102493723648975
145
+ hash: -1456604981268972750
142
146
  segments:
143
147
  - 0
144
148
  version: "0"
@@ -147,7 +151,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
147
151
  requirements:
148
152
  - - ">="
149
153
  - !ruby/object:Gem::Version
150
- hash: -1993102493723648975
154
+ hash: -1456604981268972750
151
155
  segments:
152
156
  - 0
153
157
  version: "0"
@@ -166,6 +170,8 @@ test_files:
166
170
  - spec/fixtures/details/cancelled.yml
167
171
  - spec/fixtures/details/failure.yml
168
172
  - spec/fixtures/details/success.yml
173
+ - spec/fixtures/notification/failure.yml
174
+ - spec/fixtures/notification/success.yml
169
175
  - spec/fixtures/payment/failure.yml
170
176
  - spec/fixtures/payment/success.yml
171
177
  - spec/fixtures/profile/cancel/failure.yml
@@ -176,6 +182,7 @@ test_files:
176
182
  - spec/fixtures/profile/success.yml
177
183
  - spec/fixtures/profile/suspend/failure.yml
178
184
  - spec/fixtures/profile/suspend/success.yml
185
+ - spec/paypal/notification_spec.rb
179
186
  - spec/paypal/recurring_spec.rb
180
187
  - spec/paypal/request_spec.rb
181
188
  - spec/paypal/response/checkout_details_spec.rb