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.
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,40 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ require 'money'
4
+
5
+ class SubscriptionManagementTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ @smc = SubscriptionManagement
9
+ end
10
+
11
+ def test_format_feature
12
+ feature = {'quantity' => 5, 'feature' => {'name'=> 'Quota', 'unit'=> 'Gigabyte'}}
13
+ assert_equal 'Quota: 5 Gigabytes', @smc.format_feature(feature)
14
+ feature = {'quantity' => 1, 'feature' => {'name'=> 'Quota', 'unit'=> 'Gigabyte'}}
15
+ assert_equal 'Quota: 1 Gigabyte', @smc.format_feature(feature)
16
+ feature = {'quantity' => 0, 'feature' => {'name'=> 'Quota', 'unit'=> 'Gigabyte'}}
17
+ assert_equal 'Quota: Unlimited', @smc.format_feature(feature)
18
+ feature = {'quantity' => 2, 'feature' => {'name'=> 'Users'}}
19
+ assert_equal 'Users: 2', @smc.format_feature(feature)
20
+ feature = {'feature' => {'name'=> 'SSL Encryption'}}
21
+ assert_equal 'SSL Encryption', @smc.format_feature(feature)
22
+ end
23
+
24
+ def test_format_periodicity
25
+ assert_equal 'twice a week', @smc.format_periodicity('0.5 w')
26
+ assert_equal 'each month', @smc.format_periodicity('1 m')
27
+ assert_equal 'each 2 years', @smc.format_periodicity('2 y')
28
+ assert_equal 'each 42 days', @smc.format_periodicity('42 d')
29
+ assert_raise ArgumentError do; x = @smc.format_periodicity('random'); end;
30
+ end
31
+
32
+ def test_get_taxes_id
33
+ taxes = {"ca"=>{"country"=>"CA", "taxes"=>[{"tax"=>{"name"=>"Goods and Services Tax"}, "rate"=>0.05}], "state"=>"*"},
34
+ "us_ca"=>{"country"=>"US", "taxes"=>[{"tax"=>{"name"=>"Sample tax"}, "rate"=>0.2}], "state"=>"CA"}}
35
+ assert_equal 'ca', @smc.get_taxes_id(taxes, 'CA', 'ON')
36
+ assert_equal 'us_ca', @smc.get_taxes_id(taxes, 'US', 'CA')
37
+ assert_raise StandardError do; x = @smc.get_taxes_id(taxes, 'US', 'NY'); end
38
+ end
39
+
40
+ end
data/tracker/README ADDED
@@ -0,0 +1,12 @@
1
+ See demo.rb as a starting point reference
2
+
3
+ rake tracker:create_tables will create DB sctructure
4
+
5
+ Dependencies
6
+ ============
7
+ Ruby gems:
8
+ - activerecord
9
+ - activesupport >= 2.0.0
10
+ - activemerchant >= 1.3.2
11
+ - money
12
+ - sqlite3-ruby
data/tracker/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ require 'rake'
2
+ require File.dirname(__FILE__) + '/tasks/schema'
3
+
4
+ require 'rake/testtask'
5
+ namespace :test do
6
+
7
+ Rake::TestTask.new(:remote_tracker) do |t|
8
+ t.pattern = 'test/remote/**/*_test.rb'
9
+ t.ruby_opts << '-rubygems'
10
+ t.verbose = true
11
+ end
12
+
13
+ desc "Run all remote tests"
14
+ task :remote => [:remote_tracker]
15
+
16
+ Rake::TestTask.new(:unit_tracker) do |t|
17
+ t.pattern = 'test/unit/**/*_test.rb'
18
+ t.ruby_opts << '-rubygems'
19
+ t.verbose = true
20
+ end
21
+
22
+ desc "Run all unit tests"
23
+ task :unit => [:unit_tracker]
24
+
25
+ task :rcov do
26
+ begin
27
+ require 'rcov/rcovtask'
28
+ Rcov::RcovTask.new do |t|
29
+ t.name = 'test'
30
+ t.libs << 'test'
31
+ t.test_files = FileList['test/**/**/*test.rb']
32
+ t.verbose = true
33
+ t.rcov_opts = ['-i', '^lib', '-x', 'recurring_billing']
34
+ end
35
+ rescue StandardError
36
+ # ignore missing rcov
37
+ end
38
+ end
39
+
40
+ end
File without changes
data/tracker/demo.rb ADDED
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+ # This is a smoke test for Tracker component
3
+
4
+ require 'tracker'
5
+ puts "Verifying models classes..."
6
+ Transaction
7
+ RecurringPaymentProfile
8
+ puts "Verifying DB..."
9
+ require 'test/connection'
10
+ Transaction.count
11
+ RecurringPaymentProfile.find :first
12
+ puts "All OK"
@@ -0,0 +1,134 @@
1
+ class RecurringPaymentProfile < ActiveRecord::Base
2
+ # We cannot just use `table_name_prefix = "tracker_"' because it uses broken
3
+ # cattr_accessor and applies to all ActiveRecord::Base descendants when
4
+ # tracker is loaded from Subscription Manager
5
+ def self.table_name_prefix
6
+ "tracker_"
7
+ end
8
+ has_many :transactions
9
+
10
+ # Returns single payment amount (sum of net amount and taxes)
11
+ def amount
12
+ self.net_amount+self.taxes_amount
13
+ end
14
+
15
+ def money
16
+ Money.new(self.amount, self.currency)
17
+ end
18
+
19
+ # Sets net amount
20
+ def net_money=(x_amount)
21
+ self[:net_amount] = x_amount.cents
22
+ if !self[:currency].nil? && x_amount.currency != self[:currency]
23
+ raise ArgumentError, 'Cannot change currency. Please, clean it first'
24
+ end
25
+ self[:currency] = x_amount.currency
26
+ end
27
+
28
+ # Sets taxes amount
29
+ def taxes_money=(x_amount)
30
+ self[:taxes_amount] = x_amount.cents
31
+ if !self[:currency].nil? && x_amount.currency != self[:currency]
32
+ raise ArgumentError, 'Cannot change currency. Please, clean it first'
33
+ end
34
+ self[:currency] = x_amount.currency
35
+ end
36
+
37
+ # Parses card and set related fields.
38
+ def parse_and_set_card(card, hint=nil)
39
+ self[:card_type] = card.type
40
+ number = card.number
41
+ default_hint = "#{card.first_name} #{card.last_name}, #{card.type.upcase}, #{mask_card_number(number)}, exp. #{card_exp_date(card)}"
42
+ self[:card_owner_memo] = (hint) ? hint : default_hint
43
+ end
44
+
45
+ # Masks card number (only last 4 digits are shown)
46
+ #
47
+ # Example:
48
+ # '031641616161' => 'XXXXXXXX6161'
49
+ def mask_card_number(number)
50
+ number.to_s.size < 5 ? number.to_s : (('X' * number.to_s[0..-5].length) + number.to_s[-4..-1])
51
+ end
52
+
53
+ # Returns formatted expiration date for given Card object
54
+ def card_exp_date(card)
55
+ '%04d-%02d' % [card.year, card.month]
56
+ end
57
+
58
+ # Returns formatted amount for current record
59
+ def money_formatted
60
+ '%.2f %s' % [self.amount/100.00, self.currency.upcase]
61
+ end
62
+
63
+ def net_money_formatted
64
+ '%.2f %s' % [self.net_amount/100.00, self.currency.upcase]
65
+ end
66
+
67
+ def taxes_money_formatted
68
+ '%.2f %s' % [self.taxes_amount/100.00, self.currency.upcase]
69
+ end
70
+
71
+ # Marks current profile as active and saves it
72
+ def set_profile_active_and_save!
73
+ self.status = 'active'
74
+ self.save
75
+ end
76
+
77
+ # Updates profile fields after it was UPDATEd via remote gateway
78
+ def set_profile_after_update!(amount, card, payment_options, recurring_options)
79
+
80
+ if amount
81
+ # Split amount into net/taxes
82
+ if payment_options.has_key?(:taxes_amount_included)
83
+ self.net_money = amount - payment_options[:taxes_amount_included]
84
+ self.taxes_money = payment_options[:taxes_amount_included]
85
+ payment_options.delete(:taxes_amount_included)
86
+ else
87
+ self.net_money = amount
88
+ self.taxes_amount = 0
89
+ end
90
+ end
91
+
92
+ self.parse_and_set_card(card) if card
93
+ self.subscription_name = payment_options[:subscription_name] if payment_options[:subscription_name]
94
+ unless (ro = recurring_options).empty?
95
+ self.pay_on_day_x = ro[:pay_on_day_x] unless ro[:pay_on_day_x].nil?
96
+ end
97
+ self.save
98
+ end
99
+
100
+ # Updates profile fields after it was INQUIREDd via remote gateway
101
+ def set_profile_after_inquiry!(result)
102
+ self.status = result['profile_status']
103
+ self.outstanding_balance = result['outstanding_balance'].cents
104
+ self.complete_payments_count = result['number_cycles_completed']
105
+ self.failed_payments_count = result['failed_payment_count']
106
+ self.remaining_payments_count = result['number_cycles_remaining']
107
+ self.last_synchronized_at = DateTime.now
108
+ self.save
109
+ end
110
+
111
+ # Updates given hash from current profile fields
112
+ def update_options_from_profile!(options)
113
+ options[:subscription_name] ||= self[:subscription_name]
114
+ options[:amount] ||= Money.new(self.amount, self[:currency])
115
+ options[:taxes_amount_included] ||= Money.new(self.taxes_amount, self[:currency])
116
+ options[:interval] ||= self[:periodicity]
117
+ options[:start_date] ||= (Date.new(self[:created_at].year,self[:created_at].month, self[:created_at].mday) + self[:trial_days].to_i)
118
+ unless options[:trial_days]
119
+ trial_days = options[:start_date] - Date.today
120
+ options[:trial_days] = trial_days if trial_days > 0
121
+ end
122
+ self[:complete_payments_count] = 0 unless self[:complete_payments_count].to_i > 0
123
+ options[:occurrences] = self[:total_payments_count] - self[:complete_payments_count]
124
+ options[:pay_on_day_x] ||= self[:pay_on_day_x]
125
+ end
126
+
127
+ # Marks profile as deleted
128
+ def mark_as_deleted!
129
+ self[:deleted_at] = Time.now
130
+ self[:status] = 'deleted'
131
+ self.save
132
+ end
133
+
134
+ end
@@ -0,0 +1,19 @@
1
+ class Transaction < ActiveRecord::Base
2
+ # We cannot just use `table_name_prefix = "tracker_"' because it uses broken
3
+ # cattr_accessor and applies to all ActiveRecord::Base descendants when
4
+ # tracker is loaded from Subscription Manager
5
+ def self.table_name_prefix
6
+ "tracker_"
7
+ end
8
+
9
+ belongs_to :recurring_payment_profile
10
+
11
+ def money
12
+ Money.new(self.amount, self.currency)
13
+ end
14
+
15
+ def money_formatted
16
+ '%.2f %s' % [self.amount/100.00, self.currency.upcase]
17
+ end
18
+
19
+ end
@@ -0,0 +1,103 @@
1
+ require File.dirname(__FILE__) + '/../../recurring_billing/lib/recurring_billing'
2
+
3
+ module RecurringBilling
4
+ class RecurringBillingGateway
5
+
6
+ # Create recurring payment AND store it
7
+ def create_with_persist(amount, card, payment_options={}, recurring_options={})
8
+ if payment_id = create_without_persist(amount, card, payment_options, recurring_options)
9
+ profile = RecurringPaymentProfile.new
10
+ profile.gateway_reference = payment_id
11
+ profile.gateway = code().to_s.upcase
12
+ profile.subscription_name = payment_options[:subscription_name]
13
+
14
+ # Split amount into net/taxes
15
+ if payment_options.has_key?(:taxes_amount_included)
16
+ profile.net_money = amount - payment_options[:taxes_amount_included]
17
+ profile.taxes_money = payment_options[:taxes_amount_included]
18
+ payment_options.delete(:taxes_amount_included)
19
+ else
20
+ profile.net_money = amount
21
+ profile.taxes_amount = 0
22
+ end
23
+
24
+ profile.parse_and_set_card(card)
25
+ ro = recurring_options
26
+ profile.trial_days = trial_days = ro[:trial_days].nil? ? 0 : ro[:trial_days].to_i
27
+ profile.pay_on_day_x = ro[:pay_on_day_x] unless ro[:pay_on_day_x].nil?
28
+ start_date = ro[:start_date] - trial_days
29
+ if get_midnight(start_date) == get_midnight(DateTime.now)
30
+ profile.created_at = start_date
31
+ else
32
+ profile.created_at = get_midnight(start_date)
33
+ end
34
+ profile.periodicity = '%d %s' % parse_interval(ro[:interval])
35
+ if ro[:occurrences].nil?
36
+ profile.total_payments_count = get_occurrences(ro[:start_date] - trial_days, ro[:interval], ro[:end_date])
37
+ else
38
+ profile.total_payments_count = ro[:occurrences]
39
+ end
40
+ profile.set_profile_active_and_save!
41
+ return payment_id
42
+ end
43
+ end
44
+ alias_method_chain :create, :persist
45
+
46
+ # Update recurring payment AND its local info
47
+ def update_with_persist(billing_id, amount, card, payment_options={}, recurring_options={})
48
+ profile = RecurringPaymentProfile.find_by_gateway_reference(billing_id)
49
+ #TODO: change to custom error
50
+ raise StandardError, 'Cannot update a deleted profile (#{billing_id})' if profile.status == 'deleted'
51
+
52
+ if update_without_persist(billing_id, amount, card, payment_options, recurring_options)
53
+ profile.set_profile_after_update!(amount, card, payment_options, recurring_options)
54
+ return true
55
+ end
56
+ end
57
+ alias_method_chain :update, :persist
58
+
59
+ # Inquire about recurring payment AND update its info
60
+ def inquiry_with_persist(billing_id)
61
+ result = inquiry_without_persist(billing_id)
62
+ RecurringPaymentProfile.find_by_gateway_reference(billing_id).set_profile_after_inquiry!(result)
63
+ return result
64
+ end
65
+ alias_method_chain :inquiry, :persist
66
+
67
+ # Cancel recurring payment AND update its info
68
+ def delete_with_persist(billing_id)
69
+ if delete_without_persist(billing_id)
70
+ RecurringPaymentProfile.find_by_gateway_reference(billing_id).mark_as_deleted!
71
+ return true
72
+ end
73
+ end
74
+ alias_method_chain :delete, :persist
75
+
76
+ # Tells if update or recreate is needed
77
+ def can_update?(billing_id, options)
78
+ begin
79
+ options = self.class.separate_create_update_params_from_options(options)
80
+ can_update = correct_update?(billing_id, options[:amount], options[:card], options[:payment_options], options[:recurring_options])
81
+ rescue Exception
82
+ can_update = false
83
+ end
84
+ end
85
+
86
+ # Updates or if updating is impossible, recreates profile with specified billing_id
87
+ def update_or_recreate(billing_id, options)
88
+ if can_update?(billing_id, options)
89
+ options_separated = self.class.separate_create_update_params_from_options(options)
90
+ update(billing_id, options_separated[:amount], options_separated[:card], options_separated[:payment_options], options_separated[:recurring_options])
91
+ return billing_id
92
+ else
93
+ RecurringPaymentProfile.find_by_gateway_reference(billing_id).update_options_from_profile!(options)
94
+ options_separated = self.class.separate_create_update_params_from_options(options)
95
+ correct_create?(options_separated[:amount], options_separated[:card], options_separated[:payment_options], options_separated[:recurring_options])
96
+ delete(billing_id)
97
+ return create(options_separated[:amount], options_separated[:card], options_separated[:payment_options], options_separated[:recurring_options])
98
+ end
99
+ nil
100
+ end
101
+
102
+ end
103
+ end
@@ -0,0 +1,34 @@
1
+
2
+ == Tracker module methods
3
+ Tracker module provides payment profiles local storage capability for simple CRUD
4
+ API RecurringBilling is. As a result, every RecurringBilling action is accompanied by number of Tracker database operations.
5
+ Tracker auto-includes RecurringBilling
6
+ and replaces its create, update, inquiry and delete methods with its own extended implementations while re-aliasing old ones as
7
+ XXX_without_persist. The usual behavior of replacement methods is to execute corresponding old RecurringBilling method and,
8
+ on success, to store given or returned parameters into database. Extending the methods doesn't change the syntax. Also, several
9
+ new methods are added to RecurringBilling class.
10
+
11
+ === Updating exising remote payment
12
+ The update method of RecurringBilling is limited by design: if updating payment information on remote gateway is impossible
13
+ with given set of options, update raises Exception (and quits). Given that the main reason of such behavior is that RecurringBilling
14
+ instance methods are isolated from each other payment-wise, Tracker combination of storage and gateway interaction provides an additional
15
+ option to handle such situations. If RecurringBilling returns an Exception (meaning update is impossible), the recurring payment on
16
+ gateway is cancelled and another, with updated options is created instead. For example, that way limitations on changing
17
+ :recurring_options could be overcame.
18
+
19
+ Two related methods are available for this feature. Both use payment gateway reference ID (+billing_id+) and combined options
20
+ hash (+options+) as parameters. Second parameter may be obtained from usual quad-element structure something like this:
21
+ options = {}
22
+ options[:amount] = amount
23
+ options[:card] = card
24
+ options.update(payment_options)
25
+ options.update(recurring_options)
26
+ To check if recurring payment could be updated by usual RecurringBilling means, can_update? method is used. This check is integrated into
27
+ update_or_recreate method that calls can_update? and then performs traditional update, or deletes and then re-creates the payment via gateway.
28
+ For example:
29
+ ...
30
+ options = {:start_date => Date + 1337}
31
+ print 'Warning! The billing profile will be re-created} if gateway.can_update?(billing_id, options)
32
+ gateway.update_or_recreate(billing_id, options)
33
+ ...
34
+ Please note that missing but required options for payment re-creation are calculated from database.
@@ -0,0 +1,66 @@
1
+ namespace :tracker do
2
+ desc 'Create Tracker database tables'
3
+ task :create_tables => :connection do
4
+ ActiveRecord::Base.connection.create_table :tracker_recurring_payment_profiles, :force => true do |t|
5
+ t.column :gateway_reference, :string, :unique => true
6
+ t.column :gateway, :string
7
+ t.column :subscription_name, :text
8
+ t.column :description, :text
9
+ t.column :currency, :string
10
+ t.column :net_amount, :integer, :null => false
11
+ t.column :taxes_amount, :integer, :null => false
12
+ t.column :outstanding_balance, :integer
13
+ t.column :total_payments_count, :integer
14
+ t.column :complete_payments_count, :integer
15
+ t.column :failed_payments_count, :integer
16
+ t.column :remaining_payments_count, :integer
17
+ t.column :periodicity, :string
18
+ t.column :trial_days, :integer, :default => 0
19
+ t.column :pay_on_day_x, :integer, :default => 0
20
+ t.column :status, :string
21
+ t.column :problem_status, :string
22
+ t.column :card_type, :string
23
+ t.column :card_owner_memo, :string
24
+ t.column :created_at, :datetime
25
+ t.column :updated_at, :datetime
26
+ t.column :deleted_at, :datetime
27
+ t.column :last_synchronized_at, :datetime
28
+ end
29
+ ActiveRecord::Base.connection.add_index :tracker_recurring_payment_profiles, [ :gateway ], :name => 'ix_tracker_recurring_payment_profiles_gateway'
30
+ ActiveRecord::Base.connection.add_index :tracker_recurring_payment_profiles, [ :gateway_reference ], :unique => true, :name => 'uix_tracker_recurring_payment_profiles_gateway_reference'
31
+ ActiveRecord::Base.connection.create_table :tracker_transactions, :force => true do |t|
32
+ t.column :recurring_payment_profile_id, :integer
33
+ t.column :gateway_reference, :string
34
+ t.column :currency, :string
35
+ t.column :amount, :integer
36
+ t.column :result_code, :string
37
+ t.column :result_text, :string
38
+ t.column :card_type, :string
39
+ t.column :card_owner_memo, :string
40
+ t.column :created_at, :datetime
41
+ t.column :recorded_at, :datetime
42
+ end
43
+ ActiveRecord::Base.connection.add_index :tracker_transactions, [ :recurring_payment_profile_id ]
44
+ end
45
+
46
+ desc 'Drop Tracker database tables'
47
+ task :drop_tables => :connection do
48
+ ActiveRecord::Base.connection.drop_table :tracker_recurring_payment_profiles
49
+ ActiveRecord::Base.connection.drop_table :tracker_transactions
50
+ end
51
+
52
+ # Use Rails connection when appropriate or fallback to local test db
53
+ task :connection do
54
+ connected = false
55
+ begin
56
+ begin
57
+ connected = true if ActiveRecord::Base.connection
58
+ rescue ActiveRecord::ConenctionNotEstablished
59
+ end
60
+ rescue NameError # ActiveRecord not loaded
61
+ end
62
+ require File.dirname(__FILE__) + "/../test/connection" unless connected
63
+ end
64
+ end
65
+
66
+