saasaparilla 0.1.6
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/.gitignore +13 -0
- data/.rspec +2 -0
- data/Gemfile +36 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +39 -0
- data/app/controllers/saasaparilla/admin/plans_controller.rb +87 -0
- data/app/controllers/saasaparilla/admin/subscriptions_controller.rb +34 -0
- data/app/controllers/saasaparilla/admin/transactions_controller.rb +14 -0
- data/app/controllers/saasaparilla/application_controller.rb +4 -0
- data/app/controllers/saasaparilla/billing_history_controller.rb +9 -0
- data/app/controllers/saasaparilla/contact_info_controller.rb +31 -0
- data/app/controllers/saasaparilla/credit_card_controller.rb +41 -0
- data/app/controllers/saasaparilla/invoices_controller.rb +19 -0
- data/app/controllers/saasaparilla/payments_controller.rb +71 -0
- data/app/controllers/saasaparilla/plans_controller.rb +34 -0
- data/app/controllers/saasaparilla/subscription_controller.rb +71 -0
- data/app/helpers/application_helper.rb +3 -0
- data/app/helpers/plans_helper.rb +2 -0
- data/app/helpers/saasaparilla/billing_history_helper.rb +4 -0
- data/app/mailers/saasaparilla/notifier.rb +38 -0
- data/app/models/billing_activity.rb +12 -0
- data/app/models/contact_info.rb +107 -0
- data/app/models/credit_card.rb +109 -0
- data/app/models/invoice.rb +31 -0
- data/app/models/invoice_line_item.rb +3 -0
- data/app/models/payment.rb +39 -0
- data/app/models/plan.rb +36 -0
- data/app/models/subscription.rb +363 -0
- data/app/models/transaction.rb +32 -0
- data/app/views/layouts/saasaparilla.html.haml +9 -0
- data/app/views/saasaparilla/admin/plans/_form.html.haml +45 -0
- data/app/views/saasaparilla/admin/plans/edit.html.erb +4 -0
- data/app/views/saasaparilla/admin/plans/index.html.erb +31 -0
- data/app/views/saasaparilla/admin/plans/new.html.haml +4 -0
- data/app/views/saasaparilla/admin/plans/show.html.haml +20 -0
- data/app/views/saasaparilla/admin/subscriptions/_subscription.html.haml +7 -0
- data/app/views/saasaparilla/admin/subscriptions/index.html.haml +14 -0
- data/app/views/saasaparilla/admin/subscriptions/show.html.haml +52 -0
- data/app/views/saasaparilla/admin/transactions/_transaction.html.haml +6 -0
- data/app/views/saasaparilla/admin/transactions/index.html.haml +16 -0
- data/app/views/saasaparilla/billing_history/_billing_activity.html.haml +10 -0
- data/app/views/saasaparilla/billing_history/show.html.haml +11 -0
- data/app/views/saasaparilla/contact_info/edit.html.haml +8 -0
- data/app/views/saasaparilla/credit_card/edit.html.haml +18 -0
- data/app/views/saasaparilla/invoices/show.html.haml +17 -0
- data/app/views/saasaparilla/notifier/billing_failed.html.haml +5 -0
- data/app/views/saasaparilla/notifier/billing_successful.html.haml +5 -0
- data/app/views/saasaparilla/notifier/invoice_created.html.haml +24 -0
- data/app/views/saasaparilla/notifier/pending_cancellation_notice.html.haml +6 -0
- data/app/views/saasaparilla/notifier/subscription_canceled.html.haml +4 -0
- data/app/views/saasaparilla/notifier/subscription_created.html.haml +5 -0
- data/app/views/saasaparilla/payments/edit.html.haml +10 -0
- data/app/views/saasaparilla/payments/new.html.haml +10 -0
- data/app/views/saasaparilla/payments/show.html.haml +8 -0
- data/app/views/saasaparilla/plans/edit.html.haml +34 -0
- data/app/views/saasaparilla/subscription/_contact_info_form.html.haml +19 -0
- data/app/views/saasaparilla/subscription/_credit_card_form.html.haml +10 -0
- data/app/views/saasaparilla/subscription/new.html.haml +24 -0
- data/app/views/saasaparilla/subscription/show.html.haml +73 -0
- data/autotest/discover.rb +2 -0
- data/config/routes.rb +27 -0
- data/lib/country_select/MIT-LICENSE +20 -0
- data/lib/country_select/README +15 -0
- data/lib/country_select/init.rb +1 -0
- data/lib/country_select/install.rb +2 -0
- data/lib/country_select/lib/country_select.rb +88 -0
- data/lib/country_select/uninstall.rb +1 -0
- data/lib/extensions/action_controller/base.rb +7 -0
- data/lib/extensions/active_record/nested_attributes.rb +36 -0
- data/lib/extensions/active_record/statuses.rb +16 -0
- data/lib/extensions/billable.rb +12 -0
- data/lib/generators/saasaparilla/install/install_generator.rb +29 -0
- data/lib/generators/saasaparilla/install/templates/create_saasaparilla_tables.rb +106 -0
- data/lib/generators/saasaparilla/install/templates/saasaparilla.yml +32 -0
- data/lib/initializers/auth_dot_net.rb +25 -0
- data/lib/initializers/simple_form.rb +77 -0
- data/lib/initializers/time_format.rb +3 -0
- data/lib/saasaparilla.rb +16 -0
- data/lib/saasaparilla/authentication.rb +18 -0
- data/lib/saasaparilla/authorization.rb +26 -0
- data/lib/saasaparilla/engine.rb +30 -0
- data/lib/saasaparilla/version.rb +3 -0
- data/saasaparilla.gemspec +48 -0
- data/spec/.livereload +21 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/controllers/application_controller.rb +11 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/model/user.rb +4 -0
- data/spec/dummy/app/views/layouts/application.html.erb +15 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +46 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +22 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +30 -0
- data/spec/dummy/config/environments/production.rb +49 -0
- data/spec/dummy/config/environments/test.rb +39 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +10 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/simple_form.rb +77 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/locales/simple_form.en.yml +24 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/config/saasaparilla.yml +32 -0
- data/spec/dummy/lib/templates/erb/scaffold/_form.html.erb +13 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +26 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/public/index.html +0 -0
- data/spec/dummy/public/javascripts/application.js +2 -0
- data/spec/dummy/public/javascripts/jquery.js +8374 -0
- data/spec/dummy/public/javascripts/jquery.min.js +16 -0
- data/spec/dummy/public/javascripts/rails.js +159 -0
- data/spec/dummy/public/stylesheets/.gitkeep +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/vendor/plugins/active_merchant_testing/MIT-LICENSE +20 -0
- data/spec/dummy/vendor/plugins/active_merchant_testing/README +32 -0
- data/spec/dummy/vendor/plugins/active_merchant_testing/Rakefile +23 -0
- data/spec/dummy/vendor/plugins/active_merchant_testing/init.rb +1 -0
- data/spec/dummy/vendor/plugins/active_merchant_testing/install.rb +1 -0
- data/spec/dummy/vendor/plugins/active_merchant_testing/lib/authorize_net_cim_gateway_test.rb +492 -0
- data/spec/dummy/vendor/plugins/active_merchant_testing/lib/authorize_net_gateway_test.rb +133 -0
- data/spec/dummy/vendor/plugins/active_merchant_testing/lib/paypal_express_gateway_test.rb +138 -0
- data/spec/dummy/vendor/plugins/active_merchant_testing/tasks/activemerchant_testing_tasks.rake +4 -0
- data/spec/dummy/vendor/plugins/active_merchant_testing/test/activemerchant_testing_test.rb +8 -0
- data/spec/dummy/vendor/plugins/active_merchant_testing/test/test_helper.rb +3 -0
- data/spec/dummy/vendor/plugins/active_merchant_testing/uninstall.rb +1 -0
- data/spec/factories/factories.rb +75 -0
- data/spec/integration/navigation_spec.rb +9 -0
- data/spec/mailers/notifier_spec.rb +167 -0
- data/spec/models/billing_activity_spec.rb +27 -0
- data/spec/models/contact_info_spec.rb +32 -0
- data/spec/models/credit_card_spec.rb +57 -0
- data/spec/models/invoice_line_item_spec.rb +7 -0
- data/spec/models/invoice_spec.rb +25 -0
- data/spec/models/payment_spec.rb +29 -0
- data/spec/models/plan_spec.rb +16 -0
- data/spec/models/subscription_spec.rb +414 -0
- data/spec/requests/admin_plans_spec.rb +32 -0
- data/spec/requests/admin_subscriptions_spec.rb +31 -0
- data/spec/requests/billing_history_spec.rb +26 -0
- data/spec/requests/contact_info_controller_spec.rb +42 -0
- data/spec/requests/credit_cards_spec.rb +79 -0
- data/spec/requests/invoices_spec.rb +23 -0
- data/spec/requests/payments_spec.rb +89 -0
- data/spec/requests/subscription_plans_spec.rb +34 -0
- data/spec/requests/subscriptions_spec.rb +80 -0
- data/spec/spec_helper.rb +68 -0
- data/spec/spec_helpers/spec_helpers.rb +8 -0
- metadata +611 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
class Saasaparilla::SubscriptionController < ApplicationController
|
|
2
|
+
unloadable
|
|
3
|
+
ssl_required :new, :create, :show
|
|
4
|
+
include Authentication::InstanceMethods
|
|
5
|
+
before_filter :get_subscription, :only => [:show, :destroy, :reactivate]
|
|
6
|
+
|
|
7
|
+
before_filter :require_no_subscription, :only => [:new]
|
|
8
|
+
#overide with authorization
|
|
9
|
+
def new
|
|
10
|
+
@subscription = current_billable.build_subscription
|
|
11
|
+
@subscription.build_contact_info
|
|
12
|
+
@subscription.build_credit_card
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def create
|
|
16
|
+
@subscription = current_billable.build_subscription(params[:subscription])
|
|
17
|
+
|
|
18
|
+
begin
|
|
19
|
+
if @subscription.save
|
|
20
|
+
redirect_to subscription_path
|
|
21
|
+
flash[:notice] = "Your subscription was successfully created."
|
|
22
|
+
else
|
|
23
|
+
render :action => "new"
|
|
24
|
+
flash[:error] = "Your subscription could not be created due to errors. Please review the form and correct them."
|
|
25
|
+
end
|
|
26
|
+
rescue Exception => e
|
|
27
|
+
flash[:error] = e.message
|
|
28
|
+
render :action => "new"
|
|
29
|
+
flash.discard
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def show
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def reactivate
|
|
38
|
+
if @subscription.reactivate!
|
|
39
|
+
redirect_to subscription_path
|
|
40
|
+
flash[:notice] = "Your subscription was successfully reactivated."
|
|
41
|
+
else
|
|
42
|
+
redirect_to subscription_path
|
|
43
|
+
flash[:error] = "There was a problem reactivating your account."
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def destroy
|
|
48
|
+
if @subscription.cancel
|
|
49
|
+
flash[:notice] = "Your subscription has been canceled."
|
|
50
|
+
redirect_to subscription_path
|
|
51
|
+
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
def require_no_subscription
|
|
57
|
+
unless current_billable.subscription.nil?
|
|
58
|
+
flash[:error] = "You already have a subscription"
|
|
59
|
+
redirect_to subscription_path
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
def get_subscription
|
|
63
|
+
@subscription = current_billable.subscription
|
|
64
|
+
if @subscription.nil?
|
|
65
|
+
flash[:error] = "You do not have a current subscription"
|
|
66
|
+
redirect_to new_subscription_path
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
class Saasaparilla::Notifier < ActionMailer::Base
|
|
2
|
+
default :from => Saasaparilla::CONFIG["from_email"]
|
|
3
|
+
|
|
4
|
+
def subscription_created(subscription)
|
|
5
|
+
@subscription = subscription
|
|
6
|
+
mail(:to => subscription.contact_info.email, :subject => "Subscription Created")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def invoice_created(subscription, invoice)
|
|
10
|
+
@subscription = subscription
|
|
11
|
+
@invoice = invoice
|
|
12
|
+
mail(:to => subscription.contact_info.email, :subject => "Invoice Created")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def billing_successful(subscription, amount)
|
|
16
|
+
@subscription = subscription
|
|
17
|
+
@amount = amount
|
|
18
|
+
mail(:to => subscription.contact_info.email, :subject => "Account Billing Successful")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def billing_failed(subscription)
|
|
22
|
+
@subscription = subscription
|
|
23
|
+
@url = edit_subscription_credit_card_url
|
|
24
|
+
mail(:to => subscription.contact_info.email, :subject => "Account Billing Failed")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def pending_cancellation_notice(subscription)
|
|
28
|
+
@subscription = subscription
|
|
29
|
+
@url = edit_subscription_credit_card_url
|
|
30
|
+
mail(:to => subscription.contact_info.email, :subject => "Your subscription will be canceled soon")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def subscription_canceled(subscription)
|
|
34
|
+
@subscription = subscription
|
|
35
|
+
mail(:to => subscription.contact_info.email, :subject => "Your subscription has been canceled")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class BillingActivity < ActiveRecord::Base
|
|
2
|
+
belongs_to :subscription
|
|
3
|
+
has_one :invoice
|
|
4
|
+
has_one :transaction
|
|
5
|
+
|
|
6
|
+
MESSAGES = {:success => "Thank you for your payment."}
|
|
7
|
+
scope :recent, order("created_at DESC")
|
|
8
|
+
|
|
9
|
+
scope :limit, lambda {|num|
|
|
10
|
+
limit(num)
|
|
11
|
+
}
|
|
12
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
class ContactInfo < ActiveRecord::Base
|
|
2
|
+
|
|
3
|
+
belongs_to :subscription, :dependent => :destroy
|
|
4
|
+
validates_presence_of :first_name, :last_name
|
|
5
|
+
validates_format_of :email, :with => ::Authlogic::Regex.email
|
|
6
|
+
validates_presence_of :email
|
|
7
|
+
|
|
8
|
+
with_options :if => :require_billing_address? do |contact_info|
|
|
9
|
+
contact_info.validates_presence_of :address
|
|
10
|
+
contact_info.validates_presence_of :city
|
|
11
|
+
contact_info.validates_presence_of :state
|
|
12
|
+
contact_info.validates_presence_of :zip
|
|
13
|
+
contact_info.validates_presence_of :country
|
|
14
|
+
contact_info.validates_format_of :zip, :with => /^\d{5}$/
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
with_options :if => :require_phone_number? do |contact_info|
|
|
18
|
+
contact_info.validates_numericality_of :phone_area_code
|
|
19
|
+
contact_info.validates_numericality_of :phone_prefix
|
|
20
|
+
contact_info.validates_numericality_of :phone_suffix
|
|
21
|
+
contact_info.validates_length_of :phone_area_code, :is => 3
|
|
22
|
+
contact_info.validates_length_of :phone_prefix, :is => 3
|
|
23
|
+
contact_info.validates_length_of :phone_suffix, :is => 4
|
|
24
|
+
contact_info.validates_format_of :phone_number, :with => /\d\d\d-\d\d\d-\d\d\d\d/
|
|
25
|
+
contact_info.before_validation :update_phone_number
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
attr_accessor :phone_area_code, :phone_prefix, :phone_suffix
|
|
29
|
+
|
|
30
|
+
def phone_area_code
|
|
31
|
+
self.phone_number.split('-')[0] if self.phone_number
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def phone_prefix
|
|
35
|
+
self.phone_number.split('-')[1] if self.phone_number
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def phone_suffix
|
|
39
|
+
self.phone_number.split('-')[2] if self.phone_number
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def full_name
|
|
43
|
+
"#{first_name} #{last_name}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def to_hash
|
|
47
|
+
return {:first_name => first_name,
|
|
48
|
+
:last_name => last_name,
|
|
49
|
+
:address => address,
|
|
50
|
+
:company => company,
|
|
51
|
+
:city => city,
|
|
52
|
+
:state => state,
|
|
53
|
+
:zip => zip,
|
|
54
|
+
:country => "US",
|
|
55
|
+
:phone_number => phone_number}
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def require_billing_address?
|
|
60
|
+
Saasaparilla::CONFIG["require_billing_address"] == true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def require_phone_number?
|
|
64
|
+
Saasaparilla::CONFIG["require_phone_number"] == true
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
private
|
|
68
|
+
|
|
69
|
+
def update_phone_number
|
|
70
|
+
if @phone_area_code && @phone_prefix && @phone_suffix
|
|
71
|
+
self.phone_number = "#{@phone_area_code}-#{@phone_prefix}-#{@phone_suffix}"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# def validate_phone_number
|
|
79
|
+
# return_value = true
|
|
80
|
+
# unless self.phone_area_code.length == 3
|
|
81
|
+
# self.errors.add(:base, "Area code must be three digits.")
|
|
82
|
+
# return_value = false
|
|
83
|
+
# end
|
|
84
|
+
# unless (/[0-9]/).match(self.phone_area_code)
|
|
85
|
+
# self.errors.add(:base, "Area code must be numbers only.")
|
|
86
|
+
# return_value = false
|
|
87
|
+
# end
|
|
88
|
+
# unless self.phone_prefix.length == 3
|
|
89
|
+
# self.errors.add(:base, "Phone prefix must be three digits.")
|
|
90
|
+
# return_value = false
|
|
91
|
+
# end
|
|
92
|
+
# unless (/[0-9]/).match(self.phone_prefix)
|
|
93
|
+
# self.errors.add(:base, "Phone prefix must be numbers only.")
|
|
94
|
+
# return_value = false
|
|
95
|
+
# end
|
|
96
|
+
# unless self.phone_suffix.length == 4
|
|
97
|
+
# self.errors.add(:base, "Phone suffix must be four digits.")
|
|
98
|
+
# return_value = false
|
|
99
|
+
# end
|
|
100
|
+
# unless (/[0-9]/).match(self.phone_suffix)
|
|
101
|
+
# self.errors.add(:base, "Phone suffix must be numbers only.")
|
|
102
|
+
# return_value = false
|
|
103
|
+
# end
|
|
104
|
+
# return_value
|
|
105
|
+
# end
|
|
106
|
+
|
|
107
|
+
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
class CreditCard < ActiveRecord::Base
|
|
2
|
+
|
|
3
|
+
before_validation :set_first_last_name, :on => :update
|
|
4
|
+
validate :validate_card
|
|
5
|
+
validates_presence_of :card_verification
|
|
6
|
+
validates_presence_of :first_name
|
|
7
|
+
validates_presence_of :last_name, :message => "can't be blank"
|
|
8
|
+
validates_presence_of :card_type, :message => "can't be blank"
|
|
9
|
+
validates_presence_of :card_number, :message => "can't be blank"
|
|
10
|
+
validates_presence_of :expiry_month, :message => "can't be blank"
|
|
11
|
+
validates_presence_of :expiry_year, :message => "can't be blank"
|
|
12
|
+
validates_numericality_of :expiry_month, :message => "is not a number"
|
|
13
|
+
validates_numericality_of :expiry_year, :message => "is not a number"
|
|
14
|
+
|
|
15
|
+
attr_accessor :card_type, :card_verification, :expiry_month, :expiry_year, :first_name, :last_name
|
|
16
|
+
CARD_TYPES = [["Visa", "Visa"], ["Mastercard", "Mastercard"], ["American Express", "American Express" ], ["Discover", "Discover"]]
|
|
17
|
+
|
|
18
|
+
MONTHS = (1..12).to_a
|
|
19
|
+
YEARS = ((Date.today.year)..(Date.today.year + 8)).to_a
|
|
20
|
+
|
|
21
|
+
belongs_to :subscription
|
|
22
|
+
|
|
23
|
+
before_save :mask_card_number
|
|
24
|
+
before_save :create_expiration_date
|
|
25
|
+
before_save :update_payment_profile
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def validate_card
|
|
29
|
+
unless active_merchant_card.valid?
|
|
30
|
+
active_merchant_card.errors.full_messages.each do |message|
|
|
31
|
+
case message
|
|
32
|
+
when "First name connot be empty"
|
|
33
|
+
errors[:card_name] = "cannot be empty"
|
|
34
|
+
when "Last name cannot be empty"
|
|
35
|
+
errors[:card_name] = "cannot be empty"
|
|
36
|
+
when "Month is not a valid month"
|
|
37
|
+
errors[:expiry_month] = "Month is not a valid month"
|
|
38
|
+
when "Year expired"
|
|
39
|
+
errors[:expiry_year] = "Year expired"
|
|
40
|
+
when "Number is not a valid credit card number"
|
|
41
|
+
errors[:card_number] = "is not a valid credit card number"
|
|
42
|
+
when "Verification value is required"
|
|
43
|
+
errors[:card_verification] = "value is required"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def active_merchant_card
|
|
50
|
+
@card_attributes ||= ActiveMerchant::Billing::CreditCard.new(
|
|
51
|
+
:type => card_type,
|
|
52
|
+
:number => card_number,
|
|
53
|
+
:verification_value => card_verification,
|
|
54
|
+
:month => expiry_month,
|
|
55
|
+
:year => expiry_year,
|
|
56
|
+
:first_name => first_name,
|
|
57
|
+
:last_name => last_name
|
|
58
|
+
)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def card_attributes=(cstring)
|
|
62
|
+
@card_attributes = cstring
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def create_expiration_date
|
|
70
|
+
self.expiration_date = "#{expiry_month}/#{expiry_year}"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def mask_card_number
|
|
75
|
+
|
|
76
|
+
length = card_number.length
|
|
77
|
+
masked_number = ""
|
|
78
|
+
(1..(length - 4)).each do
|
|
79
|
+
masked_number << "X"
|
|
80
|
+
end
|
|
81
|
+
masked_number << card_number[length - 4, length]
|
|
82
|
+
self.card_number = masked_number
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def update_payment_profile
|
|
86
|
+
|
|
87
|
+
unless new_record?
|
|
88
|
+
@profile = {:customer_profile_id => subscription.customer_cim_id,
|
|
89
|
+
:payment_profile => {:customer_payment_profile_id => subscription.customer_payment_profile_id,
|
|
90
|
+
:bill_to => subscription.contact_info.to_hash,
|
|
91
|
+
:payment => {:credit_card => active_merchant_card}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
response = GATEWAYCIM.update_customer_payment_profile(@profile)
|
|
95
|
+
if response.success?
|
|
96
|
+
return true
|
|
97
|
+
else
|
|
98
|
+
raise response.message
|
|
99
|
+
return false
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def set_first_last_name
|
|
105
|
+
self.first_name = subscription.contact_info.first_name
|
|
106
|
+
self.last_name = subscription.contact_info.last_name
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class Invoice < ActiveRecord::Base
|
|
2
|
+
belongs_to :billing_activity
|
|
3
|
+
has_many :invoice_line_items
|
|
4
|
+
|
|
5
|
+
after_create :create_invoice_line_item
|
|
6
|
+
after_create :generate_billing_activity
|
|
7
|
+
after_create :send_invoice_created_email
|
|
8
|
+
attr_accessor :subscription, :price, :from, :to
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def amount
|
|
12
|
+
invoice_line_items.sum(:price)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
private
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def create_invoice_line_item
|
|
19
|
+
self.invoice_line_items.create(:price => price, :from => from, :to => to )
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def generate_billing_activity
|
|
24
|
+
self.create_billing_activity(:message => "<a href='/subscription/invoices/#{self.id}'>Invoice</a>", :subscription => subscription, :amount => amount, :invoice => self)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def send_invoice_created_email
|
|
28
|
+
Saasaparilla::Notifier.invoice_created(billing_activity.subscription, self).deliver
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
class Payment < ActiveRecord::Base
|
|
2
|
+
before_validation :set_status_pending, :on => :create
|
|
3
|
+
before_update :charge_subscription
|
|
4
|
+
validate :valid_amount
|
|
5
|
+
validates_presence_of :subscription_id
|
|
6
|
+
validates_presence_of :amount, :message => "can't be blank"
|
|
7
|
+
validates_numericality_of :amount
|
|
8
|
+
has_statuses "pending", "paid", "failed"
|
|
9
|
+
belongs_to :subscription
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def valid_amount
|
|
15
|
+
if amount < 0
|
|
16
|
+
errors[:amount] = "cannot be less than 0"
|
|
17
|
+
end
|
|
18
|
+
if amount > subscription.balance
|
|
19
|
+
errors[:amount] = "cannot be more than your balance"
|
|
20
|
+
end
|
|
21
|
+
if amount == 0
|
|
22
|
+
errors[:amount] = "cannot be zero"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def charge_subscription
|
|
30
|
+
|
|
31
|
+
if subscription.bill!(amount)
|
|
32
|
+
self.status = "paid"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
def set_status_pending
|
|
37
|
+
self.status = "pending"
|
|
38
|
+
end
|
|
39
|
+
end
|
data/app/models/plan.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
class Plan < ActiveRecord::Base
|
|
2
|
+
has_dynamic_attributes
|
|
3
|
+
validates_presence_of :name, :message => "can't be blank"
|
|
4
|
+
validates_presence_of :billing_period, :message => "can't be blank"
|
|
5
|
+
validates_numericality_of :price, :message => "is not a number"
|
|
6
|
+
validates_presence_of :price, :message => "can't be blank"
|
|
7
|
+
|
|
8
|
+
has_many :subscriptions
|
|
9
|
+
|
|
10
|
+
BILLING_PERIODS = ["Monthly", "Annually"]
|
|
11
|
+
|
|
12
|
+
def respond_to?(method_sym, include_private = false)
|
|
13
|
+
@column_names = []
|
|
14
|
+
unless dynamic_attributes.nil?
|
|
15
|
+
dynamic_attributes.each do |dm|
|
|
16
|
+
@column_names << dm[0].gsub("field_", "")
|
|
17
|
+
end
|
|
18
|
+
if @column_names.include? method_sym.to_s
|
|
19
|
+
return true
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
super
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def method_missing(method, *args)
|
|
27
|
+
|
|
28
|
+
if has_dynamic_attribute?("field_#{method}")
|
|
29
|
+
return self.send("field_#{method}")
|
|
30
|
+
end
|
|
31
|
+
super
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
end
|