servicemerchant 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/MIT-LICENSE.txt +20 -0
  2. data/README.txt +231 -0
  3. data/Rakefile +122 -0
  4. data/demo.rb +69 -0
  5. data/recurring_billing/lib/am_extensions.rb +1 -0
  6. data/recurring_billing/lib/am_extensions/paypal_extension.rb +170 -0
  7. data/recurring_billing/lib/dependencies.rb +14 -0
  8. data/recurring_billing/lib/gateways.rb +5 -0
  9. data/recurring_billing/lib/gateways/authorize_net.rb +103 -0
  10. data/recurring_billing/lib/gateways/paypal.rb +124 -0
  11. data/recurring_billing/lib/recurring_billing.rb +130 -0
  12. data/recurring_billing/lib/recurring_billing.rdoc +87 -0
  13. data/recurring_billing/lib/utils.rb +81 -0
  14. data/recurring_billing/test/fixtures.yml +33 -0
  15. data/recurring_billing/test/remote/authorize_net_test.rb +36 -0
  16. data/recurring_billing/test/remote/paypal_test.rb +46 -0
  17. data/recurring_billing/test/remote/recurring_billing_test.rb +41 -0
  18. data/recurring_billing/test/test_helper.rb +153 -0
  19. data/recurring_billing/test/unit/authorize_net_gateway_class_test.rb +42 -0
  20. data/recurring_billing/test/unit/paypal_gateway_class_test.rb +23 -0
  21. data/recurring_billing/test/unit/recurring_billing_gateway_class_test.rb +35 -0
  22. data/recurring_billing/test/unit/utils_test.rb +17 -0
  23. data/subscription_management/Rakefile +29 -0
  24. data/subscription_management/lib/models/subscription.rb +9 -0
  25. data/subscription_management/lib/models/subscription_profile.rb +4 -0
  26. data/subscription_management/lib/subscription_management.rb +326 -0
  27. data/subscription_management/samples/backpack.yml +101 -0
  28. data/subscription_management/samples/basecamp.yml +71 -0
  29. data/subscription_management/samples/brainkeeper.yml +90 -0
  30. data/subscription_management/samples/campfire.yml +74 -0
  31. data/subscription_management/samples/clickandpledge.yml +24 -0
  32. data/subscription_management/samples/demo.rb +19 -0
  33. data/subscription_management/samples/elm.yml +174 -0
  34. data/subscription_management/samples/freshbooks.yml +78 -0
  35. data/subscription_management/samples/highrise.yml +100 -0
  36. data/subscription_management/samples/presets.yml +10 -0
  37. data/subscription_management/samples/tariff.outline.yml +0 -0
  38. data/subscription_management/samples/taxes.yml +21 -0
  39. data/subscription_management/subscription_management.rb +7 -0
  40. data/subscription_management/tasks/schema.rb +50 -0
  41. data/subscription_management/test/connection.rb +10 -0
  42. data/subscription_management/test/remote/subscription_management_test.rb +112 -0
  43. data/subscription_management/test/test_helper.rb +84 -0
  44. data/subscription_management/test/unit/subscription_management_test.rb +40 -0
  45. data/tracker/README +12 -0
  46. data/tracker/Rakefile +40 -0
  47. data/tracker/db/migrations/empty-directory +0 -0
  48. data/tracker/demo.rb +12 -0
  49. data/tracker/lib/models/recurring_payment_profile.rb +134 -0
  50. data/tracker/lib/models/transaction.rb +19 -0
  51. data/tracker/lib/recurring_billing_extension.rb +103 -0
  52. data/tracker/lib/recurring_billing_extension.rdoc +34 -0
  53. data/tracker/tasks/schema.rb +66 -0
  54. data/tracker/test/connection.rb +10 -0
  55. data/tracker/test/recurring_payment_profile.rb +35 -0
  56. data/tracker/test/remote/authorize_net_test.rb +68 -0
  57. data/tracker/test/remote/paypal_test.rb +115 -0
  58. data/tracker/test/test_helper.rb +87 -0
  59. data/tracker/test/unit/recurring_payment_profile_test.rb +62 -0
  60. data/tracker/tracker.rb +10 -0
  61. data/vendor/money-1.7.1/MIT-LICENSE +20 -0
  62. data/vendor/money-1.7.1/README +75 -0
  63. data/vendor/money-1.7.1/lib/bank/no_exchange_bank.rb +9 -0
  64. data/vendor/money-1.7.1/lib/bank/variable_exchange_bank.rb +30 -0
  65. data/vendor/money-1.7.1/lib/money.rb +29 -0
  66. data/vendor/money-1.7.1/lib/money/core_extensions.rb +26 -0
  67. data/vendor/money-1.7.1/lib/money/money.rb +209 -0
  68. data/vendor/money-1.7.1/lib/support/cattr_accessor.rb +57 -0
  69. metadata +153 -0
@@ -0,0 +1,42 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class AuthorizeNetGatewayTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ credentials = fixtures(:authorize_net)
7
+ assert @gw = RecurringBilling::AuthorizeNetGateway.new(credentials.update({:is_test => true}))
8
+ assert_equal @gw.name, 'Authorize.net'
9
+ @card = credit_card()
10
+ end
11
+
12
+ def test_correct_update?
13
+ #def correct_update?(billing_id, amount, card, payment_options, recurring_options)
14
+ subscr_id = 'SOMERANDOMID'
15
+ assert_raise StandardError do; @gw.correct_update?(subscr_id, nil, @card, nil, {:start_date => Date.today + 1}); end
16
+ assert_nothing_thrown do;@gw.correct_update?(subscr_id, 1, @card, nil, nil);end
17
+ assert_nothing_thrown do;@gw.correct_update?(subscr_id, 100, @card, nil, {});end
18
+
19
+ end
20
+
21
+ def test_transform_dates
22
+ def d(string);return Date.parse(string);end
23
+ def h(a,b,c,d);return {:interval=>{:length=>a, :unit=>b}, :duration=>{:start_date=>c, :occurrences=>d}};end
24
+
25
+ #syntax is: transform_dates(start_date, interval, occurrences, end_date)
26
+ assert_raise ArgumentError do; @gw.transform_dates(d('2008/01/01'), '5m', nil, nil); end
27
+ assert_raise ArgumentError do; @gw.transform_dates(d('2008/01/01'), '5m', 5, d('2009/01/01')); end
28
+ assert_raise ArgumentError do; @gw.transform_dates(d('2008/01/01'), 'm', 1, nil); end
29
+ assert_raise ArgumentError do; @gw.transform_dates(d('2008/01/01'), '0.5m', 1, nil); end
30
+ assert_raise ArgumentError do; @gw.transform_dates(d('2008/01/01'), '1 year', 1, nil); end
31
+ assert_raise ArgumentError do; @gw.transform_dates(d('2008/01/01'), '8', 1, nil); end
32
+ assert_raise ArgumentError do; @gw.transform_dates(d('2008/01/01'), '5m', -1, nil); end
33
+ assert_raise ArgumentError do; @gw.transform_dates(d('2008/01/01'), '5m', nil, d('2007/12/31')); end
34
+
35
+ assert_equal @gw.transform_dates(d('2008/01/01'), '5m', 7, nil), h(5,:months,d('2008/01/01'),7)
36
+ assert_equal @gw.transform_dates(d('2008/01/01'), '3 d', nil, d('2008/01/19')), h(3,:days,d('2008/01/01'),7)
37
+ assert_equal @gw.transform_dates(d('2008/01/01'), '3d', nil, d('2008/01/21')), h(3,:days,d('2008/01/01'),7)
38
+ assert_equal @gw.transform_dates(d('2008/01/01'), '8w', 13, nil), h(56,:days,d('2008/01/01'),13)
39
+ assert_equal @gw.transform_dates(d('2008/01/01'), '1y', nil, d('2010/12/01')), h(12,:months,d('2008/01/01'),3)
40
+ end
41
+
42
+ end
@@ -0,0 +1,23 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class PaypalGatewayTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ cred = fixtures(:paypal)
7
+ assert @gw = RecurringBilling::PaypalGateway.new(cred.update({:is_test=>true}))
8
+ assert_equal @gw.name, 'PayPal Website Payments Pro (US)'
9
+ @card = credit_card()
10
+ end
11
+
12
+ def test_true
13
+ end
14
+
15
+ # def test_correct_update?
16
+ # #def correct_update?(billing_id, amount, card, payment_options, recurring_options)
17
+ # subscr_id = 'SOMERANDOMID'
18
+ # assert_raise StandardError do; @gw.correct_update?(subscr_id, nil, @card, nil, {:start_date => Date.today + 1}); end
19
+ # assert_nothing_thrown do;@gw.correct_update?(subscr_id, 1, @card, nil, nil);end
20
+ # assert_nothing_thrown do;@gw.correct_update?(subscr_id, 100, @card, nil, {});end
21
+ # end
22
+
23
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class RecurringBillingGatewayTest < Test::Unit::TestCase
4
+
5
+ def perform_generic_test(gateway)
6
+ credentials = fixtures(gateway)
7
+ assert gw = RecurringBilling::RecurringBillingGateway.get_instance(credentials)
8
+ end
9
+
10
+ # Checking separate_create_update_params_from_options method
11
+ def test_separate_create_update_params_from_options
12
+ cc = credit_card()
13
+ payment_options = {
14
+ :subscription_name => 'Test Subscription 1337',
15
+ :order => {:invoice_number => '000000'}
16
+ }
17
+ recurring_options = {
18
+ :start_date => Date.today + 1,
19
+ :end_date => Date.today + 290,
20
+ :interval => '1m'
21
+ }
22
+ all_options = {}.update({:card => cc}).update(payment_options).update(recurring_options)
23
+ separate_options = RecurringBilling::RecurringBillingGateway.separate_create_update_params_from_options(all_options)
24
+ assert_equal separate_options, {:amount => nil, :card => cc, :payment_options => payment_options, :recurring_options => recurring_options}
25
+ end
26
+
27
+ def test_paypal
28
+ perform_generic_test(:paypal)
29
+ end
30
+
31
+ def test_authorize_net
32
+ perform_generic_test(:authorize_net)
33
+ end
34
+
35
+ end
@@ -0,0 +1,17 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class UtilsTest < Test::Unit::TestCase
4
+ def test_months_between
5
+ def d(string);return Date.parse(string);end
6
+
7
+ assert months_between(d("2008/10/01"), d("2008/10/01")), 0
8
+ assert months_between(d("2008/10/02"), d("2008/10/01")), 0
9
+ assert months_between(d("2008/11/01"), d("2008/10/02")), 0
10
+ assert months_between(d("2008/01/01"), d("2007/12/31")), 0
11
+ assert months_between(d("2008/11/01"), d("2008/10/01")), 1
12
+ assert months_between(d("2008/02/01"), d("2007/05/01")), 9
13
+ assert months_between(d("2008/02/14"), d("2007/05/01")), 9
14
+ assert months_between(d("2008/02/02"), d("2007/05/13")), 8
15
+ end
16
+
17
+ end
@@ -0,0 +1,29 @@
1
+ require 'rake'
2
+ require File.dirname(__FILE__) + '/tasks/schema.rb'
3
+
4
+
5
+ require 'rake/testtask'
6
+ namespace :test do
7
+
8
+ Rake::TestTask.new(:remote_sm) do |t|
9
+ t.pattern = 'test/remote/**/*_test.rb'
10
+ t.ruby_opts << '-rubygems'
11
+ t.verbose = true
12
+ end
13
+
14
+ desc "Run all remote tests"
15
+ task :remote => [:remote_sm]
16
+
17
+ Rake::TestTask.new(:unit_sm) do |t|
18
+ t.pattern = 'test/unit/**/*_test.rb'
19
+ t.ruby_opts << '-rubygems'
20
+ t.verbose = true
21
+ end
22
+
23
+ desc "Run all unit tests"
24
+ task :unit => [:unit_sm]
25
+
26
+ desc "Run all tests"
27
+ task :all => [:unit_sm, :remote_sm]
28
+
29
+ end
@@ -0,0 +1,9 @@
1
+ class Subscription < ActiveRecord::Base
2
+ has_many :subscription_profiles
3
+ has_many :recurring_payment_profiles, :through => :subscription_profiles
4
+
5
+ # Returns billing amount (sum of net amount + taxes)
6
+ def billing_amount
7
+ self.net_amount + self.taxes_amount
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ class SubscriptionProfile < ActiveRecord::Base
2
+ belongs_to :subscription
3
+ belongs_to :recurring_payment_profile
4
+ end
@@ -0,0 +1,326 @@
1
+ require 'yaml'
2
+ require File.dirname(__FILE__) + '/../../recurring_billing/lib/recurring_billing'
3
+ require File.dirname(__FILE__) + '/../../tracker/tracker'
4
+ require 'rubygems'
5
+ require 'active_support/inflector'
6
+
7
+ #module SubscriptionManagement
8
+
9
+ # This class allows handling of user subscriptions for given merchant service. See {PROJECT_ROOT}/demo.rb for usage example.
10
+ class SubscriptionManagement
11
+ attr_accessor :all_tariff_plans, :all_taxes, :tariff_plans_namespace, :all_gateways, :gateway
12
+
13
+ # Constructs new SubscriptionManagement object
14
+ #
15
+ # Optional +options+ hash may include:
16
+ #
17
+ # definition of tariff plan config by config file name
18
+ # - :tariff_plans_config - string defining name of YML file to read tariff plan configuration from
19
+ # definition of tariff plan config by specifying tariff plans and its namespace directly (only if :tariff_plans_config is not present)
20
+ # - :all_tariff_plans - hash containing tariff plans
21
+ # - :tariff_plans_namespace - string defining namespace for given tariff plans
22
+ # definition of tax config by config file name
23
+ # - :taxes_config - string defining name of YML file to read taxes configuration from
24
+ # definition of tax config by specifying tariff plans and its namespace directly (only if :taxes_config is not present)
25
+ # - :all_taxes - hash containing taxes
26
+ # definition of gateways config by config file name
27
+ # - :gateways_config - string defining name of YML file to read gateways configuration from
28
+ # definition of tax config by specifying tariff plans and its namespace directly (only if :gateways_config is not present)
29
+ # - :all_gateways - hash containing gateways
30
+ # definition of default gateway
31
+ # - :gateway - Symbol or string containing gateway name
32
+ def initialize(options={})
33
+ if options[:tariff_plans_config]
34
+ @all_tariff_plans = SubscriptionManagement.get_all_tariff_plans(options[:tariff_plans_config])
35
+ @tariff_plans_namespace = File.basename(options[:tariff_plans_config], '.yml').downcase.camelize
36
+ else
37
+ if options[:all_tariff_plans]
38
+ @all_tariff_plans = options[:all_tariff_plans]
39
+ end
40
+ if options[:tariff_plans_namespace]
41
+ @tariff_plan_namespace = options[:tariff_plans_namespace]
42
+ end
43
+ end
44
+
45
+ if options[:taxes_config]
46
+ @all_taxes = SubscriptionManagement.get_all_taxes(options[:taxes_config])
47
+ elsif options[:taxes]
48
+ @all_taxes = options[:all_taxes]
49
+ end
50
+
51
+ if options[:gateways_config]
52
+ @all_gateways = SubscriptionManagement.get_gateway_settings(options[:gateways_config])
53
+ elsif options[:all_gateways]
54
+ @all_gateways = options[:all_gateways]
55
+ end
56
+
57
+ if [String, Symbol].include? options[:gateway].class
58
+ @gateway = @all_gateways[options[:gateway].to_sym]
59
+ elsif options[:gateway].class == Hash
60
+ @gateway = options[:gateway]
61
+ end
62
+
63
+ return self
64
+ end
65
+
66
+ # Returns the most applicable identifier of tax area settings available for given country and state (instance)
67
+ def get_applicable_taxes_id(country, state)
68
+ return SubscriptionManagement.get_taxes_id(@all_taxes, country, state)
69
+ end
70
+
71
+ # Subscribe account for tariff plan, specified in "all_tariff_plans" property of an instance
72
+ #
73
+ # options hash should/may include following keys:
74
+ # :account_id
75
+ # :account_country
76
+ # :account_state
77
+ # :tariff_plan
78
+ # :start_date
79
+ # :quantity
80
+ # :end_date
81
+ def subscribe(options)
82
+ subscription = Subscription.new
83
+ subscription.account_id=options[:account_id]
84
+ subscription.tariff_plan_id = options[:tariff_plan]
85
+ subscription.quantity = options[:quantity]
86
+
87
+
88
+ # Set tariff-related fields
89
+ tariff = @all_tariff_plans[subscription.tariff_plan_id]
90
+ raise ArgumentError, 'Invalid tariff given: %s' % subscription.tariff_plan_id if tariff.nil?
91
+ subscription.currency = tariff["currency"]
92
+ subscription.periodicity = tariff["payment_term"]["periodicity"]
93
+
94
+ subscription.starts_on = options[:start_date] + tariff["payment_term"]["trial_days"]
95
+ subscription.ends_on = options[:end_date]
96
+
97
+ # Set tax- and payment-related fields
98
+ subscription.taxes_id = get_applicable_taxes_id(options[:account_country], options[:account_state])
99
+ total_tax = @all_taxes[subscription.taxes_id]["taxes"].inject(0){|sum,item| sum + item["rate"]} # sum all taxes
100
+ subscription.net_amount = subscription.quantity * tariff["price"]
101
+ subscription.taxes_amount = subscription.net_amount * total_tax
102
+
103
+ subscription.status = 'pending'
104
+ subscription.save
105
+
106
+ return subscription.id
107
+ end
108
+
109
+ # Proceed subscription payment through specified payment gateway
110
+ def pay_for_subscription(subscription_id, card, payment_options)
111
+ subscription = Subscription.find_by_id(subscription_id)
112
+
113
+ gw = RecurringBilling::RecurringBillingGateway.get_instance(@gateway)
114
+ tariff = @all_tariff_plans[subscription.tariff_plan_id]
115
+ recurring_options = {
116
+ :start_date => subscription.starts_on,
117
+ :trial_days => tariff['payment_term']['trial_days'],
118
+ :end_date => subscription.ends_on,
119
+ :interval => tariff['payment_term']['periodicity']
120
+ }
121
+
122
+ if payment_options[:subscription_name].nil?
123
+ payment_options[:subscription_name] = @tariff_plans_namespace+': '+tariff['service']['name']
124
+ end
125
+
126
+ payment_options[:taxes_amount_included] = Money.new(subscription.taxes_amount,subscription.currency)
127
+
128
+ gateway_id = gw.create(Money.new(subscription.billing_amount, subscription.currency), card, payment_options, recurring_options)
129
+ if !gateway_id.nil?
130
+ sp = SubscriptionProfile.new
131
+ sp.subscription_id = subscription.id
132
+ sp.recurring_payment_profile_id = RecurringPaymentProfile.find_by_gateway_reference(gateway_id).id
133
+ sp.save
134
+ subscription.status = 'ok'
135
+ subscription.save
136
+ else
137
+ raise StandardError, 'Recurring payment creation error: ' + gw.last_response.message
138
+ end
139
+ end
140
+
141
+ # Update subscription through specified payment gateway
142
+ def update_subscription(subscription_id, options)
143
+ profile = get_active_profile(subscription_id)
144
+ gw = RecurringBilling::RecurringBillingGateway.get_instance(@gateway)
145
+ unless new_gateway_reference = gw.update_or_recreate(profile.gateway_reference, options)
146
+ raise StandardError, 'Cannot update subscription through gateway: ' + gw.last_response.message
147
+ end
148
+
149
+ unless new_gateway_reference == profile.gateway_reference
150
+ sp = SubscriptionProfile.new
151
+ sp.subscription_id = subscription_id
152
+ sp.recurring_payment_profile_id = RecurringPaymentProfile.find_by_gateway_reference(new_gateway_reference).id
153
+ sp.save
154
+ end
155
+ end
156
+
157
+ # Gets active profile for given subscription_id
158
+ def get_active_profile(subscription_id)
159
+ raise StandardError, "Subscription with ID #{subscription_id} not found" if (subscription = Subscription.find_by_id(subscription_id)).nil?
160
+ raise StandardError, "Subscription with ID #{subscription_id} is inactive and cannot thus have active profiles" unless subscription.status == 'ok'
161
+
162
+ if (profile = subscription.recurring_payment_profiles.find(:first, :conditions => [ "status != 'deleted'"])).nil?
163
+ raise StandardError, "Cannot find any active profiles for subscription: #{subscription_id}"
164
+ end
165
+
166
+ return profile
167
+ end
168
+
169
+ # Checks if given subscription could be updated (re-create would be needed otherwize)
170
+ def update_possible?(subscription_id, options)
171
+ begin
172
+ gw = RecurringBilling::RecurringBillingGateway.get_instance(@gateway)
173
+ return gw.can_update?(get_active_profile(subscription_id).gateway_reference, options) ? true : false
174
+ rescue
175
+ return false
176
+ end
177
+ end
178
+
179
+ #Cancel subscription (specified by SUBSCRIPTION_ID)
180
+ def unsubscribe(subscription_id)
181
+ raise StandardError, "Subscription with ID #{subscription_id} not found" if (subscription = Subscription.find_by_id(subscription_id)).nil?
182
+ if subscription.status == 'ok' && !(profile = subscription.recurring_payment_profiles.find(:first, :conditions => [ "status != 'deleted'"])).nil?
183
+ gw = RecurringBilling::RecurringBillingGateway.get_instance(@gateway)
184
+ raise StandardError, 'Cannot cancel subscription through gateway: ' + gw.last_response.message unless gw.delete(profile.gateway_reference)
185
+ end
186
+ subscription.status = 'canceled'
187
+ subscription.save
188
+ end
189
+
190
+ #Get available features provided by given subscription (specified by SUBSCRIPTION_ID)
191
+ def get_features(subscription_id)
192
+ return {} if (subscription = Subscription.find_by_id(subscription_id)).nil? && (subscription.status != 'ok')
193
+ @all_tariff_plans[subscription.tariff_plan_id]['service']['features']
194
+ end
195
+
196
+ def get_invoice_data(invoice_id) #:nodoc:
197
+ transaction = Transaction.find invoice_id
198
+ profile = transaction.recurring_payment_profile
199
+
200
+ if transaction.money != profile.money
201
+ raise NotImplementedError, 'Transaction payment cannot differ from recurring payment'
202
+ end
203
+
204
+ subscription = SubscriptionProfile.find(:first, :conditions => ['recurring_payment_profile_id = ?', profile]).subscription
205
+ tariff = @all_tariff_plans[subscription.tariff_plan_id]
206
+ service_name = '%s %s(%s)' % [
207
+ tariff['service']['name'],
208
+ (subscription.quantity == 1 ? '' : 'x%s '% subscription.quantity),
209
+ SubscriptionManagement.format_periodicity(tariff['payment_term']['periodicity']).gsub('each','per')
210
+ ]
211
+ {
212
+ :billing_account => subscription.account_id,
213
+ :service_name => service_name,
214
+ :net_amount => profile.net_money_formatted,
215
+ :taxes_amount => profile.taxes_money_formatted,
216
+ :total_amount => profile.money_formatted,
217
+ :taxes_comment => @all_taxes[subscription.taxes_id]["name"],
218
+
219
+ :date => transaction.created_at,
220
+ :number => transaction.id,
221
+ :transaction_gateway => profile.gateway,
222
+ :transaction_id => transaction.gateway_reference,
223
+ :transaction_amount => transaction.money_formatted
224
+ }
225
+ end
226
+
227
+ class << self
228
+
229
+ # Returns whole contents of given tariff config file (by CONFIG_NAME filename)
230
+ def read_tariff_config(config_name)
231
+ return YAML.load([File.read('%s/presets.yml' % File.dirname(config_name)), File.read(config_name)].join("\n"))
232
+ end
233
+
234
+ # Retrieves tariff settings from given file (by CONFIG_NAME filename)
235
+ def get_all_tariff_plans(config_name)
236
+ return read_tariff_config(config_name)[File.basename(config_name, '.yml')]['tariff_plans']
237
+ end
238
+
239
+ # Reads tax config from given file (by CONFIG_NAME filename)
240
+ def get_all_taxes(config_name)
241
+ return YAML.load(File.read(config_name))['taxes']
242
+ end
243
+
244
+ # Reads gateway settings from given file (by CONFIG_NAME filename)
245
+ def get_gateway_settings(config_name)
246
+ def symbolize_keys(hash)
247
+ return unless hash.is_a?(Hash)
248
+
249
+ hash.symbolize_keys!
250
+ hash.each{|k,v| symbolize_keys(v)}
251
+ end
252
+
253
+ symbolize_keys(YAML.load(File.read(config_name)))
254
+ end
255
+
256
+ # Returns the most applicable identifier of tax area settings available for given COUNTRY and STATE (class)
257
+ #
258
+ # Example:
259
+ # taxes = {}
260
+ # taxes["ca"] = {"country"=>"CA", "taxes"=>[{"tax"=>{"name"=>"Tax1"}, "rate"=>0.05}], "state"=>"*"}
261
+ # taxes[""us_ca""] = {"country"=>"US", "taxes"=>[{"tax"=>{"name"=>"Sample tax"}, "rate"=>0.2}], "state"=>"CA"}
262
+ # get_taxes_id(taxes, 'RU', 'MSK') # => StandardError
263
+ # get_taxes_id(taxes, 'CA', 'ON') # => "ca"
264
+ # get_taxes_id(taxes, 'US', 'CA') # => "us_ca"
265
+ # get_taxes_id(taxes, 'US', 'FL') # => StandardError
266
+ def get_taxes_id(taxes, country, state)
267
+ return ("%s_%s" % [country, state]).downcase unless (state.empty? || taxes[("%s_%s" % [country, state]).downcase].nil?)
268
+ return country.downcase unless (country.empty? || taxes[country.downcase].nil?)
269
+ raise StandardError, 'Tax is unknown for given country and state - %s, %s' % [country, state]
270
+ end
271
+
272
+ # Returns formatted string for FEATURE hash
273
+ #
274
+ # Example 1:
275
+ # feature['feature']['name'] = 'Message Boards'
276
+ # feature['quantity'] == nil => 'Message Boards' (when we just want to show that feature is available)
277
+ # feature['quantity'] == 1, feature['feature']['unit'] == nil => 'Message Boards: 1' (when unit is obvious from feature name)
278
+ #
279
+ # Example 2:
280
+ # feature['feature']['name'] = 'Disk Quota'
281
+ # feature['quantity'] == 1, feature['feature']['unit'] == 'Gigabyte' => 'Disk Quota: 1 Gigabyte' (when unit is not obvious it has to be specified)
282
+ # feature['quantity'] == 2, feature['feature']['unit'] == 'Gigabyte' => 'Disk Quota: 2 Gigabytes'
283
+ # feature['quantity'] == 0, feature['feature']['unit'] == 'Gigabyte' => 'Disk Quota: Unlimited'
284
+ def format_feature(feature)
285
+ return "#{feature['feature']['name']}" unless feature['quantity']
286
+ quantity = feature['quantity']
287
+ if quantity == 0
288
+ return "#{feature['feature']['name']}: Unlimited"
289
+ else
290
+ return "#{feature['feature']['name']}: #{quantity}" unless feature['feature']['unit']
291
+ unit_correct_form = (quantity > 1) ? ActiveSupport::Inflector.pluralize(feature['feature']['unit']) : feature['feature']['unit']
292
+ return "#{feature['feature']['name']}: #{quantity} #{unit_correct_form}"
293
+ end
294
+ end
295
+
296
+ # Returns human-readable string for standard interval string (PERIODICITY)
297
+ #
298
+ # Sample use:
299
+ # "1w" => 'each week'
300
+ # "0.5 m" => 'twice a month'
301
+ # "10 d" => 'each 10 days'
302
+ # "3y" => 'each 3 years'
303
+ # "0.25 w" => ArgumentError
304
+ # "2 x" => ArgumentError
305
+ def format_periodicity(periodicity)
306
+ if (periodicity =~ /^(\d+|0\.5)\s*(d|w|m|y)$/i)
307
+ i_length, i_unit = $1 == '0.5' ? 0.5 : $1.to_i, $2.downcase.to_sym
308
+ else
309
+ raise ArgumentError, "Invalid periodicity given: #{periodicity}"
310
+ end
311
+
312
+ text_unit = case i_unit
313
+ when :d then 'day'
314
+ when :w then 'week'
315
+ when :m then 'month'
316
+ when :y then 'year'
317
+ end
318
+
319
+ return "twice a #{text_unit}" if i_length == 0.5
320
+ return "each #{text_unit}" if i_length == 1
321
+ return "each #{i_length} #{ActiveSupport::Inflector.pluralize(text_unit)}"
322
+ end
323
+
324
+ end
325
+ end
326
+ #end