servicemerchant 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE.txt +20 -0
- data/README.txt +231 -0
- data/Rakefile +122 -0
- data/demo.rb +69 -0
- data/recurring_billing/lib/am_extensions.rb +1 -0
- data/recurring_billing/lib/am_extensions/paypal_extension.rb +170 -0
- data/recurring_billing/lib/dependencies.rb +14 -0
- data/recurring_billing/lib/gateways.rb +5 -0
- data/recurring_billing/lib/gateways/authorize_net.rb +103 -0
- data/recurring_billing/lib/gateways/paypal.rb +124 -0
- data/recurring_billing/lib/recurring_billing.rb +130 -0
- data/recurring_billing/lib/recurring_billing.rdoc +87 -0
- data/recurring_billing/lib/utils.rb +81 -0
- data/recurring_billing/test/fixtures.yml +33 -0
- data/recurring_billing/test/remote/authorize_net_test.rb +36 -0
- data/recurring_billing/test/remote/paypal_test.rb +46 -0
- data/recurring_billing/test/remote/recurring_billing_test.rb +41 -0
- data/recurring_billing/test/test_helper.rb +153 -0
- data/recurring_billing/test/unit/authorize_net_gateway_class_test.rb +42 -0
- data/recurring_billing/test/unit/paypal_gateway_class_test.rb +23 -0
- data/recurring_billing/test/unit/recurring_billing_gateway_class_test.rb +35 -0
- data/recurring_billing/test/unit/utils_test.rb +17 -0
- data/subscription_management/Rakefile +29 -0
- data/subscription_management/lib/models/subscription.rb +9 -0
- data/subscription_management/lib/models/subscription_profile.rb +4 -0
- data/subscription_management/lib/subscription_management.rb +326 -0
- data/subscription_management/samples/backpack.yml +101 -0
- data/subscription_management/samples/basecamp.yml +71 -0
- data/subscription_management/samples/brainkeeper.yml +90 -0
- data/subscription_management/samples/campfire.yml +74 -0
- data/subscription_management/samples/clickandpledge.yml +24 -0
- data/subscription_management/samples/demo.rb +19 -0
- data/subscription_management/samples/elm.yml +174 -0
- data/subscription_management/samples/freshbooks.yml +78 -0
- data/subscription_management/samples/highrise.yml +100 -0
- data/subscription_management/samples/presets.yml +10 -0
- data/subscription_management/samples/tariff.outline.yml +0 -0
- data/subscription_management/samples/taxes.yml +21 -0
- data/subscription_management/subscription_management.rb +7 -0
- data/subscription_management/tasks/schema.rb +50 -0
- data/subscription_management/test/connection.rb +10 -0
- data/subscription_management/test/remote/subscription_management_test.rb +112 -0
- data/subscription_management/test/test_helper.rb +84 -0
- data/subscription_management/test/unit/subscription_management_test.rb +40 -0
- data/tracker/README +12 -0
- data/tracker/Rakefile +40 -0
- data/tracker/db/migrations/empty-directory +0 -0
- data/tracker/demo.rb +12 -0
- data/tracker/lib/models/recurring_payment_profile.rb +134 -0
- data/tracker/lib/models/transaction.rb +19 -0
- data/tracker/lib/recurring_billing_extension.rb +103 -0
- data/tracker/lib/recurring_billing_extension.rdoc +34 -0
- data/tracker/tasks/schema.rb +66 -0
- data/tracker/test/connection.rb +10 -0
- data/tracker/test/recurring_payment_profile.rb +35 -0
- data/tracker/test/remote/authorize_net_test.rb +68 -0
- data/tracker/test/remote/paypal_test.rb +115 -0
- data/tracker/test/test_helper.rb +87 -0
- data/tracker/test/unit/recurring_payment_profile_test.rb +62 -0
- data/tracker/tracker.rb +10 -0
- data/vendor/money-1.7.1/MIT-LICENSE +20 -0
- data/vendor/money-1.7.1/README +75 -0
- data/vendor/money-1.7.1/lib/bank/no_exchange_bank.rb +9 -0
- data/vendor/money-1.7.1/lib/bank/variable_exchange_bank.rb +30 -0
- data/vendor/money-1.7.1/lib/money.rb +29 -0
- data/vendor/money-1.7.1/lib/money/core_extensions.rb +26 -0
- data/vendor/money-1.7.1/lib/money/money.rb +209 -0
- data/vendor/money-1.7.1/lib/support/cattr_accessor.rb +57 -0
- 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,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
|