paypal-recurring 0.1.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/.gitignore +12 -0
  2. data/Gemfile.lock +32 -31
  3. data/README.rdoc +21 -5
  4. data/lib/paypal/recurring/base.rb +119 -4
  5. data/lib/paypal/recurring/notification.rb +15 -13
  6. data/lib/paypal/recurring/request.rb +38 -2
  7. data/lib/paypal/recurring/response.rb +4 -1
  8. data/lib/paypal/recurring/response/payment.rb +6 -5
  9. data/lib/paypal/recurring/response/profile.rb +4 -3
  10. data/lib/paypal/recurring/response/refund.rb +23 -0
  11. data/lib/paypal/recurring/version.rb +3 -3
  12. data/paypal-recurring.gemspec +7 -5
  13. data/spec/fixtures/checkout/failure.yml +33 -21
  14. data/spec/fixtures/checkout/success.yml +33 -21
  15. data/spec/fixtures/create_profile/failure.yml +33 -21
  16. data/spec/fixtures/create_profile/success.yml +33 -21
  17. data/spec/fixtures/details/cancelled.yml +33 -21
  18. data/spec/fixtures/details/failure.yml +33 -21
  19. data/spec/fixtures/details/success.yml +33 -21
  20. data/spec/fixtures/ipn/recurring_payment_skipped.json +30 -0
  21. data/spec/fixtures/notification/failure.yml +43 -25
  22. data/spec/fixtures/notification/success.yml +33 -15
  23. data/spec/fixtures/payment/failure.yml +33 -21
  24. data/spec/fixtures/payment/success.yml +33 -21
  25. data/spec/fixtures/profile/cancel/failure.yml +33 -21
  26. data/spec/fixtures/profile/cancel/success.yml +33 -21
  27. data/spec/fixtures/profile/failure.yml +33 -21
  28. data/spec/fixtures/profile/reactivate/failure.yml +33 -21
  29. data/spec/fixtures/profile/reactivate/success.yml +33 -21
  30. data/spec/fixtures/profile/success.yml +33 -21
  31. data/spec/fixtures/profile/suspend/failure.yml +33 -21
  32. data/spec/fixtures/profile/suspend/success.yml +33 -21
  33. data/spec/fixtures/refund/failure.yml +38 -0
  34. data/spec/fixtures/refund/success.yml +38 -0
  35. data/spec/fixtures/update_profile/failure.yml +38 -0
  36. data/spec/fixtures/update_profile/profile.yml +38 -0
  37. data/spec/fixtures/update_profile/success.yml +38 -0
  38. data/spec/paypal/request_spec.rb +17 -0
  39. data/spec/paypal/response/checkout_details_spec.rb +7 -8
  40. data/spec/paypal/response/checkout_spec.rb +1 -1
  41. data/spec/paypal/response/create_recurring_profile_spec.rb +4 -4
  42. data/spec/paypal/response/manage_profile_spec.rb +11 -11
  43. data/spec/paypal/response/profile_spec.rb +9 -8
  44. data/spec/paypal/response/refund_spec.rb +33 -0
  45. data/spec/paypal/response/request_payment_spec.rb +4 -4
  46. data/spec/paypal/response/update_recurring_profile_spec.rb +47 -0
  47. data/spec/spec_helper.rb +8 -7
  48. metadata +135 -68
data/.gitignore CHANGED
@@ -2,3 +2,15 @@
2
2
  pkg
3
3
  tmp
4
4
  examples
5
+ .bundle
6
+ db/*.sqlite3*
7
+ log/*.log
8
+ *.log
9
+ tmp/**/*
10
+ tmp/*
11
+ doc/api
12
+ doc/app
13
+ *.swp
14
+ *~
15
+ .DS_Store
16
+ .rvmrc
data/Gemfile.lock CHANGED
@@ -1,45 +1,46 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- paypal-recurring (0.1.6)
4
+ paypal-recurring (1.0.0)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
8
8
  specs:
9
- archive-tar-minitar (0.5.2)
10
- columnize (0.3.3)
11
- diff-lcs (1.1.2)
9
+ activesupport (3.2.3)
10
+ i18n (~> 0.6)
11
+ multi_json (~> 1.0)
12
+ awesome_print (1.0.2)
13
+ coderay (1.0.6)
14
+ diff-lcs (1.1.3)
12
15
  fakeweb (1.3.0)
13
- linecache19 (0.5.12)
14
- ruby_core_source (>= 0.1.4)
15
- rake (0.8.7)
16
- rspec (2.6.0)
17
- rspec-core (~> 2.6.0)
18
- rspec-expectations (~> 2.6.0)
19
- rspec-mocks (~> 2.6.0)
20
- rspec-core (2.6.4)
21
- rspec-expectations (2.6.0)
22
- diff-lcs (~> 1.1.2)
23
- rspec-mocks (2.6.0)
24
- ruby-debug-base19 (0.11.25)
25
- columnize (>= 0.3.1)
26
- linecache19 (>= 0.5.11)
27
- ruby_core_source (>= 0.1.4)
28
- ruby-debug19 (0.11.6)
29
- columnize (>= 0.3.1)
30
- linecache19 (>= 0.5.11)
31
- ruby-debug-base19 (>= 0.11.19)
32
- ruby_core_source (0.1.5)
33
- archive-tar-minitar (>= 0.5.2)
34
- vcr (1.10.0)
16
+ i18n (0.6.0)
17
+ method_source (0.7.1)
18
+ multi_json (1.3.2)
19
+ pry (0.9.9.3)
20
+ coderay (~> 1.0.5)
21
+ method_source (~> 0.7.1)
22
+ slop (>= 2.4.4, < 3)
23
+ rake (0.9.2.2)
24
+ rspec (2.9.0)
25
+ rspec-core (~> 2.9.0)
26
+ rspec-expectations (~> 2.9.0)
27
+ rspec-mocks (~> 2.9.0)
28
+ rspec-core (2.9.0)
29
+ rspec-expectations (2.9.1)
30
+ diff-lcs (~> 1.1.3)
31
+ rspec-mocks (2.9.0)
32
+ slop (2.4.4)
33
+ vcr (2.1.0)
35
34
 
36
35
  PLATFORMS
37
36
  ruby
38
37
 
39
38
  DEPENDENCIES
40
- fakeweb (~> 1.3.0)
39
+ activesupport
40
+ awesome_print
41
+ fakeweb
41
42
  paypal-recurring!
42
- rake (~> 0.8.7)
43
- rspec (~> 2.6)
44
- ruby-debug19
45
- vcr (~> 1.10)
43
+ pry
44
+ rake
45
+ rspec
46
+ vcr
data/README.rdoc CHANGED
@@ -77,6 +77,26 @@ Finally, you need to create a new recurring profile.
77
77
  response = ppr.create_recurring_profile
78
78
  puts response.profile_id
79
79
 
80
+ (Optionally) You can specify a trial period, frequency, and length.
81
+
82
+ ppr = PayPal::Recurring.new({
83
+ :amount => "9.00",
84
+ :currency => "USD",
85
+ :description => "Awesome - Monthly Subscription",
86
+ :ipn_url => "http://example.com/paypal/ipn",
87
+ :frequency => 1,
88
+ :token => "EC-05C46042TU8306821",
89
+ :period => :monthly,
90
+ :reference => "1234",
91
+ :payer_id => "WTTS5KC2T46YU",
92
+ :start_at => Time.now,
93
+ :failed => 1,
94
+ :outstanding => :next_billing,
95
+ :trial_length => 1,
96
+ :trial_period => :monthly,
97
+ :trial_frequency => 1
98
+ })
99
+
80
100
  You can manage your recurring profile.
81
101
 
82
102
  ppr = PayPal::Recurring.new(:profile_id => "I-VCEL6TRG35CU")
@@ -91,15 +111,11 @@ You should save two paramaters to your database: <tt>TOKEN</tt> and <tt>PROFILEI
91
111
  <tt>TOKEN</tt> is required when user returns to your website after he authorizes (or not) the billing process. You
92
112
  need to save it so you can find him later. You can remove this info after payment and recurring profile are set.
93
113
 
94
- The <tt>PROFILEID</tt> allows you to manage the recurring profile, like canceling billing when an user don't
114
+ The <tt>PROFILEID</tt> allows you to manage the recurring profile, like cancelling billing when an user don't
95
115
  want to use your service anymore.
96
116
 
97
117
  <b>NOTE:</b> TOKEN will expire after approximately 3 hours.
98
118
 
99
- == TO-DO
100
-
101
- * handle Instant Payment Notifications (IPN)
102
-
103
119
  == Maintainer
104
120
 
105
121
  * Nando Vieira (http://nandovieira.com.br)
@@ -5,6 +5,7 @@ module PayPal
5
5
  attr_accessor :cancel_url
6
6
  attr_accessor :currency
7
7
  attr_accessor :description
8
+ attr_accessor :note
8
9
  attr_accessor :email
9
10
  attr_accessor :failed
10
11
  attr_accessor :frequency
@@ -17,9 +18,19 @@ module PayPal
17
18
  attr_accessor :period
18
19
  attr_accessor :profile_id
19
20
  attr_accessor :reference
21
+ attr_accessor :refund_type
20
22
  attr_accessor :return_url
21
23
  attr_accessor :start_at
22
24
  attr_accessor :token
25
+ attr_accessor :transaction_id
26
+ attr_accessor :item_category
27
+ attr_accessor :item_name
28
+ attr_accessor :item_amount
29
+ attr_accessor :item_quantity
30
+ attr_accessor :trial_frequency
31
+ attr_accessor :trial_length
32
+ attr_accessor :trial_period
33
+ attr_accessor :trial_amount
23
34
 
24
35
  def initialize(options = {})
25
36
  options.each {|name, value| send("#{name}=", value)}
@@ -53,7 +64,11 @@ module PayPal
53
64
  :cancel_url,
54
65
  :currency,
55
66
  :description,
56
- :ipn_url
67
+ :ipn_url,
68
+ :item_category,
69
+ :item_name,
70
+ :item_amount,
71
+ :item_quantity
57
72
  ).merge(
58
73
  :payment_action => "Authorization",
59
74
  :no_shipping => 1,
@@ -113,7 +128,22 @@ module PayPal
113
128
  # response.completed? && response.approved?
114
129
  #
115
130
  def request_payment
116
- params = collect(:amount, :return_url, :cancel_url, :ipn_url, :currency, :description, :payer_id, :token, :reference).merge(:payment_action => "Sale")
131
+ params = collect(
132
+ :amount,
133
+ :return_url,
134
+ :cancel_url,
135
+ :ipn_url,
136
+ :currency,
137
+ :description,
138
+ :payer_id,
139
+ :token,
140
+ :reference,
141
+ :item_category,
142
+ :item_name,
143
+ :item_amount,
144
+ :item_quantity
145
+ ).merge(:payment_action => "Sale")
146
+
117
147
  request.run(:payment, params)
118
148
  end
119
149
 
@@ -133,16 +163,77 @@ module PayPal
133
163
  # :payer_id => "WTTS5KC2T46YU",
134
164
  # :start_at => Time.now,
135
165
  # :failed => 1,
136
- # :outstanding => :next_billing
166
+ # :outstanding => :next_billing,
167
+ # :trial_period => :monthly,
168
+ # :trial_length => 1,
169
+ # :trial_frequency => 1,
170
+ # :trial_amount => 0.00
137
171
  # })
138
172
  #
139
173
  # response = ppr.create_recurring_profile
140
174
  #
141
175
  def create_recurring_profile
142
- params = collect(:amount, :initial_amount, :initial_amount_action, :currency, :description, :payer_id, :token, :reference, :start_at, :failed, :outstanding, :ipn_url, :frequency, :period, :email)
176
+ params = collect(
177
+ :amount,
178
+ :initial_amount,
179
+ :initial_amount_action,
180
+ :currency,
181
+ :description,
182
+ :payer_id,
183
+ :token,
184
+ :reference,
185
+ :start_at,
186
+ :failed,
187
+ :outstanding,
188
+ :ipn_url,
189
+ :frequency,
190
+ :period,
191
+ :email,
192
+ :trial_length,
193
+ :trial_period,
194
+ :trial_frequency,
195
+ :trial_amount,
196
+ :item_category,
197
+ :item_name,
198
+ :item_amount,
199
+ :item_quantity
200
+ )
143
201
  request.run(:create_profile, params)
144
202
  end
145
203
 
204
+ # Update a recurring billing profile.
205
+ #
206
+ # ppr = PayPal::Recurring.new({
207
+ # :amount => "99.00",
208
+ # :currency => "USD",
209
+ # :description => "Awesome - Monthly Subscription",
210
+ # :note => "Changed plan to Gold",
211
+ # :ipn_url => "http://example.com/paypal/ipn",
212
+ # :reference => "1234",
213
+ # :profile_id => "I-VCEL6TRG35CU",
214
+ # :start_at => Time.now,
215
+ # :outstanding => :next_billing
216
+ # })
217
+ #
218
+ # response = ppr.update_recurring_profile
219
+ #
220
+ def update_recurring_profile
221
+ params = collect(
222
+ :amount,
223
+ :currency,
224
+ :description,
225
+ :note,
226
+ :profile_id,
227
+ :reference,
228
+ :start_at,
229
+ :outstanding,
230
+ :ipn_url,
231
+ :email
232
+ )
233
+
234
+ request.run(:update_profile, params)
235
+ end
236
+
146
237
  # Retrieve information about existing recurring profile.
147
238
  #
148
239
  # ppr = PayPal::Recurring.new(:profile_id => "I-VCEL6TRG35CU")
@@ -152,6 +243,30 @@ module PayPal
152
243
  request.run(:profile, :profile_id => profile_id)
153
244
  end
154
245
 
246
+ # Request a refund.
247
+ # ppr = PayPal::Recurring.new({
248
+ # :profile_id => "I-VCEL6TRG35CU",
249
+ # :transaction_id => "ABCEDFGH",
250
+ # :reference => "1234",
251
+ # :refund_type => :partial,
252
+ # :amount => "9.00",
253
+ # :currency => "USD"
254
+ # })
255
+ # response = ppr.refund
256
+ #
257
+ def refund
258
+ params = collect(
259
+ :transaction_id,
260
+ :reference,
261
+ :refund_type,
262
+ :amount,
263
+ :currency,
264
+ :note
265
+ )
266
+
267
+ request.run(:refund, params)
268
+ end
269
+
155
270
  private
156
271
  # Collect specified attributes and build a hash out of it.
157
272
  #
@@ -6,19 +6,21 @@ module PayPal
6
6
  attr_reader :params
7
7
 
8
8
  mapping({
9
- :type => :txn_type,
10
- :transaction_id => :txn_id,
11
- :fee => [:mc_fee, :payment_fee],
12
- :reference => [:rp_invoice_id, :custom, :invoice],
13
- :payment_id => :recurring_payment_id,
14
- :amount => [:amount, :mc_gross, :payment_gross],
15
- :currency => :mc_currency,
16
- :status => :payment_status,
17
- :payment_date => [:time_created, :payment_date],
18
- :seller_id => :receiver_id,
19
- :email => :receiver_email,
20
- :initial_amount => :initial_payment_amount,
21
- :payer_email => :payer_email
9
+ :type => :txn_type,
10
+ :transaction_id => :txn_id,
11
+ :fee => [:mc_fee, :payment_fee],
12
+ :reference => [:rp_invoice_id, :custom, :invoice],
13
+ :payment_id => :recurring_payment_id,
14
+ :amount => [:amount, :mc_gross, :payment_gross],
15
+ :currency => :mc_currency,
16
+ :status => :payment_status,
17
+ :pending_reason => :pending_reason,
18
+ :profile_status => :profile_status,
19
+ :payment_date => [:time_created, :payment_date],
20
+ :seller_id => :receiver_id,
21
+ :email => :receiver_email,
22
+ :initial_amount => :initial_payment_amount,
23
+ :payer_email => :payer_email
22
24
  })
23
25
 
24
26
  def initialize(params = {})
@@ -7,7 +7,9 @@ module PayPal
7
7
  :details => "GetExpressCheckoutDetails",
8
8
  :create_profile => "CreateRecurringPaymentsProfile",
9
9
  :profile => "GetRecurringPaymentsProfileDetails",
10
- :manage_profile => "ManageRecurringPaymentsProfileStatus"
10
+ :manage_profile => "ManageRecurringPaymentsProfileStatus",
11
+ :update_profile => "UpdateRecurringPaymentsProfile",
12
+ :refund => "RefundTransaction"
11
13
  }
12
14
 
13
15
  INITIAL_AMOUNT_ACTIONS = {
@@ -23,15 +25,30 @@ module PayPal
23
25
 
24
26
  PERIOD = {
25
27
  :daily => "Day",
28
+ :weekly => "Weekly",
26
29
  :monthly => "Month",
27
30
  :yearly => "Year"
28
31
  }
29
32
 
33
+ TRIAL_PERIOD = {
34
+ :daily => "Day",
35
+ :weekly => "Weekly",
36
+ :monthly => "Month",
37
+ :yearly => "Year"
38
+ }
39
+
30
40
  OUTSTANDING = {
31
41
  :next_billing => "AddToNextBilling",
32
42
  :no_auto => "NoAutoBill"
33
43
  }
34
44
 
45
+ REFUND_TYPE = {
46
+ :full => "Full",
47
+ :partial => "Partial",
48
+ :external => "ExternalDispute",
49
+ :other => "Other"
50
+ }
51
+
35
52
  ATTRIBUTES = {
36
53
  :action => "ACTION",
37
54
  :amount => ["PAYMENTREQUEST_0_AMT", "AMT"],
@@ -39,6 +56,11 @@ module PayPal
39
56
  :cancel_url => "CANCELURL",
40
57
  :currency => ["PAYMENTREQUEST_0_CURRENCYCODE", "CURRENCYCODE"],
41
58
  :description => ["DESC", "PAYMENTREQUEST_0_DESC", "L_BILLINGAGREEMENTDESCRIPTION0"],
59
+ :note => "NOTE",
60
+ :item_category => "L_PAYMENTREQUEST_0_ITEMCATEGORY0",
61
+ :item_name => "L_PAYMENTREQUEST_0_NAME0",
62
+ :item_amount => "L_PAYMENTREQUEST_0_AMT0",
63
+ :item_quantity => "L_PAYMENTREQUEST_0_QTY0",
42
64
  :email => "EMAIL",
43
65
  :failed => "MAXFAILEDPAYMENTS",
44
66
  :frequency => "BILLINGFREQUENCY",
@@ -55,12 +77,18 @@ module PayPal
55
77
  :period => "BILLINGPERIOD",
56
78
  :profile_id => "PROFILEID",
57
79
  :reference => ["PROFILEREFERENCE", "PAYMENTREQUEST_0_CUSTOM", "PAYMENTREQUEST_0_INVNUM"],
80
+ :refund_type => "REFUNDTYPE",
58
81
  :return_url => "RETURNURL",
59
82
  :signature => "SIGNATURE",
60
83
  :start_at => "PROFILESTARTDATE",
61
84
  :token => "TOKEN",
85
+ :transaction_id => "TRANSACTIONID",
86
+ :trial_amount => "TRIALAMT",
87
+ :trial_frequency => "TRIALBILLINGFREQUENCY",
88
+ :trial_length => "TRIALTOTALBILLINGCYCLES",
89
+ :trial_period => "TRIALBILLINGPERIOD",
62
90
  :username => "USER",
63
- :version => "VERSION",
91
+ :version => "VERSION"
64
92
  }
65
93
 
66
94
  CA_FILE = File.dirname(__FILE__) + "/cacert.pem"
@@ -149,6 +177,10 @@ module PayPal
149
177
  PERIOD.fetch(value.to_sym, value) if value
150
178
  end
151
179
 
180
+ def build_trial_period(value)
181
+ TRIAL_PERIOD.fetch(value.to_sym, value) if value
182
+ end
183
+
152
184
  def build_start_at(value) # :nodoc:
153
185
  value.respond_to?(:strftime) ? value.strftime("%Y-%m-%dT%H:%M:%SZ") : value
154
186
  end
@@ -157,6 +189,10 @@ module PayPal
157
189
  OUTSTANDING.fetch(value.to_sym, value) if value
158
190
  end
159
191
 
192
+ def build_refund_type(value) # :nodoc:
193
+ REFUND_TYPE.fetch(value.to_sym, value) if value
194
+ end
195
+
160
196
  def build_action(value) # :nodoc:
161
197
  ACTIONS.fetch(value.to_sym, value) if value
162
198
  end
@@ -7,6 +7,7 @@ module PayPal
7
7
  autoload :Payment, "paypal/recurring/response/payment"
8
8
  autoload :ManageProfile, "paypal/recurring/response/manage_profile"
9
9
  autoload :Profile, "paypal/recurring/response/profile"
10
+ autoload :Refund, "paypal/recurring/response/refund"
10
11
 
11
12
  RESPONDERS = {
12
13
  :checkout => "Checkout",
@@ -14,7 +15,9 @@ module PayPal
14
15
  :payment => "Payment",
15
16
  :profile => "Profile",
16
17
  :create_profile => "ManageProfile",
17
- :manage_profile => "ManageProfile"
18
+ :manage_profile => "ManageProfile",
19
+ :update_profile => "ManageProfile",
20
+ :refund => "Refund"
18
21
  }
19
22
 
20
23
  def self.process(method, response)