├── .gitignore ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── app ├── assets │ ├── config │ │ └── manifest.js │ ├── images │ │ ├── .keep │ │ ├── beach_lg.jpeg │ │ ├── beach_sm.jpg │ │ ├── desk_lg.jpg │ │ ├── desk_sm.jpg │ │ ├── favicon.ico │ │ ├── money_lg.jpg │ │ ├── money_sm.jpg │ │ ├── ocean_lg.jpg │ │ ├── ocean_sm.jpeg │ │ ├── puppy_lg.jpeg │ │ ├── puppy_sm.jpg │ │ ├── sf_lg.jpeg │ │ ├── sf_sm.jpg │ │ ├── snow_lg.jpeg │ │ ├── snow_sm.jpg │ │ ├── travel_lg.jpeg │ │ ├── travel_sm.jpg │ │ ├── wedding_lg.jpeg │ │ └── wedding_sm.jpg │ ├── javascripts │ │ ├── application.js │ │ ├── cable.js │ │ ├── campaigns.js │ │ └── channels │ │ │ └── .keep │ └── stylesheets │ │ ├── application.css │ │ ├── components.scss │ │ └── custom.scss ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── application_controller.rb │ ├── bank_accounts_controller.rb │ ├── campaigns_controller.rb │ ├── charges_controller.rb │ ├── concerns │ │ └── .keep │ ├── debit_cards_controller.rb │ ├── disputes_controller.rb │ ├── pages_controller.rb │ ├── payouts_controller.rb │ ├── registrations_controller.rb │ ├── stripe_accounts_controller.rb │ └── webhooks_controller.rb ├── helpers │ ├── application_helper.rb │ ├── bank_accounts_helper.rb │ ├── campaigns_helper.rb │ ├── charges_helper.rb │ ├── debit_cards_helper.rb │ ├── devise_helper.rb │ ├── disputes_helper.rb │ ├── payouts_helper.rb │ └── stripe_accounts_helper.rb ├── jobs │ └── application_job.rb ├── mailers │ └── application_mailer.rb ├── models │ ├── application_record.rb │ ├── campaign.rb │ ├── charge.rb │ ├── concerns │ │ └── .keep │ ├── stripe_account.rb │ └── user.rb └── views │ ├── admin │ └── kaminari │ │ ├── _first_page.html.erb │ │ ├── _gap.html.erb │ │ ├── _last_page.html.erb │ │ ├── _next_page.html.erb │ │ ├── _page.html.erb │ │ ├── _paginator.html.erb │ │ └── _prev_page.html.erb │ ├── bank_accounts │ ├── _bank_account_form.html.erb │ └── new.html.erb │ ├── campaigns │ ├── _campaigns.html.erb │ ├── _charges.html.erb │ ├── _checkoutform.html.erb │ ├── _donations.html.erb │ ├── _images.html.erb │ ├── _paymentform.html.erb │ ├── _transactions.html.erb │ ├── _transfers.html.erb │ ├── _verification.html.erb │ ├── dashboard.html.erb │ ├── edit.html.erb │ ├── home.html.erb │ ├── new.html.erb │ └── show.html.erb │ ├── charges │ ├── _dispute.html.erb │ └── show.html.erb │ ├── debit_cards │ ├── _debit_card_form.html.erb │ └── new.html.erb │ ├── devise │ ├── confirmations │ │ └── new.html.erb │ ├── mailer │ │ ├── confirmation_instructions.html.erb │ │ ├── password_change.html.erb │ │ ├── reset_password_instructions.html.erb │ │ └── unlock_instructions.html.erb │ ├── passwords │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── registrations │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── sessions │ │ └── new.html.erb │ ├── shared │ │ └── _links.html.erb │ └── unlocks │ │ └── new.html.erb │ ├── kaminari │ ├── _first_page.html.erb │ ├── _gap.html.erb │ ├── _last_page.html.erb │ ├── _next_page.html.erb │ ├── _page.html.erb │ ├── _paginator.html.erb │ └── _prev_page.html.erb │ ├── layouts │ ├── _footer.html.erb │ ├── _header.html.erb │ ├── _messages.html.erb │ ├── application.html.erb │ ├── mailer.html.erb │ └── mailer.text.erb │ ├── pages │ ├── pricing.html.erb │ └── terms.html.erb │ ├── payouts │ └── show.html.erb │ └── stripe_accounts │ ├── _account_form.html.erb │ ├── _identity_verification_form.html.erb │ ├── edit.html.erb │ ├── full.html.erb │ └── new.html.erb ├── bin ├── bundle ├── rails ├── rake ├── setup ├── spring └── update ├── config.ru ├── config ├── application.rb ├── boot.rb ├── cable.yml ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ └── test.rb ├── initializers │ ├── application_controller_renderer.rb │ ├── assets.rb │ ├── backtrace_silencers.rb │ ├── cookies_serializer.rb │ ├── devise.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── kaminari_config.rb │ ├── mime_types.rb │ ├── new_framework_defaults.rb │ ├── session_store.rb │ ├── stripe.rb │ └── wrap_parameters.rb ├── locales │ ├── devise.en.yml │ └── en.yml ├── puma.rb ├── routes.rb ├── secrets.yml └── spring.rb ├── db ├── migrate │ ├── 20160910151553_devise_create_users.rb │ ├── 20160910162519_create_lessons.rb │ ├── 20160910213144_rename_lessons_to_courses.rb │ ├── 20160911175135_add_image_to_course.rb │ ├── 20160911185541_add_stripe_account_to_users.rb │ ├── 20160911193440_create_stripe_accounts.rb │ ├── 20160911230223_add_fields_to_stripe_account.rb │ ├── 20160913025720_create_charges.rb │ ├── 20160915051353_change_description_in_courses.rb │ ├── 20160915051609_add_content_link_to_courses.rb │ ├── 20161227175740_rename_courses_to_campaigns.rb │ ├── 20161227180421_modify_campaigns_and_charges.rb │ ├── 20161227183231_add_goal_to_campaigns.rb │ ├── 20161228174458_remove_user_id_from_charges.rb │ ├── 20161228180341_add_name_to_charges.rb │ ├── 20161228182556_add_raised_to_campaign.rb │ ├── 20161228223505_add_active_to_campaign.rb │ ├── 20170405165023_add_more_fields_to_stripe_accounts.rb │ └── 20170502170520_add_acct_id_to_stripe_accounts.rb ├── schema.rb └── seeds.rb ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── log └── .keep ├── public ├── 404.html ├── 422.html ├── 500.html ├── apple-touch-icon-precomposed.png ├── apple-touch-icon.png ├── favicon.ico └── robots.txt ├── test ├── controllers │ ├── .keep │ ├── bank_accounts_controller_test.rb │ ├── campaigns_controller_test.rb │ ├── charges_controller_test.rb │ ├── debit_cards_controller_test.rb │ ├── disputes_controller_test.rb │ ├── payouts_controller_test.rb │ └── stripe_accounts_controller_test.rb ├── fixtures │ ├── .keep │ ├── campaigns.yml │ ├── charges.yml │ ├── files │ │ └── .keep │ ├── stripe_accounts.yml │ └── users.yml ├── helpers │ └── .keep ├── integration │ └── .keep ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── charge_test.rb │ ├── courses_test.rb │ ├── stripe_account_test.rb │ └── user_test.rb └── test_helper.rb ├── tmp └── .keep └── vendor └── assets ├── javascripts └── .keep └── stylesheets └── .keep /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | /tmp/* 17 | !/log/.keep 18 | !/tmp/.keep 19 | 20 | # Ignore Byebug command history file. 21 | .byebug_history 22 | 23 | # Ignore DS_store 24 | .DS_Store 25 | 26 | # Ignore ngrok for local testing 27 | ngrok 28 | 29 | # Ignore Gemfile.lock 30 | Gemfile.lock 31 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | 4 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 5 | gem 'rails', '~> 5.0.0', '>= 5.0.0.1' 6 | # Use Puma as the app server 7 | gem 'puma', '~> 3.0' 8 | # Use SCSS for stylesheets 9 | gem 'sass-rails', '~> 5.0' 10 | # Use Uglifier as compressor for JavaScript assets 11 | gem 'uglifier', '>= 1.3.0' 12 | # Use CoffeeScript for .coffee assets and views 13 | gem 'coffee-rails', '~> 4.2' 14 | # See https://github.com/rails/execjs#readme for more supported runtimes 15 | # gem 'therubyracer', platforms: :ruby 16 | 17 | # Use jquery as the JavaScript library 18 | gem 'jquery-rails' 19 | 20 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 21 | gem 'jbuilder', '~> 2.5' 22 | # Use Redis adapter to run Action Cable in production 23 | # gem 'redis', '~> 3.0' 24 | # Use ActiveModel has_secure_password 25 | # gem 'bcrypt', '~> 3.1.7' 26 | 27 | # Use Capistrano for deployment 28 | # gem 'capistrano-rails', group: :development 29 | 30 | # Stripe for payments... oh hell yes 31 | gem 'stripe' 32 | # Bootstrap for the pretty 33 | gem 'bootstrap-sass', '~> 3.3.6' 34 | # Use Font Awesome to make pretty icons 35 | gem 'font-awesome-rails' 36 | # Devise for use auth 37 | gem 'devise', '~> 4.2', '>= 4.2.1' 38 | # Confirmable modals 39 | gem 'data-confirm-modal' 40 | # Mailgun to gun the mails 41 | gem 'mailgun-ruby' 42 | # Paginate all the things 43 | gem 'kaminari' 44 | 45 | group :development, :test do 46 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 47 | gem 'byebug', platform: :mri 48 | # Use sqlite3 as the database for Active Record 49 | gem 'sqlite3' 50 | end 51 | 52 | group :development do 53 | # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. 54 | gem 'web-console' 55 | gem 'listen', '~> 3.0.5' 56 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 57 | gem 'spring' 58 | gem 'spring-watcher-listen', '~> 2.0.0' 59 | end 60 | 61 | group :production do 62 | gem 'pg', '~> 0.18.4' 63 | end 64 | 65 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 66 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 67 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (5.0.2) 5 | actionpack (= 5.0.2) 6 | nio4r (>= 1.2, < 3.0) 7 | websocket-driver (~> 0.6.1) 8 | actionmailer (5.0.2) 9 | actionpack (= 5.0.2) 10 | actionview (= 5.0.2) 11 | activejob (= 5.0.2) 12 | mail (~> 2.5, >= 2.5.4) 13 | rails-dom-testing (~> 2.0) 14 | actionpack (5.0.2) 15 | actionview (= 5.0.2) 16 | activesupport (= 5.0.2) 17 | rack (~> 2.0) 18 | rack-test (~> 0.6.3) 19 | rails-dom-testing (~> 2.0) 20 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 21 | actionview (5.0.2) 22 | activesupport (= 5.0.2) 23 | builder (~> 3.1) 24 | erubis (~> 2.7.0) 25 | rails-dom-testing (~> 2.0) 26 | rails-html-sanitizer (~> 1.0, >= 1.0.3) 27 | activejob (5.0.2) 28 | activesupport (= 5.0.2) 29 | globalid (>= 0.3.6) 30 | activemodel (5.0.2) 31 | activesupport (= 5.0.2) 32 | activerecord (5.0.2) 33 | activemodel (= 5.0.2) 34 | activesupport (= 5.0.2) 35 | arel (~> 7.0) 36 | activesupport (5.0.2) 37 | concurrent-ruby (~> 1.0, >= 1.0.2) 38 | i18n (~> 0.7) 39 | minitest (~> 5.1) 40 | tzinfo (~> 1.1) 41 | arel (7.1.4) 42 | autoprefixer-rails (6.7.7.2) 43 | execjs 44 | bcrypt (3.1.11) 45 | bindex (0.5.0) 46 | bootstrap-sass (3.3.7) 47 | autoprefixer-rails (>= 5.2.1) 48 | sass (>= 3.3.4) 49 | builder (3.2.3) 50 | byebug (9.0.6) 51 | coffee-rails (4.2.1) 52 | coffee-script (>= 2.2.0) 53 | railties (>= 4.0.0, < 5.2.x) 54 | coffee-script (2.4.1) 55 | coffee-script-source 56 | execjs 57 | coffee-script-source (1.12.2) 58 | concurrent-ruby (1.0.5) 59 | data-confirm-modal (1.3.0) 60 | railties (>= 3.0) 61 | devise (4.2.1) 62 | bcrypt (~> 3.0) 63 | orm_adapter (~> 0.1) 64 | railties (>= 4.1.0, < 5.1) 65 | responders 66 | warden (~> 1.2.3) 67 | domain_name (0.5.20170404) 68 | unf (>= 0.0.5, < 1.0.0) 69 | erubis (2.7.0) 70 | execjs (2.7.0) 71 | faraday (0.11.0) 72 | multipart-post (>= 1.2, < 3) 73 | ffi (1.9.18) 74 | font-awesome-rails (4.7.0.2) 75 | railties (>= 3.2, < 5.2) 76 | globalid (0.4.0) 77 | activesupport (>= 4.2.0) 78 | http-cookie (1.0.3) 79 | domain_name (~> 0.5) 80 | i18n (0.8.1) 81 | jbuilder (2.6.3) 82 | activesupport (>= 3.0.0, < 5.2) 83 | multi_json (~> 1.2) 84 | jquery-rails (4.3.1) 85 | rails-dom-testing (>= 1, < 3) 86 | railties (>= 4.2.0) 87 | thor (>= 0.14, < 2.0) 88 | kaminari (1.0.1) 89 | activesupport (>= 4.1.0) 90 | kaminari-actionview (= 1.0.1) 91 | kaminari-activerecord (= 1.0.1) 92 | kaminari-core (= 1.0.1) 93 | kaminari-actionview (1.0.1) 94 | actionview 95 | kaminari-core (= 1.0.1) 96 | kaminari-activerecord (1.0.1) 97 | activerecord 98 | kaminari-core (= 1.0.1) 99 | kaminari-core (1.0.1) 100 | listen (3.0.8) 101 | rb-fsevent (~> 0.9, >= 0.9.4) 102 | rb-inotify (~> 0.9, >= 0.9.7) 103 | loofah (2.0.3) 104 | nokogiri (>= 1.8.2) 105 | mail (2.6.5) 106 | mime-types (>= 1.16, < 4) 107 | mailgun-ruby (1.1.5) 108 | rest-client (~> 2.0) 109 | method_source (0.8.2) 110 | mime-types (3.1) 111 | mime-types-data (~> 3.2015) 112 | mime-types-data (3.2016.0521) 113 | mini_portile2 (2.1.0) 114 | minitest (5.10.1) 115 | multi_json (1.12.1) 116 | multipart-post (2.0.0) 117 | netrc (0.11.0) 118 | nio4r (2.0.0) 119 | nokogiri (1.7.1) 120 | mini_portile2 (~> 2.1.0) 121 | orm_adapter (0.5.0) 122 | pg (0.18.4) 123 | puma (3.8.2) 124 | rack (2.0.1) 125 | rack-test (0.6.3) 126 | rack (>= 1.0) 127 | rails (5.0.2) 128 | actioncable (= 5.0.2) 129 | actionmailer (= 5.0.2) 130 | actionpack (= 5.0.2) 131 | actionview (= 5.0.2) 132 | activejob (= 5.0.2) 133 | activemodel (= 5.0.2) 134 | activerecord (= 5.0.2) 135 | activesupport (= 5.0.2) 136 | bundler (>= 1.3.0, < 2.0) 137 | railties (= 5.0.2) 138 | sprockets-rails (>= 2.0.0) 139 | rails-dom-testing (2.0.2) 140 | activesupport (>= 4.2.0, < 6.0) 141 | nokogiri (~> 1.6) 142 | rails-html-sanitizer (1.0.3) 143 | loofah (~> 2.0) 144 | railties (5.0.2) 145 | actionpack (= 5.0.2) 146 | activesupport (= 5.0.2) 147 | method_source 148 | rake (>= 0.8.7) 149 | thor (>= 0.18.1, < 2.0) 150 | rake (12.0.0) 151 | rb-fsevent (0.9.8) 152 | rb-inotify (0.9.8) 153 | ffi (>= 0.5.0) 154 | responders (2.4.0) 155 | actionpack (>= 4.2.0, < 5.3) 156 | railties (>= 4.2.0, < 5.3) 157 | rest-client (2.0.2) 158 | http-cookie (>= 1.0.2, < 2.0) 159 | mime-types (>= 1.16, < 4.0) 160 | netrc (~> 0.8) 161 | sass (3.4.23) 162 | sass-rails (5.0.6) 163 | railties (>= 4.0.0, < 6) 164 | sass (~> 3.1) 165 | sprockets (>= 2.8, < 4.0) 166 | sprockets-rails (>= 2.0, < 4.0) 167 | tilt (>= 1.1, < 3) 168 | spring (2.0.1) 169 | activesupport (>= 4.2) 170 | spring-watcher-listen (2.0.1) 171 | listen (>= 2.7, < 4.0) 172 | spring (>= 1.2, < 3.0) 173 | sprockets (3.7.1) 174 | concurrent-ruby (~> 1.0) 175 | rack (> 1, < 3) 176 | sprockets-rails (3.2.0) 177 | actionpack (>= 4.0) 178 | activesupport (>= 4.0) 179 | sprockets (>= 3.0.0) 180 | sqlite3 (1.3.13) 181 | stripe (2.8.0) 182 | faraday (~> 0.9) 183 | thor (0.19.4) 184 | thread_safe (0.3.6) 185 | tilt (2.0.7) 186 | tzinfo (1.2.3) 187 | thread_safe (~> 0.1) 188 | uglifier (3.2.0) 189 | execjs (>= 0.3.0, < 3) 190 | unf (0.1.4) 191 | unf_ext 192 | unf_ext (0.0.7.4) 193 | warden (1.2.7) 194 | rack (>= 1.0) 195 | web-console (3.5.0) 196 | actionview (>= 5.0) 197 | activemodel (>= 5.0) 198 | bindex (>= 0.4.0) 199 | railties (>= 5.0) 200 | websocket-driver (0.6.5) 201 | websocket-extensions (>= 0.1.0) 202 | websocket-extensions (0.1.2) 203 | 204 | PLATFORMS 205 | ruby 206 | 207 | DEPENDENCIES 208 | bootstrap-sass (~> 3.3.6) 209 | byebug 210 | coffee-rails (~> 4.2) 211 | data-confirm-modal 212 | devise (~> 4.2, >= 4.2.1) 213 | font-awesome-rails 214 | jbuilder (~> 2.5) 215 | jquery-rails 216 | kaminari 217 | listen (~> 3.0.5) 218 | mailgun-ruby 219 | pg (~> 0.18.4) 220 | puma (~> 3.0) 221 | rails (~> 5.0.0, >= 5.0.0.1) 222 | sass-rails (~> 5.0) 223 | spring 224 | spring-watcher-listen (~> 2.0.0) 225 | sqlite3 226 | stripe 227 | tzinfo-data 228 | uglifier (>= 1.3.0) 229 | web-console 230 | 231 | BUNDLED WITH 232 | 1.14.6 233 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Deprecated 2 | 3 | This repository has moved [here](https://github.com/adamjstevenson/stripe-connect-managed-rails). 4 | 5 | # Fundraising Marketplace: A Stripe Connect example app 6 | 7 | An example application built using [Stripe Connect](https://stripe.com/docs/connect) [custom accounts](https://stripe.com/docs/connect/custom-accounts). **This application is provided as an example, but isn't meant to be run in production.** 8 | 9 | [![Dashboard demo](https://i.imgur.com/2YvhiaL.png)](https://stripe-marketplace-demo.herokuapp.com) 10 | 11 | [![Payout demo](https://i.imgur.com/6s5vm9A.png)](https://stripe-marketplace-demo.herokuapp.com) 12 | 13 | [![Charge view demo](https://i.imgur.com/GO0SsHL.png)](https://stripe-marketplace-demo.herokuapp.com) 14 | 15 | 16 | ## Demo 17 | **You can find a working demo of this application deployed and running in test mode at https://stripe-marketplace-demo.herokuapp.com/**. Feel free to create an account, create a campaign, and make donations to see some data populated in the dashboard. You can find [test card numbers](https://stripe.com/docs/testing#cards), [bank accounts](https://stripe.com/docs/testing#managed-accounts), and [identity verification](https://stripe.com/docs/connect/testing) details in Stripe's documentation. 18 | 19 | 20 | ## Features 21 | :lock: Uses [Devise](https://github.com/plataformatec/devise) for user authentication. 22 | 23 | :money_with_wings: Create fundraising campaigns and custom Stripe Connect accounts. 24 | 25 | :chart_with_upwards_trend: Fairly complete seller dashboard to view charges, create refunds, view payouts, etc. 26 | 27 | :iphone: Fully responsive for mobile browsers. 28 | 29 | :bank: Add and modify connected [bank accounts](https://stripe.com/docs/api#account_create_bank_account). 30 | 31 | :credit_card: Make donations with either [Stripe Elements](https://stripe.com/docs/elements) or [Stripe Checkout](https://stripe.com/docs/checkout). [Make successful donations](https://stripe.com/docs/testing#cards) using valid test card numbers or see declines using test cards. 32 | 33 | :sunglasses: Take a 10% [application fee](https://stripe.com/docs/connect/charges) from connected accounts for successful charges. 34 | 35 | :zap: Create payouts to debit cards using [instant payouts](https://stripe.com/docs/connect/payouts#instant-payouts) and take a 3% platform fee in return using [account debits](https://stripe.com/docs/connect/account-debits). 36 | 37 | :clipboard: Identity verification example form and dashboard prompt to work through the [identity verification](https://stripe.com/docs/connect/identity-verification) process. Includes examples of collecting all info up front vs incrementally. 38 | 39 | :poop: [Create disputes](https://stripe.com/docs/testing#disputes) and use [webhooks](https://stripe.com/docs/webhooks) to recover funds + dispute fees automatically via account debits. 40 | 41 | :arrow_right_hook: Includes [webhook signature validation](https://stripe.com/docs/webhooks#signatures) for enhanced security. 42 | 43 | 44 | ## Shortcomings, things still needed 45 | * Still pretty basic integration tests. 46 | * Email receipts/notifications/etc. 47 | * Additional features like pagination for charges/transfers, ACH payments, alternative payment methods, etc. 48 | 49 | 50 | ## Specs 51 | Built on Rails 5 and running Ruby 2.2. Uses the 2016-07-06 API version. 52 | 53 | 54 | ## Setup 55 | To run this locally, clone the repository and run bundler to install dependencies: 56 | 57 | ``` 58 | git clone https://github.com/adam-stripe/stripe-connect-managed-rails.git 59 | cd stripe-connect-managed-rails 60 | bundle install 61 | ``` 62 | 63 | Migrate: 64 | 65 | ``` 66 | $ rails db:migrate 67 | ``` 68 | 69 | Retrieve your [Stripe API keys](https://dashboard.stripe.com/account/apikeys) and set them as environment variables. You can run this app locally by starting Rails server: 70 | 71 | ``` 72 | PUBLISHABLE_KEY=YOUR_STRIPE_PUBLISHABLE_KEY SECRET_KEY=YOUR_STRIPE_SECRET_KEY ENDPOINT_SECRET=YOUR_WEBHOOK_ENDPOINT_SECRET rails s 73 | ``` 74 | -------------------------------------------------------------------------------- /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_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/.keep -------------------------------------------------------------------------------- /app/assets/images/beach_lg.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/beach_lg.jpeg -------------------------------------------------------------------------------- /app/assets/images/beach_sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/beach_sm.jpg -------------------------------------------------------------------------------- /app/assets/images/desk_lg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/desk_lg.jpg -------------------------------------------------------------------------------- /app/assets/images/desk_sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/desk_sm.jpg -------------------------------------------------------------------------------- /app/assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/favicon.ico -------------------------------------------------------------------------------- /app/assets/images/money_lg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/money_lg.jpg -------------------------------------------------------------------------------- /app/assets/images/money_sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/money_sm.jpg -------------------------------------------------------------------------------- /app/assets/images/ocean_lg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/ocean_lg.jpg -------------------------------------------------------------------------------- /app/assets/images/ocean_sm.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/ocean_sm.jpeg -------------------------------------------------------------------------------- /app/assets/images/puppy_lg.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/puppy_lg.jpeg -------------------------------------------------------------------------------- /app/assets/images/puppy_sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/puppy_sm.jpg -------------------------------------------------------------------------------- /app/assets/images/sf_lg.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/sf_lg.jpeg -------------------------------------------------------------------------------- /app/assets/images/sf_sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/sf_sm.jpg -------------------------------------------------------------------------------- /app/assets/images/snow_lg.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/snow_lg.jpeg -------------------------------------------------------------------------------- /app/assets/images/snow_sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/snow_sm.jpg -------------------------------------------------------------------------------- /app/assets/images/travel_lg.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/travel_lg.jpeg -------------------------------------------------------------------------------- /app/assets/images/travel_sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/travel_sm.jpg -------------------------------------------------------------------------------- /app/assets/images/wedding_lg.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/wedding_lg.jpeg -------------------------------------------------------------------------------- /app/assets/images/wedding_sm.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/images/wedding_sm.jpg -------------------------------------------------------------------------------- /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 any plugin's vendor/assets/javascripts directory 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. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | //= require bootstrap 15 | //= require jquery_ujs 16 | //= require data-confirm-modal 17 | //= require_tree . -------------------------------------------------------------------------------- /app/assets/javascripts/cable.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the rails generate channel command. 3 | // 4 | //= require action_cable 5 | //= require_self 6 | //= require_tree ./channels 7 | 8 | (function() { 9 | this.App || (this.App = {}); 10 | 11 | App.cable = ActionCable.createConsumer(); 12 | 13 | }).call(this); 14 | -------------------------------------------------------------------------------- /app/assets/javascripts/campaigns.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | $(".campaign-image").click(function(){ 3 | $(".img-thumbnail").removeClass("img-selected"); 4 | $("#campaign_image").val(this.id); 5 | $(this).children("img").addClass("img-selected"); 6 | }); 7 | }); -------------------------------------------------------------------------------- /app/assets/javascripts/channels/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/assets/javascripts/channels/.keep -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 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 any plugin's vendor/assets/stylesheets directory 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 other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require font-awesome 14 | *= require_self 15 | *= require_tree . 16 | */ 17 | -------------------------------------------------------------------------------- /app/assets/stylesheets/components.scss: -------------------------------------------------------------------------------- 1 | /* =========== 2 | Buttons 3 | =============*/ 4 | .btn { 5 | border-radius: 2px; 6 | padding: 6px 14px; 7 | webkit-box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.4); 8 | } 9 | .btn-group-lg > .btn, 10 | .btn-lg { 11 | padding: 10px 16px !important; 12 | font-size: 16px; 13 | } 14 | .btn-group-sm > .btn, 15 | .btn-sm { 16 | padding: 5px 10px !important; 17 | } 18 | .btn-group-xs > .btn, 19 | .btn-xs { 20 | padding: 1px 5px !important; 21 | } 22 | .btn-group .btn + .btn, 23 | .btn-group .btn + .btn-group, 24 | .btn-group .btn-group + .btn, 25 | .btn-group .btn-group + .btn-group { 26 | margin-left: 0px; 27 | } 28 | .btn-group.open .dropdown-toggle { 29 | -webkit-box-shadow: 0 0 0 100px rgba(0, 0, 0, 0.1) inset; 30 | box-shadow: 0 0 0 100px rgba(0, 0, 0, 0.1) inset; 31 | } 32 | .btn-primary, 33 | .btn-success, 34 | .btn-info, 35 | .btn-warning, 36 | .btn-danger, 37 | .btn-inverse, 38 | .btn-purple, 39 | .btn-pink { 40 | color: #ffffff !important; 41 | } 42 | .btn-default { 43 | background-color: #dae6ec; 44 | border-color: #dae6ec; 45 | } 46 | .btn-default:focus { 47 | background-color: #dae6ec; 48 | border-color: #C2CED4; 49 | } 50 | .btn-default:hover { 51 | background-color: #dae6ec; 52 | border-color: #C2CED4; 53 | } 54 | .btn-default:active { 55 | background-color: #dae6ec; 56 | border-color: #C2CED4; 57 | } 58 | .btn-default.active, 59 | .btn-default:active, 60 | .open > .dropdown-toggle.btn-default { 61 | background-color: #dae6ec !important; 62 | border-color: #C2CED4 !important; 63 | } 64 | .btn-primary { 65 | background-color: #3bafda !important; 66 | border: 1px solid #3bafda !important; 67 | } 68 | .btn-primary:hover, 69 | .btn-primary:focus, 70 | .btn-primary:active, 71 | .btn-primary.active, 72 | .btn-primary.focus, 73 | .btn-primary:active, 74 | .btn-primary:focus, 75 | .btn-primary:hover, 76 | .open > .dropdown-toggle.btn-primary { 77 | background-color: #28a5d4 !important; 78 | border: 1px solid #28a5d4 !important; 79 | } 80 | .btn-success { 81 | background-color: #00b19d !important; 82 | border: 1px solid #00b19d !important; 83 | } 84 | .btn-success:hover, 85 | .btn-success:focus, 86 | .btn-success:active, 87 | .btn-success.active, 88 | .btn-success.focus, 89 | .btn-success:active, 90 | .btn-success:focus, 91 | .btn-success:hover, 92 | .open > .dropdown-toggle.btn-success { 93 | background-color: #009886 !important; 94 | border: 1px solid #009886 !important; 95 | } 96 | .btn-info { 97 | background-color: #3ddcf7 !important; 98 | border: 1px solid #3ddcf7 !important; 99 | } 100 | .btn-info:hover, 101 | .btn-info:focus, 102 | .btn-info:active, 103 | .btn-info.active, 104 | .btn-info.focus, 105 | .btn-info:active, 106 | .btn-info:focus, 107 | .btn-info:hover, 108 | .open > .dropdown-toggle.btn-info { 109 | background-color: #25d8f6 !important; 110 | border: 1px solid #25d8f6 !important; 111 | } 112 | .btn-warning { 113 | background-color: #ffaa00 !important; 114 | border: 1px solid #ffaa00 !important; 115 | } 116 | .btn-warning:hover, 117 | .btn-warning:focus, 118 | .btn-warning:active, 119 | .btn-warning.active, 120 | .btn-warning.focus, 121 | .btn-warning:active, 122 | .btn-warning:focus, 123 | .btn-warning:hover, 124 | .open > .dropdown-toggle.btn-warning { 125 | background-color: #e69900 !important; 126 | border: 1px solid #e69900 !important; 127 | } 128 | .btn-danger { 129 | background-color: #ef5350 !important; 130 | border: 1px solid #ef5350 !important; 131 | } 132 | .btn-danger:active, 133 | .btn-danger:focus, 134 | .btn-danger:hover, 135 | .btn-danger.active, 136 | .btn-danger.focus, 137 | .btn-danger:active, 138 | .btn-danger:focus, 139 | .btn-danger:hover, 140 | .open > .dropdown-toggle.btn-danger { 141 | background-color: #ed3c39 !important; 142 | border: 1px solid #ed3c39 !important; 143 | } 144 | .btn-inverse { 145 | background-color: #4c5667 !important; 146 | border: 1px solid #4c5667 !important; 147 | } 148 | .btn-inverse:hover, 149 | .btn-inverse:focus, 150 | .btn-inverse:active, 151 | .btn-inverse.active, 152 | .btn-inverse.focus, 153 | .btn-inverse:active, 154 | .btn-inverse:focus, 155 | .btn-inverse:hover, 156 | .open > .dropdown-toggle.btn-inverse { 157 | background-color: #414a58 !important; 158 | border: 1px solid #414a58 !important; 159 | } 160 | .btn-purple { 161 | background-color: #7266ba !important; 162 | border: 1px solid #7266ba !important; 163 | } 164 | .btn-purple:hover, 165 | .btn-purple:focus, 166 | .btn-purple:active { 167 | background-color: #6254b2 !important; 168 | border: 1px solid #6254b2 !important; 169 | } 170 | .btn-pink { 171 | background-color: #f76397 !important; 172 | border: 1px solid #f76397 !important; 173 | } 174 | .btn-pink:hover, 175 | .btn-pink:focus, 176 | .btn-pink:active { 177 | background-color: #f64b87 !important; 178 | border: 1px solid #f64b87 !important; 179 | } 180 | .btn-custom { 181 | box-shadow: 1px 1px 4px rgba(0, 0, 0, 0.4); 182 | -webkit-transition: all 0.4s; 183 | -o-transition: all 0.4s; 184 | transition: all 0.4s; 185 | } 186 | .btn-rounded { 187 | border-radius: 2em; 188 | padding: 6px 18px; 189 | } 190 | .fileupload { 191 | overflow: hidden; 192 | position: relative; 193 | } 194 | .fileupload input.upload { 195 | cursor: pointer; 196 | filter: alpha(opacity=0); 197 | font-size: 20px; 198 | margin: 0; 199 | opacity: 0; 200 | padding: 0; 201 | position: absolute; 202 | right: 0; 203 | top: 0; 204 | } 205 | -------------------------------------------------------------------------------- /app/assets/stylesheets/custom.scss: -------------------------------------------------------------------------------- 1 | @import "bootstrap-sprockets"; 2 | @import "bootstrap"; 3 | @import url(https://fonts.googleapis.com/css?family=Lato:400,300); 4 | html { 5 | position: relative; 6 | min-height: 100%; 7 | } 8 | body { 9 | padding-top: 60px; 10 | font-family: "Lato"; 11 | font-size: 16px; 12 | margin-bottom: 220px; 13 | background-color: #F6F9FC; 14 | } 15 | h1, h2, h3, h4 { 16 | font-family: "Lato"; 17 | font-weight: 300; 18 | } 19 | .footer { 20 | position: absolute; 21 | bottom: 0; 22 | width: 100%; 23 | height: 200px; 24 | padding-top: 40px; 25 | background-color: #272727; 26 | color: #fff; 27 | text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6); 28 | a { 29 | color: #2494be; 30 | } 31 | a:hover { 32 | text-decoration: none; 33 | color: #fff; 34 | } 35 | } 36 | .lato { 37 | font-family: "Lato"; 38 | font-weight: 300; 39 | 40 | } 41 | .navbar-default { 42 | background-color: #fff; 43 | height: 60px; 44 | img { 45 | margin-top:10px; 46 | } 47 | .navbar-nav > .active > a, .navbar-nav > .active > a:hover, .navbar-nav > .active > a:focus { 48 | background-color: #fff; 49 | color: #000; 50 | } 51 | .navbar-collapse { 52 | background-color: #fff; 53 | } 54 | .fa { 55 | margin-right: 8px; 56 | } 57 | } 58 | .home-hero { 59 | background-image:url('https://unsplash.it/1200/600?image=0&blur'); 60 | background-size:cover; 61 | background-color: #666; 62 | color: #fff; 63 | height: 600px; 64 | h1 { 65 | font-size: 72px; 66 | } 67 | h3 { 68 | color: #f6f6f6; 69 | a { 70 | color: #fff; 71 | font-weight: bold; 72 | } 73 | } 74 | } 75 | .campaign { 76 | font-family: "Lato", Helvetica, Arial, sans-serif!important; 77 | font-weight: 300; 78 | transition: box-shadow .3s; 79 | h2 { 80 | margin-top: 0px; 81 | } 82 | a { 83 | color: #555; 84 | text-decoration: none; 85 | } 86 | a:hover { 87 | color: #7266ba; 88 | } 89 | img { 90 | width: 100%; 91 | } 92 | .panel-body { 93 | padding: 0px; 94 | } 95 | } 96 | .campaign:hover { 97 | webkit-box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1),0 5px 15px rgba(0, 0, 0, 0.07); 98 | -moz-box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1),0 5px 15px rgba(0, 0, 0, 0.07); 99 | box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1),0 5px 15px rgba(0, 0, 0, 0.07); 100 | } 101 | .campaign-content { 102 | img { 103 | width: 100%; 104 | webkit-box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1),0 5px 15px rgba(0, 0, 0, 0.07); 105 | -moz-box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1),0 5px 15px rgba(0, 0, 0, 0.07); 106 | box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1),0 5px 15px rgba(0, 0, 0, 0.07); 107 | } 108 | } 109 | .img-selected { 110 | border: 1px solid #0080FF; 111 | } 112 | .raised { 113 | color: #00b19d; 114 | } 115 | .feature { 116 | font-family: "Lato"; 117 | font-weight: 300; 118 | font-size: 26px; 119 | h2 { 120 | color: #4a4a4a; 121 | font-family: "Lato"; 122 | font-weight: 300; 123 | } 124 | p { 125 | font-size: 24px; 126 | font-weight: 300; 127 | color: #999; 128 | font-family: sans-serif; 129 | } 130 | .price { 131 | font-size: 200px 132 | } 133 | .price-sm { 134 | font-size: 100px; 135 | } 136 | } 137 | .icon-lg { 138 | font-size: 100px; 139 | } 140 | .icon-huge { 141 | font-size:200px; 142 | } 143 | .topspace { 144 | margin-top: 10px; 145 | } 146 | .topspace-lg { 147 | margin-top: 50px; 148 | } 149 | .leftspace { 150 | margin-left: 8px; 151 | } 152 | .body-padding { 153 | padding: 15px; 154 | } 155 | .red { 156 | color: #B20000; 157 | } 158 | .gray { 159 | color: #666; 160 | } 161 | .pink { 162 | color: #D80A84; 163 | } 164 | .blue { 165 | color: #0080FF; 166 | } 167 | .purple { 168 | color: #7266ba; 169 | } 170 | .white { 171 | color: #fff; 172 | } 173 | .shadow-tiny { 174 | webkit-box-shadow: 0 2px 0 rgba(37,46,53,0.06); 175 | -moz-box-shadow: 0 2px 0 rgba(37,46,53,0.06); 176 | box-shadow: 0 2px 0 rgba(37,46,53,0.06); 177 | } 178 | .shadow-sm { 179 | webkit-box-shadow: 2px 2px 2px rgba(37,46,53,0.06); 180 | -moz-box-shadow: 2px 2px 2px rgba(37,46,53,0.06); 181 | box-shadow: 2px 2px 2px rgba(37,46,53,0.06); 182 | } 183 | .field_with_errors { 184 | @extend .has-error !optional; 185 | display: inline; 186 | } 187 | .highlight { 188 | background-color: #CCE6FF; 189 | } 190 | /* flash */ 191 | .alert-danger, .alert-error { 192 | background-color: #ef5350; 193 | border-color: #eed3d7; 194 | color: #fff; 195 | text-align: left; 196 | } 197 | .alert-alert { 198 | background-color: #ef5350; 199 | border-color: #eed3d7; 200 | color: #fff; 201 | text-align: left; 202 | } 203 | .alert-success { 204 | background-color: #00b19d; 205 | border-color: #d6e9c6; 206 | color: #fff; 207 | text-align: left; 208 | } 209 | .alert-notice { 210 | background-color: #00b19d; 211 | border-color: #d6e9c6; 212 | color: #fff; 213 | text-align: left; 214 | } 215 | .alert-danger h2, .alert-error h2 { 216 | margin-top:0px; 217 | } 218 | .box-shadow { 219 | -webkit-box-shadow: 0px 2px 3px 0px rgba(50, 50, 50, 0.75); 220 | -moz-box-shadow: 0px 2px 3px 0px rgba(50, 50, 50, 0.75); 221 | box-shadow: 0px 2px 3px 0px rgba(50, 50, 50, 0.75); 222 | } 223 | .btn-xl { 224 | font-size: 30px; 225 | padding: 15px; 226 | } 227 | .placeholder label { 228 | display: none !important; 229 | } 230 | .form-control:focus, .StripeElement--focus { 231 | border-color: #939393; 232 | box-shadow: none; 233 | } 234 | .StripeElement--invalid { 235 | border-color: #a94442; 236 | box-shadow: none; 237 | } 238 | .form-control, .has-error .form-control:focus { 239 | border-width: 1px; 240 | box-shadow: none; 241 | } 242 | .panel-payment { 243 | webkit-box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1),0 5px 15px rgba(0, 0, 0, 0.07); 244 | -moz-box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1),0 5px 15px rgba(0, 0, 0, 0.07); 245 | box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1),0 5px 15px rgba(0, 0, 0, 0.07); 246 | ::-webkit-input-placeholder, input { 247 | font-size: 15px!important; 248 | color: #666; 249 | font-family: "Open Sans", sans-serif; 250 | -webkit-font-smoothing: antialiased; 251 | -moz-osx-font-smoothing: grayscale; 252 | } 253 | input { 254 | color: #000; 255 | } 256 | } 257 | #card-errors { 258 | display: none; 259 | } 260 | 261 | /* media queries */ 262 | @media only screen 263 | and (min-device-width : 375px) 264 | and (max-device-width : 667px) { 265 | .home-hero h1 { 266 | font-size: 60px; 267 | } 268 | } 269 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :exception 3 | rescue_from ActiveRecord::RecordNotFound, :with => :record_not_found 4 | 5 | # Pretty generic method to handle exceptions. 6 | # You'll probably want to do some logging, notifications, etc. 7 | def handle_error(message = "Sorry, something failed.", view = 'new') 8 | flash.now[:alert] = message 9 | render view 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/bank_accounts_controller.rb: -------------------------------------------------------------------------------- 1 | class BankAccountsController < ApplicationController 2 | before_action :authenticate_user! 3 | 4 | def new 5 | # Redirect if no stripe account exists yet 6 | unless current_user.stripe_account 7 | redirect_to new_stripe_account_path and return 8 | end 9 | 10 | begin 11 | # Retrieve the account object for this user 12 | @account = Stripe::Account.retrieve(current_user.stripe_account) 13 | 14 | # Handle exceptions from Stripe 15 | rescue Stripe::StripeError => e 16 | handle_error(e.message, 'new') 17 | 18 | # Handle any other exceptions 19 | rescue => e 20 | handle_error(e.message, 'new') 21 | end 22 | end 23 | 24 | def create 25 | # Redirect if no token is POSTed or the user doesn't have a Stripe account 26 | unless params[:stripeToken] && current_user.stripe_account 27 | redirect_to new_bank_account_path and return 28 | end 29 | 30 | begin 31 | # Retrieve the account object for this user 32 | account = Stripe::Account.retrieve(current_user.stripe_account) 33 | 34 | # Create the bank account 35 | account.external_account = params[:stripeToken] 36 | account.save 37 | 38 | # Success, send on to the dashboard 39 | flash[:success] = "Your bank account has been added!" 40 | redirect_to dashboard_path 41 | 42 | # Handle exceptions from Stripe 43 | rescue Stripe::StripeError => e 44 | handle_error(e.message, 'new') 45 | 46 | # Handle any other exceptions 47 | rescue => e 48 | handle_error(e.message, 'new') 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /app/controllers/campaigns_controller.rb: -------------------------------------------------------------------------------- 1 | class CampaignsController < ApplicationController 2 | before_action :authenticate_user!, except: [:home, :show] 3 | include CampaignsHelper 4 | 5 | def home 6 | # Retrieve all active campaigns 7 | @campaigns = Campaign.where(active: true).order(created_at: :desc).page params[:page] 8 | end 9 | 10 | def new 11 | # Redirect if no existing Stripe account 12 | unless current_user.stripe_account 13 | redirect_to new_stripe_account_path and return 14 | end 15 | 16 | # Populate random campaign info 17 | random_campaign 18 | 19 | # Create a new campaign object 20 | @campaign = Campaign.new 21 | end 22 | 23 | def create 24 | # Create a campaign for the user 25 | @campaign = current_user.campaigns.new(campaign_params) 26 | 27 | # Redirect to the campaign page once created 28 | if @campaign.save 29 | flash[:notice] = "Your campaign has been created!" 30 | redirect_to @campaign 31 | else 32 | handle_error(@campaign.errors.full_messages, 'new') 33 | end 34 | end 35 | 36 | def show 37 | # Retrieve a campaign 38 | @campaign = Campaign.find(params[:id]) 39 | 40 | # List all charges for a given campaign 41 | @charges = Charge.where(campaign_id: @campaign.id, amount_refunded: nil).order(created_at: :desc) 42 | end 43 | 44 | def dashboard 45 | # List campaigns for the current user 46 | @campaigns = current_user.campaigns.order(created_at: :desc) 47 | 48 | # Redirect if there's not a Stripe account for this user yet 49 | unless current_user.stripe_account 50 | flash[:success] = "Create an account to get started." 51 | redirect_to new_stripe_account_path and return 52 | end 53 | 54 | # Retrieve charges, transfers, balance transactions, & balance from Stripe 55 | begin 56 | @stripe_account = Stripe::Account.retrieve(current_user.stripe_account) 57 | 58 | # Last 100 charges 59 | @payments = Stripe::Charge.list( 60 | { 61 | limit: 100, 62 | expand: ['data.source_transfer.source_transaction.dispute', 'data.application_fee'], 63 | source: {object: 'all'} 64 | }, 65 | { stripe_account: current_user.stripe_account } 66 | ) 67 | 68 | # Last 100 payouts from the managed account to their bank account 69 | @payouts = Stripe::Payout.list( 70 | { 71 | limit: 100, 72 | expand: ['data.destination'] 73 | }, 74 | { stripe_account: current_user.stripe_account } 75 | ) 76 | 77 | # Retrieve available and pending balance for an account 78 | @balance = Stripe::Balance.retrieve(stripe_account: current_user.stripe_account) 79 | @balance_available = @balance.available.first.amount + @balance.pending.first.amount 80 | 81 | # Retrieve transactions with an available_on date in the future 82 | # For a large platform, it's generally preferrable to handle these async 83 | transactions = Stripe::BalanceTransaction.all( 84 | { 85 | limit: 100, 86 | available_on: {gte: Time.now.to_i} 87 | }, 88 | { stripe_account: current_user.stripe_account } 89 | ) 90 | 91 | # Iterate through transactions and sum values for each available_on date 92 | # For a production app, you'll probably want to store and query these locally instead 93 | balances = Hash.new 94 | transactions.auto_paging_each do |txn| 95 | if balances.key?(txn.available_on) 96 | balances[txn.available_on] += txn.net 97 | else 98 | balances[txn.available_on] = txn.net 99 | end 100 | end 101 | 102 | # Sort the results 103 | @transactions = balances.sort_by {|date,net| date} 104 | 105 | # Check for a debit card external account and determine amount for payout 106 | @debit_card = @stripe_account.external_accounts.find { |c| c.object == "card"} 107 | @instant_amt = @balance_available*0.97 108 | @instant_fee = @balance_available*0.03 109 | 110 | # Handle Stripe exceptions 111 | rescue Stripe::StripeError => e 112 | flash[:error] = e.message 113 | redirect_to root_path 114 | 115 | # Handle other exceptions 116 | rescue => e 117 | flash[:error] = e.message 118 | redirect_to root_path 119 | end 120 | end 121 | 122 | def edit 123 | # Retrieve the campaign 124 | @campaign = Campaign.find(params[:id]) 125 | end 126 | 127 | def update 128 | # Retrieve the campaign 129 | @campaign = Campaign.find(params[:id]) 130 | 131 | # Redirect to view campaign 132 | if @campaign.update_attributes(campaign_params) 133 | flash[:notice] = "Your campaign has been updated!" 134 | redirect_to @campaign 135 | else 136 | handle_error(@campaign.errors.full_messages, 'edit') 137 | end 138 | end 139 | 140 | def destroy 141 | # Retrieve the campaign 142 | campaign = Campaign.find(params[:id]) 143 | 144 | # Respond with deletion status 145 | if campaign.update_attributes(active: false) 146 | flash[:notice] = "Your campaign has been deleted." 147 | redirect_to dashboard_path 148 | else 149 | flash[:error] = "We weren't able to delete this campaign." 150 | redirect_to dashboard_path 151 | end 152 | end 153 | 154 | private 155 | def campaign_params 156 | params.require(:campaign).permit(:title, :description, :goal, :subscription, :image) 157 | end 158 | 159 | def random_campaign 160 | data = campaign_data.sample 161 | @campaign_title = data[:title] 162 | @campaign_description = data[:description] 163 | @campaign_image = data[:image] 164 | @goal = amounts.sample 165 | end 166 | end 167 | -------------------------------------------------------------------------------- /app/controllers/charges_controller.rb: -------------------------------------------------------------------------------- 1 | class ChargesController < ApplicationController 2 | before_action :authenticate_user!, only: [:show, :destroy] 3 | 4 | def create 5 | # Check for a stripeToken 6 | unless charge_params[:stripeToken] 7 | flash[:error] = "No payment information submitted." 8 | redirect_back(fallback_location: root_path) and return 9 | end 10 | 11 | # Check for a valid campaign ID 12 | unless charge_params[:campaign] && Campaign.exists?(charge_params[:campaign]) 13 | flash[:error] = "The campaign you specified doesn't exist." 14 | redirect_back(fallback_location: root_path) and return 15 | end 16 | 17 | # Retrieve the campaign 18 | campaign = Campaign.find(params[:campaign]) 19 | 20 | begin 21 | # Find the account ID associated with this campaign 22 | account_id = User.find(campaign.user_id).stripe_account 23 | 24 | # Convert the amount to cents 25 | amount = (100 * charge_params[:amount].tr('$', '').to_r).to_i 26 | 27 | # Create the charge with Stripe 28 | charge = Stripe::Charge.create({ 29 | source: charge_params[:stripeToken], 30 | amount: amount, 31 | currency: "usd", 32 | application_fee: amount/10, # Take a 10% application fee for the platform 33 | destination: account_id, 34 | metadata: { "name" => charge_params[:name], "campaign" => campaign.id } 35 | } 36 | ) 37 | 38 | # Save the charge details locally for later use 39 | local_charge = Charge.create( 40 | charge_id: charge.id, 41 | amount: amount, 42 | name: charge_params[:name], 43 | campaign_id: campaign.id 44 | ) 45 | 46 | # Update the amount raised for this campaign 47 | campaign.raised = campaign.raised.to_i + amount 48 | campaign.save 49 | 50 | # Let the customer know the payment was successful 51 | flash[:success] = "Thanks for your donation! Your transaction ID is #{charge.id}." 52 | redirect_to campaign_path(campaign) 53 | 54 | # Handle exceptions from Stripe 55 | rescue Stripe::StripeError => e 56 | flash[:error] = e.message 57 | redirect_to campaign_path(campaign) 58 | 59 | # Handle other failures 60 | rescue => e 61 | flash[:error] = e.message 62 | redirect_to campaign_path(campaign) 63 | end 64 | end 65 | 66 | def show 67 | begin 68 | # Retrieve the charge from Stripe 69 | @charge = Stripe::Charge.retrieve(id: params[:id], expand: ['application_fee', 'dispute']) 70 | 71 | # Validate that the user should be able to view this charge 72 | check_destination(@charge) 73 | 74 | # Get the campaign from the metadata on the charge object 75 | @campaign = Campaign.find(@charge.metadata.campaign) 76 | 77 | # Handle exceptions from Stripe 78 | rescue Stripe::StripeError => e 79 | flash[:error] = e.message 80 | redirect_to dashboard_path 81 | 82 | # Handle other failures 83 | rescue => e 84 | flash[:error] = e.message 85 | redirect_to dashboard_path 86 | end 87 | end 88 | 89 | def index 90 | end 91 | 92 | # Using destroy action to handle refunds 93 | def destroy 94 | begin 95 | # Retrieve the charge from Stripe 96 | charge = Stripe::Charge.retrieve(params[:id]) 97 | 98 | # Validate that the user should be able to view this charge 99 | check_destination(charge) 100 | 101 | # Refund the charge 102 | charge.refund(reverse_transfer: true, refund_application_fee: true) 103 | 104 | # Update the local charge 105 | local_charge = Charge.find_by charge_id: charge.id 106 | local_charge.amount_refunded = charge.amount 107 | local_charge.save 108 | 109 | # Update the amount raised for this campaign 110 | campaign = Campaign.find(local_charge.campaign_id) 111 | campaign.raised = campaign.raised.to_i - charge.amount 112 | campaign.save 113 | 114 | # Let the user know the refund was successful 115 | flash[:success] = "The charge has been refunded." 116 | redirect_to dashboard_path 117 | 118 | # Handle Stripe exceptions 119 | rescue Stripe::StripeError => e 120 | flash.now[:error] = e.message 121 | redirect_to dashboard_path 122 | 123 | # Handle other failures 124 | rescue => e 125 | flash.now[:error] = e.message 126 | redirect_to dashboard_path 127 | end 128 | end 129 | 130 | private 131 | def charge_params 132 | params.permit(:amount, :stripeToken, :name, :campaign, :authenticity_token) 133 | end 134 | 135 | # Charge is retrieved from the platform, so only the destination should have access to it 136 | def check_destination(charge) 137 | # Redirect to the dashboard if the charge doesn't belong to current user 138 | unless charge.destination.eql?(current_user.stripe_account) 139 | flash[:error] = "You don't have access to this charge." 140 | redirect_to dashboard_path 141 | end 142 | end 143 | end 144 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/debit_cards_controller.rb: -------------------------------------------------------------------------------- 1 | class DebitCardsController < ApplicationController 2 | before_action :authenticate_user! 3 | 4 | def new 5 | end 6 | 7 | def create 8 | # Redirect if no token is POSTed or the user doesn't have a Stripe account 9 | unless params[:stripeToken] && current_user.stripe_account 10 | redirect_to debit_cards_create_path and return 11 | end 12 | 13 | begin 14 | # Retrieve the account object for this user 15 | account = Stripe::Account.retrieve(current_user.stripe_account) 16 | 17 | # Create the bank account 18 | account.external_accounts.create(external_account: params[:stripeToken]) 19 | account.save 20 | 21 | # Success, send on to the dashboard 22 | flash[:success] = "Your debit card has been added!" 23 | redirect_to dashboard_path 24 | 25 | # Handle exceptions from Stripe 26 | rescue Stripe::StripeError => e 27 | handle_error(e.message, 'new') 28 | 29 | # Handle any other exceptions 30 | rescue => e 31 | handle_error(e.message, 'new') 32 | end 33 | end 34 | 35 | def destroy 36 | end 37 | 38 | def transfer 39 | begin 40 | # Retrieve the account object for this user 41 | account = Stripe::Account.retrieve(current_user.stripe_account) 42 | 43 | # Create an instant payout 44 | payout = Stripe::Payout.create( 45 | { 46 | amount: params[:amount], 47 | currency: "usd", 48 | method: "instant", 49 | destination: params[:destination] 50 | }, 51 | { stripe_account: account.id } 52 | ) 53 | 54 | # Take a 3% fee for the instant payout 55 | Stripe::Charge.create( 56 | amount: params[:fee], 57 | currency: "usd", 58 | source: account.id, 59 | description: "Instant payout fee for #{payout.id}" 60 | ) 61 | 62 | # Success, send on to the dashboard 63 | flash[:success] = "Your payout has been made!" 64 | redirect_to dashboard_path 65 | 66 | # Handle exceptions from Stripe 67 | rescue Stripe::StripeError => e 68 | flash[:error] = e.message 69 | redirect_to dashboard_path 70 | 71 | # Handle any other exceptions 72 | rescue => e 73 | flash[:error] = e.message 74 | redirect_to dashboard_path 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /app/controllers/disputes_controller.rb: -------------------------------------------------------------------------------- 1 | class DisputesController < ApplicationController 2 | before_action :authenticate_user! 3 | 4 | def create 5 | if dispute_params[:dispute_text].empty? || dispute_params[:dispute_id].empty? 6 | flash[:error] = "Please provide supporting information about this dispute" 7 | redirect_back(fallback_location: root_path) and return 8 | end 9 | 10 | begin 11 | # Retrieve the account object for this user 12 | account = Stripe::Account.retrieve(current_user.stripe_account) 13 | 14 | # Retrieve the dispute 15 | dispute = Stripe::Dispute.retrieve(dispute_params[:dispute_id]) 16 | 17 | # Add the dispute evidence 18 | dispute.evidence.uncategorized_text = dispute_params[:dispute_text] 19 | # Add dispute document if one exists 20 | dispute.evidence.uncategorized_file = dispute_params[:dispute_document] 21 | dispute.save 22 | 23 | # Success, send back to the page 24 | flash[:success] = "This dispute has been updated" 25 | redirect_back(fallback_location: root_path) and return 26 | 27 | # Handle exceptions from Stripe 28 | rescue Stripe::StripeError => e 29 | flash[:error] = e.message 30 | redirect_to dashboard_path 31 | 32 | # Handle any other exceptions 33 | rescue => e 34 | flash[:error] = e.message 35 | redirect_to dashboard_path 36 | end 37 | end 38 | 39 | private 40 | def dispute_params 41 | params.permit(:dispute_id, :dispute_text, :dispute_document) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /app/controllers/pages_controller.rb: -------------------------------------------------------------------------------- 1 | class PagesController < ApplicationController 2 | def pricing 3 | end 4 | end -------------------------------------------------------------------------------- /app/controllers/payouts_controller.rb: -------------------------------------------------------------------------------- 1 | class PayoutsController < ApplicationController 2 | before_action :authenticate_user! 3 | 4 | def show 5 | # Retrieve the payout from Stripe to get details 6 | # For large production applications, it's usually best to store this state locally 7 | if params[:id] 8 | begin 9 | @stripe_account = Stripe::Account.retrieve(current_user.stripe_account) 10 | 11 | # Get the payout details 12 | @payout = Stripe::Payout.retrieve( 13 | { 14 | id: params[:id] 15 | }, 16 | { stripe_account: current_user.stripe_account } 17 | ) 18 | 19 | # Get the balance transactions from the payout 20 | @txns = Stripe::BalanceTransaction.list( 21 | { 22 | payout: params[:id], 23 | expand: ['data.source.source_transfer', 'data.source.charge.source_transfer'], 24 | limit: 100 25 | }, 26 | { stripe_account: current_user.stripe_account } 27 | ) 28 | # Handle exceptions from Stripe 29 | rescue Stripe::StripeError => e 30 | flash[:error] = e.message 31 | redirect_to dashboard_path 32 | rescue => e 33 | # Something else happened, completely unrelated to Stripe 34 | flash[:error] = e.message 35 | redirect_to dashboard_path 36 | end 37 | else 38 | flash[:error] = "Sorry, this payout was not found" 39 | redirect_to dashboard_path 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /app/controllers/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | class RegistrationsController < Devise::RegistrationsController 2 | protected 3 | 4 | def after_sign_up_path_for(resource) 5 | new_campaign_path 6 | end 7 | end -------------------------------------------------------------------------------- /app/controllers/stripe_accounts_controller.rb: -------------------------------------------------------------------------------- 1 | class StripeAccountsController < ApplicationController 2 | before_action :authenticate_user! 3 | 4 | def new 5 | @account = StripeAccount.new 6 | end 7 | 8 | def create 9 | @account = StripeAccount.new(account_params) 10 | 11 | if @account.save 12 | begin 13 | # For readability, both account creation options are shown separately 14 | 15 | # First option: create an account with full account application info 16 | if params[:full_account] 17 | stripe_account = Stripe::Account.create( 18 | managed: true, 19 | legal_entity: { 20 | first_name: account_params[:first_name].capitalize, 21 | last_name: account_params[:last_name].capitalize, 22 | type: account_params[:account_type], 23 | dob: { 24 | day: account_params[:dob_day], 25 | month: account_params[:dob_month], 26 | year: account_params[:dob_year] 27 | }, 28 | address: { 29 | line1: account_params[:address_line1], 30 | city: account_params[:address_city], 31 | state: account_params[:address_state], 32 | postal_code: account_params[:address_postal] 33 | }, 34 | ssn_last_4: account_params[:ssn_last_4] 35 | }, 36 | tos_acceptance: { 37 | date: Time.now.to_i, 38 | ip: request.remote_ip 39 | } 40 | ) 41 | 42 | # Second option: create an account with incremental info 43 | else 44 | stripe_account = Stripe::Account.create( 45 | managed: true, 46 | legal_entity: { 47 | first_name: account_params[:first_name].capitalize, 48 | last_name: account_params[:last_name].capitalize, 49 | type: account_params[:account_type], 50 | dob: { 51 | day: account_params[:dob_day], 52 | month: account_params[:dob_month], 53 | year: account_params[:dob_year] 54 | } 55 | }, 56 | tos_acceptance: { 57 | date: Time.now.to_i, 58 | ip: request.remote_ip 59 | } 60 | ) 61 | end 62 | 63 | # If this is a business, update with these values 64 | if account_params[:account_type].eql?('company') 65 | stripe_account.legal_entity.business_name = account_params[:business_name] 66 | stripe_account.legal_entity.business_tax_id = account_params[:business_tax_id] 67 | stripe_account.save 68 | end 69 | 70 | # Save the account ID for this user for later 71 | @account.acct_id = stripe_account.id 72 | @account.save 73 | current_user.stripe_account = stripe_account.id 74 | 75 | if current_user.save 76 | flash[:success] = "Your account has been created! 77 | Next, add a bank account where you'd like to receive transfers below." 78 | redirect_to new_bank_account_path 79 | else 80 | handle_error("Sorry, we weren't able to create this account.", 'new') 81 | end 82 | 83 | # Handle exceptions from Stripe 84 | rescue Stripe::StripeError => e 85 | handle_error(e.message, 'new') 86 | 87 | # Handle any other exceptions 88 | rescue => e 89 | handle_error(e.message, 'new') 90 | end 91 | else 92 | @full_account = true if params[:full_account] 93 | handle_error(@account.errors.full_messages) 94 | end 95 | end 96 | 97 | def edit 98 | # Check for a valid account ID 99 | unless params[:id] && params[:id].eql?(current_user.stripe_account) 100 | flash[:error] = "No Stripe account specified" 101 | redirect_to dashboard_path and return 102 | end 103 | 104 | # Retrieve the Stripe account to find fields needed 105 | @stripe_account = Stripe::Account.retrieve(params[:id]) 106 | 107 | # Retrieve the local account details 108 | @account = StripeAccount.find_by(acct_id: params[:id]) 109 | 110 | if @stripe_account.verification.fields_needed.empty? 111 | flash[:success] = "Your information is all up to date." 112 | redirect_to dashboard_path and return 113 | end 114 | end 115 | 116 | # Custom action for full account info collection 117 | def full 118 | @account = StripeAccount.new 119 | @full_account = true 120 | end 121 | 122 | def update 123 | # Check for an existing Stripe account 124 | unless current_user.stripe_account 125 | redirect_to new_stripe_account_path and return 126 | end 127 | 128 | begin 129 | # Retrieve the Stripe account 130 | @stripe_account = Stripe::Account.retrieve(current_user.stripe_account) 131 | 132 | @account = StripeAccount.new(account_params) 133 | 134 | 135 | # Reject empty values 136 | account_params.each do |key, value| 137 | if value.empty? 138 | flash.now[:alert] = "Please complete all fields." 139 | render 'edit' and return 140 | end 141 | end 142 | 143 | # Iterate through each field needed 144 | @stripe_account.verification.fields_needed.each do |field| 145 | 146 | # Update each needed attribute 147 | case field 148 | when 'legal_entity.address.city' 149 | @stripe_account.legal_entity.address.city = account_params[:address_city] 150 | when 'legal_entity.address.line1' 151 | @stripe_account.legal_entity.address.line1 = account_params[:address_line1] 152 | when 'legal_entity.address.postal_code' 153 | @stripe_account.legal_entity.address.postal_code = account_params[:address_postal] 154 | when 'legal_entity.address.state' 155 | @stripe_account.legal_entity.address.state = account_params[:address_state] 156 | when 'legal_entity.dob.day' 157 | @stripe_account.legal_entity.dob.day = account_params[:dob_day] 158 | when 'legal_entity.dob.month' 159 | @stripe_account.legal_entity.dob.month = account_params[:dob_month] 160 | when 'legal_entity.dob.year' 161 | @stripe_account.legal_entity.dob.year = account_params[:dob_year] 162 | when 'legal_entity.first_name' 163 | @stripe_account.legal_entity.first_name = account_params[:first_name] 164 | when 'legal_entity.last_name' 165 | @stripe_account.legal_entity.last_name = account_params[:last_name] 166 | when 'legal_entity.ssn_last_4' 167 | @stripe_account.legal_entity.ssn_last_4 = account_params[:ssn_last_4] 168 | when 'legal_entity.type' 169 | @stripe_account.legal_entity.type = account_params[:type] 170 | when 'legal_entity.personal_id_number' 171 | @stripe_account.legal_entity.personal_id_number = account_params[:personal_id_number] 172 | when 'legal_entity.verification.document' 173 | @stripe_account.legal_entity.verification.document = account_params[:verification_document] 174 | when 'legal_entity.business_name' 175 | @stripe_account.legal_entity.business_name = account_params[:business_name] 176 | when 'legal_entity.business_tax_id' 177 | @stripe_account.legal_entity.business_tax_id = account_params[:business_tax_id] 178 | end 179 | end 180 | 181 | @stripe_account.save 182 | flash[:success] = "Thanks! Your account has been updated." 183 | redirect_to dashboard_path and return 184 | 185 | # Handle exceptions from Stripe 186 | rescue Stripe::StripeError => e 187 | handle_error(e.message, 'edit') 188 | 189 | # Handle any other exceptions 190 | rescue => e 191 | handle_error(e.message, 'edit') 192 | end 193 | end 194 | 195 | private 196 | def account_params 197 | params.require(:stripe_account).permit( 198 | :first_name, :last_name, :account_type, :dob_month, :dob_day, :dob_year, :tos, 199 | :ssn_last_4, :address_line1, :address_city, :address_state, :address_postal, :business_name, 200 | :business_tax_id, :full_account, :personal_id_number, :verification_document 201 | ) 202 | end 203 | end 204 | -------------------------------------------------------------------------------- /app/controllers/webhooks_controller.rb: -------------------------------------------------------------------------------- 1 | class WebhooksController < ApplicationController 2 | protect_from_forgery except: :stripe 3 | 4 | def stripe 5 | # Use signed webhooks 6 | endpoint_secret = ENV['ENDPOINT_SECRET'].to_s 7 | 8 | payload = request.body.read 9 | sig_header = request.env['HTTP_STRIPE_SIGNATURE'] 10 | event = nil 11 | 12 | begin 13 | event = Stripe::Webhook.construct_event( 14 | payload, sig_header, endpoint_secret 15 | ) 16 | 17 | case event.type 18 | 19 | ############# 20 | # Disputes 21 | ############# 22 | 23 | # Recover dispute amounts and fees for disputed charges 24 | when 'charge.dispute.created' 25 | # The dispute that was created 26 | dispute = event.data.object 27 | 28 | # Retrieve the charge related to this dispute 29 | charge = Stripe::Charge.retrieve(dispute.charge) 30 | 31 | # Retrieve the platform account ID. You could also store this in an env variable. 32 | platform_account = Stripe::Account.retrieve 33 | 34 | # Issue a transfer reversal to recover the funds 35 | reverse_transfer(charge) 36 | 37 | # Create an account debit to recover dispute fee 38 | debit = Stripe::Transfer.create( 39 | { 40 | amount: dispute.balance_transactions.first.fee, 41 | currency: "usd", 42 | destination: platform_account.id, 43 | description: "Dispute fee for #{charge.id}" 44 | }, 45 | { stripe_account: charge.destination } 46 | ) 47 | 48 | # Return charge amount and fees if dispute is won 49 | when 'charge.dispute.funds_reinstated' 50 | # The dispute that was created 51 | dispute = event.data.object 52 | 53 | # Retrieve the charge related to this dispute 54 | charge = Stripe::Charge.retrieve(dispute.charge) 55 | 56 | # Create a transfer to the connected account to return the dispute fee 57 | transfer = Stripe::Transfer.create( 58 | amount: dispute.balance_transactions.second.net, 59 | currency: "usd", 60 | destination: charge.destination 61 | ) 62 | 63 | # Retrieve the destination payment 64 | payment = Stripe::Charge.retrieve( 65 | { 66 | id: transfer.destination_payment 67 | }, 68 | { 69 | stripe_account: transfer.destination 70 | } 71 | ) 72 | 73 | # Update the description on the destination payment 74 | payment.description = "Chargeback reversal for #{charge.id}" 75 | payment.save 76 | 77 | end 78 | 79 | # Something bad happened with the event or retrieving details from Stripe: probably log this. 80 | rescue JSON::ParserError, Stripe::SignatureVerificationError, Stripe::StripeError => e 81 | head :bad_request 82 | 83 | # Handle other exceptions. You may want to log these for review later too. 84 | rescue => e 85 | head :bad_request 86 | end 87 | 88 | # Return a 200 89 | head :ok 90 | end 91 | 92 | private 93 | def reverse_transfer(charge) 94 | # Retrieve the transfer for the charge 95 | transfer = Stripe::Transfer.retrieve(charge.transfer) 96 | 97 | # Reverse the transfer and keep the application fee 98 | transfer.reversals.create 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | def format_amount(amount) 3 | sprintf('$%0.2f', amount.to_f / 100.0).gsub(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1,") 4 | end 5 | 6 | def format_date(created) 7 | Time.at(created).getutc.strftime("%m/%d/%Y") 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/helpers/bank_accounts_helper.rb: -------------------------------------------------------------------------------- 1 | module BankAccountsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/charges_helper.rb: -------------------------------------------------------------------------------- 1 | module ChargesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/debit_cards_helper.rb: -------------------------------------------------------------------------------- 1 | module DebitCardsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/devise_helper.rb: -------------------------------------------------------------------------------- 1 | module DeviseHelper 2 | def devise_error_messages! 3 | return "" if resource.errors.empty? 4 | messages = resource.errors.full_messages.map { |msg| content_tag(:li, msg) }.join 5 | html = <<-HTML 6 |
7 | × 8 | 9 |
10 | HTML 11 | html.html_safe 12 | end 13 | end -------------------------------------------------------------------------------- /app/helpers/disputes_helper.rb: -------------------------------------------------------------------------------- 1 | module DisputesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/payouts_helper.rb: -------------------------------------------------------------------------------- 1 | module PayoutsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/stripe_accounts_helper.rb: -------------------------------------------------------------------------------- 1 | module StripeAccountsHelper 2 | def states 3 | [ 4 | ['Alabama', 'AL'], 5 | ['Alaska', 'AK'], 6 | ['Arizona', 'AZ'], 7 | ['Arkansas', 'AR'], 8 | ['California', 'CA'], 9 | ['Colorado', 'CO'], 10 | ['Connecticut', 'CT'], 11 | ['Delaware', 'DE'], 12 | ['District of Columbia', 'DC'], 13 | ['Florida', 'FL'], 14 | ['Georgia', 'GA'], 15 | ['Hawaii', 'HI'], 16 | ['Idaho', 'ID'], 17 | ['Illinois', 'IL'], 18 | ['Indiana', 'IN'], 19 | ['Iowa', 'IA'], 20 | ['Kansas', 'KS'], 21 | ['Kentucky', 'KY'], 22 | ['Louisiana', 'LA'], 23 | ['Maine', 'ME'], 24 | ['Maryland', 'MD'], 25 | ['Massachusetts', 'MA'], 26 | ['Michigan', 'MI'], 27 | ['Minnesota', 'MN'], 28 | ['Mississippi', 'MS'], 29 | ['Missouri', 'MO'], 30 | ['Montana', 'MT'], 31 | ['Nebraska', 'NE'], 32 | ['Nevada', 'NV'], 33 | ['New Hampshire', 'NH'], 34 | ['New Jersey', 'NJ'], 35 | ['New Mexico', 'NM'], 36 | ['New York', 'NY'], 37 | ['North Carolina', 'NC'], 38 | ['North Dakota', 'ND'], 39 | ['Ohio', 'OH'], 40 | ['Oklahoma', 'OK'], 41 | ['Oregon', 'OR'], 42 | ['Pennsylvania', 'PA'], 43 | ['Puerto Rico', 'PR'], 44 | ['Rhode Island', 'RI'], 45 | ['South Carolina', 'SC'], 46 | ['South Dakota', 'SD'], 47 | ['Tennessee', 'TN'], 48 | ['Texas', 'TX'], 49 | ['Utah', 'UT'], 50 | ['Vermont', 'VT'], 51 | ['Virginia', 'VA'], 52 | ['Washington', 'WA'], 53 | ['West Virginia', 'WV'], 54 | ['Wisconsin', 'WI'], 55 | ['Wyoming', 'WY'] 56 | ] 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/campaign.rb: -------------------------------------------------------------------------------- 1 | class Campaign < ApplicationRecord 2 | belongs_to :user 3 | 4 | validates :title, 5 | presence: true, length: { minimum: 5, maximum: 100 } 6 | 7 | validates :goal, 8 | presence: true, numericality: { greater_than: 20, less_than: 1000000 } 9 | 10 | validates :description, 11 | presence: true, length: { minimum: 10, maximum: 5000 } 12 | end 13 | -------------------------------------------------------------------------------- /app/models/charge.rb: -------------------------------------------------------------------------------- 1 | class Charge < ApplicationRecord 2 | 3 | end 4 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/stripe_account.rb: -------------------------------------------------------------------------------- 1 | class StripeAccount < ApplicationRecord 2 | validates :first_name, 3 | presence: true, length: { minimum: 1, maximum: 40 } 4 | 5 | validates :last_name, 6 | presence: true, length: { minimum: 1, maximum: 40 } 7 | 8 | validates :account_type, 9 | presence: true, inclusion: { in: %w(individual company), message: "%{value} is not a valid account type"} 10 | 11 | validates :tos, 12 | inclusion: { in: [ true ], message: ": You must agree to the terms of service" } 13 | end 14 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | has_many :campaigns 3 | # Include default devise modules. Others available are: 4 | # :confirmable, :lockable, :timeoutable and :omniauthable 5 | devise :database_authenticatable, :registerable, 6 | :recoverable, :rememberable, :trackable, :validatable 7 | end 8 | -------------------------------------------------------------------------------- /app/views/admin/kaminari/_first_page.html.erb: -------------------------------------------------------------------------------- 1 | <%# Link to the "First" page 2 | - available local variables 3 | url: url to the first page 4 | current_page: a page object for the currently displayed page 5 | total_pages: total number of pages 6 | per_page: number of items to fetch per page 7 | remote: data-remote 8 | -%> 9 | 10 | <%= link_to_unless current_page.first?, t('views.pagination.first').html_safe, url, remote: remote %> 11 | 12 | -------------------------------------------------------------------------------- /app/views/admin/kaminari/_gap.html.erb: -------------------------------------------------------------------------------- 1 | <%# Non-link tag that stands for skipped pages... 2 | - available local variables 3 | current_page: a page object for the currently displayed page 4 | total_pages: total number of pages 5 | per_page: number of items to fetch per page 6 | remote: data-remote 7 | -%> 8 | <%= t('views.pagination.truncate').html_safe %> 9 | -------------------------------------------------------------------------------- /app/views/admin/kaminari/_last_page.html.erb: -------------------------------------------------------------------------------- 1 | <%# Link to the "Last" page 2 | - available local variables 3 | url: url to the last page 4 | current_page: a page object for the currently displayed page 5 | total_pages: total number of pages 6 | per_page: number of items to fetch per page 7 | remote: data-remote 8 | -%> 9 | 10 | <%= link_to_unless current_page.last?, t('views.pagination.last').html_safe, url, remote: remote %> 11 | 12 | -------------------------------------------------------------------------------- /app/views/admin/kaminari/_next_page.html.erb: -------------------------------------------------------------------------------- 1 | <%# Link to the "Next" page 2 | - available local variables 3 | url: url to the next page 4 | current_page: a page object for the currently displayed page 5 | total_pages: total number of pages 6 | per_page: number of items to fetch per page 7 | remote: data-remote 8 | -%> 9 | 10 | <%= link_to_unless current_page.last?, t('views.pagination.next').html_safe, url, rel: 'next', remote: remote %> 11 | 12 | -------------------------------------------------------------------------------- /app/views/admin/kaminari/_page.html.erb: -------------------------------------------------------------------------------- 1 | <%# Link showing page number 2 | - available local variables 3 | page: a page object for "this" page 4 | url: url to this page 5 | current_page: a page object for the currently displayed page 6 | total_pages: total number of pages 7 | per_page: number of items to fetch per page 8 | remote: data-remote 9 | -%> 10 | 11 | <%= link_to_unless page.current?, page, url, {remote: remote, rel: page.rel} %> 12 | 13 | -------------------------------------------------------------------------------- /app/views/admin/kaminari/_paginator.html.erb: -------------------------------------------------------------------------------- 1 | <%# The container tag 2 | - available local variables 3 | current_page: a page object for the currently displayed page 4 | total_pages: total number of pages 5 | per_page: number of items to fetch per page 6 | remote: data-remote 7 | paginator: the paginator that renders the pagination tags inside 8 | -%> 9 | <%= paginator.render do -%> 10 | 25 | <% end -%> 26 | -------------------------------------------------------------------------------- /app/views/admin/kaminari/_prev_page.html.erb: -------------------------------------------------------------------------------- 1 | <%# Link to the "Previous" page 2 | - available local variables 3 | url: url to the previous page 4 | current_page: a page object for the currently displayed page 5 | total_pages: total number of pages 6 | per_page: number of items to fetch per page 7 | remote: data-remote 8 | -%> 9 | 10 | <%= link_to_unless current_page.first?, t('views.pagination.previous').html_safe, url, rel: 'prev', remote: remote %> 11 | 12 | -------------------------------------------------------------------------------- /app/views/bank_accounts/_bank_account_form.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 9 | 12 |
13 |
14 |
15 |
16 | 17 | 20 |
21 |
22 |
23 |
24 |
25 |
26 | 27 | 28 |
29 |
30 |
31 |
32 | 33 | 34 |
35 |
36 |
37 |
38 |
39 | 40 |
41 |
42 | <%= hidden_field_tag :authenticity_token, form_authenticity_token -%> 43 |
44 |
45 |
-------------------------------------------------------------------------------- /app/views/bank_accounts/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, "Add a new bank account" %> 2 | <% content_for(:header) do %> 3 | 4 | 5 | 49 | <% end %> 50 |
51 |
52 |
53 |

Add a bank account

54 |
55 | <%= render 'layouts/messages' %> 56 | <%= render 'bank_account_form' %> 57 |
58 |

59 | Hint: use 110000000 and 000123456789 to simulate a successful transfer. 60 |

61 |

62 | Other test bank account numbers 63 |

64 |
65 |
66 |
67 |
68 |
69 | -------------------------------------------------------------------------------- /app/views/campaigns/_campaigns.html.erb: -------------------------------------------------------------------------------- 1 | <% @campaigns.each_slice(3) do | campaigns | %> 2 |
3 | <% campaigns.each do |campaign| %> 4 |
5 |
6 | <%= link_to campaign_path(campaign) do %> 7 |
8 | <%= image_tag("#{campaign.image}_sm.jpg") %> 9 |
10 |

<%= link_to campaign.title.titleize, campaign_path(campaign) %>

11 |

12 | <% if campaign.raised %> 13 | <%= number_to_currency(campaign.raised/100, precision: 0) %> raised so far of <%= number_to_currency(campaign.goal, precision: 0) %> goal 14 | <% else %> 15 | <%= number_to_currency(campaign.goal, precision: 0) %> goal 16 | <% end %> 17 |

18 |

19 | <%= simple_format(truncate(campaign.description, length: 90)) %> 20 |

21 |
22 |
23 | <% end %> 24 |
25 |
26 | <% end %> 27 |
28 | <% end %> 29 | -------------------------------------------------------------------------------- /app/views/campaigns/_charges.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | <% @payments.each do |payment| %> 15 | <% if payment.source_transfer.source_transaction.present? %> 16 | 17 | 32 | 33 | 34 | 35 | 44 | 45 | <% end %> 46 | <% end %> 47 | 48 |
Payment IDAmountNetCreatedActions
18 | <% if payment.source_transfer.source_transaction.source.object.eql?('card') %> 19 | 20 | <% end %> 21 | <%= link_to payment.source_transfer.source_transaction.id, charge_path(payment.source_transfer.source_transaction.id), class: "leftspace" %> 22 | <% if payment.source_transfer.source_transaction.dispute.present? %> 23 | <% if payment.source_transfer.source_transaction.dispute.status.eql?('needs_response') %> 24 | Disputed 25 | <% elsif payment.source_transfer.source_transaction.dispute.status.eql?('lost') %> 26 | Dispute lost 27 | <% elsif payment.source_transfer.source_transaction.dispute.status.eql?('won') %> 28 | Dispute won 29 | <% end %> 30 | <% end %> 31 | <%= number_to_currency(payment.amount/100) %><%= format_amount(payment.amount - payment.application_fee.amount) %><%= format_date(payment.created) %> 36 | <% if payment.refunded %> 37 | 38 | Refunded 39 | 40 | <% else %> 41 | <%= link_to "Refund", charge_path(payment.source_transfer.source_transaction.id), method: :delete, data: { confirm: "Are you sure you want to refund this charge?", disable_with: " Refunding charge..." }, class: "btn btn-sm btn-block btn-custom btn-danger" %> 42 | <% end %> 43 |
49 |
50 |
51 | -------------------------------------------------------------------------------- /app/views/campaigns/_checkoutform.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | <%= hidden_field_tag :authenticity_token, form_authenticity_token %> 5 |
6 |
7 |
8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 |
16 | 17 | 18 |
19 |
20 |
21 | 22 | 23 | 24 |
25 |
26 |
27 |

28 | Use Stripe Elements 29 |

30 | -------------------------------------------------------------------------------- /app/views/campaigns/_donations.html.erb: -------------------------------------------------------------------------------- 1 | <% @charges.each do | charge | %> 2 |
3 |
4 |

5 | 6 | 7 | 8 | 9 | <%= number_to_currency(charge.amount/100) %> donated by <%= charge.name.split.first %> 10 | <%= time_ago_in_words(charge.created_at) %> ago 11 |

12 |
13 |
14 |
15 | <% end %> 16 | -------------------------------------------------------------------------------- /app/views/campaigns/_images.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | <%= image_tag("money_sm.jpg", alt: "Generic money image", class: "img-thumbnail") %> 5 | 6 |
7 |
8 | 9 | <%= image_tag("travel_sm.jpg", alt: "Generic travel image", class: "img-thumbnail") %> 10 | 11 |
12 |
13 | 14 | <%= image_tag("beach_sm.jpg", alt: "Generic beach image", class: "img-thumbnail") %> 15 | 16 |
17 |
18 |
19 |
20 | 21 | <%= image_tag("desk_sm.jpg", alt: "Generic desk image", class: "img-thumbnail") %> 22 | 23 |
24 |
25 | 26 | <%= image_tag("puppy_sm.jpg", alt: "Generic puppy image", class: "img-thumbnail") %> 27 | 28 |
29 |
30 | 31 | <%= image_tag("snow_sm.jpg", alt: "Generic snow image", class: "img-thumbnail") %> 32 | 33 |
34 |
35 |
36 |
37 | 38 | <%= image_tag("ocean_sm.jpg", alt: "Generic ocean image", class: "img-thumbnail") %> 39 | 40 |
41 |
42 | 43 | <%= image_tag("sf_sm.jpg", alt: "Generic SF image", class: "img-thumbnail") %> 44 | 45 |
46 |
47 | 48 | <%= image_tag("wedding_sm.jpg", alt: "Generic wedding image", class: "img-thumbnail") %> 49 | 50 |
51 |
-------------------------------------------------------------------------------- /app/views/campaigns/_paymentform.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | <%= hidden_field_tag :authenticity_token, form_authenticity_token %> 5 |
6 |
7 |
8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 |
16 | 17 | 18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 | 36 |
37 |
38 |
39 |
40 |
41 |

42 | Use Stripe Checkout 43 |

44 | -------------------------------------------------------------------------------- /app/views/campaigns/_transactions.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | <% @transactions.each do |date,net| %> 12 | <% unless net.eql?(0) %> 13 | 14 | 15 | 16 | 17 | <% end %> 18 | <% end %> 19 | 20 |
Balance amountAvailable for transfer after
<%= format_amount(net) %><%= format_date(date) %>
21 |
22 |
-------------------------------------------------------------------------------- /app/views/campaigns/_transfers.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | <% @payouts.each do |payout| %> 14 | 15 | 24 | 31 | 32 | 33 | 34 | <% end %> 35 | 36 |
Transfer IDDestinationAmountDeposit date
16 | <% if payout.type.eql?('bank_account') %> 17 | 18 | <%= link_to payout.id, payout_path(payout.id), class: "leftspace" %> 19 | <% else %> 20 | 21 | <%= payout.id %> 22 | <% end %> 23 | 25 | <% if payout.type.eql?('bank_account') %> 26 | <%= payout.destination.bank_name %> ending in <%= payout.destination.last4 %> 27 | <% elsif payout.type.eql?('card') %> 28 | <%= payout.destination.brand %> ending in <%= payout.destination.last4 %> 29 | <% end %> 30 | <%= format_amount(payout.amount) %><%= format_date(payout.arrival_date) %>
37 |
38 |
39 | -------------------------------------------------------------------------------- /app/views/campaigns/_verification.html.erb: -------------------------------------------------------------------------------- 1 | <% if @stripe_account.verification.fields_needed %> 2 |
3 |
4 |
5 |

6 | We need some information to continue processing donations for you. 7 |

8 |

9 | <%= link_to "Update your information", edit_stripe_account_path(@stripe_account.id), class: "btn btn-lg btn-warning btn-custom" %> 10 |

11 |
12 |
13 |
14 | <% end %> -------------------------------------------------------------------------------- /app/views/campaigns/dashboard.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, 'Fundraising Marketplace | Dashboard' %> 2 |
3 |
4 |
5 | <%= render 'layouts/messages' %> 6 |
7 |
8 | <% unless @stripe_account.verification.fields_needed.empty? %> 9 | <%= render 'verification' %> 10 | <% end %> 11 | 12 | <% unless @campaigns.empty? %> 13 | <%= render 'campaigns' %> 14 | <% else %> 15 | <%= link_to "Create your first fundraising campaign to get started", new_campaign_path %>. 16 | <% end %> 17 | 21 |
22 |
23 | <% unless @payments.data.empty? %> 24 | <%= render 'charges' %> 25 | <% if @balance_available > 0 %> 26 |
27 |

Want your funds immediately?

28 |

29 | For an extra 3% fee, we'll create an instant transfer to your debit card. 30 |

31 | <% if @debit_card %> 32 | <%= button_to "Get this transfer instantly", instant_transfer_path, data: { confirm: "Instant payout #{format_amount(@instant_amt)} to your #{@debit_card.brand} debit card ending in #{@debit_card.last4}?", disable_with: "Transferring funds..." }, method: :post, params: { amount: @instant_amt.round, fee: @instant_fee.round, destination: @debit_card.id }, class: "btn btn-success btn-lg btn-custom" %> 33 | <% else %> 34 | <%= link_to "Add a debit card", debit_cards_new_path, class: "btn btn-lg btn-success btn-custom" %> 35 | <% end %> 36 |
37 | <% end %> 38 | <% else %> 39 | No donations yet. 40 | <% end %> 41 |
42 |
43 | 44 |
45 |
46 | <% unless @payouts.data.empty? %> 47 | <%= render 'transfers' %> 48 | <% else %> 49 | No payouts yet. 50 | <% end %> 51 |
52 |
53 |
54 | -------------------------------------------------------------------------------- /app/views/campaigns/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, 'Fundraising Marketplace | Edit your campaign' %> 2 |
3 |
4 |
5 |

Edit your campaign

6 |
7 | <%= render 'layouts/messages' %> 8 |
9 |
10 | <%= form_for @campaign do | f | %> 11 |
12 | <%= f.label :title, "Title" %> 13 | <%= f.text_field :title, class: "form-control input-lg" %> 14 |
15 |
16 | <%= f.label :goal, "Goal" %> 17 |
18 | $ 19 | <%= f.text_field :goal, class: "form-control input-lg" %> 20 |
21 |
22 |
23 | <%= f.label :description, "Description" %> 24 | <%= f.text_area :description, class: "form-control input-lg" %> 25 |
26 |
27 | <%= render 'images' %> 28 |
29 |
30 | <%= f.button "Update Campaign", class: "btn btn-primary btn-lg btn-block btn-custom", data: {disable_with: " Updating campaign..."} %> 31 |
32 | <%= f.hidden_field :image %> 33 | <% end %> 34 |
35 |
36 |
37 |
38 |
39 |
-------------------------------------------------------------------------------- /app/views/campaigns/home.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, 'Stripe Connect Example App' %> 2 | <% if params[:page] %> 3 | 4 | <% else %> 5 |
6 |
7 |
8 |
9 | <%= render 'layouts/messages' %> 10 |

Fundraising Marketplace

11 |

A Stripe Connect example app

12 |

13 | <% if user_signed_in? %> 14 | <%= link_to "Create a campaign", new_campaign_path, class: "btn btn-xl btn-primary btn-custom btn-block shadow-sm" %> 15 | <% else %> 16 | <%= link_to "Create an account", new_user_registration_path, class: "btn btn-xl btn-primary btn-custom btn-block shadow-sm" %> 17 | <% end %> 18 |

19 |
20 |
21 |
22 |
23 | <% end %> 24 |
25 |
26 |
27 | 28 | <%= render 'campaigns' %> 29 |
30 |
31 |

32 | <%= page_entries_info @campaigns, entry_name: 'campaign' %> 33 |

34 |

35 | <%= link_to_previous_page @campaigns, 'Previous Page', class: "btn btn-primary btn-custom" %> 36 | <%= link_to_next_page @campaigns, 'Next Page', class: "btn btn-primary btn-custom" %> 37 |

38 |
39 |
40 |
41 |
42 |
43 | -------------------------------------------------------------------------------- /app/views/campaigns/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, 'Fundraising Marketplace | Sell a course' %> 2 |
3 |
4 |
5 |

Create a campaign

6 |
7 | <%= render 'layouts/messages' %> 8 |
9 |
10 | <%= form_for @campaign do | campaign | %> 11 |
12 | <%= campaign.label :title, "Title" %> 13 | <%= campaign.text_field :title, class: "form-control input-lg", placeholder: "Help me pay my college tuition!", value: @campaign.title || @campaign_title %> 14 |
15 |
16 | <%= campaign.label :goal, "Goal ($20 - $1,000,000)" %> 17 |
18 | $ 19 | <%= campaign.text_field :goal, class: "form-control input-lg", placeholder: "200.00", value: @campaign.goal || @goal %> 20 |
21 |
22 |
23 | <%= campaign.label :description, "Description" %> 24 | <%= campaign.text_area :description, class: "form-control input-lg", placeholder: "My tuition is expensive! Please donate if you want to help me pay it.", value: @campaign.description || @campaign_description, rows: 4 %> 25 |
26 |
27 | <%= campaign.label :image, "Select an image for your campaign" %> 28 | <%= render 'images' %> 29 | <%= campaign.hidden_field :image, value: @campaign.image || @campaign_image %> 30 |
31 |
32 | <%= campaign.button "Create Campaign", class: "btn btn-primary btn-lg btn-block btn-custom", data: {disable_with: " Creating campaign..."} %> 33 |
34 | <% end %> 35 |
36 |
37 |
38 |
39 |
40 |
41 | -------------------------------------------------------------------------------- /app/views/campaigns/show.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, "#{@campaign.title.titleize} | Fundraising Marketplace" %> 2 | <% content_for(:header) do %> 3 | <% if params[:checkout].present? %> 4 | 5 | 49 | <% else %> 50 | 51 | 142 | <% end %> 143 | <% end %> 144 |
145 |
146 |
147 | <%= render 'layouts/messages' %> 148 | <% unless @campaign.active %> 149 |
150 | This campaign is no longer active. 151 |
152 | <% end %> 153 |
154 | <%= image_tag("#{@campaign.image}_lg.jpg", class: "img-responsive") %> 155 |
156 | 159 |

160 | <% if @campaign.raised %> 161 | <%= format_amount(@campaign.raised) %> raised so far of <%= number_to_currency(@campaign.goal, precision: 0) %> goal 162 | <% else %> 163 | <%= number_to_currency(@campaign.goal) %> goal 164 | <% end %> 165 |

166 |
167 | <%= simple_format(@campaign.description) %> 168 |
169 | 170 | <% unless @campaign.raised.to_i > 0 %> 171 | This campaign has not received any donations yet. 172 | <% else %> 173 | <%= render 'donations' %> 174 | <% end %> 175 |
176 |
177 | <% if @campaign.active %> 178 | <% if params[:checkout].present? %> 179 | <%= render 'checkoutform' %> 180 | <% else %> 181 | <%= render 'paymentform' %> 182 | <% end %> 183 |
184 |

185 | Hint: use a Stripe test card and any valid expiry. 186 |

187 |
188 | <% end %> 189 | <% if current_user && @campaign.user_id.eql?(current_user.id) && @campaign.active %> 190 | 191 |

192 | <%= link_to "Edit this campaign", edit_campaign_path(@campaign), 193 | class: "btn btn-lg btn-primary btn-custom btn-block"%> 194 |

195 |

196 | <%= link_to "Delete this campaign", campaign_path(@campaign), method: :delete, data: { confirm: "Are you sure you want to delete this campaign?" }, class: "btn btn-lg btn-block btn-custom btn-danger" %> 197 |

198 | <% end %> 199 |
200 |
201 |
202 | -------------------------------------------------------------------------------- /app/views/charges/_dispute.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

Respond to this dispute

3 |
4 |
5 |
6 |
7 |
8 |
9 | 10 | 11 |
12 | Share any communication with the customer or feedback on why this donation shouldn't have been disputed 13 |
14 |
15 |
16 | 17 | 18 | Upload any supporting documentation (optional) 19 |
20 |
21 |
22 |
23 |
24 | 25 |
26 |
27 |
28 | <%= hidden_field_tag :authenticity_token, form_authenticity_token -%> 29 | <%= hidden_field_tag :dispute_id, @charge.dispute.id -%> 30 | 31 |
32 |
33 | 34 | 35 | 86 | -------------------------------------------------------------------------------- /app/views/charges/show.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, 'Fundraising Marketplace | Review charge' %> 2 |
3 |
4 |
5 | <%= render 'layouts/messages' %> 6 |
7 |
8 |
9 |
10 | 13 |
14 |
15 |
16 |
From
17 |
<%= @charge.metadata.name %>
18 |
19 |
Payment method
20 |
<%= @charge.source.brand %> ending in <%= @charge.source.last4 %>
21 |
22 |
For
23 |
<%= link_to @campaign.title, campaign_path(@campaign) %>
24 |
25 |
Marketplace fee
26 |
<%= format_amount(@charge.application_fee.amount) %>
27 |
28 |
Net earnings
29 |
<%= format_amount(@charge.amount - @charge.application_fee.amount) %>
30 |
31 |
Created
32 |
<%= format_date(@charge.created) %>
33 |
34 |
Refunded
35 |
<%= @charge.refunded %>
36 |
37 |
Disputed
38 |
<%= @charge.dispute.present? %>
39 |
40 |
41 | <% if @charge.refunded %> 42 | 43 | <% elsif @charge.dispute.present? %> 44 |
45 | <% if @charge.dispute.status.eql?('needs_response') %> 46 | Needs response 47 | This donation was disputed. <%= format_amount(@charge.dispute.balance_transactions.first.net.abs) %> in fees and disputed funds have been withdrawn from your account. 48 | <% elsif @charge.dispute.status.eql?('won') %> 49 | Won 50 | This dispute was won in your favor. <%= format_amount(@charge.dispute.balance_transactions.first.net.abs) %> in fees and disputed funds have been returned to your account. 51 | <% elsif @charge.dispute.status.eql?('lost') %> 52 | Lost 53 | The bank sided in favor of the donor. <%= format_amount(@charge.dispute.balance_transactions.first.net.abs) %> in fees and disputed funds have been withdrawn from your account. 54 | <% end %> 55 |
56 | <% else %> 57 | <%= link_to "Refund this donation", charge_path(@charge.id), method: :delete, data: { confirm: "Are you sure you want to refund this charge?", disable_with: " Refunding charge..." }, class: "btn btn-lg btn-block btn-custom btn-danger" %> 58 | <% end %> 59 |
60 |
61 | <% if @charge.dispute.present? && (@charge.dispute.status.eql?('needs_response') || @charge.dispute.status.eql?('under_review')) %> 62 | <%= render 'dispute' %> 63 | <% end %> 64 |
65 |
66 |
67 | -------------------------------------------------------------------------------- /app/views/debit_cards/_debit_card_form.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | <%= hidden_field_tag :authenticity_token, form_authenticity_token %> 5 |
6 |
7 |
8 |
9 | 10 | 11 |
12 |
13 |
14 |
15 |
16 |
17 | 18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 | 28 |
29 |
30 |
31 |
32 |
-------------------------------------------------------------------------------- /app/views/debit_cards/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, "Add debit card" %> 2 | <% content_for(:header) do %> 3 | 4 | 79 | <% end %> 80 |
81 |
82 |
83 |

Add a debit card

84 |

Get an instant payout

85 |
86 | <%= render 'layouts/messages' %> 87 | <%= render 'debit_card_form' %> 88 |
89 |

90 | Hint: use 4000056655665556 and any valid expiry. 91 |

92 |

93 | See all test debit card numbers 94 |

95 |
96 |
97 |
98 |
99 |
-------------------------------------------------------------------------------- /app/views/devise/confirmations/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend confirmation instructions

2 | 3 | <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true, value: (resource.pending_reconfirmation? ? resource.unconfirmed_email : resource.email) %> 9 |
10 | 11 |
12 | <%= f.submit "Resend confirmation instructions" %> 13 |
14 | <% end %> 15 | 16 | <%= render "devise/shared/links" %> 17 | -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Welcome <%= @email %>!

2 | 3 |

You can confirm your account email through the link below:

4 | 5 |

<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

6 | -------------------------------------------------------------------------------- /app/views/devise/mailer/password_change.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

We're contacting you to notify you that your password has been changed.

4 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Someone has requested a link to change your password. You can do this through the link below.

4 | 5 |

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

6 | 7 |

If you didn't request this, please ignore this email.

8 |

Your password won't change until you access the link above and create a new one.

9 | -------------------------------------------------------------------------------- /app/views/devise/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

4 | 5 |

Click the link below to unlock your account:

6 | 7 |

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

8 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Change your password

2 | 3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | <%= f.hidden_field :reset_password_token %> 6 | 7 |
8 | <%= f.label :password, "New password" %>
9 | <% if @minimum_password_length %> 10 | (<%= @minimum_password_length %> characters minimum)
11 | <% end %> 12 | <%= f.password_field :password, autofocus: true, autocomplete: "off" %> 13 |
14 | 15 |
16 | <%= f.label :password_confirmation, "Confirm new password" %>
17 | <%= f.password_field :password_confirmation, autocomplete: "off" %> 18 |
19 | 20 |
21 | <%= f.submit "Change my password" %> 22 |
23 | <% end %> 24 | 25 | <%= render "devise/shared/links" %> 26 | -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, 'Fundraising Marketplace | Reset your password' %> 2 |
3 |
4 |
5 |

Forgot your password?

6 | <%= devise_error_messages! %> 7 | <%= render 'layouts/messages' %> 8 |
9 |
10 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> 11 |
12 | <%= f.label :email %> 13 | <%= f.email_field :email, autofocus: true, class: "form-control input-lg", placeholder: "Email address" %> 14 |
15 |
16 | <%= f.button "Reset password", class: "btn btn-lg btn-primary btn-block btn-custom", data: {disable_with: " Resetting..."} %> 17 |
18 | <% end %> 19 |
20 |
21 |
22 |
23 |
-------------------------------------------------------------------------------- /app/views/devise/registrations/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, 'Fundraising Marketplace | Your account' %> 2 |
3 |
4 |
5 |

Manage your account

6 | <%= devise_error_messages! %> 7 | <%= render 'layouts/messages' %> 8 |
9 |
10 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> 11 | <%= devise_error_messages! %> 12 | 13 |
14 | <%= f.label :email %> 15 | <%= f.email_field :email, class: 'form-control input-lg', placeholder: "Email", autofocus: true %> 16 |
17 | 18 | <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> 19 |
Currently waiting confirmation for: <%= resource.unconfirmed_email %>
20 | <% end %> 21 | 22 |
23 | <%= f.password_field :password, autocomplete: "off", class: 'form-control input-lg', placeholder: "New password" %> 24 | Leave blank if you don't want to change your password 25 |
26 | 27 |
28 | <%= f.password_field :password_confirmation, autocomplete: "off", class: 'form-control input-lg', placeholder: "Confirm new password" %> 29 |
30 | 31 |
32 | <%= f.password_field :current_password, autocomplete: "off", class: 'form-control input-lg', placeholder: "Current password" %> 33 |
34 | 35 |
36 | <%= f.submit "Update", class: "btn btn-primary btn-lg btn-block btn-custom", data: {disable_with: " Updating your account..."} %> 37 |
38 | <% end %> 39 |
40 |
41 |
42 |
43 |
44 |
45 |

46 | <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure you want to cancel your account? You can't undo this" }, method: :delete, class: "btn btn-danger btn-lg btn-block btn-custom" %> 47 |

48 |

49 | <%= link_to "Back", :back %> 50 |

51 |
52 |
53 |
54 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, 'Fundraising Marketplace | Sign up' %> 2 |
3 |
4 |
5 |

Sign up

6 | <%= devise_error_messages! %> 7 | <%= render 'layouts/messages' %> 8 |
9 |
10 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> 11 |
12 | <%= f.label :email %> 13 | <%= f.email_field :email, class: 'form-control input-lg', placeholder: "Email", autofocus: true %> 14 |
15 |
16 | <%= f.label :password %> 17 | <%= f.password_field :password, autocomplete: "off", class: 'form-control input-lg', placeholder: "Password" %> 18 |
19 |
20 | <%= f.label :password_confirmation %> 21 | <%= f.password_field :password_confirmation, autocomplete: "off", class: 'form-control input-lg', placeholder: "Confirm password" %> 22 |
23 |
24 | <%= f.button "Sign up", class: "btn btn-primary btn-lg btn-block btn-custom", data: {disable_with: " Signing up..."} %> 25 |
26 | <% end %> 27 |
28 |
29 |
30 | Already have an account? <%= link_to "Sign in", new_user_session_path %> 31 |
32 |
33 |
34 |
-------------------------------------------------------------------------------- /app/views/devise/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, 'Fundraising Marketplace | Sign in' %> 2 |
3 |
4 |
5 |

Sign in to your account

6 | <%= devise_error_messages! %> 7 | <%= render 'layouts/messages' %> 8 |
9 |
10 | <%= form_for(resource, as: resource_name, 11 | url: session_path(resource_name)) do |f| %> 12 |
13 | <%= f.label :email %> 14 | <%= f.email_field :email, autofocus: true, class: "form-control input-lg", placeholder: "Email address" %> 15 |
16 |
17 | <%= f.label :password %> 18 | <%= f.password_field :password, autocomplete: "off", class: "form-control input-lg", placeholder: "Password" %> 19 |
20 |
21 | <%= f.button "Log in", class: "btn btn-lg btn-block btn-primary btn-custom", data: {disable_with: " Logging in..."} %> 22 |
23 | <% end %> 24 |
25 | <%= link_to "Forgot your password?", new_user_password_path %> 26 |
27 |
28 |
29 |
30 | Need an account? <%= link_to "Sign up", new_user_registration_path %> 31 |
32 |
33 |
34 |
-------------------------------------------------------------------------------- /app/views/devise/shared/_links.html.erb: -------------------------------------------------------------------------------- 1 | <%- if controller_name != 'sessions' %> 2 | <%= link_to "Log in", new_session_path(resource_name) %>
3 | <% end -%> 4 | 5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %> 6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end -%> 8 | 9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> 10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
11 | <% end -%> 12 | 13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> 14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
15 | <% end -%> 16 | 17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> 18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
19 | <% end -%> 20 | 21 | <%- if devise_mapping.omniauthable? %> 22 | <%- resource_class.omniauth_providers.each do |provider| %> 23 | <%= link_to "Sign in with #{OmniAuth::Utils.camelize(provider)}", omniauth_authorize_path(resource_name, provider) %>
24 | <% end -%> 25 | <% end -%> 26 | -------------------------------------------------------------------------------- /app/views/devise/unlocks/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend unlock instructions

2 | 3 | <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true %> 9 |
10 | 11 |
12 | <%= f.submit "Resend unlock instructions" %> 13 |
14 | <% end %> 15 | 16 | <%= render "devise/shared/links" %> 17 | -------------------------------------------------------------------------------- /app/views/kaminari/_first_page.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, :remote => remote %> 3 |
  • 4 | -------------------------------------------------------------------------------- /app/views/kaminari/_gap.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= content_tag :a, raw(t 'views.pagination.truncate') %> 3 |
  • 4 | -------------------------------------------------------------------------------- /app/views/kaminari/_last_page.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {:remote => remote} %> 3 |
  • 4 | -------------------------------------------------------------------------------- /app/views/kaminari/_next_page.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, :rel => 'next', :remote => remote %> 3 |
  • 4 | -------------------------------------------------------------------------------- /app/views/kaminari/_page.html.erb: -------------------------------------------------------------------------------- 1 | <% if page.current? %> 2 |
  • 3 | <%= content_tag :a, page, remote: remote, rel: (page.next? ? 'next' : (page.prev? ? 'prev' : nil)) %> 4 |
  • 5 | <% else %> 6 |
  • 7 | <%= link_to page, url, remote: remote, rel: (page.next? ? 'next' : (page.prev? ? 'prev' : nil)) %> 8 |
  • 9 | <% end %> 10 | -------------------------------------------------------------------------------- /app/views/kaminari/_paginator.html.erb: -------------------------------------------------------------------------------- 1 | <%= paginator.render do -%> 2 | 15 | <% end -%> 16 | -------------------------------------------------------------------------------- /app/views/kaminari/_prev_page.html.erb: -------------------------------------------------------------------------------- 1 |
  • 2 | <%= link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, :rel => 'prev', :remote => remote %> 3 |
  • 4 | -------------------------------------------------------------------------------- /app/views/layouts/_footer.html.erb: -------------------------------------------------------------------------------- 1 | 15 | -------------------------------------------------------------------------------- /app/views/layouts/_header.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/layouts/_messages.html.erb: -------------------------------------------------------------------------------- 1 | <% flash.each do |key, value| %> 2 |
    3 | × 4 | 17 |
    18 | <% end %> -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | <%= yield(:page_title) %> 8 | <%= csrf_meta_tags %> 9 | <%= stylesheet_link_tag 'application', media: 'all' %> 10 | <%= javascript_include_tag 'application' %> 11 | <%= favicon_link_tag 'favicon.ico' %> 12 | <%= yield(:header) %> 13 | 14 | 15 | 16 | <%= render 'layouts/header' %> 17 | <%= yield %> 18 | <%= render 'layouts/footer' %> 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/views/pages/pricing.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, 'Fundraising Marketplace | Pricing' %> 2 |
    3 |
    4 |
    5 |

    Simple, transparent pricing

    6 |
    7 |

    10%

    8 | Fee on funds raised 9 |
    10 |
    11 |
    12 |

    $0

    13 | Refunds 14 |
    15 |
    16 |

    $0

    17 | Chargebacks 18 |
    19 |
    20 |

    $0

    21 | Setup 22 |
    23 |
    24 |
    25 |

    26 | <%= link_to "Sign up for an account", new_user_registration_path, class: "btn btn-xl btn-primary btn-custom btn-block shadow-sm" %> 27 |

    28 |
    29 |
    30 |
    -------------------------------------------------------------------------------- /app/views/pages/terms.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, 'Fundraising Marketplace | Terms' %> 2 |
    3 |
    4 |
    5 |

    Terms of service

    6 |
    7 |
    8 |
    9 |
    10 |

    11 | Your managed accounts must agree to Stripe's Connected Account Agreement. Read more about this in this support article. 12 |

    13 |
    14 |
    15 |
    -------------------------------------------------------------------------------- /app/views/payouts/show.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, "View payout" %> 2 |
    3 |
    4 |
    5 |

    Payout details

    6 |
    7 | <%= render 'layouts/messages' %> 8 |
    9 |
    10 |
    11 | Payout overview 12 |
    13 |
    14 |
    Date paid
    15 |
    <%= format_date(@payout.created) %>
    16 |
    17 |
    18 |
    Payout amount
    19 |
    <%= format_amount(@payout.amount) %>
    20 |
    21 |
    22 |
    Bank account
    23 |
    <%= "#{@payout.bank_account.bank_name} ending in #{@payout.bank_account.last4}" %>
    24 |
    25 |
    26 |
    27 |
    28 |
    29 | Payout transactions 30 |
    31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | <% @txns.auto_paging_each do |txn| %> 42 | <% unless txn.type.eql?('transfer') && txn.source.type.eql?('bank_account') %> 43 | 44 | 61 | 62 | 63 | 64 | 65 | <% end %> 66 | <% end %> 67 | 68 |
    TypeGrossFeeTotal
    45 | <% if txn.type.eql?('payment') && txn.source.source_transfer.source_transaction.present? %> 46 | <%= link_to "Payment", charge_path(txn.source.source_transfer.source_transaction) %> 47 | <% elsif txn.type.eql?('payment_refund') %> 48 | <%= link_to "Payment refund", charge_path(txn.source.charge.source_transfer.source_transaction) %> 49 | <% elsif txn.type.eql?('adjustment') %> 50 | Marketplace fee refund 51 | <% elsif txn.type.eql?('transfer') && txn.source.method.eql?('instant') %> 52 | Instant payout <%= txn.source.id %> 53 | <% elsif txn.type.eql?('transfer') && txn.source.type.eql?('stripe_account') %> 54 | <%= txn.description %> 55 | <% elsif txn.description.present? %> 56 | <%= txn.description %> 57 | <% else %> 58 | <%= txn.type %> 59 | <% end %> 60 | <%= format_amount(txn.amount) %><%= format_amount(txn.fee) %><%= format_amount(txn.net) %>
    69 |
    70 |
    71 |
    72 |
    73 |
    74 | -------------------------------------------------------------------------------- /app/views/stripe_accounts/_account_form.html.erb: -------------------------------------------------------------------------------- 1 |
    2 |
    3 | <%= form_for @account do | f | %> 4 |
    5 |
    6 |
    7 | <%= f.label :account_type, "Account Type" %> 8 | <%= f.select(:account_type, options_for_select([["Individual","individual"], ["Company","company"]], @account[:account_type]), {}, { class: "form-control input-lg" }) %> 9 |
    10 |
    11 |
    12 |
    13 |
    14 |
    15 |
    16 | <%= f.label :business_name, "Business name (as it appears to the IRS)" %> 17 | <%= f.text_field :business_name, class: "form-control input-lg", placeholder: "My Business LLC" %> 18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 | <%= f.label :business_tax_id, "Business tax ID/EIN" %> 25 | <%= f.text_field :business_tax_id, class: "form-control input-lg", placeholder: "27-0000000" %> 26 |
    27 |
    28 |
    29 |
    30 |
    31 |
    32 |
    33 | <%= f.label :first_name, "First Name" %> 34 | <%= f.text_field :first_name, class: "form-control input-lg", placeholder: "Jane" %> 35 |
    36 |
    37 |
    38 |
    39 | <%= f.label :last_name, "Last Name" %> 40 | <%= f.text_field :last_name, class: "form-control input-lg", placeholder: "Doe" %> 41 |
    42 |
    43 |
    44 | <% if @full_account %> 45 |
    46 |
    47 |
    48 | <%= f.label :ssn_last_4, "Last 4 digits of your SSN" %> 49 | <%= f.text_field :ssn_last_4, class: "form-control input-lg", placeholder: "6789" %> 50 |
    51 |
    52 |
    53 | <%= hidden_field_tag :full_account, value: "true" %> 54 | <% end %> 55 |
    56 |
    57 |
    58 |
    59 | <%= f.label :dob_month, "DOB Month" %> 60 | <%= f.select(:dob_month, options_for_select([["January", "1"],["February", "2"],["March", "3"],["April", "4"],["May", "5"],["June", "6"],["July", "7"],["August", "8"],["September", "9"],["October", "10"],["November", "11"],["December", "12"]], @account[:dob_month]), {}, { class: "form-control input-lg" }) %> 61 |
    62 |
    63 |
    64 |
    65 | <%= f.label :dob_day, "DOB Day" %> 66 | <%= f.select(:dob_day, options_for_select((1..31).each{|n| [n, n]}, @account[:dob_day]), {}, { class: "form-control input-lg" }) %> 67 |
    68 |
    69 |
    70 |
    71 | <%= f.label :dob_year, "DOB Year" %> 72 | <%= f.select(:dob_year, options_for_select((1912..2016).each{|n| [n, n]}, @account[:dob_year]), {}, { class: "form-control input-lg" }) %> 73 |
    74 |
    75 |
    76 |
    77 | <% if @full_account %> 78 |
    79 |
    80 |
    81 | <%= f.label :address_line1, "Street address" %> 82 | <%= f.text_field :address_line1, class: "form-control input-lg", placeholder: "185 Berry St" %> 83 |
    84 |
    85 |
    86 |
    87 |
    88 |
    89 | <%= f.label :address_city, "City" %> 90 | <%= f.text_field :address_city, class: "form-control input-lg", placeholder: "San Francisco" %> 91 |
    92 |
    93 |
    94 |
    95 | <%= f.label :address_state, "State" %> 96 | <%= f.select(:address_state, options_for_select(states, @account[:address_state]), {}, { class: "form-control input-lg" }) %> 97 |
    98 |
    99 |
    100 |
    101 | <%= f.label :address_postal, "Zip" %> 102 | <%= f.text_field :address_postal, class: "form-control input-lg", placeholder: "90210" %> 103 |
    104 |
    105 |
    106 |
    107 | <% end %> 108 |
    109 |
    110 |
    111 | <%= f.check_box :tos %> 112 | <%= f.label :tos do %> 113 | I agree to the <%= link_to "terms of service", terms_path %>. 114 | <% end %> 115 |
    116 |
    117 |
    118 |
    119 |
    120 |
    121 | <%= f.button "Create Account", class: "btn btn-primary btn-lg btn-block btn-custom", data: {disable_with: " Creating account..."} %> 122 |
    123 |
    124 |
    125 | <% end %> 126 |
    127 |
    128 | -------------------------------------------------------------------------------- /app/views/stripe_accounts/edit.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, 'Fundraising Marketplace | Submit your account info' %> 2 |
    3 |
    4 |
    5 |

    6 | Update your account 7 |

    8 |
    9 | <%= render 'layouts/messages' %> 10 | <%= render 'identity_verification_form' %> 11 |
    12 |
    13 |
    14 |
    15 | 18 |
    19 |
    -------------------------------------------------------------------------------- /app/views/stripe_accounts/full.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, 'Fundraising Marketplace | Submit your info' %> 2 |
    3 |
    4 |
    5 |

    Submit your account application

    6 |
    7 | <%= render 'layouts/messages' %> 8 | <%= render 'account_form' %> 9 |

    10 | <%= link_to 'Collect info incrementally', new_stripe_account_path %> 11 |

    12 |
    13 |
    14 |
    15 |
    -------------------------------------------------------------------------------- /app/views/stripe_accounts/new.html.erb: -------------------------------------------------------------------------------- 1 | <%= content_for :page_title, 'Fundraising Marketplace | Get Paid' %> 2 |
    3 |
    4 |
    5 |

    Get Paid

    6 |
    7 | <%= render 'layouts/messages' %> 8 | <%= render 'account_form' %> 9 |

    10 | <%= link_to 'Collect all info up front', stripe_accounts_full_path %> 11 |

    12 |
    13 |
    14 |
    15 |
    -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # puts "\n== Copying sample files ==" 22 | # unless File.exist?('config/database.yml') 23 | # cp 'config/database.yml.sample', 'config/database.yml' 24 | # end 25 | 26 | puts "\n== Preparing database ==" 27 | system! 'bin/rails db:setup' 28 | 29 | puts "\n== Removing old logs and tempfiles ==" 30 | system! 'bin/rails log:clear tmp:clear' 31 | 32 | puts "\n== Restarting application server ==" 33 | system! 'bin/rails restart' 34 | end 35 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)) 11 | Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) } 12 | gem 'spring', match[1] 13 | require 'spring/binstub' 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module SellerDashboard 10 | class Application < Rails::Application 11 | # Settings in config/environments/* take precedence over those specified here. 12 | # Application configuration should go into files in config/initializers 13 | # -- all .rb files in that directory are automatically loaded. 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite version 3.x 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem 'sqlite3' 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: 5 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: db/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: db/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: db/production.sqlite3 26 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => 'public, max-age=172800' 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Don't care if the mailer can't send. 30 | config.action_mailer.raise_delivery_errors = false 31 | 32 | config.action_mailer.perform_caching = false 33 | 34 | # Print deprecation notices to the Rails logger. 35 | config.active_support.deprecation = :log 36 | 37 | # Raise an error on page load if there are pending migrations. 38 | config.active_record.migration_error = :page_load 39 | 40 | # Debug mode disables concatenation and preprocessing of assets. 41 | # This option may cause significant delays in view rendering with a large 42 | # number of complex assets. 43 | config.assets.debug = true 44 | 45 | # Suppress logger output for asset requests. 46 | config.assets.quiet = true 47 | 48 | # Raises error for missing translations 49 | # config.action_view.raise_on_missing_translations = true 50 | 51 | # Use an evented file watcher to asynchronously detect changes in source code, 52 | # routes, locales, etc. This feature depends on the listen gem. 53 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 54 | 55 | # Configure actionmailer for devise 56 | config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } 57 | end 58 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Disable serving static files from the `/public` folder by default since 18 | # Apache or NGINX already handles this. 19 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 20 | 21 | # Compress JavaScripts and CSS. 22 | config.assets.js_compressor = :uglifier 23 | # config.assets.css_compressor = :sass 24 | 25 | # Do not fallback to assets pipeline if a precompiled asset is missed. 26 | config.assets.compile = false 27 | 28 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 29 | 30 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 31 | # config.action_controller.asset_host = 'http://assets.example.com' 32 | 33 | # Specifies the header that your server uses for sending files. 34 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 35 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 36 | 37 | # Mount Action Cable outside main process or domain 38 | # config.action_cable.mount_path = nil 39 | # config.action_cable.url = 'wss://example.com/cable' 40 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 41 | 42 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 43 | # config.force_ssl = true 44 | 45 | # Use the lowest log level to ensure availability of diagnostic information 46 | # when problems arise. 47 | config.log_level = :debug 48 | 49 | # Prepend all log lines with the following tags. 50 | config.log_tags = [ :request_id ] 51 | 52 | # Use a different cache store in production. 53 | # config.cache_store = :mem_cache_store 54 | 55 | # Use a real queuing backend for Active Job (and separate queues per environment) 56 | # config.active_job.queue_adapter = :resque 57 | # config.active_job.queue_name_prefix = "seller-dashboard_#{Rails.env}" 58 | config.action_mailer.perform_caching = false 59 | 60 | # Ignore bad email addresses and do not raise email delivery errors. 61 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 62 | # config.action_mailer.raise_delivery_errors = false 63 | 64 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 65 | # the I18n.default_locale when a translation cannot be found). 66 | config.i18n.fallbacks = true 67 | 68 | # Send deprecation notices to registered listeners. 69 | config.active_support.deprecation = :notify 70 | 71 | # Use default logging formatter so that PID and timestamp are not suppressed. 72 | config.log_formatter = ::Logger::Formatter.new 73 | 74 | # Use a different logger for distributed setups. 75 | # require 'syslog/logger' 76 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 77 | 78 | if ENV["RAILS_LOG_TO_STDOUT"].present? 79 | logger = ActiveSupport::Logger.new(STDOUT) 80 | logger.formatter = config.log_formatter 81 | config.logger = ActiveSupport::TaggedLogging.new(logger) 82 | end 83 | 84 | # Do not dump schema after migrations. 85 | config.active_record.dump_schema_after_migration = false 86 | end 87 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => 'public, max-age=3600' 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/kaminari_config.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | Kaminari.configure do |config| 3 | config.default_per_page = 6 4 | # config.max_per_page = nil 5 | # config.window = 4 6 | # config.outer_window = 0 7 | # config.left = 0 8 | # config.right = 0 9 | # config.page_method_name = :page 10 | # config.param_name = :page 11 | # config.params_on_first_page = false 12 | end 13 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/new_framework_defaults.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains migration options to ease your Rails 5.0 upgrade. 4 | # 5 | # Read the Rails 5.0 release notes for more info on each option. 6 | 7 | # Enable per-form CSRF tokens. Previous versions had false. 8 | Rails.application.config.action_controller.per_form_csrf_tokens = true 9 | 10 | # Enable origin-checking CSRF mitigation. Previous versions had false. 11 | Rails.application.config.action_controller.forgery_protection_origin_check = true 12 | 13 | # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. 14 | # Previous versions had false. 15 | ActiveSupport.to_time_preserves_timezone = true 16 | 17 | # Require `belongs_to` associations by default. Previous versions had false. 18 | Rails.application.config.active_record.belongs_to_required_by_default = true 19 | 20 | # Do not halt callback chains when a callback returns false. Previous versions had true. 21 | ActiveSupport.halt_callback_chains_on_return_false = false 22 | 23 | # Configure SSL options to enable HSTS with subdomains. Previous versions had false. 24 | Rails.application.config.ssl_options = { hsts: { subdomains: true } } 25 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_seller-dashboard_session' 4 | -------------------------------------------------------------------------------- /config/initializers/stripe.rb: -------------------------------------------------------------------------------- 1 | Rails.configuration.stripe = { 2 | :publishable_key => ENV['PUBLISHABLE_KEY'], 3 | :secret_key => ENV['SECRET_KEY'] 4 | } 5 | 6 | Stripe.api_key = Rails.configuration.stripe[:secret_key] 7 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/devise.en.yml: -------------------------------------------------------------------------------- 1 | # Additional translations at https://github.com/plataformatec/devise/wiki/I18n 2 | 3 | en: 4 | devise: 5 | confirmations: 6 | confirmed: "Your email address has been successfully confirmed." 7 | send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." 8 | send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." 9 | failure: 10 | already_authenticated: "You are already signed in." 11 | inactive: "Your account is not activated yet." 12 | invalid: "Invalid %{authentication_keys} or password." 13 | locked: "Your account is locked." 14 | last_attempt: "You have one more attempt before your account is locked." 15 | not_found_in_database: "Invalid %{authentication_keys} or password." 16 | timeout: "Your session expired. Please sign in again to continue." 17 | unauthenticated: "You need to sign in or sign up before continuing." 18 | unconfirmed: "You have to confirm your email address before continuing." 19 | mailer: 20 | confirmation_instructions: 21 | subject: "Confirmation instructions" 22 | reset_password_instructions: 23 | subject: "Reset password instructions" 24 | unlock_instructions: 25 | subject: "Unlock instructions" 26 | password_change: 27 | subject: "Password Changed" 28 | omniauth_callbacks: 29 | failure: "Could not authenticate you from %{kind} because \"%{reason}\"." 30 | success: "Successfully authenticated from %{kind} account." 31 | passwords: 32 | no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." 33 | send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." 34 | send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." 35 | updated: "Your password has been changed successfully. You are now signed in." 36 | updated_not_active: "Your password has been changed successfully." 37 | registrations: 38 | destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." 39 | signed_up: "Welcome! Add your information below to create a fundraising campaign." 40 | signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." 41 | signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." 42 | signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." 43 | update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." 44 | updated: "Your account has been updated successfully." 45 | sessions: 46 | signed_in: "Signed in successfully." 47 | signed_out: "Signed out successfully." 48 | already_signed_out: "Signed out successfully." 49 | unlocks: 50 | send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." 51 | send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." 52 | unlocked: "Your account has been unlocked successfully. Please sign in to continue." 53 | errors: 54 | messages: 55 | already_confirmed: "was already confirmed, please try signing in" 56 | confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" 57 | expired: "has expired, please request a new one" 58 | not_found: "not found" 59 | not_locked: "was not locked" 60 | not_saved: 61 | one: "1 error prohibited this %{resource} from being saved:" 62 | other: "%{count} errors prohibited this %{resource} from being saved:" 63 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum, this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests, default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. If you use this option 30 | # you need to make sure to reconnect any threads in the `on_worker_boot` 31 | # block. 32 | # 33 | # preload_app! 34 | 35 | # The code in the `on_worker_boot` will be called if you are using 36 | # clustered mode by specifying a number of `workers`. After each worker 37 | # process is booted this block will be run, if you are using `preload_app!` 38 | # option you will want to use this block to reconnect to any threads 39 | # or connections that may have been created at application boot, Ruby 40 | # cannot share connections between processes. 41 | # 42 | # on_worker_boot do 43 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord) 44 | # end 45 | 46 | # Allow puma to be restarted by `rails restart` command. 47 | plugin :tmp_restart 48 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | 3 | devise_for :users, controllers: { registrations: "registrations" } 4 | 5 | root to: "campaigns#home" 6 | get 'dashboard', to: 'campaigns#dashboard' 7 | get 'pricing', to: 'pages#pricing' 8 | get 'terms', to: 'pages#terms' 9 | get 'stripe_accounts/full', to: 'stripe_accounts#full' 10 | get 'debit_cards/new' 11 | post 'debit_cards/create', to: 'debit_cards#create' 12 | post 'debit_cards/destroy', to: 'debit_cards#destroy' 13 | post 'disputes', to: 'disputes#create' 14 | post 'instant_transfer', to: 'debit_cards#transfer' 15 | get 'payouts/:id', to: 'payouts#show', as: 'payout' 16 | post 'webhooks/stripe', to: 'webhooks#stripe' 17 | 18 | resources :campaigns 19 | 20 | resources :stripe_accounts 21 | 22 | resources :charges 23 | 24 | resources :bank_accounts 25 | end 26 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rails secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: cb8b29ab66181a6dbefabc9939b0c9e599a8788fd450a218caad32bac552aaa473836aa7286019b7d108fc4a968b7363f8814fa35b14efe4dd3fe85ee8f76421 15 | 16 | test: 17 | secret_key_base: 85767ecd3e5e4b26f92be965ec4bc5cc2850a13ef4999bb208039b68b80e020669ce791e3f91e048b80ad3693a5e45f61c77e61740eca164ccf32962882784b2 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /db/migrate/20160910151553_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :users do |t| 4 | ## Database authenticatable 5 | t.string :email, null: false, default: "" 6 | t.string :encrypted_password, null: false, default: "" 7 | 8 | ## Recoverable 9 | t.string :reset_password_token 10 | t.datetime :reset_password_sent_at 11 | 12 | ## Rememberable 13 | t.datetime :remember_created_at 14 | 15 | ## Trackable 16 | t.integer :sign_in_count, default: 0, null: false 17 | t.datetime :current_sign_in_at 18 | t.datetime :last_sign_in_at 19 | t.string :current_sign_in_ip 20 | t.string :last_sign_in_ip 21 | 22 | ## Confirmable 23 | # t.string :confirmation_token 24 | # t.datetime :confirmed_at 25 | # t.datetime :confirmation_sent_at 26 | # t.string :unconfirmed_email # Only if using reconfirmable 27 | 28 | ## Lockable 29 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts 30 | # t.string :unlock_token # Only if unlock strategy is :email or :both 31 | # t.datetime :locked_at 32 | 33 | 34 | t.timestamps null: false 35 | end 36 | 37 | add_index :users, :email, unique: true 38 | add_index :users, :reset_password_token, unique: true 39 | # add_index :users, :confirmation_token, unique: true 40 | # add_index :users, :unlock_token, unique: true 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /db/migrate/20160910162519_create_lessons.rb: -------------------------------------------------------------------------------- 1 | class CreateLessons < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :lessons do |t| 4 | t.integer :user_id 5 | t.string :title 6 | t.string :description 7 | t.integer :price 8 | t.boolean :subscription 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20160910213144_rename_lessons_to_courses.rb: -------------------------------------------------------------------------------- 1 | class RenameLessonsToCourses < ActiveRecord::Migration[5.0] 2 | def change 3 | rename_table :lessons, :courses 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160911175135_add_image_to_course.rb: -------------------------------------------------------------------------------- 1 | class AddImageToCourse < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :courses, :image, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160911185541_add_stripe_account_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddStripeAccountToUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :stripe_account, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160911193440_create_stripe_accounts.rb: -------------------------------------------------------------------------------- 1 | class CreateStripeAccounts < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :stripe_accounts do |t| 4 | t.string :first_name 5 | t.string :last_name 6 | t.string :account_type 7 | t.integer :dob_month 8 | t.integer :dob_day 9 | t.integer :dob_year 10 | 11 | t.timestamps 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /db/migrate/20160911230223_add_fields_to_stripe_account.rb: -------------------------------------------------------------------------------- 1 | class AddFieldsToStripeAccount < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :stripe_accounts, :address_city, :string 4 | add_column :stripe_accounts, :address_state, :string 5 | add_column :stripe_accounts, :address_line1, :string 6 | add_column :stripe_accounts, :address_postal, :string 7 | add_column :stripe_accounts, :tos, :boolean 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20160913025720_create_charges.rb: -------------------------------------------------------------------------------- 1 | class CreateCharges < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :charges do |t| 4 | t.string :charge_id 5 | t.integer :amount 6 | t.integer :amount_refunded 7 | t.integer :user_id 8 | t.integer :course_id 9 | 10 | t.timestamps 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /db/migrate/20160915051353_change_description_in_courses.rb: -------------------------------------------------------------------------------- 1 | class ChangeDescriptionInCourses < ActiveRecord::Migration[5.0] 2 | def change 3 | change_column :courses, :description, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20160915051609_add_content_link_to_courses.rb: -------------------------------------------------------------------------------- 1 | class AddContentLinkToCourses < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :courses, :content_link, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20161227175740_rename_courses_to_campaigns.rb: -------------------------------------------------------------------------------- 1 | class RenameCoursesToCampaigns < ActiveRecord::Migration[5.0] 2 | def change 3 | rename_table :courses, :campaigns 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20161227180421_modify_campaigns_and_charges.rb: -------------------------------------------------------------------------------- 1 | class ModifyCampaignsAndCharges < ActiveRecord::Migration[5.0] 2 | def change 3 | remove_column :campaigns, :content_link, :string 4 | remove_column :campaigns, :price, :integer 5 | rename_column :charges, :course_id, :campaign_id 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20161227183231_add_goal_to_campaigns.rb: -------------------------------------------------------------------------------- 1 | class AddGoalToCampaigns < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :campaigns, :goal, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20161228174458_remove_user_id_from_charges.rb: -------------------------------------------------------------------------------- 1 | class RemoveUserIdFromCharges < ActiveRecord::Migration[5.0] 2 | def change 3 | remove_column :charges, :user_id, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20161228180341_add_name_to_charges.rb: -------------------------------------------------------------------------------- 1 | class AddNameToCharges < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :charges, :name, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20161228182556_add_raised_to_campaign.rb: -------------------------------------------------------------------------------- 1 | class AddRaisedToCampaign < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :campaigns, :raised, :integer 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20161228223505_add_active_to_campaign.rb: -------------------------------------------------------------------------------- 1 | class AddActiveToCampaign < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :campaigns, :active, :boolean, default: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170405165023_add_more_fields_to_stripe_accounts.rb: -------------------------------------------------------------------------------- 1 | class AddMoreFieldsToStripeAccounts < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :stripe_accounts, :ssn_last_4, :string 4 | add_column :stripe_accounts, :business_name, :string 5 | add_column :stripe_accounts, :business_tax_id, :string 6 | add_column :stripe_accounts, :personal_id_number, :string 7 | add_column :stripe_accounts, :verification_document, :string 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20170502170520_add_acct_id_to_stripe_accounts.rb: -------------------------------------------------------------------------------- 1 | class AddAcctIdToStripeAccounts < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :stripe_accounts, :acct_id, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 20170502170520) do 14 | 15 | create_table "campaigns", force: :cascade do |t| 16 | t.integer "user_id" 17 | t.string "title" 18 | t.text "description" 19 | t.boolean "subscription" 20 | t.datetime "created_at", null: false 21 | t.datetime "updated_at", null: false 22 | t.string "image" 23 | t.integer "goal" 24 | t.integer "raised" 25 | t.boolean "active", default: true 26 | end 27 | 28 | create_table "charges", force: :cascade do |t| 29 | t.string "charge_id" 30 | t.integer "amount" 31 | t.integer "amount_refunded" 32 | t.integer "campaign_id" 33 | t.datetime "created_at", null: false 34 | t.datetime "updated_at", null: false 35 | t.string "name" 36 | end 37 | 38 | create_table "stripe_accounts", force: :cascade do |t| 39 | t.string "first_name" 40 | t.string "last_name" 41 | t.string "account_type" 42 | t.integer "dob_month" 43 | t.integer "dob_day" 44 | t.integer "dob_year" 45 | t.datetime "created_at", null: false 46 | t.datetime "updated_at", null: false 47 | t.string "address_city" 48 | t.string "address_state" 49 | t.string "address_line1" 50 | t.string "address_postal" 51 | t.boolean "tos" 52 | t.string "ssn_last_4" 53 | t.string "business_name" 54 | t.string "business_tax_id" 55 | t.string "personal_id_number" 56 | t.string "verification_document" 57 | t.string "acct_id" 58 | end 59 | 60 | create_table "users", force: :cascade do |t| 61 | t.string "email", default: "", null: false 62 | t.string "encrypted_password", default: "", null: false 63 | t.string "reset_password_token" 64 | t.datetime "reset_password_sent_at" 65 | t.datetime "remember_created_at" 66 | t.integer "sign_in_count", default: 0, null: false 67 | t.datetime "current_sign_in_at" 68 | t.datetime "last_sign_in_at" 69 | t.string "current_sign_in_ip" 70 | t.string "last_sign_in_ip" 71 | t.datetime "created_at", null: false 72 | t.datetime "updated_at", null: false 73 | t.string "stripe_account" 74 | t.index ["email"], name: "index_users_on_email", unique: true 75 | t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true 76 | end 77 | 78 | end 79 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/lib/assets/.keep -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/lib/tasks/.keep -------------------------------------------------------------------------------- /log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/log/.keep -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The page you were looking for doesn't exist.

    62 |

    You may have mistyped the address or the page may have moved.

    63 |
    64 |

    If you are the application owner check the logs for more information.

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The change you wanted was rejected.

    62 |

    Maybe you tried to change something you didn't have access to.

    63 |
    64 |

    If you are the application owner check the logs for more information.

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    We're sorry, but something went wrong.

    62 |
    63 |

    If you are the application owner check the logs for more information.

    64 |
    65 | 66 | 67 | -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/public/apple-touch-icon-precomposed.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/test/controllers/.keep -------------------------------------------------------------------------------- /test/controllers/bank_accounts_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class BankAccountsControllerTest < ActionDispatch::IntegrationTest 4 | include Devise::Test::IntegrationHelpers 5 | 6 | test "should redirect if not authenticated" do 7 | get new_bank_account_path 8 | assert_redirected_to new_user_session_path 9 | end 10 | 11 | test 'should not allow post if not authenticated' do 12 | post bank_accounts_path "stripeToken": "tok_123" 13 | assert_redirected_to new_user_session_path 14 | end 15 | 16 | test 'should redirect if no stripe account' do 17 | sign_in @user 18 | get new_bank_account_path 19 | assert_redirected_to new_stripe_account_path 20 | end 21 | 22 | test 'should load bank accounts page if stripe account exists' do 23 | sign_in @user 24 | @user.stripe_account = "acct_abc123" 25 | get new_bank_account_path 26 | assert_response :success 27 | end 28 | 29 | test 'should attach a valid bank account' do 30 | sign_in @user 31 | create_stripe_account 32 | @user.stripe_account = @stripe_account.id 33 | create_bank_token 34 | post bank_accounts_path, params: { stripeToken: @btok.id } 35 | assert_redirected_to dashboard_path 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /test/controllers/campaigns_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class CampaignsControllerTest < ActionDispatch::IntegrationTest 4 | include Devise::Test::IntegrationHelpers 5 | 6 | test "should load root path" do 7 | get root_path 8 | assert_response :success 9 | assert_select 'title', "Stripe Connect Example App" 10 | end 11 | 12 | test "should load campaign" do 13 | get campaign_path(@campaign) 14 | assert_response :success 15 | end 16 | 17 | test "should require login to create a campaign" do 18 | get new_campaign_path 19 | assert_redirected_to new_user_session_path 20 | end 21 | 22 | test "should redirect if no stripe account" do 23 | sign_in @user 24 | get new_campaign_path 25 | assert_redirected_to new_stripe_account_path 26 | end 27 | 28 | test 'should create a campaign successfully' do 29 | sign_in @user 30 | create_stripe_account 31 | @user.stripe_account = @stripe_account.id 32 | post campaigns_path, params: { campaign: { title: "Title", description: "Help me do a thing", goal: 100, image: "https://unsplash.it" } } 33 | assert_redirected_to campaign_path(Campaign.last) 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /test/controllers/charges_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ChargesControllerTest < ActionDispatch::IntegrationTest 4 | include Devise::Test::IntegrationHelpers 5 | 6 | test "should not show charges unless logged in" do 7 | get charge_path(@campaign) 8 | assert_redirected_to new_user_session_path 9 | end 10 | 11 | test "should require token to create a charge" do 12 | post charges_path, params: { amount: 100, name: "User", campaign: @campaign } 13 | assert_response :redirect 14 | end 15 | 16 | test "should create successful charge" do 17 | create_stripe_account 18 | @campaign.user_id = @user.id 19 | @user.stripe_account = @stripe_account.id 20 | @user.save 21 | charge_amount = rand(10..50) 22 | post charges_path, params: { amount: charge_amount, name: "User", campaign: @campaign.id, stripeToken: 'tok_visa' } 23 | assert_equal(charge_amount*100, Charge.last.amount) 24 | end 25 | 26 | 27 | end 28 | -------------------------------------------------------------------------------- /test/controllers/debit_cards_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DebitCardsControllerTest < ActionDispatch::IntegrationTest 4 | include Devise::Test::IntegrationHelpers 5 | 6 | test "should get new" do 7 | sign_in @user 8 | create_stripe_account 9 | @user.stripe_account = @stripe_account.id 10 | get debit_cards_new_url 11 | assert_response :success 12 | end 13 | 14 | end 15 | -------------------------------------------------------------------------------- /test/controllers/disputes_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class DisputesControllerTest < ActionDispatch::IntegrationTest 4 | 5 | end 6 | -------------------------------------------------------------------------------- /test/controllers/payouts_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class PayoutsControllerTest < ActionDispatch::IntegrationTest 4 | test "should require valid payout ID" do 5 | get payout_path('fake') 6 | assert_response :redirect 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /test/controllers/stripe_accounts_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class StripeAccountsControllerTest < ActionDispatch::IntegrationTest 4 | include Devise::Test::IntegrationHelpers 5 | 6 | test "should redirect if not logged in" do 7 | get new_stripe_account_path 8 | assert_redirected_to new_user_session_path 9 | end 10 | 11 | test "should require existing stripe account to edit" do 12 | sign_in @user 13 | get edit_stripe_account_path("acct_123") 14 | assert_redirected_to dashboard_path 15 | end 16 | 17 | test "should load new account creation path" do 18 | sign_in @user 19 | get new_stripe_account_path 20 | assert_response :success 21 | end 22 | 23 | test "should reject invalid account details and throw an error" do 24 | sign_in @user 25 | post stripe_accounts_path, params: { 26 | stripe_account: { 27 | dob_day: "100", 28 | dob_month: "00", 29 | dob_year: "1992", 30 | account_type: "individual", 31 | tos: "true" 32 | } 33 | } 34 | assert_nil @user.stripe_account 35 | assert_not_empty flash 36 | end 37 | 38 | test "should successfully create an account" do 39 | sign_in @user 40 | post stripe_accounts_path, params: { 41 | stripe_account: { 42 | first_name: "Test", 43 | last_name: "Mctesterson", 44 | dob_day: "29", 45 | dob_month: "04", 46 | dob_year: "1992", 47 | account_type: "individual", 48 | tos: "true" 49 | } 50 | } 51 | @user.reload 52 | assert_match(/acct_/, @user.stripe_account) 53 | assert_redirected_to new_bank_account_path 54 | end 55 | 56 | end 57 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/test/fixtures/.keep -------------------------------------------------------------------------------- /test/fixtures/campaigns.yml: -------------------------------------------------------------------------------- 1 | one: 2 | id: 1 3 | user_id: 1 4 | title: Title 5 | description: Description -------------------------------------------------------------------------------- /test/fixtures/charges.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | charge_id: MyString 5 | amount: 1 6 | amount_refunded: 1 7 | campaign_id: 1 8 | 9 | two: 10 | charge_id: MyString 11 | amount: 1 12 | amount_refunded: 1 13 | campaign_id: 1 14 | -------------------------------------------------------------------------------- /test/fixtures/files/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/test/fixtures/files/.keep -------------------------------------------------------------------------------- /test/fixtures/stripe_accounts.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | one: 4 | first_name: MyString 5 | last_name: MyString 6 | account_type: MyString 7 | dob_month: 1 8 | dob_day: 1 9 | dob_year: 1 10 | 11 | two: 12 | first_name: MyString 13 | last_name: MyString 14 | account_type: MyString 15 | dob_month: 1 16 | dob_day: 1 17 | dob_year: 1 18 | -------------------------------------------------------------------------------- /test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 2 | 3 | # This model initially had no columns defined. If you add columns to the 4 | # model remove the '{}' from the fixture names and add the columns immediately 5 | # below each fixture, per the syntax in the comments below 6 | # 7 | one: { 8 | id: 1, 9 | email: "email@hotmail.com", 10 | encrypted_password: "$2a$10$ZU0x7nDXA1EKbUAjwKTCwegV/yfddZkvfKWEa9/arb5pBQuy/1oFu" 11 | } 12 | # column: value 13 | # 14 | two: { 15 | email: user2@test.com 16 | } 17 | # column: value 18 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/test/helpers/.keep -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/test/integration/.keep -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/test/mailers/.keep -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/test/models/.keep -------------------------------------------------------------------------------- /test/models/charge_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ChargeTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/courses_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class LessonTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/stripe_account_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class StripeAccountTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/models/user_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require File.expand_path('../../config/environment', __FILE__) 3 | require 'rails/test_help' 4 | 5 | Stripe.api_key = ENV['SECRET_KEY'] 6 | 7 | class ActiveSupport::TestCase 8 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 9 | fixtures :all 10 | 11 | setup do 12 | @user = users(:one) 13 | @campaign = campaigns(:one) 14 | end 15 | 16 | # Create a US bank token 17 | def create_bank_token 18 | @btok = Stripe::Token.create( 19 | bank_account: { 20 | country: "US", 21 | currency: "usd", 22 | routing_number: "110000000", 23 | account_number: "000123456789" 24 | } 25 | ) 26 | end 27 | 28 | # Create a Stripe account to use for tests 29 | def create_stripe_account 30 | @stripe_account = Stripe::Account.create(managed: true, country: "us") 31 | end 32 | end -------------------------------------------------------------------------------- /tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/tmp/.keep -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/vendor/assets/javascripts/.keep -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/adam-stripe/stripe-connect-managed-rails/da793f778a318633d33aea846bcd36fd3b0b20f2/vendor/assets/stylesheets/.keep --------------------------------------------------------------------------------