freemium-ajb 0.0.4

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.
Files changed (93) hide show
  1. data/.coveralls.yml +1 -0
  2. data/.gitignore +27 -0
  3. data/.rspec +1 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +6 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.md +20 -0
  9. data/README.md +1 -0
  10. data/app/mailers/freemium_mailer.rb +36 -0
  11. data/app/views/subscription_mailer/admin_report.text.erb +4 -0
  12. data/app/views/subscription_mailer/expiration_notice.text.erb +1 -0
  13. data/app/views/subscription_mailer/expiration_warning.text.erb +1 -0
  14. data/app/views/subscription_mailer/invoice.text.erb +5 -0
  15. data/freemium.gemspec +29 -0
  16. data/lib/freemium.rb +25 -0
  17. data/lib/freemium/configuration.rb +27 -0
  18. data/lib/freemium/coupon.rb +37 -0
  19. data/lib/freemium/coupon_redemption.rb +59 -0
  20. data/lib/freemium/credit_card.rb +222 -0
  21. data/lib/freemium/engine.rb +7 -0
  22. data/lib/freemium/gateways/base.rb +65 -0
  23. data/lib/freemium/gateways/brain_tree.rb +175 -0
  24. data/lib/freemium/gateways/test.rb +36 -0
  25. data/lib/freemium/rates.rb +33 -0
  26. data/lib/freemium/response.rb +24 -0
  27. data/lib/freemium/subscription.rb +384 -0
  28. data/lib/freemium/subscription_change.rb +20 -0
  29. data/lib/freemium/subscription_plan.rb +26 -0
  30. data/lib/freemium/testing/app/controllers/application_controller.rb +7 -0
  31. data/lib/freemium/testing/application.rb +46 -0
  32. data/lib/freemium/testing/config/database.yml +11 -0
  33. data/lib/freemium/testing/config/routes.rb +3 -0
  34. data/lib/freemium/transaction.rb +15 -0
  35. data/lib/freemium/version.rb +3 -0
  36. data/lib/generators/freemium/install/install_generator.rb +58 -0
  37. data/lib/generators/freemium/install/templates/db/migrate/create_coupon_redemptions.rb +18 -0
  38. data/lib/generators/freemium/install/templates/db/migrate/create_coupons.rb +28 -0
  39. data/lib/generators/freemium/install/templates/db/migrate/create_credit_cards.rb +14 -0
  40. data/lib/generators/freemium/install/templates/db/migrate/create_subscription_changes.rb +21 -0
  41. data/lib/generators/freemium/install/templates/db/migrate/create_subscription_plans.rb +14 -0
  42. data/lib/generators/freemium/install/templates/db/migrate/create_subscriptions.rb +31 -0
  43. data/lib/generators/freemium/install/templates/db/migrate/create_transactions.rb +17 -0
  44. data/lib/generators/freemium/install/templates/freemium.rb +16 -0
  45. data/lib/generators/freemium/install/templates/models/coupon.rb +3 -0
  46. data/lib/generators/freemium/install/templates/models/coupon_redemption.rb +3 -0
  47. data/lib/generators/freemium/install/templates/models/credit_card.rb +3 -0
  48. data/lib/generators/freemium/install/templates/models/subscription.rb +3 -0
  49. data/lib/generators/freemium/install/templates/models/subscription_change.rb +3 -0
  50. data/lib/generators/freemium/install/templates/models/subscription_plan.rb +3 -0
  51. data/lib/generators/freemium/install/templates/models/transaction.rb +3 -0
  52. data/lib/generators/views/USAGE +3 -0
  53. data/lib/generators/views/views_generator.rb +39 -0
  54. data/lib/tasks/freemium.rake +17 -0
  55. data/spec/dummy/Rakefile +7 -0
  56. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  57. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  58. data/spec/dummy/app/mailers/mailers.rb +1 -0
  59. data/spec/dummy/app/models/models.rb +31 -0
  60. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  61. data/spec/dummy/config.ru +4 -0
  62. data/spec/dummy/config/application.rb +45 -0
  63. data/spec/dummy/config/boot.rb +10 -0
  64. data/spec/dummy/config/database.yml +21 -0
  65. data/spec/dummy/config/environment.rb +6 -0
  66. data/spec/dummy/config/environments/development.rb +26 -0
  67. data/spec/dummy/config/environments/production.rb +49 -0
  68. data/spec/dummy/config/environments/test.rb +36 -0
  69. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  70. data/spec/dummy/config/initializers/freemium.rb +16 -0
  71. data/spec/dummy/config/initializers/inflections.rb +10 -0
  72. data/spec/dummy/config/initializers/mem_db.rb +12 -0
  73. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  74. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  75. data/spec/dummy/config/initializers/session_store.rb +8 -0
  76. data/spec/dummy/config/locales/en.yml +5 -0
  77. data/spec/dummy/config/routes.rb +58 -0
  78. data/spec/dummy/db/schema.rb +90 -0
  79. data/spec/dummy/script/rails +6 -0
  80. data/spec/fixtures/credit_cards.yml +11 -0
  81. data/spec/fixtures/subscription_plans.yml +15 -0
  82. data/spec/fixtures/subscriptions.yml +28 -0
  83. data/spec/fixtures/users.yml +16 -0
  84. data/spec/lib/tasks/run_billing_rake_spec.rb +14 -0
  85. data/spec/models/coupon_redemption_spec.rb +287 -0
  86. data/spec/models/credit_card_spec.rb +124 -0
  87. data/spec/models/manual_billing_spec.rb +165 -0
  88. data/spec/models/subscription_plan_spec.rb +46 -0
  89. data/spec/models/subscription_spec.rb +386 -0
  90. data/spec/spec_helper.rb +19 -0
  91. data/spec/support/helpers.rb +18 -0
  92. data/spec/support/shared_contexts/rake.rb +19 -0
  93. metadata +270 -0
@@ -0,0 +1,165 @@
1
+ require 'spec_helper'
2
+
3
+ describe Subscription do
4
+ fixtures :users, :subscriptions, :subscription_plans, :credit_cards
5
+
6
+
7
+ before(:each) do
8
+ Freemium.configuration.gateway = Freemium::Gateways::Test.new
9
+ end
10
+
11
+ it "should find billable" do
12
+ # making a one-off fixture set, basically
13
+ create_billable_subscription # this subscription should be billable
14
+ create_billable_subscription(:paid_through => Date.today) # this subscription should be billable
15
+ create_billable_subscription(:coupon => Coupon.create!(:description => "Complimentary", :discount_percentage => 100)) # should NOT be billable because it's free
16
+ create_billable_subscription(:subscription_plan => subscription_plans(:free)) # should NOT be billable because it's free
17
+ create_billable_subscription(:paid_through => Date.today + 1) # should NOT be billable because it's paid far enough out
18
+ s = create_billable_subscription # should be billable because it's past due
19
+ s.update_attribute :expire_on, Date.today + 1
20
+
21
+ expirable = Subscription.send(:find_billable)
22
+ expirable.all? { |subscription| subscription.paid? }.should be_true, "free subscriptions aren't billable"
23
+ expirable.all? { |subscription| !subscription.in_trial? }.should be_true, "subscriptions that have been paid are no longer in the trial period"
24
+ expirable.all? { |subscription| subscription.paid_through <= Date.today }.should be_true, "subscriptions paid through tomorrow aren't billable yet"
25
+ expirable.size.should eql(3)
26
+
27
+ Freemium.configuration.gateway.stub!(:charge).and_return(
28
+ Transaction.new(
29
+ :billing_key => s.billing_key,
30
+ :amount => s.rate,
31
+ :success => false
32
+ )
33
+ )
34
+ end
35
+
36
+ it "should not change expire_on on failure overdue payment" do
37
+ subscription = create_billable_subscription # should NOT be billable because it's already expiring
38
+ expire_on = Date.today + 2
39
+ paid_through = subscription.paid_through
40
+ subscription.update_attribute :expire_on, expire_on
41
+ subscription.reload
42
+ subscription.expire_on.should eql(expire_on)
43
+ expirable = Subscription.send(:find_billable)
44
+ expirable.size.should eql(1), "Subscriptions in their grace period should be retried"
45
+
46
+ Freemium.configuration.gateway.stub!(:charge).and_return(
47
+ Transaction.new(
48
+ :billing_key => subscription.billing_key,
49
+ :amount => subscription.rate,
50
+ :success => false
51
+ )
52
+ )
53
+
54
+ lambda do
55
+ transaction = subscription.charge!
56
+ subscription.expire_on.should eql(expire_on), "Billing failed on existing overdue account but the expire_on date was changed"
57
+ end.should_not raise_error
58
+ end
59
+
60
+ it "should change expire_on on success overdue payment" do
61
+ subscription = create_billable_subscription # should NOT be billable because it's already expiring
62
+ expire_on = Date.today + 2
63
+ paid_through = subscription.paid_through
64
+ subscription.update_attribute :expire_on, expire_on
65
+
66
+ expirable = Subscription.send(:find_billable)
67
+ expirable.size.should eql(1), "Subscriptions in their grace period should be retried"
68
+
69
+ Freemium.configuration.gateway.stub!(:charge).and_return(
70
+ Transaction.new(
71
+ :billing_key => subscription.billing_key,
72
+ :amount => subscription.rate,
73
+ :success => true
74
+ )
75
+ )
76
+
77
+ assert_nothing_raised do
78
+ transaction = subscription.charge!
79
+ transaction.subscription.paid_through.to_s.should eql((paid_through >> 1).to_s), "extended by a month"
80
+ subscription.expire_on.should be_nil, "Billing succeeded on existing overdue account but the expire_on date was not reset"
81
+ end
82
+ end
83
+
84
+ it "should charge a subscription" do
85
+ subscription = Subscription.first
86
+ subscription.coupon = Coupon.create!(:description => "Complimentary", :discount_percentage => 30)
87
+ subscription.save!
88
+
89
+ paid_through = subscription.paid_through
90
+
91
+ Freemium.configuration.gateway.stub!(:charge).and_return(
92
+ Transaction.new(
93
+ :billing_key => subscription.billing_key,
94
+ :amount => subscription.rate,
95
+ :success => true
96
+ )
97
+ )
98
+
99
+ lambda do
100
+ transaction = subscription.charge!
101
+ transaction.subscription.paid_through.to_s.should eql((paid_through >> 1).to_s)#, "extended by a month"
102
+ end.should_not raise_error
103
+
104
+ subscription = subscription.reload
105
+ subscription.transactions.empty?.should be_false
106
+ subscription.transactions.last.should be_true
107
+ subscription.transactions.last.success?.should be_true
108
+ subscription.transactions.last.message?.should_not be_nil
109
+ ((Time.now - 1.minute) < subscription.last_transaction_at).should be_true
110
+ Transaction.since(Date.today).empty?.should be_false
111
+ subscription.transactions.last.amount.should eql(subscription.rate)
112
+ subscription.reload.paid_through.to_s.should eql((paid_through >> 1).to_s), "extended by a month"
113
+ end
114
+
115
+
116
+ it "should charge an aborted subscription" do
117
+ subscription = Subscription.first
118
+ subscription.coupon = Coupon.create!(:description => "Complimentary", :discount_percentage => 30)
119
+ subscription.save!
120
+
121
+ paid_through = subscription.paid_through
122
+ subscription.transactions.empty?.should be_true
123
+
124
+ Freemium.configuration.gateway.stub!(:charge).and_return(
125
+ Transaction.new(
126
+ :billing_key => subscription.billing_key,
127
+ :amount => subscription.rate,
128
+ :success => true
129
+ )
130
+ )
131
+
132
+ subscription.should_receive(:receive_payment).and_raise(RuntimeError)
133
+ subscription.charge!
134
+ subscription.reload.transactions.empty?.should be_false
135
+ end
136
+
137
+ it "should not charge a subscription" do
138
+ subscription = Subscription.first
139
+ paid_through = subscription.paid_through
140
+ Freemium.configuration.gateway.stub!(:charge).and_return(
141
+ Transaction.new(
142
+ :billing_key => subscription.billing_key,
143
+ :amount => subscription.rate,
144
+ :success => false
145
+ )
146
+ )
147
+
148
+ subscription.expire_on.should be_nil
149
+ lambda { subscription.charge! }.should_not raise_error
150
+ subscription.reload.paid_through.should eql(paid_through), "not extended"
151
+ subscription.expire_on.should_not be_nil
152
+ subscription.transactions.last.success?.should be_false
153
+ end
154
+
155
+ protected
156
+
157
+ def create_billable_subscription(options = {})
158
+ Subscription.create!({
159
+ :subscription_plan => subscription_plans(:premium),
160
+ :subscribable => User.new(:name => 'a'),
161
+ :paid_through => Date.today - 1,
162
+ :credit_card => CreditCard.sample
163
+ }.merge(options))
164
+ end
165
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe SubscriptionPlan do
4
+ fixtures :users, :subscriptions, :subscription_plans, :credit_cards
5
+
6
+ it "should have associations" do
7
+ [subscriptions(:bobs_subscription)].should == (subscription_plans(:basic).subscriptions)
8
+ end
9
+
10
+ it "should have rate intervals" do
11
+ plan = SubscriptionPlan.new(:rate_cents => 3041)
12
+ plan.daily_rate.should eql(Money.new(99))
13
+ plan.monthly_rate.should eql(Money.new(3041))
14
+ plan.yearly_rate.should eql(Money.new(36492))
15
+ end
16
+
17
+ it "should create plan" do
18
+ plan = create_plan
19
+ plan.new_record?.should be_false
20
+ end
21
+
22
+ it "should have errors" do
23
+ [:name, :rate_cents].each do |field|
24
+ plan = create_plan(field => nil)
25
+ plan.new_record?.should be_true
26
+ plan.should have(1).errors_on(field)
27
+ end
28
+ end
29
+
30
+ it "should serialize features" do
31
+ plan = create_plan
32
+ plan.features.should == {}
33
+ plan2 = create_plan(features: {foo: 1})
34
+ plan2.features[:foo].should == 1
35
+ end
36
+
37
+ protected
38
+
39
+ def create_plan(options = {})
40
+ SubscriptionPlan.create({
41
+ :name => 'super-duper-ultra-premium',
42
+ :key => 'super-duper-ultra-premium',
43
+ :rate_cents => 99995
44
+ }.merge(options))
45
+ end
46
+ end
@@ -0,0 +1,386 @@
1
+ require 'spec_helper'
2
+
3
+ describe Subscription do
4
+ fixtures :users, :subscriptions, :subscription_plans, :credit_cards
5
+
6
+ def build_subscription(options = {})
7
+ Subscription.new({
8
+ :subscription_plan => subscription_plans(:free),
9
+ :subscribable => users(:sue)
10
+ }.merge(options))
11
+ end
12
+
13
+ def create_transaction_for(amount, subscription)
14
+ Transaction.create :amount => amount, :subscription => subscription, :success => true, :billing_key => 12345
15
+ end
16
+
17
+ def assert_changed(subscribable, reason, original_plan, new_plan)
18
+ changes = SubscriptionChange.where(["subscribable_id = ? AND subscribable_type = ?", subscribable.id, subscribable.class.to_s]).last
19
+ changes.should_not be_nil
20
+ changes.reason.should eql(reason.to_s)
21
+ changes.original_subscription_plan.should eql(original_plan)
22
+ changes.new_subscription_plan.should eq(new_plan)
23
+ changes.original_rate_cents.should eql(original_plan ? original_plan.rate.cents : 0)
24
+ changes.new_rate_cents.should eql(new_plan ? new_plan.rate.cents : 0)
25
+ end
26
+
27
+ it "should create free subscription" do
28
+ subscription = build_subscription(:subscription_plan => subscription_plans(:free))
29
+ subscription.save!
30
+
31
+ subscription.should_not be_new_record
32
+ subscription.reload.started_on.should eql(Date.today)
33
+ subscription.in_trial?.should be_false
34
+ subscription.paid_through.should be_nil
35
+ subscription.paid?.should be_false
36
+
37
+ #TODO understand what is that
38
+ assert_changed(subscription.subscribable, :new, nil, subscription_plans(:free))
39
+ end
40
+
41
+ it "should create paid subscription" do
42
+ Freemium.configuration.days_free_trial = 30
43
+
44
+ subscription = build_subscription(:subscription_plan => subscription_plans(:basic), :credit_card => CreditCard.sample)
45
+ subscription.save!
46
+
47
+ subscription.should_not be_new_record
48
+ subscription.reload.started_on.should eql(Date.today)
49
+ subscription.in_trial?.should be_true
50
+ subscription.paid_through.should_not be_nil
51
+ subscription.paid_through.should eql(Date.today + Freemium.configuration.days_free_trial)
52
+ subscription.paid?.should be_true
53
+ subscription.billing_key.should_not be_nil
54
+
55
+ assert_changed(subscription.subscribable, :new, nil, subscription_plans(:basic))
56
+ end
57
+
58
+ it "should be upgraded from free" do
59
+ subscription = build_subscription(:subscription_plan => subscription_plans(:free))
60
+ subscription.save!
61
+
62
+ new_date = Date.today + 10.days
63
+ Date.stub!(:today => new_date)
64
+
65
+ subscription.in_trial?.should be_false
66
+ subscription.subscription_plan = subscription_plans(:basic)
67
+ subscription.credit_card = CreditCard.sample
68
+ subscription.save!
69
+
70
+ subscription.reload.started_on.should eql(new_date)
71
+ subscription.paid_through.should_not be_nil
72
+ subscription.in_trial?.should be_false
73
+ subscription.paid_through.should eql(new_date)
74
+ subscription.paid?.should be_true
75
+ subscription.billing_key.should_not be_nil
76
+
77
+ assert_changed(subscription.subscribable, :upgrade, subscription_plans(:free), subscription_plans(:basic))
78
+ end
79
+
80
+ it "should be downgraded" do
81
+ subscription = build_subscription(:subscription_plan => subscription_plans(:basic), :credit_card => CreditCard.sample)
82
+ subscription.save!
83
+
84
+ new_date = Date.today + 10.days
85
+ Date.stub!(:today => new_date)
86
+
87
+ subscription.subscription_plan = subscription_plans(:free)
88
+ subscription.save!
89
+
90
+ subscription.reload.started_on.should eql(new_date)
91
+ subscription.paid_through.should be_nil
92
+ subscription.paid?.should be_false
93
+ subscription.billing_key.should be_nil
94
+ subscription.credit_card.should be_nil
95
+
96
+ assert_changed(subscription.subscribable, :downgrade, subscription_plans(:basic), subscription_plans(:free))
97
+ end
98
+
99
+ it "should have associations" do
100
+ assert_equal users(:bob), subscriptions(:bobs_subscription).subscribable
101
+ assert_equal subscription_plans(:basic), subscriptions(:bobs_subscription).subscription_plan
102
+ end
103
+
104
+ it "should have remaining_days" do
105
+ assert_equal 20, subscriptions(:bobs_subscription).remaining_days
106
+ end
107
+
108
+ it "should have remaining_value" do
109
+ assert_equal Money.new(840), subscriptions(:bobs_subscription).remaining_value
110
+ end
111
+
112
+ ##
113
+ ## Upgrade / Downgrade service credits
114
+ ##
115
+
116
+ it "should upgrade credit" do
117
+ subscription = subscriptions(:bobs_subscription)
118
+ new_plan = subscription_plans(:premium)
119
+
120
+ subscription.remaining_value.cents.should > 0
121
+ expected_paid_through = Date.today + (subscription.remaining_value.cents / new_plan.daily_rate.cents)
122
+ subscription.subscription_plan = subscription_plans(:premium)
123
+ subscription.save!
124
+
125
+ subscription.paid_through.should eql(expected_paid_through)
126
+ end
127
+
128
+ it "should upgrade no credit for free trial" do
129
+ subscription = build_subscription(:subscription_plan => subscription_plans(:premium), :credit_card => CreditCard.sample)
130
+ subscription.save!
131
+
132
+ subscription.paid_through.should eql(Date.today + Freemium.configuration.days_free_trial)
133
+ subscription.in_trial?.should be_true
134
+
135
+ subscription.subscription_plan = subscription_plans(:basic)
136
+ subscription.save!
137
+
138
+ subscription.paid_through.should eql(Date.today)
139
+ subscription.in_trial?.should be_false
140
+ end
141
+
142
+ ##
143
+ ## Validations
144
+ ##
145
+
146
+ it "should test missing fields" do
147
+ [:subscription_plan, :subscribable].each do |field|
148
+ subscription = build_subscription(field => nil)
149
+ subscription.save
150
+
151
+ subscription.should be_new_record
152
+ subscription.should have(1).errors_on(field)
153
+ end
154
+ end
155
+
156
+ ##
157
+ ## Receiving payment
158
+ ##
159
+
160
+ it "should receive monthly payment" do
161
+ subscription = subscriptions(:bobs_subscription)
162
+ paid_through = subscription.paid_through
163
+ subscription.credit(subscription_plans(:basic).rate)
164
+ subscription.save!
165
+
166
+ subscription.paid_through.to_s.should eql((paid_through >> 1).to_s) #extended by one month
167
+ subscription.transactions.should_not be_nil
168
+ end
169
+
170
+ its "should receive quarterly payment" do
171
+ subscription = subscriptions(:bobs_subscription)
172
+ paid_through = subscription.paid_through
173
+ subscription.credit(subscription_plans(:basic).rate * 3)
174
+ subscription.save!
175
+ subscription.paid_through.to_s.should eql((paid_through >> 3).to_s) #extended by three months
176
+ end
177
+
178
+ it "should receive partial payment" do
179
+ subscription = subscriptions(:bobs_subscription)
180
+ paid_through = subscription.paid_through
181
+ subscription.credit(subscription_plans(:basic).rate * 0.5)
182
+ subscription.save!
183
+ subscription.paid_through.to_s.should eql((paid_through + 15).to_s) #extended by 15 days
184
+ end
185
+
186
+ it "should send invoice when receiving payment" do
187
+ subscription = subscriptions(:bobs_subscription)
188
+ ActionMailer::Base.deliveries = []
189
+ transaction = create_transaction_for(subscription_plans(:basic).rate, subscription)
190
+ subscription.receive_payment!(transaction)
191
+ ActionMailer::Base.deliveries.size.should eql(1)
192
+ end
193
+
194
+ it "should save transaction message when receiving payment" do
195
+ subscription = subscriptions(:bobs_subscription)
196
+ transaction = create_transaction_for(subscription_plans(:basic).rate, subscription)
197
+ subscription.receive_payment!(transaction)
198
+ transaction.reload.message.should match(/^now paid through/)
199
+ end
200
+
201
+ it "should receive payment though invoice has delivered with error" do
202
+ subscription = subscriptions(:bobs_subscription)
203
+ paid_through = subscription.paid_through
204
+ transaction = create_transaction_for(subscription_plans(:basic).rate, subscription)
205
+ Freemium.configuration.mailer.should_receive(:invoice).and_raise(RuntimeError)
206
+ subscription.receive_payment!(transaction)
207
+ subscription = subscription.reload
208
+ subscription.paid_through.to_s.should eql((paid_through >> 1).to_s) #extended by one month
209
+ end
210
+
211
+ ##
212
+ ## Requiring Credit Cards ...
213
+ ##
214
+
215
+ it "should require credit card for pay plan" do
216
+ subscription = build_subscription(:subscription_plan => subscription_plans(:premium))
217
+ subscription.stub!(:credit_card => nil)
218
+ subscription.should have(1).errors_on(:credit_card)
219
+ end
220
+
221
+ it "should not require credit card for free_plan" do
222
+ subscription = build_subscription
223
+ subscription.should have(0).errors_on(:credit_card)
224
+ end
225
+
226
+ ##
227
+ ## Expiration
228
+ ##
229
+
230
+ it "should expire instance" do
231
+ Freemium.configuration.expired_plan = subscription_plans(:free)
232
+ Freemium.configuration.gateway.should_receive(:cancel).once.and_return(nil)
233
+ ActionMailer::Base.deliveries = []
234
+ subscriptions(:bobs_subscription).expire!
235
+
236
+ ActionMailer::Base.deliveries.size.should eql(1) #notice is sent to user
237
+ subscriptions(:bobs_subscription).reload.subscription_plan.should eql(subscription_plans(:free)) #subscription is downgraded to free
238
+ subscriptions(:bobs_subscription).billing_key.should be_nil #billing key is thrown away
239
+ subscriptions(:bobs_subscription).reload.billing_key.should be_nil #billing key is thrown away
240
+
241
+ assert_changed(subscriptions(:bobs_subscription).subscribable, :expiration, subscription_plans(:basic), subscription_plans(:free))
242
+ end
243
+
244
+ it "should expire instance with expired_plan_key = nil" do
245
+ Freemium.configuration.expired_plan = nil
246
+ Freemium.configuration.gateway.should_receive(:cancel).once.and_return(nil)
247
+ ActionMailer::Base.deliveries = []
248
+ subscriptions(:bobs_subscription).expire!
249
+
250
+ ActionMailer::Base.deliveries.size.should eql(1) #notice is sent to user
251
+ subscriptions(:bobs_subscription).subscription_plan.should eql(subscription_plans(:basic)) #subscription was not changed
252
+ subscriptions(:bobs_subscription).billing_key.should be_nil #billing key is thrown away
253
+ subscriptions(:bobs_subscription).reload.billing_key.should be_nil #billing key is thrown away
254
+ end
255
+
256
+ it "should expire class" do
257
+ Freemium.configuration.expired_plan = subscription_plans(:free)
258
+ Freemium.configuration.expired_plan.should eql(subscription_plans(:free))
259
+ Freemium.configuration.expired_plan.should_not be_nil
260
+ subscriptions(:bobs_subscription).update_attributes(:paid_through => Date.today - 4, :expire_on => Date.today)
261
+ ActionMailer::Base.deliveries = []
262
+
263
+ subscriptions(:bobs_subscription).subscription_plan.should eql(subscription_plans(:basic))
264
+
265
+ Freemium.configuration.expired_plan.should eql(subscription_plans(:free))
266
+ Subscription.find_expired.each(&:expire!)
267
+
268
+ subscriptions(:bobs_subscription).expire!
269
+ subscriptions(:bobs_subscription).reload.subscription_plan.should eql(subscription_plans(:free))
270
+ subscriptions(:bobs_subscription).reload.started_on.should eql(Date.today)
271
+ ActionMailer::Base.deliveries.size.should > 0
272
+
273
+ assert_changed(subscriptions(:bobs_subscription).subscribable, :expiration, subscription_plans(:basic), subscription_plans(:free))
274
+ end
275
+
276
+ it "should expire after grace " do
277
+ subscriptions(:bobs_subscription).expire_on.should be_nil
278
+ subscriptions(:bobs_subscription).paid_through = Date.today - 2
279
+ ActionMailer::Base.deliveries = []
280
+
281
+ subscriptions(:bobs_subscription).expire_after_grace!
282
+
283
+ ActionMailer::Base.deliveries.size.should eql(1)
284
+ subscriptions(:bobs_subscription).reload.expire_on.should eql(Date.today + Freemium.configuration.days_grace)
285
+ end
286
+
287
+ it "should expire after grace with remaining period" do
288
+ subscriptions(:bobs_subscription).paid_through = Date.today + 1
289
+ subscriptions(:bobs_subscription).expire_after_grace!
290
+
291
+ subscriptions(:bobs_subscription).reload.expire_on.should eql(Date.today + 1 + Freemium.configuration.days_grace)
292
+ end
293
+
294
+ it "should test grace and expiration" do
295
+ Freemium.configuration.days_grace.should eql(3)
296
+
297
+ subscription = Subscription.new(:paid_through => Date.today + 5)
298
+ subscription.in_grace?.should be_false
299
+ subscription.expired?.should be_false
300
+
301
+ # a subscription that's pastdue but hasn't been flagged to expire yet.
302
+ # this could happen if a billing process skips, in which case the subscriber
303
+ # should still get a full grace period beginning from the failed attempt at billing.
304
+ # even so, the subscription is "in grace", even if the grace period hasn't officially started.
305
+ subscription = Subscription.new(:paid_through => Date.today - 5)
306
+ subscription.in_grace?.should be_true
307
+ subscription.expired?.should be_false
308
+
309
+ # expires tomorrow
310
+ subscription = Subscription.new(:paid_through => Date.today - 5, :expire_on => Date.today + 1)
311
+ subscription.remaining_days_of_grace.should eql(0)
312
+ subscription.in_grace?.should be_true
313
+ subscription.expired?.should be_false
314
+
315
+ # expires today
316
+ subscription = Subscription.new(:paid_through => Date.today - 5, :expire_on => Date.today)
317
+ subscription.remaining_days_of_grace.should eql(-1)
318
+ subscription.in_grace?.should be_false
319
+ subscription.expired?.should be_true
320
+ end
321
+
322
+ ##
323
+ ## Deleting (possibly from a cascading delete, such as User.find(5).delete)
324
+ ##
325
+
326
+ it "should delete canceles in gateway" do
327
+ Freemium.configuration.gateway.should_receive(:cancel).once.and_return(nil)
328
+ subscriptions(:bobs_subscription).destroy
329
+
330
+ assert_changed(subscriptions(:bobs_subscription).subscribable, :cancellation, subscription_plans(:basic), nil)
331
+ end
332
+
333
+ ##
334
+ ## The Subscription#credit_card= shortcut
335
+ ##
336
+ it "should add credit card" do
337
+ subscription = build_subscription(:subscription_plan => subscription_plans(:premium))
338
+ cc = CreditCard.sample
339
+ response = Freemium::Response.new(true)
340
+ response.billing_key = "alphabravo"
341
+ Freemium.configuration.gateway.should_receive(:store).with(cc).and_return(response)
342
+
343
+ subscription.credit_card = cc
344
+ lambda { subscription.save! }.should_not raise_error
345
+ subscription.billing_key.should eql("alphabravo")
346
+ end
347
+
348
+ it "should update a credit card" do
349
+ subscription = Subscription.where("billing_key IS NOT NULL").first
350
+ cc = CreditCard.sample
351
+ response = Freemium::Response.new(true)
352
+ response.billing_key = "new code"
353
+ Freemium.configuration.gateway.should_receive(:update).with(subscription.billing_key, cc).and_return(response)
354
+
355
+ subscription.credit_card = cc
356
+ lambda { subscription.save! }.should_not raise_error
357
+ subscription.billing_key.should eql("new code") #catches any change to the billing key
358
+ end
359
+
360
+ it "should update an expired credit card" do
361
+ subscription = Subscription.where("billing_key IS NOT NULL").first
362
+ cc = CreditCard.sample
363
+ response = Freemium::Response.new(true)
364
+ Freemium.configuration.gateway.should_receive(:update).with(subscription.billing_key, cc).and_return(response)
365
+
366
+ subscription.expire_on = Time.now
367
+ subscription.save.should be_true
368
+ subscription.reload.expire_on.should_not be_nil
369
+
370
+ subscription.credit_card = cc
371
+ lambda { subscription.save! }.should_not raise_error
372
+ #subscription.expire_on.should be_nil
373
+ #subscription.reload.expire_on.should be_nil
374
+ end
375
+
376
+ it "should fail to add credit card" do
377
+ subscription = build_subscription(:subscription_plan => subscription_plans(:premium))
378
+ cc = CreditCard.sample
379
+ response = Freemium::Response.new(false)
380
+ Freemium.configuration.gateway.should_receive(:store).and_return(response)
381
+
382
+ subscription.credit_card = cc
383
+ lambda { subscription.save! }.should raise_error(Freemium::CreditCardStorageError)
384
+ end
385
+
386
+ end