saucy 0.11.2 → 0.11.3
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +5 -0
- data/README.md +8 -1
- data/app/mailers/billing_mailer.rb +1 -0
- data/app/views/billing_mailer/problem.html.erb +1 -1
- data/app/views/billing_mailer/problem.text.erb +1 -1
- data/lib/saucy/subscription.rb +6 -2
- data/spec/mailers/billing_mailer_spec.rb +5 -0
- data/spec/models/subscription_spec.rb +75 -0
- metadata +10 -52
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -43,10 +43,17 @@ Personalizable emails such as trial expiration notice and activation encourageme
|
|
43
43
|
|
44
44
|
If you have an account with Braintree with multiple merchant accounts you'll want to configure the merchant account for this application:
|
45
45
|
|
46
|
-
Saucy::Configuration.merchant_account_id = 'your merchant account id'
|
46
|
+
Saucy::Configuration.merchant_account_id = 'your merchant account id'
|
47
47
|
|
48
48
|
In addition, there are a number of strings such as application name, support url, automated emails, etc. that are provided and customized with i18n translations. You can customize these in your app, and you can see what they are by looking at config/locales/en.yml in saucy.
|
49
49
|
|
50
|
+
Saucy comes with a basic admin area, which has http auth protection. This defaults to admin/admin:
|
51
|
+
|
52
|
+
Saucy::Configuration.admin_username = 'admin'
|
53
|
+
Saucy::Configuration.admin_password = 'admin'
|
54
|
+
|
55
|
+
You definitely should change that in your config.
|
56
|
+
|
50
57
|
There is a `saucy:daily` rake task which should be run on a regular basis to send receipts and payment processing problem emails.
|
51
58
|
|
52
59
|
Saucy accounts become "activated" once an initial setup step is complete.
|
@@ -12,6 +12,7 @@ class BillingMailer < ActionMailer::Base
|
|
12
12
|
|
13
13
|
def problem(account, transaction)
|
14
14
|
@account = account
|
15
|
+
@transaction = transaction
|
15
16
|
mail(:to => account.customer.email,
|
16
17
|
:subject => I18n.t(:subject,
|
17
18
|
:scope => [:saucy, :mailers, :billing_mailer, :problem],
|
@@ -1,4 +1,4 @@
|
|
1
|
-
<p>Unfortunately there was a problem processing your latest payment for your <%= @account.name -%> account on <%= t("app_name") %>.</p>
|
1
|
+
<p>Unfortunately there was a problem processing your latest payment on <%= @transaction.created_at.to_s(:short_date) %> for your <%= @account.name -%> account on <%= t("app_name") %>.</p>
|
2
2
|
|
3
3
|
<p>Some functionality of your <%= @account.name -%> account on <%= t("app_name") %> is now disabled.</p>
|
4
4
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
Unfortunately there was a problem processing your latest payment for your <%= @account.name -%> account on <%= t("app_name") %>.
|
1
|
+
Unfortunately there was a problem processing your latest payment on <%= @transaction.created_at.to_s(:short_date) %> for your <%= @account.name -%> account on <%= t("app_name") %>.
|
2
2
|
|
3
3
|
Some functionality of your <%= @account.name -%> account on <%= t("app_name") %> is now disabled.
|
4
4
|
|
data/lib/saucy/subscription.rb
CHANGED
@@ -65,6 +65,10 @@ module Saucy
|
|
65
65
|
subscription_status == Braintree::Subscription::Status::PastDue
|
66
66
|
end
|
67
67
|
|
68
|
+
def most_recent_transaction
|
69
|
+
subscription.transactions.sort_by(&:created_at).last
|
70
|
+
end
|
71
|
+
|
68
72
|
private
|
69
73
|
|
70
74
|
def within_limits_for?(new_plan)
|
@@ -228,9 +232,9 @@ module Saucy
|
|
228
232
|
account.next_billing_date = account.subscription.next_billing_date
|
229
233
|
account.save!
|
230
234
|
if account.past_due?
|
231
|
-
BillingMailer.problem(account, account.
|
235
|
+
BillingMailer.problem(account, account.most_recent_transaction).deliver!
|
232
236
|
else
|
233
|
-
BillingMailer.receipt(account, account.
|
237
|
+
BillingMailer.receipt(account, account.most_recent_transaction).deliver!
|
234
238
|
Saucy::Notifications.notify_observers("billed", :account => account)
|
235
239
|
end
|
236
240
|
end
|
@@ -30,6 +30,11 @@ describe "with an account and transaction" do
|
|
30
30
|
it "sets the problem mail reply-to to the support address" do
|
31
31
|
subject.reply_to.should == [Saucy::Configuration.support_email_address]
|
32
32
|
end
|
33
|
+
|
34
|
+
it "includes the attempted billing date" do
|
35
|
+
billing_date = account.subscription.transactions.first.created_at
|
36
|
+
subject.to_s.should include(billing_date.to_s(:short_date))
|
37
|
+
end
|
33
38
|
end
|
34
39
|
|
35
40
|
describe BillingMailer, "expiring trial" do
|
@@ -339,6 +339,81 @@ describe Account, "with a paid subscription" do
|
|
339
339
|
end
|
340
340
|
end
|
341
341
|
|
342
|
+
context "with multiple historical transactions" do
|
343
|
+
let(:subscription) { FakeBraintree.subscriptions[subject.subscription_token] }
|
344
|
+
|
345
|
+
let!(:earlier_transaction_date) { 2.months.ago }
|
346
|
+
let!(:later_transaction_date) { 1.month.ago }
|
347
|
+
|
348
|
+
def build_transaction(attributes)
|
349
|
+
common_transaction_attributes = {
|
350
|
+
:status => Braintree::Transaction::Status::Settled,
|
351
|
+
:subscription_id => subject.subscription_token
|
352
|
+
}
|
353
|
+
|
354
|
+
FakeBraintree.transaction = common_transaction_attributes.merge(attributes)
|
355
|
+
FakeBraintree.generated_transaction
|
356
|
+
end
|
357
|
+
|
358
|
+
before do
|
359
|
+
subscription["next_billing_date"] = 2.months.from_now
|
360
|
+
end
|
361
|
+
|
362
|
+
context "and a past due account" do
|
363
|
+
let(:earlier_transaction) {
|
364
|
+
build_transaction(:created_at => earlier_transaction_date, :status => Braintree::Transaction::Status::Failed)
|
365
|
+
}
|
366
|
+
|
367
|
+
let(:later_transaction) {
|
368
|
+
build_transaction(:created_at => later_transaction_date, :status => Braintree::Transaction::Status::Failed)
|
369
|
+
}
|
370
|
+
|
371
|
+
before do
|
372
|
+
subscription["status"] = Braintree::Subscription::Status::PastDue
|
373
|
+
subscription["transactions"] = [later_transaction, earlier_transaction]
|
374
|
+
end
|
375
|
+
|
376
|
+
it "receives a problem email with the correct transaction" do
|
377
|
+
Timecop.travel(subject.next_billing_date + 1.day) do
|
378
|
+
ActionMailer::Base.deliveries.clear
|
379
|
+
Account.update_subscriptions!
|
380
|
+
|
381
|
+
ActionMailer::Base.deliveries.any? do |email|
|
382
|
+
email.subject =~ /problem/i &&
|
383
|
+
email.to_s.include?(later_transaction_date.strftime("%x"))
|
384
|
+
end.should be
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
context "and an actively billed account" do
|
390
|
+
let(:earlier_transaction) {
|
391
|
+
build_transaction(:created_at => earlier_transaction_date, :status => Braintree::Transaction::Status::Settled)
|
392
|
+
}
|
393
|
+
|
394
|
+
let(:later_transaction) {
|
395
|
+
build_transaction(:created_at => later_transaction_date, :status => Braintree::Transaction::Status::Settled)
|
396
|
+
}
|
397
|
+
|
398
|
+
before do
|
399
|
+
subscription["status"] = Braintree::Subscription::Status::Active
|
400
|
+
subscription["transactions"] = [later_transaction, earlier_transaction]
|
401
|
+
end
|
402
|
+
|
403
|
+
it "receives a receipt email with the correct transaction for active accounts" do
|
404
|
+
Timecop.travel(subject.next_billing_date + 1.day) do
|
405
|
+
ActionMailer::Base.deliveries.clear
|
406
|
+
Account.update_subscriptions!
|
407
|
+
|
408
|
+
ActionMailer::Base.deliveries.any? do |email|
|
409
|
+
email.subject =~ /receipt/i &&
|
410
|
+
email.to_s.include?(later_transaction_date.strftime("%x"))
|
411
|
+
end.should be
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
342
417
|
context "and an active subscription due 2 months from now" do
|
343
418
|
let(:subscription) { FakeBraintree.subscriptions[subject.subscription_token] }
|
344
419
|
|
metadata
CHANGED
@@ -1,13 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: saucy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 55
|
5
4
|
prerelease:
|
6
|
-
|
7
|
-
- 0
|
8
|
-
- 11
|
9
|
-
- 2
|
10
|
-
version: 0.11.2
|
5
|
+
version: 0.11.3
|
11
6
|
platform: ruby
|
12
7
|
authors:
|
13
8
|
- thoughtbot, inc.
|
@@ -20,118 +15,84 @@ autorequire:
|
|
20
15
|
bindir: bin
|
21
16
|
cert_chain: []
|
22
17
|
|
23
|
-
date: 2011-
|
18
|
+
date: 2011-10-03 00:00:00 Z
|
24
19
|
dependencies:
|
25
20
|
- !ruby/object:Gem::Dependency
|
26
21
|
name: clearance
|
27
|
-
prerelease: false
|
28
22
|
requirement: &id001 !ruby/object:Gem::Requirement
|
29
23
|
none: false
|
30
24
|
requirements:
|
31
25
|
- - ~>
|
32
26
|
- !ruby/object:Gem::Version
|
33
|
-
hash: 55
|
34
|
-
segments:
|
35
|
-
- 0
|
36
|
-
- 11
|
37
|
-
- 2
|
38
27
|
version: 0.11.2
|
39
28
|
type: :runtime
|
29
|
+
prerelease: false
|
40
30
|
version_requirements: *id001
|
41
31
|
- !ruby/object:Gem::Dependency
|
42
32
|
name: formtastic
|
43
|
-
prerelease: false
|
44
33
|
requirement: &id002 !ruby/object:Gem::Requirement
|
45
34
|
none: false
|
46
35
|
requirements:
|
47
36
|
- - ">="
|
48
37
|
- !ruby/object:Gem::Version
|
49
|
-
hash: 11
|
50
|
-
segments:
|
51
|
-
- 1
|
52
|
-
- 2
|
53
38
|
version: "1.2"
|
54
39
|
type: :runtime
|
40
|
+
prerelease: false
|
55
41
|
version_requirements: *id002
|
56
42
|
- !ruby/object:Gem::Dependency
|
57
43
|
name: railties
|
58
|
-
prerelease: false
|
59
44
|
requirement: &id003 !ruby/object:Gem::Requirement
|
60
45
|
none: false
|
61
46
|
requirements:
|
62
47
|
- - ">="
|
63
48
|
- !ruby/object:Gem::Version
|
64
|
-
hash: 1
|
65
|
-
segments:
|
66
|
-
- 3
|
67
|
-
- 0
|
68
|
-
- 3
|
69
49
|
version: 3.0.3
|
70
50
|
type: :runtime
|
51
|
+
prerelease: false
|
71
52
|
version_requirements: *id003
|
72
53
|
- !ruby/object:Gem::Dependency
|
73
54
|
name: braintree
|
74
|
-
prerelease: false
|
75
55
|
requirement: &id004 !ruby/object:Gem::Requirement
|
76
56
|
none: false
|
77
57
|
requirements:
|
78
58
|
- - ">="
|
79
59
|
- !ruby/object:Gem::Version
|
80
|
-
hash: 19
|
81
|
-
segments:
|
82
|
-
- 2
|
83
|
-
- 6
|
84
|
-
- 2
|
85
60
|
version: 2.6.2
|
86
61
|
type: :runtime
|
62
|
+
prerelease: false
|
87
63
|
version_requirements: *id004
|
88
64
|
- !ruby/object:Gem::Dependency
|
89
65
|
name: sham_rack
|
90
|
-
prerelease: false
|
91
66
|
requirement: &id005 !ruby/object:Gem::Requirement
|
92
67
|
none: false
|
93
68
|
requirements:
|
94
69
|
- - "="
|
95
70
|
- !ruby/object:Gem::Version
|
96
|
-
hash: 29
|
97
|
-
segments:
|
98
|
-
- 1
|
99
|
-
- 3
|
100
|
-
- 3
|
101
71
|
version: 1.3.3
|
102
72
|
type: :runtime
|
73
|
+
prerelease: false
|
103
74
|
version_requirements: *id005
|
104
75
|
- !ruby/object:Gem::Dependency
|
105
76
|
name: sinatra
|
106
|
-
prerelease: false
|
107
77
|
requirement: &id006 !ruby/object:Gem::Requirement
|
108
78
|
none: false
|
109
79
|
requirements:
|
110
80
|
- - ">="
|
111
81
|
- !ruby/object:Gem::Version
|
112
|
-
hash: 23
|
113
|
-
segments:
|
114
|
-
- 1
|
115
|
-
- 1
|
116
|
-
- 2
|
117
82
|
version: 1.1.2
|
118
83
|
type: :runtime
|
84
|
+
prerelease: false
|
119
85
|
version_requirements: *id006
|
120
86
|
- !ruby/object:Gem::Dependency
|
121
87
|
name: aruba
|
122
|
-
prerelease: false
|
123
88
|
requirement: &id007 !ruby/object:Gem::Requirement
|
124
89
|
none: false
|
125
90
|
requirements:
|
126
91
|
- - "="
|
127
92
|
- !ruby/object:Gem::Version
|
128
|
-
hash: 27
|
129
|
-
segments:
|
130
|
-
- 0
|
131
|
-
- 2
|
132
|
-
- 6
|
133
93
|
version: 0.2.6
|
134
94
|
type: :development
|
95
|
+
prerelease: false
|
135
96
|
version_requirements: *id007
|
136
97
|
description: Clearance-based Rails engine for Software as a Service (Saas) that provides account and project management
|
137
98
|
email: support@thoughtbot.com
|
@@ -326,7 +287,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
326
287
|
requirements:
|
327
288
|
- - ">="
|
328
289
|
- !ruby/object:Gem::Version
|
329
|
-
hash:
|
290
|
+
hash: 1978592027769739368
|
330
291
|
segments:
|
331
292
|
- 0
|
332
293
|
version: "0"
|
@@ -335,9 +296,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
335
296
|
requirements:
|
336
297
|
- - ">="
|
337
298
|
- !ruby/object:Gem::Version
|
338
|
-
hash: 3
|
339
|
-
segments:
|
340
|
-
- 0
|
341
299
|
version: "0"
|
342
300
|
requirements: []
|
343
301
|
|