├── .gitignore ├── .rspec ├── .ruby-gemset ├── .ruby-version ├── CHANGELOG.textile ├── Gemfile ├── Gemfile.lock ├── README ├── README.textile ├── Rakefile ├── app ├── assets │ ├── images │ │ └── .keep │ ├── javascripts │ │ └── application.js │ └── stylesheets │ │ ├── 1st_load_framework.css.scss │ │ ├── application.css.scss │ │ └── pricing.css.scss ├── controllers │ ├── application_controller.rb │ ├── concerns │ │ └── .keep │ ├── content_controller.rb │ ├── products_controller.rb │ ├── registrations_controller.rb │ ├── users_controller.rb │ └── visitors_controller.rb ├── helpers │ └── application_helper.rb ├── jobs │ └── mailing_list_signup_job.rb ├── mailers │ ├── .keep │ ├── application_mailer.rb │ └── user_mailer.rb ├── models │ ├── .keep │ ├── concerns │ │ └── .keep │ ├── plan.rb │ └── user.rb ├── services │ ├── create_admin_service.rb │ └── create_plan_service.rb └── views │ ├── content │ ├── gold.html.erb │ ├── platinum.html.erb │ └── silver.html.erb │ ├── devise │ ├── passwords │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── registrations │ │ ├── edit.html.erb │ │ └── new.html.erb │ └── sessions │ │ └── new.html.erb │ ├── layouts │ ├── _messages.html.erb │ ├── _nav_links_for_auth.html.erb │ ├── _navigation.html.erb │ ├── _navigation_links.html.erb │ ├── application.html.erb │ ├── mailer.html.erb │ └── mailer.text.erb │ ├── pages │ └── about.html.erb │ ├── products │ └── product.pdf │ ├── user_mailer │ ├── expire_email.html.erb │ └── expire_email.text.erb │ ├── users │ ├── _user.html.erb │ ├── index.html.erb │ └── show.html.erb │ └── visitors │ └── index.html.erb ├── bin ├── bundle ├── rails ├── rake ├── rspec ├── setup └── spring ├── config.ru ├── config ├── application.rb ├── boot.rb ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── active_job.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── devise.rb │ ├── devise_permitted_parameters.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── payola.rb │ ├── session_store.rb │ └── wrap_parameters.rb ├── locales │ ├── devise.en.yml │ └── en.yml ├── routes.rb └── secrets.yml ├── db ├── migrate │ ├── 20150407101017_devise_create_users.rb │ ├── 20150407101020_add_name_to_users.rb │ ├── 20150407101027_add_role_to_users.rb │ ├── 20150408022915_create_plans.rb │ ├── 20150408022948_add_plan_ref_to_users.rb │ └── 20150408023212_remove_name_from_users.rb ├── schema.rb └── seeds.rb ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── public ├── 404.html ├── 422.html ├── 500.html ├── favicon.ico ├── humans.txt └── robots.txt ├── spec ├── factories │ └── users.rb ├── models │ └── user_spec.rb ├── rails_helper.rb └── spec_helper.rb └── vendor └── assets ├── javascripts └── .keep └── stylesheets └── .keep /.gitignore: -------------------------------------------------------------------------------- 1 | #---------------------------------------------------------------------------- 2 | # Ignore these files when commiting to a git repository. 3 | # 4 | # See http://help.github.com/ignore-files/ for more about ignoring files. 5 | # 6 | # The original version of this file is found here: 7 | # https://github.com/RailsApps/rails-composer/blob/master/files/gitignore.txt 8 | # 9 | # Corrections? Improvements? Create a GitHub issue: 10 | # http://github.com/RailsApps/rails-composer/issues 11 | #---------------------------------------------------------------------------- 12 | 13 | # bundler state 14 | /.bundle 15 | /vendor/bundle/ 16 | /vendor/ruby/ 17 | 18 | # minimal Rails specific artifacts 19 | db/*.sqlite3 20 | /db/*.sqlite3-journal 21 | /log/* 22 | /tmp/* 23 | 24 | # add /config/database.yml if it contains passwords 25 | # /config/database.yml 26 | 27 | # various artifacts 28 | **.war 29 | *.rbc 30 | *.sassc 31 | .redcar/ 32 | .sass-cache 33 | /config/config.yml 34 | /coverage.data 35 | /coverage/ 36 | /db/*.javadb/ 37 | /db/*.sqlite3 38 | /doc/api/ 39 | /doc/app/ 40 | /doc/features.html 41 | /doc/specs.html 42 | /public/cache 43 | /public/stylesheets/compiled 44 | /public/system/* 45 | /spec/tmp/* 46 | /cache 47 | /capybara* 48 | /capybara-*.html 49 | /gems 50 | /specifications 51 | rerun.txt 52 | pickle-email-*.html 53 | .zeus.sock 54 | 55 | # If you find yourself ignoring temporary files generated by your text editor 56 | # or operating system, you probably want to add a global ignore instead: 57 | # git config --global core.excludesfile ~/.gitignore_global 58 | # 59 | # Here are some files you may want to ignore globally: 60 | 61 | # scm revert files 62 | **.orig 63 | 64 | # Mac finder artifacts 65 | .DS_Store 66 | 67 | # Netbeans project directory 68 | /nbproject/ 69 | 70 | # RubyMine project files 71 | .idea 72 | 73 | # Textmate project files 74 | /*.tmproj 75 | 76 | # vim artifacts 77 | **.swp 78 | 79 | # Environment files that may contain sensitive data 80 | .env 81 | .powenv 82 | 83 | # tilde files are usually backup files from a text editor 84 | *~ 85 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | --require spec_helper 4 | --require rails_helper 5 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | rails-stripe-membership-saas 2 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.2.3 2 | -------------------------------------------------------------------------------- /CHANGELOG.textile: -------------------------------------------------------------------------------- 1 | h1. CHANGELOG 2 | 3 | h2. 2.0.2 May 11, 2015 4 | 5 | * find subscription by owner_id not email address (contributed by ThreeWest) 6 | 7 | h2. 2.0.1 April 23, 2015 8 | 9 | * update to Ruby 2.2.2 10 | 11 | h2. 2.0.0 April 8, 2015 12 | 13 | * new version using the Payola gem 14 | 15 | h2. 1.0.11 July 16, 2013 16 | 17 | * Stripe API 2013-07-05 version removed 'active_card' 18 | 19 | h2. 1.0.10 January 30, 2013 20 | 21 | * accommodate change from 'factory_girl_rails' 4.1.0 to 4.2.0 (fix issue #52) 22 | 23 | h2. 1.0.9 January 22, 2013 24 | 25 | * Devise default minimum password length changed from six to eight 26 | * fix issue #49, prevent creation of new roles by users 27 | 28 | h2. 1.0.8 December 16, 2012 29 | 30 | * add option to use a coupon when subscribing (fix issue #36) 31 | * use metatag to set STRIPE_PUBLIC_KEY in 'app/assets/javascripts/registrations.js' file (fix issue #29) 32 | * use 'find_or_create_by_name' in the 'db/seeds.rb' file to avoid duplicate roles (fix issue #41) 33 | * set roles and admin user credentials from 'application.yml' file 34 | 35 | h2. 1.0.7 December 14, 2012 36 | 37 | * use 'figaro' gem to set local environment variables for Stripe credentials 38 | 39 | h2. 1.0.6 December 7, 2012 40 | 41 | * handle cancellation when Stripe customer is already deleted 42 | * change message to 'Credit card acceptance is pending' to handle 'card declined' situations 43 | * fix issue #17, eliminate display of empty error div when form has errors 44 | 45 | h2. 1.0.5 December 2, 2012 46 | 47 | * fix issue #17: handle 'card declined' errors 48 | * add tests for Stripe authorization (contributed by Taylor Mock) 49 | * improve Registrations controller so current password is not required to change plan or credit card 50 | * fix issue #25: Capybara needs disambiguation 51 | 52 | h2. 1.0.4 November 13, 2012 53 | 54 | * display month numbers for credit card expiry on new and edit registration forms (contributed by Aaron Breckenridge) 55 | 56 | h2. 1.0.3 November 11, 2012 57 | 58 | * fix issue #15: ddded the stripe error response div to the account/edit page (contributed by Matt Witek) 59 | 60 | h2. 1.0.2 November 9, 2012 61 | 62 | * fix issue #14: make sure plan parameter persists after failed registration (contributed by Matt Witek) 63 | * fix issue #13: removing a user subscription plan won't alter the Role database (contributed by Taylor Mock) 64 | * fix issue #12: example.com email addresses can be used in development and test but not production (contributed by Matt Witek) 65 | 66 | h2. 1.0.1 October 27, 2012 67 | 68 | * fix issue #8: mass assignment error when changing plans 69 | * require a new user to choose a subscription plan (contributed by Taylor Mock) 70 | * add mailer specs (contributed by Taylor Mock) 71 | * fix rspec test for content controller (contributed by Jordan Andree) 72 | 73 | h2. 1.0.0 October 25, 2012 74 | 75 | * initial release (build using rails_apps_composer 2.2.16) 76 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | ruby '2.2.3' 3 | gem 'rails', '4.2.5' 4 | gem 'sqlite3' 5 | gem 'sass-rails', '~> 5.0' 6 | gem 'uglifier', '>= 1.3.0' 7 | gem 'coffee-rails', '~> 4.1.0' 8 | gem 'jquery-rails' 9 | gem 'jbuilder', '~> 2.0' 10 | group :development, :test do 11 | gem 'byebug' 12 | end 13 | group :development do 14 | gem 'web-console', '~> 2.0' 15 | gem 'spring' 16 | end 17 | gem 'bootstrap-sass' 18 | gem 'devise' 19 | gem 'gibbon' 20 | gem 'high_voltage' 21 | gem 'payola-payments' 22 | gem 'sucker_punch' 23 | group :development do 24 | gem 'better_errors' 25 | gem 'quiet_assets' 26 | gem 'rails_layout' 27 | gem 'spring-commands-rspec' 28 | end 29 | group :development, :test do 30 | gem 'factory_girl_rails' 31 | gem 'faker' 32 | gem 'rspec-rails' 33 | gem 'stripe-ruby-mock', '~> 2.1.1', :require => 'stripe_mock' 34 | end 35 | group :test do 36 | gem 'capybara' 37 | gem 'database_cleaner' 38 | gem 'launchy' 39 | gem 'selenium-webdriver' 40 | end 41 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | aasm (4.5.0) 5 | actionmailer (4.2.5) 6 | actionpack (= 4.2.5) 7 | actionview (= 4.2.5) 8 | activejob (= 4.2.5) 9 | mail (~> 2.5, >= 2.5.4) 10 | rails-dom-testing (~> 1.0, >= 1.0.5) 11 | actionpack (4.2.5) 12 | actionview (= 4.2.5) 13 | activesupport (= 4.2.5) 14 | rack (~> 1.6) 15 | rack-test (~> 0.6.2) 16 | rails-dom-testing (~> 1.0, >= 1.0.5) 17 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 18 | actionview (4.2.5) 19 | activesupport (= 4.2.5) 20 | builder (~> 3.1) 21 | erubis (~> 2.7.0) 22 | rails-dom-testing (~> 1.0, >= 1.0.5) 23 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 24 | activejob (4.2.5) 25 | activesupport (= 4.2.5) 26 | globalid (>= 0.3.0) 27 | activemodel (4.2.5) 28 | activesupport (= 4.2.5) 29 | builder (~> 3.1) 30 | activerecord (4.2.5) 31 | activemodel (= 4.2.5) 32 | activesupport (= 4.2.5) 33 | arel (~> 6.0) 34 | activesupport (4.2.5) 35 | i18n (~> 0.7) 36 | json (~> 1.7, >= 1.7.7) 37 | minitest (~> 5.1) 38 | thread_safe (~> 0.3, >= 0.3.4) 39 | tzinfo (~> 1.1) 40 | addressable (2.3.8) 41 | arel (6.0.3) 42 | autoprefixer-rails (6.1.0.1) 43 | execjs 44 | json 45 | bcrypt (3.1.10) 46 | better_errors (2.1.1) 47 | coderay (>= 1.0.0) 48 | erubis (>= 2.6.6) 49 | rack (>= 0.9.0) 50 | binding_of_caller (0.7.2) 51 | debug_inspector (>= 0.0.1) 52 | blankslate (3.1.3) 53 | bootstrap-sass (3.3.5.1) 54 | autoprefixer-rails (>= 5.0.0.1) 55 | sass (>= 3.3.0) 56 | builder (3.2.2) 57 | byebug (8.2.0) 58 | capybara (2.5.0) 59 | mime-types (>= 1.16) 60 | nokogiri (>= 1.3.3) 61 | rack (>= 1.0.0) 62 | rack-test (>= 0.5.4) 63 | xpath (~> 2.0) 64 | celluloid (0.17.2) 65 | celluloid-essentials 66 | celluloid-extras 67 | celluloid-fsm 68 | celluloid-pool 69 | celluloid-supervision 70 | timers (>= 4.1.1) 71 | celluloid-essentials (0.20.5) 72 | timers (>= 4.1.1) 73 | celluloid-extras (0.20.5) 74 | timers (>= 4.1.1) 75 | celluloid-fsm (0.20.5) 76 | timers (>= 4.1.1) 77 | celluloid-pool (0.20.5) 78 | timers (>= 4.1.1) 79 | celluloid-supervision (0.20.5) 80 | timers (>= 4.1.1) 81 | childprocess (0.5.8) 82 | ffi (~> 1.0, >= 1.0.11) 83 | coderay (1.1.0) 84 | coffee-rails (4.1.0) 85 | coffee-script (>= 2.2.0) 86 | railties (>= 4.0.0, < 5.0) 87 | coffee-script (2.4.1) 88 | coffee-script-source 89 | execjs 90 | coffee-script-source (1.10.0) 91 | dante (0.2.0) 92 | database_cleaner (1.5.1) 93 | debug_inspector (0.0.2) 94 | devise (3.5.2) 95 | bcrypt (~> 3.0) 96 | orm_adapter (~> 0.1) 97 | railties (>= 3.2.6, < 5) 98 | responders 99 | thread_safe (~> 0.1) 100 | warden (~> 1.2.3) 101 | diff-lcs (1.2.5) 102 | domain_name (0.5.25) 103 | unf (>= 0.0.5, < 1.0.0) 104 | erubis (2.7.0) 105 | execjs (2.6.0) 106 | factory_girl (4.5.0) 107 | activesupport (>= 3.0.0) 108 | factory_girl_rails (4.5.0) 109 | factory_girl (~> 4.5.0) 110 | railties (>= 3.0.0) 111 | faker (1.5.0) 112 | i18n (~> 0.5) 113 | faraday (0.9.2) 114 | multipart-post (>= 1.2, < 3) 115 | ffi (1.9.10) 116 | gibbon (2.1.2) 117 | faraday (>= 0.9.1) 118 | multi_json (>= 1.11.0) 119 | globalid (0.3.6) 120 | activesupport (>= 4.1.0) 121 | high_voltage (2.4.0) 122 | hitimes (1.2.3) 123 | http-cookie (1.0.2) 124 | domain_name (~> 0.5) 125 | i18n (0.7.0) 126 | jbuilder (2.3.2) 127 | activesupport (>= 3.0.0, < 5) 128 | multi_json (~> 1.2) 129 | jimson-temp (0.9.5) 130 | blankslate (>= 3.1.2) 131 | multi_json (~> 1.0) 132 | rack (~> 1.4) 133 | rest-client (~> 1.0) 134 | jquery-rails (4.0.5) 135 | rails-dom-testing (~> 1.0) 136 | railties (>= 4.2.0) 137 | thor (>= 0.14, < 2.0) 138 | json (1.8.3) 139 | launchy (2.4.3) 140 | addressable (~> 2.3) 141 | loofah (2.0.3) 142 | nokogiri (>= 1.5.9) 143 | mail (2.6.3) 144 | mime-types (>= 1.16, < 3) 145 | mime-types (2.6.2) 146 | mini_portile (0.6.2) 147 | minitest (5.8.2) 148 | multi_json (1.11.2) 149 | multipart-post (2.0.0) 150 | netrc (0.11.0) 151 | nokogiri (1.6.6.3) 152 | mini_portile (~> 0.6.0) 153 | orm_adapter (0.5.0) 154 | payola-payments (1.3.2) 155 | aasm (>= 4.0.7) 156 | jquery-rails 157 | rails (>= 4.1) 158 | stripe (= 1.20.1) 159 | stripe_event (>= 1.3.0) 160 | quiet_assets (1.1.0) 161 | railties (>= 3.1, < 5.0) 162 | rack (1.6.4) 163 | rack-test (0.6.3) 164 | rack (>= 1.0) 165 | rails (4.2.5) 166 | actionmailer (= 4.2.5) 167 | actionpack (= 4.2.5) 168 | actionview (= 4.2.5) 169 | activejob (= 4.2.5) 170 | activemodel (= 4.2.5) 171 | activerecord (= 4.2.5) 172 | activesupport (= 4.2.5) 173 | bundler (>= 1.3.0, < 2.0) 174 | railties (= 4.2.5) 175 | sprockets-rails 176 | rails-deprecated_sanitizer (1.0.3) 177 | activesupport (>= 4.2.0.alpha) 178 | rails-dom-testing (1.0.7) 179 | activesupport (>= 4.2.0.beta, < 5.0) 180 | nokogiri (~> 1.6.0) 181 | rails-deprecated_sanitizer (>= 1.0.1) 182 | rails-html-sanitizer (1.0.2) 183 | loofah (~> 2.0) 184 | rails_layout (1.0.28) 185 | railties (4.2.5) 186 | actionpack (= 4.2.5) 187 | activesupport (= 4.2.5) 188 | rake (>= 0.8.7) 189 | thor (>= 0.18.1, < 2.0) 190 | rake (10.4.2) 191 | responders (2.1.0) 192 | railties (>= 4.2.0, < 5) 193 | rest-client (1.8.0) 194 | http-cookie (>= 1.0.2, < 2.0) 195 | mime-types (>= 1.16, < 3.0) 196 | netrc (~> 0.7) 197 | rspec-core (3.4.0) 198 | rspec-support (~> 3.4.0) 199 | rspec-expectations (3.4.0) 200 | diff-lcs (>= 1.2.0, < 2.0) 201 | rspec-support (~> 3.4.0) 202 | rspec-mocks (3.4.0) 203 | diff-lcs (>= 1.2.0, < 2.0) 204 | rspec-support (~> 3.4.0) 205 | rspec-rails (3.4.0) 206 | actionpack (>= 3.0, < 4.3) 207 | activesupport (>= 3.0, < 4.3) 208 | railties (>= 3.0, < 4.3) 209 | rspec-core (~> 3.4.0) 210 | rspec-expectations (~> 3.4.0) 211 | rspec-mocks (~> 3.4.0) 212 | rspec-support (~> 3.4.0) 213 | rspec-support (3.4.0) 214 | rubyzip (1.1.7) 215 | sass (3.4.19) 216 | sass-rails (5.0.4) 217 | railties (>= 4.0.0, < 5.0) 218 | sass (~> 3.1) 219 | sprockets (>= 2.8, < 4.0) 220 | sprockets-rails (>= 2.0, < 4.0) 221 | tilt (>= 1.1, < 3) 222 | selenium-webdriver (2.48.1) 223 | childprocess (~> 0.5) 224 | multi_json (~> 1.0) 225 | rubyzip (~> 1.0) 226 | websocket (~> 1.0) 227 | spring (1.4.3) 228 | spring-commands-rspec (1.0.4) 229 | spring (>= 0.9.1) 230 | sprockets (3.4.0) 231 | rack (> 1, < 3) 232 | sprockets-rails (2.3.3) 233 | actionpack (>= 3.0) 234 | activesupport (>= 3.0) 235 | sprockets (>= 2.8, < 4.0) 236 | sqlite3 (1.3.11) 237 | stripe (1.20.1) 238 | json (~> 1.8.1) 239 | mime-types (>= 1.25, < 3.0) 240 | rest-client (~> 1.4) 241 | stripe-ruby-mock (2.1.1) 242 | dante (>= 0.2.0) 243 | jimson-temp 244 | stripe (= 1.20.1) 245 | stripe_event (1.5.0) 246 | activesupport (>= 3.1) 247 | stripe (~> 1.6) 248 | sucker_punch (1.6.0) 249 | celluloid (~> 0.17.2) 250 | thor (0.19.1) 251 | thread_safe (0.3.5) 252 | tilt (2.0.1) 253 | timers (4.1.1) 254 | hitimes 255 | tzinfo (1.2.2) 256 | thread_safe (~> 0.1) 257 | uglifier (2.7.2) 258 | execjs (>= 0.3.0) 259 | json (>= 1.8.0) 260 | unf (0.1.4) 261 | unf_ext 262 | unf_ext (0.0.7.1) 263 | warden (1.2.3) 264 | rack (>= 1.0) 265 | web-console (2.2.1) 266 | activemodel (>= 4.0) 267 | binding_of_caller (>= 0.7.2) 268 | railties (>= 4.0) 269 | sprockets-rails (>= 2.0, < 4.0) 270 | websocket (1.2.2) 271 | xpath (2.0.0) 272 | nokogiri (~> 1.3) 273 | 274 | PLATFORMS 275 | ruby 276 | 277 | DEPENDENCIES 278 | better_errors 279 | bootstrap-sass 280 | byebug 281 | capybara 282 | coffee-rails (~> 4.1.0) 283 | database_cleaner 284 | devise 285 | factory_girl_rails 286 | faker 287 | gibbon 288 | high_voltage 289 | jbuilder (~> 2.0) 290 | jquery-rails 291 | launchy 292 | payola-payments 293 | quiet_assets 294 | rails (= 4.2.5) 295 | rails_layout 296 | rspec-rails 297 | sass-rails (~> 5.0) 298 | selenium-webdriver 299 | spring 300 | spring-commands-rspec 301 | sqlite3 302 | stripe-ruby-mock (~> 2.1.1) 303 | sucker_punch 304 | uglifier (>= 1.3.0) 305 | web-console (~> 2.0) 306 | 307 | BUNDLED WITH 308 | 1.10.6 309 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Rails Stripe Membership Saas 2 | ======================== 3 | 4 | You can use this project as a starting point for a Rails web application. It requires Rails 4.2 and uses Devise for user management and authentication, Bootstrap for CSS styling, with Payola and Stripe for recurring billing and product sales. 5 | 6 | "Devise":http://github.com/plataformatec/devise 7 | 8 | "Bootstrap":http://twitter.github.com/bootstrap/ 9 | 10 | "Stripe":https://stripe.com/ 11 | 12 | "Payola":https://www.payola.io/ 13 | 14 | ________________________ 15 | 16 | For more information, please see the updated README file on GitHub: 17 | 18 | "README":https://github.com/railsapps/rails-stripe-membership-saas 19 | 20 | ________________________ 21 | 22 | "MIT License":http://www.opensource.org/licenses/mit-license 23 | -------------------------------------------------------------------------------- /README.textile: -------------------------------------------------------------------------------- 1 | h1. !http://railsapps.github.io/images/rails-36x36.jpg(Rails Application for a Membership, Subscription, or SaaS Site)! Membership Site with Stripe 2 | 3 | Rails 4.2 example application demonstrates how to use "Stripe":https://stripe.com/ and the "Payola":https://www.payola.io/ gem for a Rails membership site, subscription site, or SaaS site (software-as-a-service). Use this example application as a starter app for your own web applications. 4 | 5 | This application replaces a very popular Rails 3.2 implementation, available in a "Rails 3.2 branch":https://github.com/RailsApps/rails-stripe-membership-saas/tree/rails3.2, and now uses the "Payola":https://www.payola.io/ gem. 6 | 7 | !http://railsapps.github.io/images/join/join-railsapps.png(Join the RailsApps Project)!:http://railsapps.github.io/ 8 | 9 | See the tutorial for a complete explanation of the code: 10 | 11 | * "*Membership Site with Stripe*":https://tutorials.railsapps.org/rails-stripe-membership-saas 12 | 13 | This is a complex application. To understand the code, you'll need the tutorial. 14 | 15 | The application uses: 16 | 17 | * "Payola":https://www.payola.io/ for Stripe integration with Rails 18 | * "Stripe":https://stripe.com/ for credit card payment processing 19 | * "Devise":https://github.com/plataformatec/devise for user management and authentication 20 | * "Role-Based Authorization":http://railsapps.github.io/rails-devise-roles for administrator access 21 | * "Bootstrap":http://getbootstrap.com/ front-end framework 22 | 23 | Other tutorials may be helpful: 24 | 25 | * "Signup and Download":https://tutorials.railsapps.org/rails-signup-download 26 | * "Role-Based Authorization":http://railsapps.github.io/rails-devise-roles 27 | * "Devise Tutorial":http://railsapps.github.io/rails-devise/ 28 | * "RSpec Tutorial":http://railsapps.github.io/rspec.html 29 | 30 | You can build this application in only a few minutes using the "Rails Composer":http://railsapps.github.io/rails-composer/ tool. 31 | 32 | !http://railsapps.github.io/images/rails-stripe-membership-saas.png(Rails Application for a Membership, Subscription, or SaaS Site)! 33 | 34 | h4. From the RailsApps Project 35 | 36 | The "RailsApps":http://railsapps.github.io/ open source project offers starter applications and tutorials for Rails developers. Generate the applications with the Rails Composer tool. 37 | 38 | All the code is explained in the Capstone Rails Tutorials. You can purchase the "Capstone Rails Tutorials":https://tutorials.railsapps.org/ to support the project. 39 | 40 | h4. If You Are New to Rails 41 | 42 | If you're new to Rails, see "What is Ruby on Rails?":http://railsapps.github.io/what-is-ruby-rails.html, the book "Learn Ruby on Rails":http://learn-rails.com/learn-ruby-on-rails.html, and recommendations for a "Rails tutorial":https://tutorials.railsapps.org/rails-tutorial. 43 | 44 | h4. Database 45 | 46 | The application requires a database. The example application uses SQLite with Rails ActiveRecord. You can easily substitute PostgreSQL, MySQL, or other databases. 47 | 48 | h4. Front-end Framework 49 | 50 | The example application integrates Bootstrap for a navigation bar and flash messages. The forms are set up to use Bootstrap. You can customize the application to use other front-end frameworks such as Zurb Foundation. 51 | 52 | h2. What's Here 53 | 54 | Stripe offers two approaches to implementing payment processing. "Stripe Checkout":https://stripe.com/checkout is Stripe’s entry-level approach. Stripe Checkout competes with the button-based payment options from Google, PayPal, or Amazon, adding a pop-up payment form to any web page. Stripe Checkout is very limited because the pop-up payment form cannot be customized for use with a Rails application. Our "Stripe Checkout Tutorial":https://tutorials.railsapps.org/tutorials/rails-stripe-checkout shows how to combine Stripe Checkout with Devise for simple applications. 55 | 56 | "Stripe.js":https://stripe.com/docs/stripe.js is optimal for use with a Rails application, allowing full customization of a payment form and integration with Rails form processing. The "rails-stripe-coupons":https://github.com/RailsApps/rails-stripe-coupons starter application implements a payment feature using Stripe JS so a visitor pays to download a PDF file. The application accommodates promotional coupons and adds payment forms to landing pages, for real-world payment processing. 57 | 58 | This example application, "rails-stripe-membership-saas":https://github.com/RailsApps/rails-stripe-membership-saas, provides subscription billing using Stripe and the Payola gem. 59 | 60 | The application offers these features: 61 | 62 | * tiered pricing for multiple subscription plans 63 | * optional "free trial" subscription as well as free accounts using Stripe 64 | * uses Stripe for no local credit card storage 65 | * Stripe accepts credit card payments from customers in any country or currency 66 | * PCI compliance using the Stripe JavaScript library 67 | * Stripe handles recurring billing, retries if payment fails, and cancels subscription if retries fail 68 | * paid subscriptions are created only after a successful credit card transaction 69 | * subscribers can upgrade or downgrade subscription plans 70 | * subscribers can cancel subscription plans 71 | 72 | Additionally: 73 | 74 | * background processing adds the email address to a mailing list 75 | * background processing completes payment for a speedy response 76 | 77 | h2. What Is Not Here 78 | 79 | There are additional features you may want for a SaaS application, such as: 80 | 81 | * Basecamp-style subdomains (each user gets their own subdomain) 82 | * "multitenancy":http://en.wikipedia.org/wiki/Multitenancy database segmentation 83 | 84 | These features are not included in this application. For multitenancy, try Brad Robertson's "Apartment":https://github.com/bradrobertson/apartment gem. 85 | 86 | h2. Similar Examples and Tutorials 87 | 88 | This is one in a series of Rails example apps and tutorials from the "RailsApps Project":http://railsapps.github.io/. See a list of additional "Rails examples, tutorials, and starter apps":http://railsapps.github.io/rails-examples-tutorials.html. Related example applications may be useful: 89 | 90 | * "Learn Rails":https://github.com/RailsApps/learn-rails companion to the book "Learn Ruby on Rails":http://learn-rails.com/learn-ruby-on-rails.html 91 | * "Foundation and Rails":http://railsapps.github.io/rails-foundation/ shows how to integrate Foundation 92 | * "Bootstrap and Rails":http://railsapps.github.io/rails-bootstrap/ shows to integrate Bootstrap 93 | * "Mailing Lists with Active Job":https://tutorials.railsapps.org/rails-mailinglist-activejob for MailChimp subscriptions 94 | * "OmniAuth and Rails":https://github.com/RailsApps/rails-omniauth uses OmniAuth for authentication 95 | * "Devise and Rails":https://github.com/RailsApps/rails-devise uses Devise for authentication 96 | * "Role-Based Authorization":http://railsapps.github.io/rails-devise-roles using simple roles 97 | * "Pundit and Rails":https://github.com/RailsApps/rails-devise-pundit uses Pundit for authorization 98 | * "Signup and Download":https://tutorials.railsapps.org/rails-signup-download combines authentication with authorization and a PDF download 99 | * "Stripe Checkout":https://tutorials.railsapps.org/rails-stripe-checkout/ 100 | * "Stripe JS With Coupons":https://tutorials.railsapps.org/rails-stripe-coupons/ 101 | 102 | h2. Accounts You Will Need 103 | 104 | Get the accounts you will need before deploying the application. 105 | 106 | h3. Email 107 | 108 | Devise provides a "Forgot Password?" feature that resets a password and sends instructions to the user. You'll need an email service provider to send email from the application. You can use "Gmail":https://accounts.google.com/SignUp?service=mail during development. You can get a free "Gmail":https://accounts.google.com/SignUp?service=mail account if you don't already have one. For production, Gmail is not robust. Use transactional email services, such as "Mandrill":http://mandrill.com/, to send email in production. See the article "Send Email with Rails":http://railsapps.github.io/rails-send-email.html for more information. 109 | 110 | h3. Stripe 111 | 112 | A "Stripe":https://stripe.com/ account is required. Before you start, go to the "Stripe website":https://stripe.com/ and set up an account. You don’t need a credit card merchant account or payment gateway. There’s no approval process to delay getting started. 113 | 114 | h3. Merchant Account 115 | 116 | Your business will need a merchant account in order to accept credit card payments. If you already have a merchant account, you can use it with Stripe. Stripe provides a merchant account as part of its service, so you don't need to obtain one. 117 | 118 | h3. MailChimp 119 | 120 | A "MailChimp":http://mailchimp.com account is optional. The application User model includes a method to subscribe a new user to a MailChimp mailing list when an account is created. If you choose to use this feature, you'll need a MailChimp account. 121 | 122 | When visitors submit an email address, the application will add them to a MailChimp list. To access MailChimp, we’ll need a MailChimp API key. "Log in to MailChimp"https://admin.mailchimp.com/ to get your API key. Click your name at the top of the navigation menu, then click "Account". Click "Extras," then "API keys." You have to generate an API key; MailChimp doesn’t create one automatically. 123 | 124 | You’ll also need to create a MailChimp mailing list. The MailChimp "Lists" page has a button for "Create List." The list name and other details are up to you. We’ll need the MAILCHIMP_LIST_ID for the mailing list you’ve created. To find the list ID, on the MailChimp "Lists" page, click the "down arrow" for a menu and click "Settings." At the bottom of the "List Settings" page, you’ll find the unique ID for the mailing list. 125 | 126 | With MailChimp, you can send a welcome message automatically when the visitor signs up for the mailing list. Use the welcome message to inform the visitor that they've successfully subscribed to the mailing list. It’s a bit difficult to find the MailChimp option to create a welcome message. Strangely, MailChimp considers a welcome message a "form." Here's how to find it. On the MailChimp "Lists" page, click the "down arrow" for a list and click "Signup forms." Then click "General forms." On the "Create Forms" page, there is a drop-down list of "Forms & Response Emails." The gray box shows "Signup form." Click the down arrow. Select the menu item named "Final 'Welcome' Email" and you'll be able to create a welcome message. 127 | 128 | h3. Hosting 129 | 130 | We provide instructions to deploy the application to "Heroku":https://www.heroku.com/ which provides Rails application hosting. It costs nothing to set up a Heroku account and deploy as many applications as you want. To deploy an app to Heroku, you must have a Heroku account. Visit Heroku "to set up an account":https://id.heroku.com/signup/devcenter. 131 | 132 | h3. SSL 133 | 134 | Visitors to your website will be sending credit card information from their browser to Stripe’s servers when they sign up for a subscription. The Stripe JavaScript library will open an SSL connection to Stripe’s servers when the form is submitted. You can host your site without SSL and your users’ credit card numbers will be protected on the way to Stripe’s servers. However, your security-conscious visitors will be uneasy if they see that the web URL for your registration page begins with @http://@ and not @https://@ (indicating an SSL connection). For their peace of mind (and the higher conversion rate that comes with trust), you should host your website with an SSL connection. Additionally, as a general practice, it is wise to host any webapp that requires login over an SSL connection. 135 | 136 | If you’re deploying with Heroku, you can access any Heroku app over SSL at https://myapp.herokuapp.com/. Setting up an SSL certificate for a custom domain on Heroku can be a hassle but there’s a convenient alternative that is a better value. You can use "CloudFlare":http://cloudflare.com/ to get free SSL without purchasing or installing an SSL certificate. CloudFlare is a content delivery network (CDN) and website optimizer; their free plan includes "SSL":https://www.cloudflare.com/ssl. See the article "Configuring CloudFlare DNS for a Heroku App":http://www.higherorderheroku.com/articles/cloudflare-dns-heroku/. If you’re deploying on Heroku, you can wait until you’ve deployed to sign up for a Cloudflare account. 137 | 138 | h2. Dependencies 139 | 140 | Before generating your application, you will need: 141 | 142 | * The Ruby language - version 2.2 143 | * The Rails gem - version 4.2 144 | 145 | See the article "Installing Rails":http://railsapps.github.io/installing-rails.html for instructions about setting up Rails and your development environment. See the article "Updating to Rails 4.2":http://railsapps.github.io/updating-rails.html if you are using Rails 4.1. You must install Rails 4.2 to use this application because Active Job is not available in earlier versions of Rails. 146 | 147 | h2. Getting the Application 148 | 149 | h3. Local 150 | 151 | You have several options for getting the code on your own machine. You can _fork_, _clone_, or _generate_. 152 | 153 | h4. Fork 154 | 155 | If you'd like to add features (or bug fixes) to improve the example application, you can fork the GitHub repo and "make pull requests":http://help.github.com/send-pull-requests/. Your code contributions are welcome! 156 | 157 | h4. Clone 158 | 159 | If you want to copy and customize the app with changes that are only useful for your own project, you can clone the GitHub repo. You'll need to search-and-replace the project name throughout the application. You probably should generate the app instead (see below). To clone: 160 | 161 |
162 | $ git clone git://github.com/RailsApps/rails-stripe-membership-saas.git 163 |164 | 165 | You'll need "git":http://git-scm.com/ on your machine. See "Rails and Git":http://railsapps.github.io/rails-git.html. 166 | 167 | h4. Generate 168 | 169 | If you want to use the project as a starter application, use the "Rails Composer":http://railsapps.github.io/rails-composer/ tool to generate a new version of the example app. You'll be able to give it your own project name when you generate the app. Generating the application gives you additional options. 170 | 171 | To build the example application, Rails 4.2 must be installed in your development environment. Run the command: 172 | 173 |
174 | $ rails new rails-stripe-membership-saas -m https://raw.github.com/RailsApps/rails-composer/master/composer.rb 175 |176 | 177 | The @$@ character indicates a shell prompt; don't include it when you run the command. 178 | 179 | This creates a new Rails app named @rails-stripe-membership-saas@ on your computer. You can use a different name if you wish. 180 | 181 | You'll see a prompt: 182 | 183 |
184 | option Build a starter application? 185 | 1) Build a RailsApps example application 186 | 2) Contributed applications 187 | 3) Custom application 188 |189 | 190 | Enter "1" to select *Build a RailsApps example application*. You'll see a prompt: 191 | 192 |
193 | option Choose a starter application. 194 | 1) learn-rails 195 | 2) rails-bootstrap 196 | 3) rails-foundation 197 | 4) rails-mailinglist-activejob 198 | 5) rails-omniauth 199 | 6) rails-devise 200 | 7) rails-devise-roles 201 | 8) rails-devise-pundit 202 | 9) rails-signup-download 203 | 10) rails-stripe-checkout 204 | 11) rails-stripe-coupons 205 | 12) rails-stripe-membership-saas 206 |207 | 208 | Choose *rails-stripe-membership-saas*. The Rails Composer tool may give you other options (other applications may have been added since these notes were written). 209 | 210 | The application generator template will ask you for additional preferences: 211 | 212 |
213 | option Web server for development? 214 | 1) WEBrick (default) 215 | 2) Thin 216 | 3) Unicorn 217 | 4) Puma 218 | 5) Phusion Passenger (Apache/Nginx) 219 | 6) Phusion Passenger (Standalone) 220 | option Web server for production? 221 | 1) Same as development 222 | 2) Thin 223 | 3) Unicorn 224 | 4) Puma 225 | 5) Phusion Passenger (Apache/Nginx) 226 | 6) Phusion Passenger (Standalone) 227 | option Database used in development? 228 | 1) SQLite 229 | 2) PostgreSQL 230 | 3) MySQL 231 | option Template engine? 232 | 1) ERB 233 | 2) Haml 234 | 3) Slim 235 | option Test framework? 236 | 1) None 237 | 2) RSpec with Capybara 238 | option Continuous testing? 239 | 1) None 240 | 2) Guard 241 | option Add support for sending email? 242 | 1) None 243 | 2) Gmail 244 | 3) SMTP 245 | 4) SendGrid 246 | 5) Mandrill 247 | option Admin interface for database? 248 | 1) None 249 | 2) Upmin 250 | option Install page-view analytics? 251 | 1) None 252 | 2) Google Analytics 253 | 3) Segment.io 254 | option Prepare for deployment? 255 | 1) no 256 | 2) Heroku 257 | 3) Capistrano 258 | option Set a robots.txt file to ban spiders? (y/n) n 259 | option Create a GitHub repository? (y/n) n 260 | option Use or create a project-specific rvm gemset? (y/n) y 261 |262 | 263 | h4. Web Servers 264 | 265 | If you plan to deploy to Heroku, select Puma as your production webserver. Puma is recommended by Heroku. 266 | 267 | h4. Database 268 | 269 | Use SQLite for development on Mac or Linux, unless you already have PostgreSQL installed locally. Use PostgreSQL if you plan to deploy to Heroku. You can easily change the database later if you select SQLite to start. 270 | 271 | h4. Template Engine 272 | 273 | The example application uses the default "ERB" Rails template engine. Optionally, you can use another template engine, such as Haml or Slim. See instructions for "Haml and Rails":http://railsapps.github.io/rails-haml.html. 274 | 275 | h4. Testing 276 | 277 | If you are a beginner, select "None." 278 | 279 | h4. Email 280 | 281 | Choose Gmail for development if you already have a Gmail account. Choose SendGrid or Mandrill for production if your site will be heavily used. 282 | 283 | h4. Other Choices 284 | 285 | Set a robots.txt file to ban spiders if you want to keep your new site out of Google search results. 286 | 287 | If you choose to create a GitHub repository, the generator will prompt you for a GitHub username and password. 288 | 289 | It is a good idea to use "RVM":https://rvm.io/, the Ruby Version Manager, and create a project-specific RVM gemset (not available on Windows). See "Installing Rails":http://railsapps.github.io/installing-rails.html. 290 | 291 | h4. Troubleshooting 292 | 293 | If you get an error "OpenSSL certificate verify failed" or "Gem::RemoteFetcher::FetchError: SSL_connect" see the article "OpenSSL errors and Rails":http://railsapps.github.io/openssl-certificate-verify-failed.html. 294 | 295 | h3. Edit the README 296 | 297 | If you're storing the app in a GitHub repository, please edit the README files to add a description of the app and your contact info. If you don't change the README, people will think I am the author of your version of the application. 298 | 299 | h2. Getting Started 300 | 301 | See the article "Installing Rails":http://railsapps.github.io/installing-rails.html to make sure your development environment is prepared properly. 302 | 303 | h3. Use RVM 304 | 305 | I recommend using "RVM":https://rvm.io/, the Ruby Version Manager, to create a project-specific gemset for the application. If you generate the application with the Rails Composer tool, you can create a project-specific gemset. 306 | 307 | h3. Gems 308 | 309 | Here are the gems used by the application: 310 | 311 | * "bootstrap-sass":https://github.com/thomas-mcdonald/bootstrap-sass provides a front-end framework 312 | * "devise":http://github.com/plataformatec/devise for authentication and user management 313 | * "gibbon":https://github.com/amro/gibbon provides an API wrapper for MailChimp 314 | * "payola-payments":https://github.com/thoughtbot/high_voltage a Rails engine for Stripe integration 315 | * "sucker_punch":https://github.com/brandonhilkert/sucker_punch provides queuing for background jobs 316 | 317 | These gems make development easier: 318 | 319 | * "better_errors":https://github.com/charliesome/better_errors - helps when things go wrong 320 | * "quiet_assets":https://github.com/evrone/quiet_assets - suppresses distracting messages in the log 321 | * "rails_layout":https://github.com/RailsApps/rails_layout - generates files for an application layout 322 | 323 | h3. Install the Required Gems 324 | 325 | If you used the "Rails Composer":http://railsapps.github.io/rails-composer/ tool to generate the example app, the application template script has already run the @bundle install@ command. 326 | 327 | If not, you should run the @bundle install@ command to install the required gems on your computer: 328 | 329 |
330 | $ bundle install 331 |332 | 333 | You can check which gems are installed on your computer with: 334 | 335 |
336 | $ gem list 337 |338 | 339 | Keep in mind that you have installed these gems locally. When you deploy the app to another server, the same gems (and versions) must be available. 340 | 341 | h3. Configuration File 342 | 343 | To consolidate configuration settings in a single location, we store credentials in the *config/secrets.yml* file. To keep your credentials private, use Unix environment variables to set your credentials. See the article "Rails Environment Variables":http://railsapps.github.io/rails-environment-variables.html for more information. 344 | 345 | Add your credentials to the file *config/secrets.yml*: 346 | 347 |
348 | # Make sure the secrets in this file are kept private 349 | # if you're sharing your code publicly. 350 | 351 | development: 352 | admin_name: First User 353 | admin_email: user@example.com 354 | admin_password: changeme 355 | email_provider_username: <%= ENV["GMAIL_USERNAME"] %> 356 | email_provider_password: <%= ENV["GMAIL_PASSWORD"] %> 357 | domain_name: example.com 358 | mailchimp_api_key: <%= ENV["MAILCHIMP_API_KEY"] %> 359 | mailchimp_list_id: <%= ENV["MAILCHIMP_LIST_ID"] %> 360 | stripe_api_key: <%= ENV["STRIPE_API_KEY"] %> 361 | stripe_publishable_key: <%= ENV["STRIPE_PUBLISHABLE_KEY"] %> 362 | secret_key_base: very_long_random_string 363 | 364 | test: 365 | domain_name: example.com 366 | secret_key_base: very_long_random_string 367 | 368 | # Do not keep production secrets in the repository, 369 | # instead read values from the environment. 370 | production: 371 | admin_name: <%= ENV["ADMIN_NAME"] %> 372 | admin_email: <%= ENV["ADMIN_EMAIL"] %> 373 | admin_password: <%= ENV["ADMIN_PASSWORD"] %> 374 | email_provider_username: <%= ENV["GMAIL_USERNAME"] %> 375 | email_provider_password: <%= ENV["GMAIL_PASSWORD"] %> 376 | domain_name: example.com 377 | mailchimp_api_key: <%= ENV["MAILCHIMP_API_KEY"] %> 378 | mailchimp_list_id: <%= ENV["MAILCHIMP_LIST_ID"] %> 379 | stripe_api_key: <%= ENV["STRIPE_API_KEY"] %> 380 | stripe_publishable_key: <%= ENV["STRIPE_PUBLISHABLE_KEY"] %> 381 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 382 |383 | 384 | All configuration values in the *config/secrets.yml* file are available anywhere in the application as variables. For example, @Rails.application.secrets.email_provider_username@ will return the string set in the Unix environment variable @GMAIL_USERNAME@. 385 | 386 | If you don't want to use Unix environment variables, you can set each value directly in the *config/secrets.yml* file. The file must be in your git repository when you deploy to Heroku. However, you shouldn't save the file to a public GitHub repository where other people can see your credentials. 387 | 388 | For the Gmail username and password, enter the credentials you use to log in to Gmail when you check your inbox. See the article "Send Email with Rails":http://railsapps.github.io/rails-send-email.html if you are using Google two factor authentication. 389 | 390 | The values for @admin_email@ and @admin_password@ are used when the database is seeded. You will be able to log in to the application with these credentials. Note that it's not necessary to personalize the *config/secrets.yml* file before you deploy your app. You can deploy the app with an example user and then use the application's "Edit Account" feature to change email address and password after you log in. Use this feature to log in as an administrator and change the email and password to your own. 391 | 392 | The variable @domain_name@ is used for sending email. You can use @example.com@ in development. If you already have a custom domain name you'll use when you deploy the application, you can set @domain_name@. If you deploy the application to Heroku, you'll set @domain_name@ with the unique name you've given your application on Heroku. You'll have to wait until you deploy to know the name you'll use on Heroku. 393 | 394 | See the section "Accounts You Will Need" to learn where to find the @mailchimp_api_key@ and @mailchimp_list_id@ credentials. 395 | 396 | You can find the @stripe_api_key@ and @stripe_publishable_key@ credentials when you log in to view the "Stripe Dashboard":https://dashboard.stripe.com/account/apikeys. Look under "Account Settings" for the "API Keys" tab. If you use the "Test Secret Key" and "Test Publishable Key" during development, you can enter payments without getting charged. 397 | 398 | h3. Roles 399 | 400 | The application manages access for users in multiple roles: @admin@ and @user@, as well as three roles corresponding to subscription plans: @silver@, @gold@, and @platinum@. You can customize the application for additional roles if needed. 401 | 402 | Roles are defined in the *app/models/user.rb* file (the @User@ model). 403 | 404 |
405 | class User < ActiveRecord::Base 406 | . 407 | . 408 | . 409 | enum role: [:user, :admin, :silver, :gold, :platinum] 410 | after_initialize :set_default_role, :if => :new_record? 411 | 412 | def set_default_role 413 | self.role ||= :user 414 | end 415 | 416 | end 417 |418 | 419 | You can change the available roles by changing the array @[:user, :admin, :silver, :gold, :platinum]@. 420 | 421 | The application uses the ActiveRecord @enum@ method to manage roles. ActiveRecord provides convenient methods to query the role attribute: 422 | 423 |
424 | user.admin! # sets the role to "admin" 425 | user.admin? # => true 426 | user.role # => "admin" 427 |428 | 429 | See documentation for "ActiveRecord::Enum":http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html for details. 430 | 431 | h3. Database Seed File 432 | 433 | The *db/seeds.rb* file initializes the database with default values. 434 | 435 |
436 | # This file should contain all the record creation needed to seed the database with its default values. 437 | # The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). 438 | # 439 | # Examples: 440 | # 441 | # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) 442 | # Mayor.create(name: 'Emanuel', city: cities.first) 443 | user = CreateAdminService.new.call 444 | puts 'CREATED ADMIN USER: ' << user.email 445 | CreatePlanService.new.call 446 | puts 'CREATED PLANS' 447 |448 | 449 | @CreateAdminService@ is a service object that obtains @admin_email@ and @admin_password@ values from the *config/secrets.yml* file. You can examine the file *app/services/create_admin_service.rb* to see how a new user is created. @CreatePlanService@ is a service object that creates subscription plans. 450 | 451 | h3. Set the Database 452 | 453 | If you've used the Rails Composer tool to generate the application, the database is already set up with @rake db:migrate@ and @rake db:seed@. 454 | 455 | If you've cloned the repo, prepare the database and add the default user to the database by running the commands: 456 | 457 |
458 | $ rake db:migrate 459 | $ rake db:seed 460 |461 | 462 | Use @rake db:reset@ if you want to empty and reseed the database. 463 | 464 | If you’re not using "rvm":https://rvm.io/, the Ruby Version Manager, you should preface each rake command with @bundle exec@. You don’t need to use @bundle exec@ if you are using rvm version 1.11.0 or newer. 465 | 466 | h3. Stripe Webhooks 467 | 468 | When a credit card expires or a monthly transaction is declined, Stripe will automatically retry a recurring payment after it fails. After a number of attempts (set in your Stripe account settings), Stripe will cancel the subscription. Your application needs to know to deny access for a subscriber with an expired account. Stripe provides webhooks to communicate events to you (for details, see the "Stripe webhooks documentation":https://stripe.com/docs/webhooks). 469 | 470 | A Stripe webhook is an HTTP request from Stripe's servers to your site, containing JSON data that provides data about the event, plus an event id that can be used to retrieve the data from the Stripe server. The example application responds to Stripe webhooks, using an implementation provided by Danny Whalen’s "stripe_event":https://github.com/integrallis/stripe_event gem, which is provided with the Payola gem. The application responds to webhook requests at "https://www.example.com/payola/events":https://www.example.com/payola/events. 471 | 472 | The example application only responds to "customer.subscription.deleted" events. You can customize the application to respond to other events (such as sending a thank you email in response to an “invoice.payment_succeeded” event). 473 | 474 | For webhooks to work, you must visit your Stripe dashboard at "https://manage.stripe.com/#account/webhooks":https://manage.stripe.com/#account/webhooks and add the URL for your application, such as "https://www.example.com/payola/events":https://www.example.com/payola/events/. 475 | 476 | h3. Change your Application's Secret Token 477 | 478 | If you've used the Rails Composer tool to generate the application, the application's secret token will be unique, just as with any Rails application generated with the @rails new@ command. 479 | 480 | However, if you've cloned the application directly from GitHub, it is crucial that you change the application's secret token before deploying your application in production mode. Otherwise, people could change their session information, and potentially access your site without permission. Your secret token should be at least 30 characters long and completely random. 481 | 482 | Get a unique secret token: 483 | 484 |
485 | rake secret 486 |487 | 488 | Edit the *config/secrets.yml* file to change the secret token. 489 | 490 | h2. Test the App 491 | 492 | You can check that your application runs properly by entering the command: 493 | 494 |
495 | $ rails server 496 |497 | 498 | To see your application in action, open a browser window and navigate to "http://localhost:3000/":http://localhost:3000. 499 | 500 | You should see a home page with a navigation bar. Click "Sign Up." 501 | 502 | Fill in the form. Use the fake credit card number 4242424242424242 and any three-digit number for a security code. Submit the form. 503 | 504 | You’ll be redirected to a page for your subscription plan and see a message “Welcome! You have signed up successfully.” You’ll be logged in as a new user. 505 | 506 | Visit your Stripe dashboard at https://manage.stripe.com/#test/customers and see the new customer you’ve created. If you check the customer details, you should see the fake credit card number and a payment record. 507 | 508 | Sign out and sign in as the administrator (with the credentials in the *config/secrets.yml* file). You'll be able to see a list of users. 509 | 510 | Stop the server with Control-C. If you test the app by starting the web server and then leave the server running while you install new gems, you’ll have to restart the server to see any changes. The same is true for changes to configuration files in the config folder. This can be confusing to new Rails developers because you can change files in the app folders without restarting the server. Stop the server each time after testing and you will avoid this issue. 511 | 512 | h2. Deploy to Heroku 513 | 514 | Heroku provides low cost, easily configured Rails application hosting. 515 | 516 | You can deploy from the command line. 517 | 518 | If you've set configuration values in the *config/secrets.yml* file, you'll need to set them as Heroku environment variables. You can set Heroku environment variables directly with @heroku config:add@. For example: 519 | 520 |
521 | $ heroku config:add MAILCHIMP_API_KEY='a6v34ggf23c123098765fcc6c996c540-us2' MAILCHIMP_LIST_ID='4x8bfgb034' 522 |523 | 524 | Complete Heroku deployment with: 525 | 526 |
527 | $ git push heroku master 528 |529 | 530 | See the "Tutorial for Rails on Heroku":http://railsapps.github.io/rails-heroku-tutorial.html for details. 531 | 532 | h2. Troubleshooting 533 | 534 | Problems? Check the "issues":https://github.com/RailsApps/rails-stripe-membership-saas/issues. 535 | 536 | h2. Issues 537 | 538 | Please create a "GitHub issue":https://github.com/RailsApps/rails-stripe-membership-saas/issues if you identify any problems or have suggestions for improvements. 539 | 540 | h2. Where to Get Help 541 | 542 | Your best source for help with problems is "Stack Overflow":http://stackoverflow.com/questions/tagged/ruby-on-rails-3. Your issue may have been encountered and addressed by others. 543 | 544 | Use the tag "railsapps" on Stack Overflow for extra attention. 545 | 546 | h2. Contributing 547 | 548 | If you make improvements to this application, please share with others. 549 | 550 | Send the author a message, create an "issue":https://github.com/RailsApps/rails-stripe-membership-saas/issues, or fork the project and submit a pull request. 551 | 552 | If you add functionality to this application, create an alternative implementation, or build an application that is similar, please contact me and I'll add a note to the README so that others can find your work. 553 | 554 | h2. Credits 555 | 556 | Daniel Kehoe implemented the application and wrote the tutorial. 557 | 558 | Is the app useful to you? Follow the project on Twitter: "@rails_apps":http://twitter.com/rails_apps 559 | and tweet some praise. I'd love to know you were helped out by what I've put together. 560 | 561 | h2. MIT License 562 | 563 | "MIT License":http://www.opensource.org/licenses/mit-license 564 | 565 | Copyright ©2014-15 Daniel Kehoe 566 | 567 | h2. Useful Links 568 | 569 | |_. Getting Started |_. Articles |_. Tutorials | 570 | | "Ruby on Rails":http://railsapps.github.io/ruby-and-rails.html | "Analytics for Rails":http://railsapps.github.io/rails-google-analytics.html | "Rails Bootstrap":http://railsapps.github.io/twitter-bootstrap-rails.html | 571 | | "What is Ruby on Rails?":http://railsapps.github.io/what-is-ruby-rails.html | "Heroku and Rails":http://railsapps.github.io/rails-heroku-tutorial.html | "Rails Foundation":http://railsapps.github.io/rails-foundation.html | 572 | | "Learn Ruby on Rails":http://learn-rails.com/learn-ruby-on-rails.html | "JavaScript and Rails":http://railsapps.github.io/rails-javascript-include-external.html | "RSpec Tutorial":http://railsapps.github.io/rspec.html | 573 | | "Rails Tutorial":https://tutorials.railsapps.org/rails-tutorial | "Rails Environment Variables":http://railsapps.github.io/rails-environment-variables.html | "Rails Devise Tutorial":http://railsapps.github.io/tutorial-rails-devise.html | 574 | | "Ruby on Rails Tutorial for Beginners":http://learn-rails.com/ruby-on-rails-tutorial-for-beginners | "Git and GitHub with Rails":http://railsapps.github.io/rails-git.html | "Devise RSpec":http://railsapps.github.io/tutorial-rails-devise-rspec-cucumber.html | 575 | | "Install Ruby on Rails":http://railsapps.github.io/installing-rails.html | "Send Email with Rails":http://railsapps.github.io/rails-send-email.html | "Devise Bootstrap":http://railsapps.github.io/tutorial-rails-bootstrap-devise-cancan.html | 576 | | "Install Ruby on Rails - Mac OS X":http://railsapps.github.io/installrubyonrails-mac.html | "Haml and Rails":http://railsapps.github.io/rails-haml.html | "Rails Membership Site with Stripe":https://tutorials.railsapps.org/rails-stripe-membership-saas | 577 | | "Install Ruby on Rails - Ubuntu":http://railsapps.github.io/installrubyonrails-ubuntu.html | "Rails Application Layout":http://railsapps.github.io/rails-default-application-layout.html | "Rails Subscription Site with Recurly":https://tutorials.railsapps.org/rails-recurly-subscription-saas | 578 | | "Ruby on Rails - Nitrous.io":http://railsapps.github.io/rubyonrails-nitrous-io.html | "HTML5 Boilerplate for Rails":http://railsapps.github.io/rails-html5-boilerplate.html | "Startup Prelaunch Signup Application":https://tutorials.railsapps.org/rails-prelaunch-signup | 579 | | "Update Rails":http://railsapps.github.io/updating-rails.html | "Example Gemfiles for Rails":http://railsapps.github.io/rails-3-2-example-gemfile.html | 580 | | "Rails Composer":http://railsapps.github.io/rails-composer/ | "Rails Application Templates":http://railsapps.github.io/rails-application-templates.html | 581 | | "Rails Examples":http://railsapps.github.io/ | "Rails Product Planning":http://railsapps.github.io/rails-product-planning.html | 582 | | "Rails Starter Apps":http://railsapps.github.io/rails-examples-tutorials.html | "Rails Project Management":http://railsapps.github.io/rails-project-management.html | 583 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RailsApps/rails-stripe-membership-saas/cdcd1b8e8fc6d7380c4a5436564ffcd8ca5ebe2e/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | //= require payola 15 | //= require jquery_ujs 16 | //= require bootstrap-sprockets 17 | //= require_tree . 18 | -------------------------------------------------------------------------------- /app/assets/stylesheets/1st_load_framework.css.scss: -------------------------------------------------------------------------------- 1 | // import the CSS framework 2 | @import "bootstrap-sprockets"; 3 | @import "bootstrap"; 4 | 5 | // make all images responsive by default 6 | img { 7 | @extend .img-responsive; 8 | margin: 0 auto; 9 | } 10 | // override for the 'Home' navigation link 11 | .navbar-brand { 12 | font-size: inherit; 13 | } 14 | 15 | // THESE ARE EXAMPLES YOU CAN MODIFY 16 | // create your own classes 17 | // to make views framework-neutral 18 | .column { 19 | @extend .col-md-6; 20 | @extend .text-center; 21 | } 22 | .form { 23 | @extend .col-md-6; 24 | } 25 | .form-centered { 26 | @extend .col-md-6; 27 | @extend .text-center; 28 | } 29 | .submit { 30 | @extend .btn; 31 | @extend .btn-primary; 32 | @extend .btn-lg; 33 | } 34 | // apply styles to HTML elements 35 | // to make views framework-neutral 36 | main { 37 | @extend .container; 38 | background-color: #eee; 39 | padding-bottom: 80px; 40 | width: 100%; 41 | margin-top: 51px; // accommodate the navbar 42 | } 43 | section { 44 | @extend .row; 45 | margin-top: 20px; 46 | } 47 | 48 | // Styles for form views 49 | // using Bootstrap 50 | // generated by the rails_layout gem 51 | .authform { 52 | padding-top: 30px; 53 | max-width: 320px; 54 | margin: 0 auto; 55 | } 56 | .authform form { 57 | @extend .well; 58 | @extend .well-lg; 59 | padding-bottom: 40px; 60 | } 61 | .authform .right { 62 | float: right !important; 63 | } 64 | .authform .button { 65 | @extend .btn; 66 | @extend .btn-primary; 67 | } 68 | .authform fieldset { 69 | @extend .well; 70 | } 71 | #error_explanation:not(:empty) { 72 | @extend .alert; 73 | @extend .alert-danger; 74 | } 75 | #error_explanation h2 { 76 | font-size: 16px; 77 | } 78 | .button-xs { 79 | @extend .btn; 80 | @extend .btn-primary; 81 | @extend .btn-xs; 82 | } 83 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any styles 10 | * defined in the other CSS/SCSS files in this directory. It is generally better to create a new 11 | * file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/pricing.css.scss: -------------------------------------------------------------------------------- 1 | // home page 2 | .plans{ 3 | text-align: center; 4 | } 5 | .plan{ 6 | background-color: #111575; 7 | } 8 | .plan.featured-plan{ 9 | background-color: #CCAB00; 10 | } 11 | .plan h2{ 12 | line-height: 100px; 13 | color: #fff; 14 | } 15 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # Prevent CSRF attacks by raising an exception. 3 | # For APIs, you may want to use :null_session instead. 4 | protect_from_forgery with: :exception 5 | 6 | def after_sign_in_path_for(resource) 7 | case current_user.role 8 | when 'admin' 9 | users_path 10 | when 'silver' 11 | content_silver_path 12 | when 'gold' 13 | content_gold_path 14 | when 'platinum' 15 | content_platinum_path 16 | else 17 | root_path 18 | end 19 | end 20 | 21 | end 22 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RailsApps/rails-stripe-membership-saas/cdcd1b8e8fc6d7380c4a5436564ffcd8ca5ebe2e/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/content_controller.rb: -------------------------------------------------------------------------------- 1 | class ContentController < ApplicationController 2 | before_action :authenticate_user! 3 | 4 | def silver 5 | redirect_to root_path, :notice => "Access denied." unless current_user.silver? 6 | end 7 | 8 | def gold 9 | redirect_to root_path, :notice => "Access denied." unless current_user.gold? 10 | end 11 | 12 | def platinum 13 | redirect_to root_path, :notice => "Access denied." unless current_user.platinum? 14 | end 15 | 16 | end 17 | -------------------------------------------------------------------------------- /app/controllers/products_controller.rb: -------------------------------------------------------------------------------- 1 | class ProductsController < ApplicationController 2 | before_action :authenticate_user! 3 | before_action :identify_product 4 | 5 | def show 6 | send_file @path, :disposition => "attachment; filename=#{@file}" 7 | end 8 | 9 | private 10 | def identify_product 11 | valid_characters = "^[0-9a-zA-Z]*$".freeze 12 | unless params[:id].blank? 13 | @product_id = params[:id] 14 | @product_id = @product_id.tr("^#{valid_characters}", '') 15 | else 16 | raise "Filename missing" 17 | end 18 | unless params[:format].blank? 19 | @format = params[:format] 20 | @format = @format.tr("^#{valid_characters}", '') 21 | else 22 | raise "File extension missing" 23 | end 24 | @path = "app/views/products/#{@product_id}.#{@format}" 25 | @file = "#{@product_id}.#{@format}" 26 | end 27 | 28 | end 29 | -------------------------------------------------------------------------------- /app/controllers/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | class RegistrationsController < Devise::RegistrationsController 2 | include Payola::StatusBehavior 3 | before_action :cancel_subscription, only: [:destroy] 4 | 5 | def new 6 | build_resource({}) 7 | unless params[:plan].nil? 8 | @plan = Plan.find_by!(stripe_id: params[:plan]) 9 | resource.plan = @plan 10 | end 11 | yield resource if block_given? 12 | respond_with self.resource 13 | end 14 | 15 | def create 16 | build_resource(sign_up_params) 17 | plan = Plan.find_by!(id: params[:user][:plan_id].to_i) 18 | resource.role = User.roles[plan.stripe_id] unless resource.admin? 19 | resource.save 20 | yield resource if block_given? 21 | if resource.persisted? 22 | if resource.active_for_authentication? 23 | set_flash_message :notice, :signed_up if is_flashing_format? 24 | sign_up(resource_name, resource) 25 | subscribe 26 | else 27 | set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format? 28 | expire_data_after_sign_in! 29 | subscribe 30 | end 31 | else 32 | clean_up_passwords resource 33 | render json: 34 | {error: resource.errors.full_messages.to_sentence}, 35 | status: 400 36 | end 37 | end 38 | 39 | def change_plan 40 | plan = Plan.find_by!(id: params[:user][:plan_id].to_i) 41 | unless plan == current_user.plan 42 | role = User.roles[plan.stripe_id] 43 | if current_user.update_attributes!(plan: plan, role: role) 44 | subscription = Payola::Subscription.find_by!(owner_id: current_user.id) 45 | Payola::ChangeSubscriptionPlan.call(subscription, plan) 46 | redirect_to edit_user_registration_path, :notice => "Plan changed." 47 | else 48 | flash[:alert] = 'Unable to change plan.' 49 | build_resource 50 | render :edit 51 | end 52 | end 53 | end 54 | 55 | private 56 | 57 | def sign_up_params 58 | params.require(:user).permit(:email, 59 | :password, :password_confirmation, :plan_id) 60 | end 61 | 62 | def subscribe 63 | return if resource.admin? 64 | params[:plan] = current_user.plan 65 | subscription = Payola::CreateSubscription.call(params, current_user) 66 | current_user.save 67 | render_payola_status(subscription) 68 | end 69 | 70 | def cancel_subscription 71 | subscription = Payola::Subscription.find_by!(owner_id: current_user.id, state: 'active') 72 | Payola::CancelSubscription.call(subscription) 73 | end 74 | 75 | end 76 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | before_action :authenticate_user! 3 | before_action :admin_only, :except => :show 4 | 5 | def index 6 | @users = User.all 7 | end 8 | 9 | def show 10 | @user = User.find(params[:id]) 11 | unless current_user.admin? 12 | unless @user == current_user 13 | redirect_to :back, :alert => "Access denied." 14 | end 15 | end 16 | end 17 | 18 | def update 19 | @user = User.find(params[:id]) 20 | if @user.update_attributes(secure_params) 21 | redirect_to users_path, :notice => "User updated." 22 | else 23 | redirect_to users_path, :alert => "Unable to update user." 24 | end 25 | end 26 | 27 | def destroy 28 | user = User.find(params[:id]) 29 | user.destroy 30 | redirect_to users_path, :notice => "User deleted." 31 | end 32 | 33 | private 34 | 35 | def admin_only 36 | unless current_user.admin? 37 | redirect_to :back, :alert => "Access denied." 38 | end 39 | end 40 | 41 | def secure_params 42 | params.require(:user).permit(:role) 43 | end 44 | 45 | end 46 | -------------------------------------------------------------------------------- /app/controllers/visitors_controller.rb: -------------------------------------------------------------------------------- 1 | class VisitorsController < ApplicationController 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/jobs/mailing_list_signup_job.rb: -------------------------------------------------------------------------------- 1 | class MailingListSignupJob < ActiveJob::Base 2 | 3 | def perform(user) 4 | logger.info "signing up #{user.email}" 5 | user.subscribe 6 | end 7 | 8 | end 9 | -------------------------------------------------------------------------------- /app/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RailsApps/rails-stripe-membership-saas/cdcd1b8e8fc6d7380c4a5436564ffcd8ca5ebe2e/app/mailers/.keep -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "from@example.com" 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/mailers/user_mailer.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ActionMailer::Base 2 | default :from => "do-not-reply@example.com" 3 | 4 | def expire_email(user) 5 | mail(:to => user.email, :subject => "Subscription Cancelled") 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RailsApps/rails-stripe-membership-saas/cdcd1b8e8fc6d7380c4a5436564ffcd8ca5ebe2e/app/models/.keep -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RailsApps/rails-stripe-membership-saas/cdcd1b8e8fc6d7380c4a5436564ffcd8ca5ebe2e/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/plan.rb: -------------------------------------------------------------------------------- 1 | class Plan < ActiveRecord::Base 2 | include Payola::Plan 3 | 4 | has_many :users 5 | validates :stripe_id, inclusion: { in: Plan.pluck('DISTINCT stripe_id'), 6 | message: "not a valid subscription plan" } 7 | 8 | def redirect_path(subscription) 9 | '/' 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ActiveRecord::Base 2 | enum role: [:user, :admin, :silver, :gold, :platinum] 3 | after_initialize :set_default_role, :if => :new_record? 4 | after_initialize :set_default_plan, :if => :new_record? 5 | # after_create :sign_up_for_mailing_list 6 | 7 | belongs_to :plan 8 | validates_associated :plan 9 | 10 | def set_default_role 11 | self.role ||= :user 12 | end 13 | 14 | def set_default_plan 15 | self.plan ||= Plan.last 16 | end 17 | 18 | # Include default devise modules. Others available are: 19 | # :confirmable, :lockable, :timeoutable and :omniauthable 20 | devise :database_authenticatable, :registerable, 21 | :recoverable, :rememberable, :trackable, :validatable 22 | 23 | def sign_up_for_mailing_list 24 | MailingListSignupJob.perform_later(self) 25 | end 26 | 27 | def subscribe 28 | mailchimp = Gibbon::Request.new(api_key: Rails.application.secrets.mailchimp_api_key) 29 | list_id = Rails.application.secrets.mailchimp_list_id 30 | result = mailchimp.lists(list_id).members.create( 31 | body: { 32 | email_address: self.email, 33 | status: 'subscribed' 34 | }) 35 | Rails.logger.info("Subscribed #{self.email} to MailChimp") if result 36 | end 37 | 38 | end 39 | -------------------------------------------------------------------------------- /app/services/create_admin_service.rb: -------------------------------------------------------------------------------- 1 | class CreateAdminService 2 | def call 3 | user = User.find_or_create_by!(email: Rails.application.secrets.admin_email) do |user| 4 | user.password = Rails.application.secrets.admin_password 5 | user.password_confirmation = Rails.application.secrets.admin_password 6 | user.admin! 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/services/create_plan_service.rb: -------------------------------------------------------------------------------- 1 | class CreatePlanService 2 | def call 3 | p1 = Plan.where(name: 'Platinum').first_or_initialize do |p| 4 | p.amount = 2900 5 | p.interval = 'month' 6 | p.stripe_id = 'platinum' 7 | end 8 | p1.save!(:validate => false) 9 | p2 = Plan.where(name: 'Gold').first_or_initialize do |p| 10 | p.amount = 1900 11 | p.interval = 'month' 12 | p.stripe_id = 'gold' 13 | end 14 | p2.save!(:validate => false) 15 | p3 = Plan.where(name: 'Silver').first_or_initialize do |p| 16 | p.amount = 900 17 | p.interval = 'month' 18 | p.stripe_id = 'silver' 19 | end 20 | p3.save!(:validate => false) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/views/content/gold.html.erb: -------------------------------------------------------------------------------- 1 |
Find me in app/views/content/gold.html.erb
3 | -------------------------------------------------------------------------------- /app/views/content/platinum.html.erb: -------------------------------------------------------------------------------- 1 |Find me in app/views/content/platinum.html.erb
3 | -------------------------------------------------------------------------------- /app/views/content/silver.html.erb: -------------------------------------------------------------------------------- 1 |Find me in app/views/content/silver.html.erb
3 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |We'll send password reset instructions.
5 | <%= devise_error_messages! %> 6 |Unhappy? We'll be sad to see you go.
46 | <%= button_to "Cancel my account", registration_path(resource_name), :data => { :confirm => "Are you sure?" }, :method => :delete, :class => 'button right' %> 47 |4 | This web application was created with 5 | <%= link_to('Rails Composer', 'http://railsapps.github.io/rails-composer/') %> 6 | from the <%= link_to('RailsApps project', 'http://railsapps.github.io/') %>. 7 |
8 | -------------------------------------------------------------------------------- /app/views/products/product.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RailsApps/rails-stripe-membership-saas/cdcd1b8e8fc6d7380c4a5436564ffcd8ca5ebe2e/app/views/products/product.pdf -------------------------------------------------------------------------------- /app/views/user_mailer/expire_email.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 |9 | Your subscription has been cancelled. 10 |
11 |12 | We are sorry to see you go. We'd love to have you back. 13 | Visit example.com anytime to create a new subscription. 14 |
15 | 16 | -------------------------------------------------------------------------------- /app/views/user_mailer/expire_email.text.erb: -------------------------------------------------------------------------------- 1 | Subscription Cancelled 2 | 3 | Your subscription has been cancelled. 4 | 5 | We are sorry to see you go. We'd love to have you back. 6 | Visit example.com anytime to create a new subscription. -------------------------------------------------------------------------------- /app/views/users/_user.html.erb: -------------------------------------------------------------------------------- 1 |Name: <%= @user.name if @user.name %>
3 |Email: <%= @user.email if @user.email %>
4 | -------------------------------------------------------------------------------- /app/views/visitors/index.html.erb: -------------------------------------------------------------------------------- 1 | <% if user_signed_in? %> 2 | <% if current_user.admin? %> 3 |<%= link_to 'User count:', users_path %> <%= User.count %>
5 | <% else %> 6 |You may have mistyped the address or the page may have moved.
63 |If you are the application owner check the logs for more information.
65 |Maybe you tried to change something you didn't have access to.
63 |If you are the application owner check the logs for more information.
65 |If you are the application owner check the logs for more information.
64 |