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
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'acts_as_subscription/error'
|
2
|
+
|
3
|
+
module ActsAsSubscription
|
4
|
+
module Subscription
|
5
|
+
|
6
|
+
# Provides a consistent interface to the various recurring billing services.
|
7
|
+
#
|
8
|
+
# There are three ways to initialize the backend :
|
9
|
+
# 1. Pass the parameters to the _acts_as_subscription_ method inside the model :
|
10
|
+
#
|
11
|
+
# class Subscription < ActiveRecord::Base
|
12
|
+
# acts_as_subscription :backend => :cheddar_getter,
|
13
|
+
# :user => 'my_user',
|
14
|
+
# :password => 'my_pass',
|
15
|
+
# :product_code => 'my_product'
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# 2. Set the params in rails via <tt>config/initializers/acts_as_subscription.rb</tt> :
|
19
|
+
#
|
20
|
+
# ActsAsSubscription::Subscription::Backend.config = {
|
21
|
+
# :backend => :cheddar_getter,
|
22
|
+
# :user => 'my_user',
|
23
|
+
# :password => 'my_pass',
|
24
|
+
# :product_code => 'my_product'
|
25
|
+
# }
|
26
|
+
#
|
27
|
+
# 3. Add a <tt>config/subscription.yml</tt> file to your rails project :
|
28
|
+
#
|
29
|
+
# development:
|
30
|
+
# backend: 'cheddar_getter'
|
31
|
+
# user: 'my_user'
|
32
|
+
# password: 'my_pass'
|
33
|
+
# product_code: 'my_product'
|
34
|
+
|
35
|
+
module Backend
|
36
|
+
|
37
|
+
# Stores the backend configuration data.
|
38
|
+
mattr_accessor :config
|
39
|
+
|
40
|
+
# Stores an instance of the backend for a particular recurring billing service.
|
41
|
+
mattr_accessor :instance
|
42
|
+
|
43
|
+
# Sets the configuration hash for the backend.
|
44
|
+
#
|
45
|
+
# _args_ should be a hash containing the subscription backend account and product information :
|
46
|
+
# * <tt>backend</tt> : Name of the recurring billing service being used. Current choices are
|
47
|
+
# <tt>:chargify</tt>, <tt>:cheddar_getter</tt> and <tt>:recurly</tt>.
|
48
|
+
# * <tt>user</tt> : Username used to log into the billling service API.
|
49
|
+
# * <tt>password</tt> : Password used to log into the billling service API.
|
50
|
+
# * <tt>product_code</tt> : Code of your product on the backend service.
|
51
|
+
#
|
52
|
+
# If the backend configuration has already been set, _args_ will be merged with the current settings.
|
53
|
+
def self.config=(*args)
|
54
|
+
args = args.extract_options!
|
55
|
+
|
56
|
+
if @@config
|
57
|
+
@@config = self.config.merge(args)
|
58
|
+
else
|
59
|
+
@@config = args
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# Validates the configuration options, and creates an instance of the requested <tt>:backend</tt>.
|
64
|
+
#
|
65
|
+
# _args_ should be a hash containing the subscription backend account and product information.
|
66
|
+
def self.initialize(*args)
|
67
|
+
# Don't do anything if the backend has already been loaded.
|
68
|
+
return if self.instance
|
69
|
+
|
70
|
+
self.read_yaml_options
|
71
|
+
self.config = *args
|
72
|
+
self.validate_options!
|
73
|
+
|
74
|
+
# Make sure the backend is a valid class.
|
75
|
+
begin
|
76
|
+
require "acts_as_subscription/backend/#{self.config[:backend]}_client"
|
77
|
+
class_name = "#{self.config[:backend].to_s}_client".camelize
|
78
|
+
klass = ActsAsSubscription::Subscription::Backend.const_get(class_name)
|
79
|
+
self.instance = klass.new(self.config[:user], self.config[:password], self.config[:product_code])
|
80
|
+
rescue LoadError
|
81
|
+
raise ActsAsSubscription::Subscription::Error::BackendError.new("Backend '#{self.config[:backend]}' could not be found")
|
82
|
+
rescue NameError
|
83
|
+
raise ActsAsSubscription::Subscription::Error::BackendError.new("Backend '#{self.config[:backend]}' does not appear to implement class '#{class_name}'")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# Creates a customer on the backend subscription service, using the settings from the given
|
88
|
+
# <tt>subscription</tt> instance.
|
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
|
+
#
|
97
|
+
# Returns true if the operation was successful, otherwise an error message.
|
98
|
+
def self.create_subscription(subscription)
|
99
|
+
self.instance.create_subscription(subscription)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Updates a customer on the backend subscription service, using the settings from the given
|
103
|
+
# <tt>subscription</tt> instance.
|
104
|
+
#
|
105
|
+
# <tt>subscription</tt> should be a subclass of <tt>ActiveRecord::Base</tt> that implements
|
106
|
+
# <tt>acts_as_subscription</tt> :
|
107
|
+
#
|
108
|
+
# class Subscription < ActiveRecord::Base
|
109
|
+
# acts_as_subscription
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# Returns true if the update was successful, otherwise an error message.
|
113
|
+
def self.update_subscription(subscription)
|
114
|
+
self.instance.update_subscription(subscription)
|
115
|
+
end
|
116
|
+
|
117
|
+
# Cancels the customer with the given <tt>customer_code</tt> on the backend subscription service.
|
118
|
+
#
|
119
|
+
# Returns true if the cancellation was successful, otherwise an error message.
|
120
|
+
def self.cancel_subscription!(customer_code)
|
121
|
+
self.instance.cancel_subscription!(customer_code)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns a list of subscription plans registered with the backend subscription service.
|
125
|
+
def self.plans
|
126
|
+
self.instance.plans
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
protected
|
131
|
+
|
132
|
+
# Tries to read configuration options from the subscription.yml configuration file.
|
133
|
+
def self.read_yaml_options
|
134
|
+
# Try to get initial config from a YAML file if possible.
|
135
|
+
if Rails.root
|
136
|
+
config_path = File.join(Rails.root, 'config/subscription.yml')
|
137
|
+
if FileTest.exists?(config_path)
|
138
|
+
config = YAML::load(ERB.new(IO.read(config_path)).result)[Rails.env]
|
139
|
+
# Convert string keys to symbols.
|
140
|
+
config = config.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
141
|
+
self.config = config
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Validates the configuration options, and makes sure all required parameters are present.
|
147
|
+
def self.validate_options!
|
148
|
+
unless self.config[:backend]
|
149
|
+
raise ActsAsSubscription::Subscription::Error::ArgumentError.new(':backend parameter must be provided')
|
150
|
+
end
|
151
|
+
|
152
|
+
unless self.config[:product_code]
|
153
|
+
raise ActsAsSubscription::Subscription::Error::ArgumentError.new(':product_code parameter must be provided')
|
154
|
+
end
|
155
|
+
|
156
|
+
unless self.config[:user]
|
157
|
+
raise ActsAsSubscription::Subscription::Error::ArgumentError.new(':user parameter must be provided')
|
158
|
+
end
|
159
|
+
|
160
|
+
unless self.config[:password]
|
161
|
+
raise ActsAsSubscription::Subscription::Error::ArgumentError.new(':password parameter must be provided')
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module ActsAsSubscription
|
2
|
+
module Subscription
|
3
|
+
module Error # :nodoc:
|
4
|
+
|
5
|
+
# Raised when invalid arguments are passed.
|
6
|
+
class ArgumentError < Exception
|
7
|
+
end
|
8
|
+
|
9
|
+
# Raised if there is a problem with the backend configuration.
|
10
|
+
class BackendError < Exception
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'rails'
|
2
|
+
require 'acts_as_subscription'
|
3
|
+
|
4
|
+
module ActsAsSubscription
|
5
|
+
class Railtie < Rails::Railtie # :nodoc:
|
6
|
+
initializer :'acts_as_subscription.hook' do
|
7
|
+
ActiveSupport.on_load(:active_record) do
|
8
|
+
ActiveRecord::Base.send(:include, ActsAsSubscription::Subscription)
|
9
|
+
ActiveRecord::Base.send(:include, ActsAsSubscription::SubscriptionPlan)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,258 @@
|
|
1
|
+
require 'acts_as_subscription/backend'
|
2
|
+
|
3
|
+
module ActsAsSubscription
|
4
|
+
module Subscription # :nodoc:
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
# Class methods for an ActsAsSubscription::Subscription instance.
|
8
|
+
#
|
9
|
+
# Handles validation of all subscription-related information, such as :
|
10
|
+
#
|
11
|
+
# * customer_code
|
12
|
+
# * email
|
13
|
+
# * first_name
|
14
|
+
# * last_name
|
15
|
+
# * zip_code
|
16
|
+
# * cc_expiration_month
|
17
|
+
# * cc_expiration_year
|
18
|
+
# * cc_verification_value
|
19
|
+
# * cc_number
|
20
|
+
#
|
21
|
+
# These class methods should be included in ActiveRecord::Base to provide
|
22
|
+
# subscription validation support.
|
23
|
+
module ClassMethods
|
24
|
+
|
25
|
+
# Main entry point - any subclass of ActiveRecord::Base can include the
|
26
|
+
# _acts_as_subscription_ method to activate subscription functionality.
|
27
|
+
#
|
28
|
+
# _args_ should be a hash containing the subscription backend account and product information :
|
29
|
+
# * <tt>backend</tt> : Name of the recurring billing service being used. Current choices are
|
30
|
+
# <tt>:chargify</tt>, <tt>:cheddar_getter</tt> and <tt>:recurly</tt>.
|
31
|
+
# * <tt>user</tt> : Username used to log into the billling service API.
|
32
|
+
# * <tt>password</tt> : Password used to log into the billling service API.
|
33
|
+
# * <tt>product_code</tt> : Code of your product on the backend service.
|
34
|
+
#
|
35
|
+
# There are three ways to initialize the backend :
|
36
|
+
# 1. Pass the parameters to the _acts_as_subscription_ method from within a model :
|
37
|
+
#
|
38
|
+
# class Subscription < ActiveRecord::Base
|
39
|
+
# acts_as_subscription :backend => :cheddar_getter,
|
40
|
+
# :user => 'my_user',
|
41
|
+
# :password => 'my_pass',
|
42
|
+
# :product_code => 'my_product'
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# 2. Set the params directly via the Backend module :
|
46
|
+
# ActsAsSubscription::Subscription::Backend.config = {
|
47
|
+
# :backend => :cheddar_getter,
|
48
|
+
# :user => 'my_user',
|
49
|
+
# :password => 'my_pass',
|
50
|
+
# :product_code => 'my_product'
|
51
|
+
# }
|
52
|
+
#
|
53
|
+
# 3. Add a <tt>config/subscription.yml</tt> file to your rails project :
|
54
|
+
#
|
55
|
+
# development:
|
56
|
+
# backend: 'cheddar_getter'
|
57
|
+
# user: 'my_user'
|
58
|
+
# password: 'my_pass'
|
59
|
+
# product_code: 'my_product'
|
60
|
+
def acts_as_subscription(*args)
|
61
|
+
|
62
|
+
ActsAsSubscription::Subscription::Backend.config = *args
|
63
|
+
ActsAsSubscription::Subscription::Backend.initialize
|
64
|
+
|
65
|
+
validates :customer_code,
|
66
|
+
:presence => true
|
67
|
+
|
68
|
+
validates :email,
|
69
|
+
:presence => true
|
70
|
+
|
71
|
+
validates :first_name,
|
72
|
+
:presence => true
|
73
|
+
|
74
|
+
validates :last_name,
|
75
|
+
:presence => true
|
76
|
+
|
77
|
+
validates :plan_code,
|
78
|
+
:presence => true
|
79
|
+
|
80
|
+
validates :zip_code,
|
81
|
+
:presence => true,
|
82
|
+
:if => :require_zip_code?
|
83
|
+
|
84
|
+
validates :cc_expiration_month,
|
85
|
+
:presence => true,
|
86
|
+
:unless => :is_free_plan?
|
87
|
+
|
88
|
+
validates :cc_expiration_year,
|
89
|
+
:presence => true,
|
90
|
+
:unless => :is_free_plan?
|
91
|
+
|
92
|
+
validates :cc_verification_value,
|
93
|
+
:presence => true,
|
94
|
+
:if => :require_verification_value?
|
95
|
+
|
96
|
+
validates :cc_number,
|
97
|
+
:presence => true,
|
98
|
+
:unless => :is_free_plan?
|
99
|
+
|
100
|
+
validates_associated :credit_card,
|
101
|
+
:unless => :is_free_plan?
|
102
|
+
|
103
|
+
before_validation :update_credit_card
|
104
|
+
|
105
|
+
before_validation :assign_customer_code,
|
106
|
+
:on => :create
|
107
|
+
|
108
|
+
validate :validate_credit_card
|
109
|
+
|
110
|
+
attr_accessible :cc_expiration_month,
|
111
|
+
:cc_expiration_year,
|
112
|
+
:cc_number,
|
113
|
+
:cc_verification_value,
|
114
|
+
:customer_code,
|
115
|
+
:email,
|
116
|
+
:first_name,
|
117
|
+
:last_name,
|
118
|
+
:plan_code,
|
119
|
+
:zip_code
|
120
|
+
|
121
|
+
# Removed this for the time being, since we often want to perform this before_save
|
122
|
+
# on a different model (such as User), if the forms are nested. Having before_save
|
123
|
+
# on both models causes quite a few problems. This before_save must be specified
|
124
|
+
# by hand in the implementing model.
|
125
|
+
#before_save :backend_save
|
126
|
+
|
127
|
+
self.send(:include, ActsAsSubscription::Subscription::SubscriptionInstanceMethods)
|
128
|
+
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Instance methods for an ActsAsSubscription::Subscription instance, which use ActiveMerchant
|
133
|
+
# to validate credit card information.
|
134
|
+
#
|
135
|
+
# These instance methods should be included in ActiveRecord::Base to provide
|
136
|
+
# subscription validation support.
|
137
|
+
module SubscriptionInstanceMethods
|
138
|
+
|
139
|
+
# Used as an interface to ActiveMerchant for credit card validation.
|
140
|
+
attr_reader :credit_card
|
141
|
+
|
142
|
+
# Allows entry of a complete credit card number, even though we only store the last 4
|
143
|
+
# digits internally.
|
144
|
+
attr_accessor :cc_number
|
145
|
+
|
146
|
+
# Returns true if the current plan is free, false otherwise.
|
147
|
+
def is_free_plan?
|
148
|
+
# If no plan-code is given, assume this is a paid plan to be on the safe side.
|
149
|
+
return false unless self.plan_code
|
150
|
+
|
151
|
+
return (self.plan_code =~ /free/i) != nil
|
152
|
+
end
|
153
|
+
|
154
|
+
# Instantiates an instance of ActiveMerchant::Billing::CreditCard if one hasn't already
|
155
|
+
# been created.
|
156
|
+
def credit_card
|
157
|
+
self[:credit_card] ||= instantiate_credit_card
|
158
|
+
end
|
159
|
+
|
160
|
+
def backend_save
|
161
|
+
p "+++++++++++++++++++++++ saving +++++++++++++++++++++++"
|
162
|
+
if self.new_record?
|
163
|
+
result = ActsAsSubscription::Subscription::Backend.instance.create_subscription(self)
|
164
|
+
else
|
165
|
+
result = ActsAsSubscription::Subscription::Backend.instance.update_subscription(self)
|
166
|
+
end
|
167
|
+
|
168
|
+
# No way of knowing what fields the errors are for for some backends - just add a top-level error.
|
169
|
+
unless result == true
|
170
|
+
self.errors.add :base, result
|
171
|
+
return false
|
172
|
+
end
|
173
|
+
|
174
|
+
return true
|
175
|
+
end
|
176
|
+
|
177
|
+
# This can be overridden in the implementing model, to turn off zip-code validation.
|
178
|
+
def require_zip_code?
|
179
|
+
return self.is_free_plan? == false
|
180
|
+
end
|
181
|
+
|
182
|
+
# This can be overridden in the implementing model, to turn off verification-value validation.
|
183
|
+
def require_verification_value?
|
184
|
+
return self.is_free_plan? == false
|
185
|
+
end
|
186
|
+
|
187
|
+
protected
|
188
|
+
|
189
|
+
# Assigns a unique UUID for the customer.
|
190
|
+
def assign_customer_code
|
191
|
+
self.customer_code ||= UUIDTools::UUID.random_create.to_s
|
192
|
+
end
|
193
|
+
|
194
|
+
# Updates the ActiveMerchant::Billing::CreditCard instance with the information
|
195
|
+
# provided by the user, prior to validation.
|
196
|
+
def update_credit_card
|
197
|
+
self.credit_card.first_name = self.first_name
|
198
|
+
self.credit_card.last_name = self.last_name
|
199
|
+
self.credit_card.month = self.cc_expiration_month
|
200
|
+
self.credit_card.year = self.cc_expiration_year
|
201
|
+
self.credit_card.verification_value = self.cc_verification_value
|
202
|
+
self.credit_card.number = self.cc_number
|
203
|
+
self.cc_last_digits = self.credit_card.last_digits
|
204
|
+
end
|
205
|
+
|
206
|
+
# Creates an instance of ActiveMerchant::Billing::CreditCard, and adds a couple
|
207
|
+
# of utility methods to make it compatible with ActiveRecord.
|
208
|
+
def instantiate_credit_card(attributes = {})
|
209
|
+
ActiveMerchant::Billing::CreditCard.require_verification_value = require_verification_value?
|
210
|
+
|
211
|
+
credit_card = ActiveMerchant::Billing::CreditCard.new(attributes)
|
212
|
+
|
213
|
+
def credit_card.new_record?
|
214
|
+
true
|
215
|
+
end
|
216
|
+
|
217
|
+
def credit_card.persisted?
|
218
|
+
false
|
219
|
+
end
|
220
|
+
|
221
|
+
credit_card
|
222
|
+
end
|
223
|
+
|
224
|
+
# Returns true if the credit card information provided is valid, or false otherwise.
|
225
|
+
def validate_credit_card
|
226
|
+
return if is_free_plan?
|
227
|
+
|
228
|
+
unless credit_card.valid?
|
229
|
+
credit_card.errors.each do |field, messages|
|
230
|
+
messages.each do |message|
|
231
|
+
# Only add the error if one doesn't already exist - don't need to report
|
232
|
+
# 'credit card is invalid' etc if it is already blank.
|
233
|
+
errors.add("cc_#{field}".to_sym, message) unless errors["cc_#{field}".to_sym].length > 0
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
|
245
|
+
|
246
|
+
# Need to monkey-patch this since validation complains and errors that it is returning nil.
|
247
|
+
module ActionView # :nodoc:
|
248
|
+
module Helpers # :nodoc:
|
249
|
+
module ActiveModelInstanceTag # :nodoc:
|
250
|
+
def error_message
|
251
|
+
# Original :
|
252
|
+
# object.errors[@method_name]
|
253
|
+
# Patched :
|
254
|
+
object.errors[@method_name] || []
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'acts_as_subscription/backend'
|
2
|
+
|
3
|
+
module ActsAsSubscription
|
4
|
+
module SubscriptionPlan # :nodoc:
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
# Class methods for an ActsAsSubscription::SubscriptionPlan instance.
|
8
|
+
#
|
9
|
+
# Handles validation of all subscription_plan-related information, such as :
|
10
|
+
#
|
11
|
+
# * code
|
12
|
+
# * name
|
13
|
+
# * description
|
14
|
+
# * price
|
15
|
+
#
|
16
|
+
# These class methods should be included in ActiveRecord::Base to provide
|
17
|
+
# subscription_plan validation support.
|
18
|
+
module ClassMethods
|
19
|
+
|
20
|
+
# Main entry point - any subclass of ActiveRecord::Base can include the
|
21
|
+
# _acts_as_subscription_plan_ method to activate subscription_plan
|
22
|
+
# functionality.
|
23
|
+
def acts_as_subscription_plan
|
24
|
+
|
25
|
+
validates :code,
|
26
|
+
:presence => true,
|
27
|
+
:uniqueness => {:case_sensitive => false}
|
28
|
+
|
29
|
+
validates :name,
|
30
|
+
:presence => true
|
31
|
+
|
32
|
+
validates :billing_frequency,
|
33
|
+
:presence => true
|
34
|
+
|
35
|
+
validates :recurring_charge,
|
36
|
+
:presence => true,
|
37
|
+
:format => { :with => /^\d+??(?:\.\d{0,2})?$/ },
|
38
|
+
:numericality => {:greater_than_or_equal_to => 0}
|
39
|
+
|
40
|
+
attr_accessible :code,
|
41
|
+
:name,
|
42
|
+
:description,
|
43
|
+
:billing_frequency,
|
44
|
+
:recurring_charge,
|
45
|
+
:active
|
46
|
+
|
47
|
+
# Synchronizes the local SubscriptionPlan database with the remote backend. This method
|
48
|
+
# will try to update existing plans with new data if possible (based on the :code of
|
49
|
+
# the plan).
|
50
|
+
#
|
51
|
+
# It will *NOT* however delete any plans locally that have been removed from
|
52
|
+
# the reote system. It is the responsibilty of the developer to remove local copies
|
53
|
+
# themselves if a plan is removed from the remote subscription backend. For safety
|
54
|
+
# it seems more sensible not to automate the removal of plans.
|
55
|
+
def self.sync!
|
56
|
+
plans = ActsAsSubscription::Subscription::Backend.plans
|
57
|
+
plans.each do |plan|
|
58
|
+
# Try to find an existing plan with this code.
|
59
|
+
existing = self.find_by_code(plan[:code])
|
60
|
+
|
61
|
+
if existing
|
62
|
+
existing.update_attributes(plan)
|
63
|
+
else
|
64
|
+
existing = self.new(plan)
|
65
|
+
end
|
66
|
+
|
67
|
+
existing.save
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns an array of the current plans, suitable for using with a select
|
72
|
+
# or radio input within a form. This is provided more as an example, and
|
73
|
+
# should be over-ridden in your implementing class if you want to do
|
74
|
+
# currency formatting etc.
|
75
|
+
def form_options
|
76
|
+
plans = self.find_all_by_active(true)
|
77
|
+
result = []
|
78
|
+
plans.each do |plan|
|
79
|
+
label = plan.name
|
80
|
+
if plan.recurring_charge > 0
|
81
|
+
label = "#{plan.name} ($#{plan.recurring_charge})"
|
82
|
+
end
|
83
|
+
result << [label, plan.code]
|
84
|
+
end
|
85
|
+
|
86
|
+
return result
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'acts_as_subscription/railtie.rb')
|
2
|
+
|
3
|
+
module ActsAsSubscription # :nodoc:
|
4
|
+
autoload :Subscription, File.join(File.dirname(__FILE__), 'acts_as_subscription/subscription')
|
5
|
+
autoload :SubscriptionPlan, File.join(File.dirname(__FILE__), 'acts_as_subscription/subscription_plan')
|
6
|
+
end
|
7
|
+
|