paid_up 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +46 -0
  5. data/LICENSE.txt +20 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +64 -0
  8. data/Rakefile +48 -0
  9. data/VERSION +1 -0
  10. data/app/controllers/paid_up/paid_up_controller.rb +12 -0
  11. data/app/controllers/paid_up/plans_controller.rb +7 -0
  12. data/app/controllers/paid_up/subscriptions_controller.rb +37 -0
  13. data/app/helpers/paid_up/features_helper.rb +65 -0
  14. data/app/helpers/paid_up/paid_up_helper.rb +14 -0
  15. data/app/helpers/paid_up/plans_helper.rb +53 -0
  16. data/app/helpers/paid_up/subscriptions_helper.rb +21 -0
  17. data/app/models/ability.rb +37 -0
  18. data/app/models/paid_up/feature.rb +19 -0
  19. data/app/models/paid_up/features_plan.rb +14 -0
  20. data/app/models/paid_up/plan.rb +99 -0
  21. data/app/views/devise/confirmations/new.html.haml +13 -0
  22. data/app/views/devise/passwords/edit.html.haml +19 -0
  23. data/app/views/devise/passwords/new.html.haml +14 -0
  24. data/app/views/devise/registrations/_new_form.html.haml +23 -0
  25. data/app/views/devise/registrations/edit.html.haml +39 -0
  26. data/app/views/devise/registrations/new.html.haml +11 -0
  27. data/app/views/devise/sessions/_new_form.html.haml +21 -0
  28. data/app/views/devise/sessions/new.html.haml +11 -0
  29. data/app/views/devise/shared/_links.html.haml +14 -0
  30. data/app/views/devise/unlocks/new.html.haml +14 -0
  31. data/app/views/paid_up/features/_abilities_table.html.haml +22 -0
  32. data/app/views/paid_up/features/_table.html.haml +16 -0
  33. data/app/views/paid_up/plans/index.html.haml +18 -0
  34. data/app/views/paid_up/subscriptions/index.html.haml +16 -0
  35. data/app/views/paid_up/subscriptions/new.html.haml +101 -0
  36. data/bin/rails +12 -0
  37. data/config/initializers/stripe.rb +6 -0
  38. data/config/locales/en.yml +85 -0
  39. data/config/routes.rb +7 -0
  40. data/coverage/.last_run.json +5 -0
  41. data/coverage/.resultset.json +1488 -0
  42. data/coverage/.resultset.json.lock +0 -0
  43. data/db/migrate/20150407105900_create_paid_up_features_plans_table.rb +9 -0
  44. data/db/migrate/20150407110100_create_paid_up_features_table.rb +11 -0
  45. data/db/migrate/20150407110101_create_paid_up_plans_table.rb +14 -0
  46. data/lib/generators/paid_up/install/install_generator.rb +59 -0
  47. data/lib/generators/paid_up/install/templates/initializer.rb.erb +5 -0
  48. data/lib/generators/paid_up/utils.rb +16 -0
  49. data/lib/paid_up/configuration.rb +22 -0
  50. data/lib/paid_up/engine.rb +17 -0
  51. data/lib/paid_up/integer.rb +9 -0
  52. data/lib/paid_up/localization.rb +31 -0
  53. data/lib/paid_up/mixins.rb +139 -0
  54. data/lib/paid_up/railtie.rb +4 -0
  55. data/lib/paid_up/stripe_extensions.rb +17 -0
  56. data/lib/paid_up/table_validator.rb +9 -0
  57. data/lib/paid_up/unlimited.rb +17 -0
  58. data/lib/paid_up/version.rb +7 -0
  59. data/lib/paid_up.rb +27 -0
  60. data/paid_up.gemspec +248 -0
  61. data/spec/controllers/paid_up/plans_spec.rb +26 -0
  62. data/spec/controllers/paid_up/subscriptions_spec.rb +187 -0
  63. data/spec/dummy/README.rdoc +28 -0
  64. data/spec/dummy/Rakefile +6 -0
  65. data/spec/dummy/app/assets/javascripts/application.js +16 -0
  66. data/spec/dummy/app/assets/stylesheets/application.css.scss +16 -0
  67. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  68. data/spec/dummy/app/models/group.rb +3 -0
  69. data/spec/dummy/app/models/user.rb +9 -0
  70. data/spec/dummy/app/views/layouts/application.html.haml +31 -0
  71. data/spec/dummy/app/views/pages/index.html.haml +1 -0
  72. data/spec/dummy/bin/bundle +3 -0
  73. data/spec/dummy/bin/rails +4 -0
  74. data/spec/dummy/bin/rake +4 -0
  75. data/spec/dummy/bin/setup +29 -0
  76. data/spec/dummy/config/application.rb +32 -0
  77. data/spec/dummy/config/boot.rb +5 -0
  78. data/spec/dummy/config/database.yml +25 -0
  79. data/spec/dummy/config/environment.rb +5 -0
  80. data/spec/dummy/config/environments/development.rb +46 -0
  81. data/spec/dummy/config/environments/production.rb +79 -0
  82. data/spec/dummy/config/environments/test.rb +45 -0
  83. data/spec/dummy/config/initializers/assets.rb +11 -0
  84. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  85. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  86. data/spec/dummy/config/initializers/devise.rb +259 -0
  87. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  88. data/spec/dummy/config/initializers/high_voltage.rb +3 -0
  89. data/spec/dummy/config/initializers/inflections.rb +16 -0
  90. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  91. data/spec/dummy/config/initializers/paid_up.rb +5 -0
  92. data/spec/dummy/config/initializers/session_store.rb +3 -0
  93. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  94. data/spec/dummy/config/locales/devise.en.yml +60 -0
  95. data/spec/dummy/config/locales/en.yml +23 -0
  96. data/spec/dummy/config/routes.rb +4 -0
  97. data/spec/dummy/config/secrets.yml +22 -0
  98. data/spec/dummy/config.ru +4 -0
  99. data/spec/dummy/db/development.sqlite3 +0 -0
  100. data/spec/dummy/db/migrate/20150406154440_create_users_table.rb +7 -0
  101. data/spec/dummy/db/migrate/20150517175135_create_groups_table.rb +11 -0
  102. data/spec/dummy/db/migrate/20150518000915_add_devise_to_users.rb +49 -0
  103. data/spec/dummy/db/migrate/20150518000917_create_paid_up_features_plans_table.paid_up.rb +10 -0
  104. data/spec/dummy/db/migrate/20150518000918_create_paid_up_features_table.paid_up.rb +12 -0
  105. data/spec/dummy/db/migrate/20150518000919_create_paid_up_plans_table.paid_up.rb +15 -0
  106. data/spec/dummy/db/migrate/20150518000920_add_stripe_id_column_to_user.rb +6 -0
  107. data/spec/dummy/db/schema.rb +75 -0
  108. data/spec/dummy/db/seeds/features.seeds.rb +19 -0
  109. data/spec/dummy/db/seeds/features_plans.seeds.rb +51 -0
  110. data/spec/dummy/db/seeds/plans.seeds.rb +91 -0
  111. data/spec/dummy/db/seeds.rb +0 -0
  112. data/spec/dummy/db/test.sqlite3 +0 -0
  113. data/spec/dummy/lib/assets/.keep +0 -0
  114. data/spec/dummy/log/.keep +0 -0
  115. data/spec/dummy/log/development.log +108393 -0
  116. data/spec/dummy/public/404.html +67 -0
  117. data/spec/dummy/public/422.html +67 -0
  118. data/spec/dummy/public/500.html +66 -0
  119. data/spec/dummy/public/assets/.sprockets-manifest-34b01376fc56586f4f4fd19e7b1c0e35.json +1 -0
  120. data/spec/dummy/public/assets/application-27f004bb902952dbbaad25f0f28c312f29ffee315c94fa51dea6b5ec6dc993e6.css +13 -0
  121. data/spec/dummy/public/assets/application-3a226fbacd7ba9a2b9f7972fafdd7b2486c34707d96a60c26f3bbe4579c29ca6.js +116 -0
  122. data/spec/dummy/public/assets/bootstrap/glyphicons-halflings-regular-13634da87d9e23f8c3ed9108ce1724d183a39ad072e73e1b3d8cbf646d2d0407.eot +0 -0
  123. data/spec/dummy/public/assets/bootstrap/glyphicons-halflings-regular-42f60659d265c1a3c30f9fa42abcbb56bd4a53af4d83d316d6dd7a36903c43e5.svg +288 -0
  124. data/spec/dummy/public/assets/bootstrap/glyphicons-halflings-regular-a26394f7ede100ca118eff2eda08596275a9839b959c226e15439557a5a80742.woff +0 -0
  125. data/spec/dummy/public/assets/bootstrap/glyphicons-halflings-regular-e395044093757d82afcb138957d06a1ea9361bdcf0b442d06a18a8051af57456.ttf +0 -0
  126. data/spec/dummy/public/assets/bootstrap/glyphicons-halflings-regular-fe185d11a49676890d47bb783312a0cda5a44c4039214094e7957b4c040ef11c.woff2 +0 -0
  127. data/spec/dummy/public/favicon.ico +0 -0
  128. data/spec/dummy/test/controllers/plans_controller_controller_test.rb +7 -0
  129. data/spec/factories/feature.rb +7 -0
  130. data/spec/factories/features_plan.rb +7 -0
  131. data/spec/factories/plan.rb +7 -0
  132. data/spec/factories/user.rb +7 -0
  133. data/spec/models/paid_up/feature_spec.rb +21 -0
  134. data/spec/models/paid_up/features_plan_spec.rb +7 -0
  135. data/spec/models/paid_up/plan_spec.rb +156 -0
  136. data/spec/models/user_spec.rb +304 -0
  137. data/spec/paid_up_spec.rb +7 -0
  138. data/spec/rails_helper.rb +65 -0
  139. data/spec/routing/paid_up/plans_spec.rb +11 -0
  140. data/spec/routing/paid_up/subscription_spec.rb +23 -0
  141. data/spec/spec_helper.rb +87 -0
  142. data/spec/support/controller_macros.rb +12 -0
  143. data/spec/support/factory_girl.rb +3 -0
  144. data/spec/support/plans_and_features.rb +209 -0
  145. data/spec/support/stripe.rb +23 -0
  146. data/spec/support/subscribers.rb +44 -0
  147. data/spec/views/paid_up/plans_spec.rb +54 -0
  148. data/spec/views/paid_up/subscriptions_spec.rb +58 -0
  149. metadata +488 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8749a10b3993668ec11892a4a49391b4235db01c
4
+ data.tar.gz: 6f348418bef2d0fdc4c5b4b485050574bb8dbec9
5
+ SHA512:
6
+ metadata.gz: c58032ff450db87621d911fb5162b148359fa0c0af89a584e0dbd5b14c985ac58b226ec16d6b8390809a85dda234bdd1a524769edb700d874f726f9f125e5a68
7
+ data.tar.gz: 199c1168d660868995886ded446078f81403f91fa3ae144cf714417bffdc3ac13baddf09aeaa6d9336c134d84108a5ac5597cf1612de83cb135d076ce24bf0d3
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - ruby-2.2.1
data/Gemfile ADDED
@@ -0,0 +1,46 @@
1
+ source "https://rubygems.org"
2
+
3
+ gem 'rails', '>= 4.2'
4
+ gem 'rails-i18n'
5
+ gem 'haml-rails'
6
+ gem 'jquery-rails'
7
+ gem 'uglifier'
8
+ gem 'jeweler'
9
+
10
+ gem 'bootstrap_leather'
11
+
12
+ gem 'seedbank'
13
+ gem 'chronic'
14
+ gem 'money'
15
+
16
+ gem 'devise'
17
+ gem 'cancan'
18
+ gem 'stripe'
19
+
20
+ group :test, :development do
21
+ gem 'rspec-rails'
22
+ gem 'rspec-collection_matchers'
23
+ gem 'database_cleaner'
24
+ gem 'sqlite3'
25
+ gem 'high_voltage'
26
+ gem 'forgery'
27
+ gem 'bootstrap-sass', '~> 3.3.4'
28
+ gem 'sass-rails', '>= 3.2'
29
+ end
30
+
31
+ group :test do
32
+ gem 'cucumber-rails', require: false
33
+ gem 'shoulda-matchers'
34
+ gem 'minitest', '~>5.5.1'
35
+ gem "launchy", "~> 2.1.2", require: false
36
+ gem 'factory_girl_rails', require: false
37
+ gem 'coveralls', require: false
38
+ end
39
+
40
+ # Declare any dependencies that are still in development here instead of in
41
+ # your gemspec. These might include edge Rails or gems from your path or
42
+ # Git. Remember to move these dependencies to your gemspec before releasing
43
+ # your gem to rubygems.org.
44
+
45
+ # To use debugger
46
+ # gem 'debugger'
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2015 Gem Vein
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2015 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,64 @@
1
+ Paid Up
2
+ ===========
3
+
4
+ [![GitHub version](https://badge.fury.io/gh/gemvein%2Fpaid_up.svg)](http://badge.fury.io/gh/gemvein%2Fpaid_up)
5
+ [![Build Status](https://travis-ci.org/gemvein/paid_up.svg)](https://travis-ci.org/gemvein/paid_up)
6
+ [![Coverage Status](https://coveralls.io/repos/gemvein/paid_up/badge.png)](https://coveralls.io/r/gemvein/paid_up)
7
+
8
+ Paid Up is a start-to-finish Stripe subscription engine. You set up the plans you want on Stripe, and the gem gives you a way to tie those plans to authenticated users, granting them abilities based on the features outlined for their plan.
9
+
10
+ * Ruby 2, Rails 4
11
+ * Authentication by Devise
12
+ * Authorization by CanCan
13
+ * Subscription by Stripe
14
+ * Assumes you will be using some variety of Bootstrap, and designed to be quite responsive out of the box, but included views can be overridden with custom views.
15
+
16
+ Installation
17
+ ----------------------------
18
+ First, add paid_up to your `Gemfile`:
19
+
20
+ gem 'paid_up'
21
+
22
+ To start with, you will need a User model and the corresponding table, set up with your business rules for users, such as profile fields or theme configuration.
23
+
24
+ Next, install PaidUp for your user model by executing these commands:
25
+
26
+ bundle install
27
+ rails g paid_up:install
28
+ rake db:migrate
29
+
30
+ Configuration
31
+ ----------------------------
32
+
33
+ Edit your config file at `config/initializers/paid_up.rb` to set up some other key details.
34
+
35
+ Set your environment variables with your STRIPE_PUBLISHABLE_KEY and your STRIPE_SECRET_KEY. (Check your operating system or IDE's documentation for details)
36
+
37
+ Stripe Setup
38
+ ----------------------------
39
+
40
+ Using your own code or Stripe's convenient web interface, add the plans you intend to offer. Each will also need a record in your own database, so for each `Stripe::Plan` you create, note the `id` and use it as the `stripe_id` in the corresponding `PaidUp::Plan`. At a minimum, you will need an anonymous plan, a free plan, both with a cost amount of `0`; and also at least one paid plan.
41
+
42
+ Next, add a `Stripe::Customer` to serve as the Anonymous User, and subscribe that customer to the anonymous plan. Note the customer's `id` and copy that into your stripe configuration file.
43
+
44
+ Features Setup
45
+ ----------------------------
46
+
47
+ Set up each `PaidUp::Feature` using your own admin, the console, or seed data. Associate the features with the corresponding plans using the `PaidUp::FeaturesPlan` joining table model. For example, check out the seeds files in [`spec/dummy/db/seeds/`](spec/dummy/db/seeds/)
48
+
49
+ Contributing to Paid Up
50
+ ----------------------------
51
+
52
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
53
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
54
+ * Fork the project.
55
+ * Start a feature/bugfix branch.
56
+ * Commit and push until you are happy with your contribution.
57
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
58
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
59
+
60
+ Copyright
61
+ ---------
62
+
63
+ Copyright (c) 2015 Gem Vein. See LICENSE.txt for further details.
64
+
data/Rakefile ADDED
@@ -0,0 +1,48 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gem|
9
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
10
+ gem.name = "paid_up"
11
+ gem.homepage = "http://www.gemvein.com/museum/cases/paid_up"
12
+ gem.license = "MIT"
13
+ gem.summary = %Q{Allows a model of your choosing to subscribe to a plan, which enables features.}
14
+ gem.description = %Q{Allows a model of your choosing (such as users) to subscribe to a plan, which enables features.}
15
+ gem.email = "karen.e.lundgren@gmail.com"
16
+ gem.authors = ["Karen Lundgren"]
17
+ # dependencies defined in Gemfile
18
+ end
19
+ Jeweler::RubygemsDotOrgTasks.new
20
+
21
+ require 'rspec/core'
22
+ require 'rspec/core/rake_task'
23
+ RSpec::Core::RakeTask.new(:spec) do |spec|
24
+ spec.pattern = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
28
+ spec.pattern = 'spec/**/*_spec.rb'
29
+ spec.rcov = true
30
+ end
31
+
32
+ task :default => :spec
33
+
34
+ require 'rdoc/task'
35
+
36
+ RDoc::Task.new(:rdoc) do |rdoc|
37
+ rdoc.rdoc_dir = 'rdoc'
38
+ rdoc.title = 'PaidUp'
39
+ rdoc.options << '--line-numbers'
40
+ rdoc.rdoc_files.include('README.rdoc')
41
+ rdoc.rdoc_files.include('lib/**/*.rb')
42
+ end
43
+
44
+
45
+
46
+
47
+ Bundler::GemHelper.install_tasks
48
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,12 @@
1
+ module PaidUp
2
+ class PaidUpController < ApplicationController
3
+ helper :all
4
+
5
+ before_action :set_locale
6
+
7
+ private
8
+ def set_locale
9
+ I18n.locale = params[:locale] || I18n.default_locale
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,7 @@
1
+ module PaidUp
2
+ class PlansController < PaidUpController
3
+ def index
4
+ @plans = PaidUp::Plan.subscribable
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,37 @@
1
+ module PaidUp
2
+ class SubscriptionsController < PaidUpController
3
+ before_filter :authenticate_user!
4
+ before_filter :set_plan, only: [:new, :create]
5
+
6
+ def index
7
+ # nothing to do, everything we need is in current_user
8
+ end
9
+
10
+ def new
11
+ # nothing to do, @plan set by #set_plan
12
+ if current_user.can_downgrade_to? @plan || @plan.amount == 0
13
+ create
14
+ end
15
+ end
16
+
17
+ def create
18
+ # @plan set by #set_plan
19
+ if current_user.subscribe_to_plan(@plan, params[:stripeToken])
20
+ redirect_to subscriptions_path, flash: { notice: :you_are_now_subscribed_to_the_plan.l(plan_name: current_user.plan.name) }
21
+ else
22
+ redirect_to new_plan_subscription_path @plan, flash: { error: current_user.errors.full_messages || :could_not_subscribe_to_plan.l(plan: @plan.name) }
23
+ end
24
+ rescue Stripe::InvalidRequestError => e
25
+ flash[:error] = e.message
26
+ redirect_to plans_path
27
+ rescue Stripe::CardError => e
28
+ flash[:error] = e.message
29
+ redirect_to new_plan_subscription_path
30
+ end
31
+
32
+ private
33
+ def set_plan
34
+ @plan = PaidUp::Plan.find(params[:plan_id])
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,65 @@
1
+ module PaidUp
2
+ module FeaturesHelper
3
+ include PaidUp::PaidUpHelper
4
+
5
+ def features_dl(plan)
6
+ data = {}
7
+ features = PaidUp::Feature.all
8
+
9
+ for feature in features
10
+ data[feature.title] = feature_display feature, plan
11
+ end
12
+
13
+ dl data
14
+ end
15
+
16
+ def feature_display(feature, plan)
17
+ if feature.setting_type == 'boolean'
18
+ if plan.feature_setting(feature.id)
19
+ icon 'ok'
20
+ else
21
+ icon 'remove'
22
+ end
23
+ elsif plan.feature_unlimited?(feature.id)
24
+ :unlimited.l
25
+ else
26
+ plan.feature_setting(feature.id)
27
+ end
28
+ end
29
+
30
+ def features_table(options = {})
31
+ features = PaidUp::Feature.all
32
+
33
+ if !options[:should_add_buttons].nil?
34
+ should_add_buttons = options[:should_add_buttons]
35
+ options.delete(:should_add_buttons)
36
+ else
37
+ should_add_buttons = true
38
+ end
39
+
40
+ plans = PaidUp::Plan.subscribable
41
+ if options[:only].present?
42
+ plans = plans.where('id IN (?)', options[:only])
43
+ options.delete(:only)
44
+ end
45
+ if options[:except].present?
46
+ plans = plans.where('NOT ( id IN (?) )', options[:except])
47
+ options.delete(:except)
48
+ end
49
+
50
+ if options[:highlight].present?
51
+ highlight_plan = options[:highlight]
52
+ options.delete(:highlight)
53
+ else
54
+ highlight_plan = nil
55
+ end
56
+
57
+ render(partial: 'paid_up/features/table', locals: { should_add_buttons: should_add_buttons, plans: plans, features: features, highlight_plan: highlight_plan, html_options: options})
58
+ end
59
+
60
+ def feature_abilities_table(options = {})
61
+ features = PaidUp::Feature.all
62
+ render(partial: 'paid_up/features/abilities_table', locals: { features: features, html_options: options})
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,14 @@
1
+ module PaidUp
2
+ module PaidUpHelper
3
+ def date_range(start_date, end_date)
4
+ dates = []
5
+ if start_date.present? && (start_date.is_a?(Date) || start_date.is_a?(Integer))
6
+ dates << start_date.to_date
7
+ end
8
+ if end_date.present? && (end_date.is_a?(Date) || end_date.is_a?(Integer))
9
+ dates << end_date.to_date
10
+ end
11
+ dates.join('&mdash;').html_safe
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,53 @@
1
+ module PaidUp
2
+ module PlansHelper
3
+ include ::ActionView::Helpers::NumberHelper
4
+
5
+ def plan_charge_human(plan)
6
+ plan.money.format + '/' + plan_period_phrase(plan)
7
+ end
8
+
9
+ def plan_period_phrase(plan)
10
+ period_phrase = ''
11
+ if plan.interval_count > 1
12
+ period_phrase += plan.interval_count.to_s
13
+ period_phrase += ' '
14
+ end
15
+ period_phrase + plan.interval.capitalize
16
+ end
17
+
18
+ def plan_button(plan, text = nil, html_options = {})
19
+ data = {}
20
+ css_class = 'btn '
21
+ disabled_state = false
22
+ link = paid_up.new_plan_subscription_path(plan)
23
+ if user_signed_in?
24
+ text ||= :subscribe.l
25
+ if current_user.can_upgrade_to? plan
26
+ icon_class = 'arrow-up'
27
+ css_class += 'btn-success'
28
+ elsif current_user.can_downgrade_to? plan
29
+ icon_class = 'arrow-down'
30
+ css_class += 'btn-danger'
31
+ data[:confirm] = :are_you_sure.l
32
+ elsif current_user.is_subscribed_to? plan
33
+ icon_class = 'ok'
34
+ css_class += 'btn-disabled'
35
+ disabled_state = true
36
+ link = '#'
37
+ text = :already_subscribed.l
38
+ else # Plans are equal in sort_order
39
+ icon_class = 'arrow-right'
40
+ css_class += 'btn-info'
41
+ end
42
+ else
43
+ text ||= :sign_up.l
44
+ icon_class = 'arrow-right'
45
+ css_class += 'btn-success'
46
+ end
47
+ html_options[:method] ||= :get
48
+ html_options[:disabled] ||= disabled_state
49
+ icon_button_to css_class, icon_class, text, link, html_options
50
+ end
51
+
52
+ end
53
+ end
@@ -0,0 +1,21 @@
1
+ module PaidUp
2
+ module SubscriptionsHelper
3
+ include PaidUp::PaidUpHelper
4
+
5
+ def subscription_dl(subscription)
6
+ data = {}
7
+
8
+ data[:status.l] = subscription.status
9
+
10
+ if subscription.current_period_start || subscription.current_period_end
11
+ data[:paid_thru.l]= date_range(subscription.current_period_start, subscription.current_period_end)
12
+ end
13
+
14
+ if subscription.trial_start || subscription.trial_end
15
+ data[:trial_period.l]= date_range(subscription.trial_start, subscription.trial_end)
16
+ end
17
+
18
+ dl data
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ class Ability
2
+ include CanCan::Ability
3
+
4
+ def initialize(user)
5
+ user ||= User.new # anonymous user (not logged in)
6
+
7
+ # Rails Application's initialization could go here.
8
+
9
+ initialize_paid_up(user)
10
+ end
11
+
12
+ def initialize_paid_up(user)
13
+ features = PaidUp::Feature.all
14
+
15
+ for feature in features
16
+
17
+ case feature.setting_type
18
+ when 'table_rows'
19
+ model = feature.name.classify.constantize
20
+ if user.table_rows_allowed(feature.name) > 0 || user.table_rows_allowed(feature.name) == -1
21
+ can :manage, model, :user => user
22
+ can :own, model
23
+ unless user.table_rows_remaining(feature.name) > 0
24
+ cannot :create, model
25
+ end
26
+ end
27
+ can :read, model
28
+ when 'boolean'
29
+ if user.plan.feature_setting feature.id
30
+ can :use, feature.name.to_sym
31
+ end
32
+ else
33
+ raise(:unknown_feature_type.l)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,19 @@
1
+ class PaidUp::Feature < ActiveRecord::Base
2
+ has_many :features_plans, class_name: 'PaidUp::FeaturesPlan'
3
+ has_many :plans, :through => :features_plans, class_name: 'PaidUp::Plan'
4
+
5
+ validates_presence_of :name, :title, :setting_type
6
+
7
+ validates_with PaidUp::TableValidator, field: 'setting_type', comparison: 'table_rows', found_in: 'name'
8
+
9
+ def feature_model_name
10
+ if setting_type != 'table_rows'
11
+ raise :no_conversion_of_type_features_to_table_rows.l type: setting_type
12
+ end
13
+ name.classify
14
+ end
15
+
16
+ def feature_model
17
+ feature_model_name.constantize
18
+ end
19
+ end
@@ -0,0 +1,14 @@
1
+ class PaidUp::FeaturesPlan < ActiveRecord::Base
2
+ belongs_to :plan, class_name: 'PaidUp::Plan'
3
+ belongs_to :feature, class_name: 'PaidUp::Feature'
4
+ validates_presence_of :setting
5
+
6
+ after_initialize :catch_unlimited_in_setting
7
+
8
+ private
9
+ def catch_unlimited_in_setting
10
+ if setting == PaidUp::Unlimited.to_i(:db)
11
+ self.setting = PaidUp::Unlimited
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,99 @@
1
+ class PaidUp::Plan < ActiveRecord::Base
2
+ has_many :features_plans, class_name: 'PaidUp::FeaturesPlan'
3
+ has_many :features, :through => :features_plans, class_name: 'PaidUp::Feature'
4
+
5
+ validates_presence_of :description, :name
6
+
7
+ after_initialize :load_stripe_data
8
+
9
+ attr_accessor :stripe_data
10
+
11
+ default_scope { order('sort_order ASC') }
12
+ scope :subscribable, -> { where('sort_order >= ?', 0) }
13
+ scope :free, -> { find_by_stripe_id(PaidUp.configuration.free_plan_stripe_id) }
14
+
15
+ def reload(*args, &blk)
16
+ super *args, &blk
17
+ load_stripe_data
18
+ self
19
+ end
20
+
21
+ def feature_setting_by_name(feature_name)
22
+ feature = PaidUp::Feature.find_by_name(feature_name) || raise(:feature_not_found.l)
23
+ feature_setting(feature.id)
24
+ end
25
+
26
+ def feature_setting(feature_id)
27
+ feature = PaidUp::Feature.find(feature_id) || raise(:feature_not_found.l)
28
+ raw = features_plans.where(feature_id: feature_id)
29
+ case feature.setting_type
30
+ when 'boolean'
31
+ if raw.empty?
32
+ false
33
+ else
34
+ raw.first.setting != 0
35
+ end
36
+ when 'table_rows'
37
+ if raw.empty?
38
+ 0
39
+ else
40
+ raw.first.setting
41
+ end
42
+ else
43
+ raise :error_handling_feature_setting.l feature: feature
44
+ end
45
+ end
46
+
47
+ def feature_unlimited?(feature_id)
48
+ feature_setting(feature_id) == PaidUp::Unlimited.to_i
49
+ end
50
+
51
+ def interval
52
+ if stripe_data.present?
53
+ stripe_data.interval
54
+ else
55
+ :default_interval.l
56
+ end
57
+ end
58
+
59
+ def interval_count
60
+ if stripe_data.present?
61
+ stripe_data.interval_count
62
+ else
63
+ 1
64
+ end
65
+ end
66
+
67
+ def amount
68
+ if stripe_data.present?
69
+ stripe_data.amount
70
+ else
71
+ 0
72
+ end
73
+ end
74
+
75
+ def money
76
+ Money.new(amount, currency)
77
+ end
78
+
79
+ def charge
80
+ money.amount
81
+ end
82
+
83
+ def currency
84
+ if stripe_data.present?
85
+ stripe_data.currency.upcase
86
+ else
87
+ :default_currency.l.upcase
88
+ end
89
+ end
90
+
91
+ private
92
+
93
+ def load_stripe_data
94
+ if stripe_id.present?
95
+ self.stripe_data = Stripe::Plan.retrieve stripe_id
96
+ end
97
+ end
98
+
99
+ end
@@ -0,0 +1,13 @@
1
+ - add_title :resend_confirmation_instructions.l
2
+ = form_for(resource, as: resource_name, url: confirmation_path(resource_name), class: 'form-horizontal', html: { method: :post }) do |f|
3
+ = devise_error_messages!
4
+ .form-group
5
+ = f.label :email, class: 'col-sm-2 control-label'
6
+ .col-sm-2
7
+ = f.email_field :email, autofocus: true, class: 'col-sm-offset-2 form-control'
8
+ .form-group
9
+ .col-sm-2
10
+ .col-sm-offset-2
11
+ = f.submit :resend_confirmation_instructions.l
12
+ - add_widget do
13
+ = render "devise/shared/links"
@@ -0,0 +1,19 @@
1
+ - add_title :change_your_password.l
2
+ = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, class: 'form-horizontal' }) do |f|
3
+ = devise_error_messages!
4
+ = f.hidden_field :reset_password_token
5
+ .form-group
6
+ = f.label :password, "New password", class: 'col-sm-2 control-label'
7
+ .col-sm-2
8
+ = f.password_field :password, autofocus: true, autocomplete: "off", class: 'col-sm-offset-2 form-control'
9
+ .form-group
10
+ = f.label :password_confirmation, "Confirm new password", class: 'col-sm-2 control-label'
11
+ .col-sm-2
12
+ = f.password_field :password_confirmation, autocomplete: "off", class: 'col-sm-offset-2 form-control'
13
+ .form-group
14
+ .col-sm-2
15
+ .col-sm-2
16
+ .col-sm-offset-2
17
+ = f.submit :change_your_password.l, class: 'btn btn-primary'
18
+ - add_widget do
19
+ = render "devise/shared/links"
@@ -0,0 +1,14 @@
1
+ - add_title :forgot_your_password.l
2
+ = form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post, class: 'form-horizontal' }) do |f|
3
+ = devise_error_messages!
4
+ .form-group
5
+ = f.label :email, class: 'col-sm-2 control-label'
6
+ .col-sm-2
7
+ = f.email_field :email, autofocus: true, class: 'col-sm-offset-2 form-control'
8
+ .form-group
9
+ .col-sm-2
10
+ .col-sm-2
11
+ .col-sm-offset-2
12
+ = f.submit :send_me_reset_password_instructions.l, class: 'btn btn-primary'
13
+ - add_widget do
14
+ = render "devise/shared/links"