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,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
+