acts_as_subscription 0.0.1
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.
- data/.autotest +2 -0
- data/.rspec +3 -0
- data/.rvmrc +1 -0
- data/Gemfile +22 -0
- data/LICENSE.txt +19 -0
- data/README.rdoc +21 -0
- data/Rakefile +54 -0
- data/VERSION +1 -0
- data/acts_as_subscription.gemspec +104 -0
- data/autotest/discover.rb +1 -0
- data/lib/acts_as_subscription/backend/chargify_client.rb +173 -0
- data/lib/acts_as_subscription/backend/cheddar_getter_client.rb +151 -0
- data/lib/acts_as_subscription/backend/dummy_client.rb +49 -0
- data/lib/acts_as_subscription/backend/recurly_client.rb +58 -0
- data/lib/acts_as_subscription/backend.rb +168 -0
- data/lib/acts_as_subscription/error.rb +15 -0
- data/lib/acts_as_subscription/railtie.rb +13 -0
- data/lib/acts_as_subscription/subscription.rb +258 -0
- data/lib/acts_as_subscription/subscription_plan.rb +93 -0
- data/lib/acts_as_subscription.rb +7 -0
- data/spec/backend/backend/chargify_client_spec.rb +204 -0
- data/spec/backend/backend/cheddar_getter_client_spec.rb +191 -0
- data/spec/backend/backend_spec.rb +82 -0
- data/spec/models/subscription_plan_spec.rb +118 -0
- data/spec/models/subscription_spec.rb +207 -0
- data/spec/spec_helper.rb +110 -0
- data/spec/support/backend.yml.example +14 -0
- data/spec/support/database.yml +17 -0
- data/spec/support/models.rb +21 -0
- data/spec/support/schema.rb +30 -0
- metadata +244 -0
data/.autotest
ADDED
data/.rspec
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm 1.8.7@acts_as_subscription
|
data/Gemfile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
source :gemcutter
|
2
|
+
|
3
|
+
gem 'activemerchant', '~> 1.9.3'
|
4
|
+
gem 'chargify_api_ares', '~> 0.3.7'
|
5
|
+
gem 'hpricot', '>= 0.6'
|
6
|
+
gem 'mousetrap', '~> 0.5.0'
|
7
|
+
gem 'rails', '~> 3.0.3'
|
8
|
+
gem 'uuidtools'
|
9
|
+
|
10
|
+
group :development do
|
11
|
+
gem 'bundler', '~> 1.0.0'
|
12
|
+
gem 'jeweler', '~> 1.5.2'
|
13
|
+
gem 'rcov', '>= 0.9.9'
|
14
|
+
end
|
15
|
+
|
16
|
+
group :test do
|
17
|
+
gem 'autotest', '4.4.6'
|
18
|
+
gem 'autotest-fsevent', '0.2.4'
|
19
|
+
gem 'autotest-growl', '0.2.9'
|
20
|
+
gem 'rspec', '2.0.0.rc'
|
21
|
+
gem 'sqlite3-ruby', :require => 'sqlite3'
|
22
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2010 Dave Perrett, http://recursive-design.com/
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
= acts_as_subscription
|
2
|
+
|
3
|
+
Description goes here.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 Dave Perrett. See LICENSE for details.
|
18
|
+
|
19
|
+
== Notes
|
20
|
+
|
21
|
+
* Make sure 'Require Credit Card at signup?' is *unchecked* for free plans on Chargify.
|
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "acts_as_subscription"
|
8
|
+
gem.summary = "ActsAsSubscription is a plugin for rails that provides recurring subscription capabilities to a model."
|
9
|
+
gem.description = "With ActsAsSubscription, you can hook your model into several subscription services, such as CheddarGetter."
|
10
|
+
gem.email = "mail@recursive-design.com"
|
11
|
+
gem.homepage = "http://recursive-design.com/"
|
12
|
+
gem.authors = ["Dave Perrett"]
|
13
|
+
gem.rubyforge_project = 'nowarning'
|
14
|
+
end
|
15
|
+
Jeweler::GemcutterTasks.new
|
16
|
+
rescue LoadError
|
17
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'rspec/core/rake_task'
|
21
|
+
RSpec::Core::RakeTask.new('spec')
|
22
|
+
task :default => :spec
|
23
|
+
|
24
|
+
def version
|
25
|
+
File.exist?('VERSION') ? File.read('VERSION') : ""
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'rake/rdoctask'
|
29
|
+
Rake::RDocTask.new do |rdoc|
|
30
|
+
rdoc.rdoc_dir = 'rdoc'
|
31
|
+
rdoc.title = "acts_as_subscription #{version}"
|
32
|
+
rdoc.rdoc_files.include('README*')
|
33
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
34
|
+
end
|
35
|
+
|
36
|
+
namespace :gem do
|
37
|
+
|
38
|
+
desc 'Clean build products'
|
39
|
+
task :clean do
|
40
|
+
rm_f 'pkg'
|
41
|
+
end
|
42
|
+
|
43
|
+
desc 'Build the gem'
|
44
|
+
task :build => :clean do
|
45
|
+
system 'rake gemspec'
|
46
|
+
system 'rake build'
|
47
|
+
end
|
48
|
+
|
49
|
+
desc 'Push the gem to rubygems'
|
50
|
+
task :publish => [:clean, :build] do
|
51
|
+
system "gem push pkg/acts_as_subscription-#{version}.gem"
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{acts_as_subscription}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Dave Perrett"]
|
12
|
+
s.date = %q{2011-02-01}
|
13
|
+
s.description = %q{With ActsAsSubscription, you can hook your model into several subscription services, such as CheddarGetter.}
|
14
|
+
s.email = %q{mail@recursive-design.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE.txt",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".autotest",
|
21
|
+
".rspec",
|
22
|
+
".rvmrc",
|
23
|
+
"Gemfile",
|
24
|
+
"LICENSE.txt",
|
25
|
+
"README.rdoc",
|
26
|
+
"Rakefile",
|
27
|
+
"VERSION",
|
28
|
+
"acts_as_subscription.gemspec",
|
29
|
+
"autotest/discover.rb",
|
30
|
+
"lib/acts_as_subscription.rb",
|
31
|
+
"lib/acts_as_subscription/backend.rb",
|
32
|
+
"lib/acts_as_subscription/backend/chargify_client.rb",
|
33
|
+
"lib/acts_as_subscription/backend/cheddar_getter_client.rb",
|
34
|
+
"lib/acts_as_subscription/backend/dummy_client.rb",
|
35
|
+
"lib/acts_as_subscription/backend/recurly_client.rb",
|
36
|
+
"lib/acts_as_subscription/error.rb",
|
37
|
+
"lib/acts_as_subscription/railtie.rb",
|
38
|
+
"lib/acts_as_subscription/subscription.rb",
|
39
|
+
"lib/acts_as_subscription/subscription_plan.rb",
|
40
|
+
"spec/backend/backend/chargify_client_spec.rb",
|
41
|
+
"spec/backend/backend/cheddar_getter_client_spec.rb",
|
42
|
+
"spec/backend/backend_spec.rb",
|
43
|
+
"spec/models/subscription_plan_spec.rb",
|
44
|
+
"spec/models/subscription_spec.rb",
|
45
|
+
"spec/spec_helper.rb",
|
46
|
+
"spec/support/backend.yml.example",
|
47
|
+
"spec/support/database.yml",
|
48
|
+
"spec/support/models.rb",
|
49
|
+
"spec/support/schema.rb"
|
50
|
+
]
|
51
|
+
s.homepage = %q{http://recursive-design.com/}
|
52
|
+
s.require_paths = ["lib"]
|
53
|
+
s.rubyforge_project = %q{nowarning}
|
54
|
+
s.rubygems_version = %q{1.3.7}
|
55
|
+
s.summary = %q{ActsAsSubscription is a plugin for rails that provides recurring subscription capabilities to a model.}
|
56
|
+
s.test_files = [
|
57
|
+
"spec/backend/backend/chargify_client_spec.rb",
|
58
|
+
"spec/backend/backend/cheddar_getter_client_spec.rb",
|
59
|
+
"spec/backend/backend_spec.rb",
|
60
|
+
"spec/models/subscription_plan_spec.rb",
|
61
|
+
"spec/models/subscription_spec.rb",
|
62
|
+
"spec/spec_helper.rb",
|
63
|
+
"spec/support/models.rb",
|
64
|
+
"spec/support/schema.rb"
|
65
|
+
]
|
66
|
+
|
67
|
+
if s.respond_to? :specification_version then
|
68
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
69
|
+
s.specification_version = 3
|
70
|
+
|
71
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
72
|
+
s.add_runtime_dependency(%q<activemerchant>, ["~> 1.9.3"])
|
73
|
+
s.add_runtime_dependency(%q<chargify_api_ares>, ["~> 0.3.7"])
|
74
|
+
s.add_runtime_dependency(%q<hpricot>, [">= 0.6"])
|
75
|
+
s.add_runtime_dependency(%q<mousetrap>, ["~> 0.5.0"])
|
76
|
+
s.add_runtime_dependency(%q<rails>, ["~> 3.0.3"])
|
77
|
+
s.add_runtime_dependency(%q<uuidtools>, [">= 0"])
|
78
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
79
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.5.2"])
|
80
|
+
s.add_development_dependency(%q<rcov>, [">= 0.9.9"])
|
81
|
+
else
|
82
|
+
s.add_dependency(%q<activemerchant>, ["~> 1.9.3"])
|
83
|
+
s.add_dependency(%q<chargify_api_ares>, ["~> 0.3.7"])
|
84
|
+
s.add_dependency(%q<hpricot>, [">= 0.6"])
|
85
|
+
s.add_dependency(%q<mousetrap>, ["~> 0.5.0"])
|
86
|
+
s.add_dependency(%q<rails>, ["~> 3.0.3"])
|
87
|
+
s.add_dependency(%q<uuidtools>, [">= 0"])
|
88
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
89
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
90
|
+
s.add_dependency(%q<rcov>, [">= 0.9.9"])
|
91
|
+
end
|
92
|
+
else
|
93
|
+
s.add_dependency(%q<activemerchant>, ["~> 1.9.3"])
|
94
|
+
s.add_dependency(%q<chargify_api_ares>, ["~> 0.3.7"])
|
95
|
+
s.add_dependency(%q<hpricot>, [">= 0.6"])
|
96
|
+
s.add_dependency(%q<mousetrap>, ["~> 0.5.0"])
|
97
|
+
s.add_dependency(%q<rails>, ["~> 3.0.3"])
|
98
|
+
s.add_dependency(%q<uuidtools>, [">= 0"])
|
99
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
100
|
+
s.add_dependency(%q<jeweler>, ["~> 1.5.2"])
|
101
|
+
s.add_dependency(%q<rcov>, [">= 0.9.9"])
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
Autotest.add_discovery { "rspec2" }
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'chargify_api_ares'
|
2
|
+
|
3
|
+
module ActsAsSubscription::Subscription::Backend
|
4
|
+
|
5
|
+
# A backend for the Chargify recurring billing service.
|
6
|
+
#
|
7
|
+
# http://chargify.com
|
8
|
+
class ChargifyClient
|
9
|
+
|
10
|
+
# Initializes the backend, and authenticates with the subscription service.
|
11
|
+
#
|
12
|
+
# Required parameters are :
|
13
|
+
# * <tt>user</tt> : The login used to connect to the remote API.
|
14
|
+
# * <tt>password</tt> : The password used to connect to the remote API.
|
15
|
+
# * <tt>product_code</tt> : The code for the product being sold.
|
16
|
+
def initialize(user, password, product_code)
|
17
|
+
Chargify.configure do |c|
|
18
|
+
c.subdomain = user
|
19
|
+
c.api_key = password
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Creates a customer on the backend subscription service, using the settings from the given
|
24
|
+
# <tt>subscription</tt> instance.
|
25
|
+
#
|
26
|
+
# <tt>subscription</tt> should be a subclass of <tt>ActiveRecord::Base</tt> that implements
|
27
|
+
# <tt>acts_as_subscription</tt> :
|
28
|
+
#
|
29
|
+
# class Subscription < ActiveRecord::Base
|
30
|
+
# acts_as_subscription
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# Returns true if the operation was successful, otherwise an error message.
|
34
|
+
def create_subscription(subscription)
|
35
|
+
customer_attributes = self.get_customer_attributes(subscription)
|
36
|
+
customer = Chargify::Customer.create(customer_attributes)
|
37
|
+
if customer.errors.length == 0
|
38
|
+
subscription_attributes = self.get_subscription_attributes(subscription)
|
39
|
+
subscription = Chargify::Subscription.create(subscription_attributes)
|
40
|
+
if subscription.errors.length > 0
|
41
|
+
return subscription.errors[:base][0]
|
42
|
+
else
|
43
|
+
return true
|
44
|
+
end
|
45
|
+
else
|
46
|
+
return customer.errors[:base][0]
|
47
|
+
end
|
48
|
+
return 'An unknown error occurred.'
|
49
|
+
rescue Exception => error
|
50
|
+
return error.message
|
51
|
+
end
|
52
|
+
|
53
|
+
# Updates a customer on the backend subscription service, using the settings from the given
|
54
|
+
# <tt>subscription</tt> instance.
|
55
|
+
#
|
56
|
+
# <tt>subscription</tt> should be a subclass of <tt>ActiveRecord::Base</tt> that implements
|
57
|
+
# <tt>acts_as_subscription</tt> :
|
58
|
+
#
|
59
|
+
# class Subscription < ActiveRecord::Base
|
60
|
+
# acts_as_subscription
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# Returns true if the update was successful, otherwise an error message.
|
64
|
+
def update_subscription(subscription)
|
65
|
+
customer = Chargify::Customer.find_by_reference(subscription.customer_code)
|
66
|
+
customer.first_name = subscription.first_name,
|
67
|
+
customer.last_name = subscription.last_name,
|
68
|
+
customer.email = subscription.email,
|
69
|
+
customer.reference = subscription.customer_code
|
70
|
+
if customer.save
|
71
|
+
sub = Chargify::Subscription.find_by_customer_reference(subscription.customer_code)
|
72
|
+
sub.product_handle = subscription.plan_code
|
73
|
+
subscription_attributes = self.get_subscription_attributes(subscription)
|
74
|
+
# If the user is down-grading to a free plan, credit_card_attributes may not be available.
|
75
|
+
if subscription_attributes[:credit_card_attributes]
|
76
|
+
sub.credit_card_attributes = subscription_attributes[:credit_card_attributes]
|
77
|
+
end
|
78
|
+
if sub.save
|
79
|
+
return true
|
80
|
+
else
|
81
|
+
return sub.errors[:base][0]
|
82
|
+
end
|
83
|
+
else
|
84
|
+
return customer.errors[:base][0]
|
85
|
+
end
|
86
|
+
rescue Exception => error
|
87
|
+
return error.message
|
88
|
+
end
|
89
|
+
|
90
|
+
# Cancels the customer with the given <tt>customer_code</tt> on the backend subscription service.
|
91
|
+
#
|
92
|
+
# Returns true if the cancellation was successful, or false otherwise.
|
93
|
+
def cancel_subscription!(customer_code)
|
94
|
+
sub = Chargify::Subscription.find_by_customer_reference(customer_code)
|
95
|
+
response = sub.cancel
|
96
|
+
doc = Hpricot::XML(response.body)
|
97
|
+
return (response.code == "200" and (doc/:subscription/:state).text == 'canceled')
|
98
|
+
rescue Exception => error
|
99
|
+
return false
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns a list of subscription plans registered with the backend subscription service.
|
103
|
+
def plans
|
104
|
+
result = []
|
105
|
+
Chargify::Product.all.each do |product|
|
106
|
+
result << {
|
107
|
+
:name => product.name,
|
108
|
+
:code => product.handle,
|
109
|
+
:active => true, # Doesn't seem to be an option to make inactive.
|
110
|
+
:description => product.description,
|
111
|
+
:billing_frequency => product.interval_unit,
|
112
|
+
:recurring_charge => product.price_in_cents.to_f
|
113
|
+
}
|
114
|
+
end
|
115
|
+
|
116
|
+
return result
|
117
|
+
end
|
118
|
+
|
119
|
+
protected
|
120
|
+
|
121
|
+
# Converts a Subscription instance into a hash of parameters used to update a customer on
|
122
|
+
# the remote subscription service.
|
123
|
+
#
|
124
|
+
# <tt>subscription</tt> should be a subclass of <tt>ActiveRecord::Base</tt> that implements
|
125
|
+
# <tt>acts_as_subscription</tt> :
|
126
|
+
#
|
127
|
+
# class Subscription < ActiveRecord::Base
|
128
|
+
# acts_as_subscription
|
129
|
+
# end
|
130
|
+
def get_customer_attributes(subscription)
|
131
|
+
attributes = {
|
132
|
+
:first_name => subscription.first_name,
|
133
|
+
:last_name => subscription.last_name,
|
134
|
+
:email => subscription.email,
|
135
|
+
:reference => subscription.customer_code
|
136
|
+
}
|
137
|
+
|
138
|
+
return attributes
|
139
|
+
end
|
140
|
+
|
141
|
+
# Converts a Subscription instance into a hash of parameters used to update a subscription on
|
142
|
+
# the remote subscription service.
|
143
|
+
#
|
144
|
+
# <tt>subscription</tt> should be a subclass of <tt>ActiveRecord::Base</tt> that implements
|
145
|
+
# <tt>acts_as_subscription</tt> :
|
146
|
+
#
|
147
|
+
# class Subscription < ActiveRecord::Base
|
148
|
+
# acts_as_subscription
|
149
|
+
# end
|
150
|
+
def get_subscription_attributes(subscription)
|
151
|
+
attributes = {
|
152
|
+
:customer_reference => subscription.customer_code,
|
153
|
+
:product_handle => subscription.plan_code.downcase,
|
154
|
+
}
|
155
|
+
|
156
|
+
unless subscription.is_free_plan?
|
157
|
+
attributes[:credit_card_attributes] = {
|
158
|
+
:first_name => subscription.first_name,
|
159
|
+
:last_name => subscription.last_name,
|
160
|
+
:full_number => subscription.cc_number,
|
161
|
+
:expiration_month => subscription.cc_expiration_month,
|
162
|
+
:expiration_year => subscription.cc_expiration_year,
|
163
|
+
:billing_zip_code => subscription.zip_code,
|
164
|
+
}
|
165
|
+
end
|
166
|
+
|
167
|
+
return attributes
|
168
|
+
end
|
169
|
+
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
@@ -0,0 +1,151 @@
|
|
1
|
+
module ActsAsSubscription::Subscription::Backend
|
2
|
+
|
3
|
+
# A backend for the CheddarGetter recurring billing service.
|
4
|
+
#
|
5
|
+
# https://cheddargetter.com
|
6
|
+
class CheddarGetterClient
|
7
|
+
|
8
|
+
# Initializes the backend, and authenticates with the subscription service.
|
9
|
+
#
|
10
|
+
# Required parameters are :
|
11
|
+
# * <tt>user</tt> : The login used to connect to the remote API.
|
12
|
+
# * <tt>password</tt> : The password used to connect to the remote API.
|
13
|
+
# * <tt>product_code</tt> : The code for the product being sold.
|
14
|
+
def initialize(user, password, product_code)
|
15
|
+
Mousetrap.product_code = product_code
|
16
|
+
Mousetrap.authenticate(user, password)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Creates a customer on the backend subscription service, using the settings from the given
|
20
|
+
# <tt>subscription</tt> instance.
|
21
|
+
#
|
22
|
+
# <tt>subscription</tt> should be a subclass of <tt>ActiveRecord::Base</tt> that implements
|
23
|
+
# <tt>acts_as_subscription</tt> :
|
24
|
+
#
|
25
|
+
# class Subscription < ActiveRecord::Base
|
26
|
+
# acts_as_subscription
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# Returns true if the operation was successful, otherwise an error message.
|
30
|
+
def create_subscription(subscription)
|
31
|
+
customer_attributes = self.get_customer_attributes(subscription)
|
32
|
+
Mousetrap::Customer.create customer_attributes
|
33
|
+
return true
|
34
|
+
rescue Exception => error
|
35
|
+
return error.message
|
36
|
+
end
|
37
|
+
|
38
|
+
# Updates a customer on the backend subscription service, using the settings from the given
|
39
|
+
# <tt>subscription</tt> instance.
|
40
|
+
#
|
41
|
+
# <tt>subscription</tt> should be a subclass of <tt>ActiveRecord::Base</tt> that implements
|
42
|
+
# <tt>acts_as_subscription</tt> :
|
43
|
+
#
|
44
|
+
# class Subscription < ActiveRecord::Base
|
45
|
+
# acts_as_subscription
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# Returns true if the update was successful, otherwise an error message.
|
49
|
+
def update_subscription(subscription)
|
50
|
+
customer_attributes = self.get_customer_attributes(subscription)
|
51
|
+
customer = Mousetrap::Customer.new(customer_attributes)
|
52
|
+
customer.save
|
53
|
+
return true
|
54
|
+
rescue Exception => error
|
55
|
+
return error.message
|
56
|
+
end
|
57
|
+
|
58
|
+
# Cancels the customer with the given <tt>customer_code</tt> on the backend subscription service.
|
59
|
+
#
|
60
|
+
# Returns true if the cancellation was successful, or false otherwise.
|
61
|
+
def cancel_subscription!(customer_code)
|
62
|
+
customer = Mousetrap::Customer[customer_code]
|
63
|
+
result = customer.try(:cancel)
|
64
|
+
return (result != nil)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns a list of subscription plans registered with the backend subscription service.
|
68
|
+
def plans
|
69
|
+
result = []
|
70
|
+
Mousetrap::Plan.all.each do |plan|
|
71
|
+
result << {
|
72
|
+
:name => plan.name,
|
73
|
+
:code => plan.code,
|
74
|
+
:active => (plan.active == "1"),
|
75
|
+
:description => plan.description,
|
76
|
+
# CheddarGetter returns 'monthly' etc instead of 'month' - remove trailing 'ly'.
|
77
|
+
:billing_frequency => plan.billing_frequency.sub(/ly$/i, ''),
|
78
|
+
:recurring_charge => plan.recurring_charge.to_f
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
return result
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
86
|
+
|
87
|
+
# Converts a Subscription instance into a hash of parameters used to update the remote
|
88
|
+
# subscription service.
|
89
|
+
#
|
90
|
+
# <tt>subscription</tt> should be a subclass of <tt>ActiveRecord::Base</tt> that implements
|
91
|
+
# <tt>acts_as_subscription</tt> :
|
92
|
+
#
|
93
|
+
# class Subscription < ActiveRecord::Base
|
94
|
+
# acts_as_subscription
|
95
|
+
# end
|
96
|
+
def get_customer_attributes(subscription)
|
97
|
+
customer_attributes = {
|
98
|
+
:email => subscription.email,
|
99
|
+
:code => subscription.customer_code,
|
100
|
+
:first_name => subscription.first_name,
|
101
|
+
:last_name => subscription.last_name,
|
102
|
+
:subscription_attributes => {
|
103
|
+
:plan_code => subscription.plan_code,
|
104
|
+
:billing_first_name => subscription.first_name,
|
105
|
+
:billing_last_name => subscription.last_name,
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
customer_attributes[:subscription_attributes][:credit_card_number] = subscription.cc_number if subscription.cc_number
|
110
|
+
customer_attributes[:subscription_attributes][:credit_card_expiration_month] = subscription.cc_expiration_month if subscription.cc_expiration_month
|
111
|
+
customer_attributes[:subscription_attributes][:credit_card_expiration_year] = subscription.cc_expiration_year if subscription.cc_expiration_year
|
112
|
+
customer_attributes[:subscription_attributes][:billing_zip_code] = subscription.zip_code if subscription.zip_code
|
113
|
+
|
114
|
+
# The mousetrap gem doesn't actually support :credit_card_verification_value for some reason - try to send in a patch sometime.
|
115
|
+
#customer_attributes[:subscription_attributes][:credit_card_verification_value] = subscription.cc_verification_code if subscription.cc_verification_code
|
116
|
+
|
117
|
+
return customer_attributes
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# Need to patch Mousetrap::Plan to allow us some extra information about
|
126
|
+
# subscription plans. By default it only returns :name and :code.
|
127
|
+
module Mousetrap # :nodoc:
|
128
|
+
class Plan < Resource # :nodoc:
|
129
|
+
attr_accessor \
|
130
|
+
:active,
|
131
|
+
:code,
|
132
|
+
:name,
|
133
|
+
:description,
|
134
|
+
:billing_frequency,
|
135
|
+
:recurring_charge
|
136
|
+
|
137
|
+
protected
|
138
|
+
|
139
|
+
def self.attributes_from_api(attributes)
|
140
|
+
{
|
141
|
+
:active => attributes['isActive'],
|
142
|
+
:code => attributes['code'],
|
143
|
+
:name => attributes['name'],
|
144
|
+
:description => attributes['description'],
|
145
|
+
:billing_frequency => attributes['billingFrequency'],
|
146
|
+
:recurring_charge => attributes['recurringChargeAmount']
|
147
|
+
}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module ActsAsSubscription::Subscription::Backend
|
2
|
+
|
3
|
+
class DummyClient # :nodoc:
|
4
|
+
|
5
|
+
def initialize(user, password, product_code)
|
6
|
+
true
|
7
|
+
end
|
8
|
+
|
9
|
+
def create_subscription(options)
|
10
|
+
true
|
11
|
+
end
|
12
|
+
|
13
|
+
def update_subscription(options)
|
14
|
+
true
|
15
|
+
end
|
16
|
+
|
17
|
+
def cancel_subscription!(customer_code)
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
def plans
|
22
|
+
[{
|
23
|
+
:code =>'FREE',
|
24
|
+
:name =>'Free',
|
25
|
+
:active =>true,
|
26
|
+
:description =>'The price is right!',
|
27
|
+
:billing_frequency =>'month',
|
28
|
+
:recurring_charge =>0.0
|
29
|
+
}, {
|
30
|
+
:code =>'PERSONAL',
|
31
|
+
:name =>'Personal',
|
32
|
+
:active =>true,
|
33
|
+
:description =>'Single-user. can access calendars, reminders and to-dos.',
|
34
|
+
:billing_frequency =>'month',
|
35
|
+
:recurring_charge =>9.95
|
36
|
+
}, {
|
37
|
+
:code =>'PRO',
|
38
|
+
:name =>'Pro',
|
39
|
+
:active =>true,
|
40
|
+
:description =>'All the bells and whistles.',
|
41
|
+
:billing_frequency =>'month',
|
42
|
+
:recurring_charge =>19.95
|
43
|
+
}]
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module ActsAsSubscription::Subscription::Backend
|
2
|
+
|
3
|
+
# A backend for the Recurly recurring billing service.
|
4
|
+
#
|
5
|
+
# http://recurly.com
|
6
|
+
class RecurlyClient
|
7
|
+
|
8
|
+
# Initializes the backend, and authenticates with the subscription service.
|
9
|
+
#
|
10
|
+
# Required parameters are :
|
11
|
+
# * <tt>user</tt> : The login used to connect to the remote API.
|
12
|
+
# * <tt>password</tt> : The password used to connect to the remote API.
|
13
|
+
# * <tt>product_code</tt> : The code for the product being sold.
|
14
|
+
def initialize(user, password, product_code)
|
15
|
+
false
|
16
|
+
end
|
17
|
+
|
18
|
+
# Creates a customer on the backend subscription service, using the settings from the given
|
19
|
+
# <tt>subscription</tt> instance.
|
20
|
+
#
|
21
|
+
# <tt>subscription</tt> should be a subclass of <tt>ActiveRecord::Base</tt> that implements
|
22
|
+
# <tt>acts_as_subscription</tt> :
|
23
|
+
#
|
24
|
+
# class Subscription < ActiveRecord::Base
|
25
|
+
# acts_as_subscription
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# Returns true if the operation was successful, otherwise an error message.
|
29
|
+
def create_subscription(subscription)
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
# Updates a customer on the backend subscription service, using the settings from the given
|
34
|
+
# <tt>subscription</tt> instance.
|
35
|
+
#
|
36
|
+
# <tt>subscription</tt> should be a subclass of <tt>ActiveRecord::Base</tt> that implements
|
37
|
+
# <tt>acts_as_subscription</tt> :
|
38
|
+
#
|
39
|
+
# class Subscription < ActiveRecord::Base
|
40
|
+
# acts_as_subscription
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# Returns true if the update was successful, otherwise an error message.
|
44
|
+
def update_subscription(subscription)
|
45
|
+
false
|
46
|
+
end
|
47
|
+
|
48
|
+
# Cancels the customer with the given <tt>customer_code</tt> on the backend subscription service.
|
49
|
+
#
|
50
|
+
# Returns true if the cancellation was successful, or false otherwise.
|
51
|
+
def cancel_subscription!(customer_code)
|
52
|
+
false
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|