acts_as_subscription 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|