servicemerchant 0.1.0
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/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
|