diff --git a/Gemfile b/Gemfile index f6445e486..85fd26a0c 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,9 @@ gem 'rails', '~> 4.2.3' gem 'rails-api' gem 'spring', :group => :development +# payments +gem 'stripe' + # persistance gem 'pg' gem 'foreigner' diff --git a/Gemfile.lock b/Gemfile.lock index 0fcda718d..16f0d6f6e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -83,6 +83,8 @@ GEM rails (< 6) diff-lcs (1.2.5) docile (1.1.5) + domain_name (0.5.20160310) + unf (>= 0.0.5, < 1.0.0) erubis (2.7.0) factory_girl (4.5.0) activesupport (>= 3.0.0) @@ -99,6 +101,8 @@ GEM activesupport (>= 3) hashie (3.4.2) highline (1.7.2) + http-cookie (1.0.2) + domain_name (~> 0.5) i18n (0.7.0) json (1.8.3) loofah (2.0.2) @@ -126,6 +130,7 @@ GEM net-ssh (2.9.2) net-ssh-gateway (1.2.0) net-ssh (>= 2.6.5) + netrc (0.11.0) nokogiri (1.6.6.2) mini_portile (~> 0.6.0) omniauth (1.2.2) @@ -192,6 +197,10 @@ GEM redcarpet (3.3.3) responders (2.1.0) railties (>= 4.2.0, < 5) + rest-client (1.8.0) + http-cookie (>= 1.0.2, < 2.0) + mime-types (>= 1.16, < 3.0) + netrc (~> 0.7) rspec-core (3.3.1) rspec-support (~> 3.3.0) rspec-expectations (3.3.0) @@ -227,12 +236,17 @@ GEM actionpack (>= 3.0) activesupport (>= 3.0) sprockets (>= 2.8, < 4.0) + stripe (1.41.0) + rest-client (~> 1.4) thor (0.19.1) thread_safe (0.3.5) tilt (2.0.1) timecop (0.7.4) tzinfo (1.2.2) thread_safe (~> 0.1) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.2) warden (1.2.6) rack (>= 1.0) @@ -274,4 +288,8 @@ DEPENDENCIES simplecov sinatra spring + stripe timecop + +BUNDLED WITH + 1.11.2 diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 36b1aaf50..b8f3ae8e1 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -11,6 +11,7 @@ def index def create group = Group.create(group_params) group.add_admin(current_user) + group.add_customer(current_user.email) render json: [group] end @@ -31,8 +32,30 @@ def update render json: [group] end + api :POST, '/groups/:id/add_card', 'Add a credit card that pays for the group' + def add_card + group = Group.find(params[:id]) + if current_user.is_admin_for?(group) + group.add_card(params[:stripeEmail], params[:stripeToken]) + render status: 200, nothing: true + else + render status: 403, nothing: true + end + end + + api :POST, '/groups/:id/extend_trial', 'Extend the group trial by 30 days' + def extend_trial + group = Group.find(params[:id]) + if current_user.is_admin_for?(group) + group.extend_trial() + render status: 200, nothing: true + else + render status: 403, nothing: true + end + end + private def group_params - params.require(:group).permit(:name, :currency_code) + params.require(:group).permit(:name, :currency_code, :plan) end end diff --git a/app/models/group.rb b/app/models/group.rb index c632f4f40..0820a750a 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -16,6 +16,37 @@ def add_admin(user) end end + def add_customer(email) + customer = Stripe::Customer.create( + :description => "Customer for Group " + self.id.to_s, + :email => email, + :plan => 1 + ) + self.customer_id = customer.id + self.trial_end = Time.at(customer.subscriptions.data[0].trial_end) + self.plan = "paid" + self.save + end + + def add_card(email, token) + customer = Stripe::Customer.retrieve(self.customer_id) + customer.source = token + customer.email = email + customer.save + end + + def extend_trial + # the number of seconds in thirty days + thirty_days = 60 * 24 * 30 + + customer = Stripe::Customer.retrieve(self.customer_id) + sub = Stripe::Subscription.retrieve(customer.subscriptions.data[0].id) + sub.trial_end = sub.trial_end + thirty_days + sub.save + self.trial_end = Time.at(sub.trial_end) + self.save + end + def add_member(user) memberships.create!(member: user, is_admin: false) end diff --git a/app/serializers/group_serializer.rb b/app/serializers/group_serializer.rb index fde66c17c..8b2180a91 100644 --- a/app/serializers/group_serializer.rb +++ b/app/serializers/group_serializer.rb @@ -5,5 +5,7 @@ class GroupSerializer < ActiveModel::Serializer :balance, :currency_symbol, :currency_code, - :is_launched + :is_launched, + :plan, + :trial_end end diff --git a/config/initializers/stripe.rb b/config/initializers/stripe.rb new file mode 100644 index 000000000..7e4a7875f --- /dev/null +++ b/config/initializers/stripe.rb @@ -0,0 +1,6 @@ +Rails.configuration.stripe = { + :publishable_key => ENV['STRIPE_PUBLISHABLE_KEY'], + :secret_key => ENV['STRIPE_SECRET_KEY'] +} + +Stripe.api_key = Rails.configuration.stripe[:secret_key] diff --git a/config/routes.rb b/config/routes.rb index 205d4d77f..d19495594 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -24,7 +24,13 @@ end end - resources :groups, only: [:index, :show, :create, :update] + + resources :groups, only: [:index, :show, :create, :update] do + member do + post :add_card + post :extend_trial + end + end resources :comments, only: [:index, :create] diff --git a/db/migrate/20160419140651_add_customer_to_group.rb b/db/migrate/20160419140651_add_customer_to_group.rb new file mode 100644 index 000000000..2edcf27f8 --- /dev/null +++ b/db/migrate/20160419140651_add_customer_to_group.rb @@ -0,0 +1,7 @@ +class AddCustomerToGroup < ActiveRecord::Migration + def change + add_column :groups, :customer_id, :string + add_column :groups, :trial_end, :datetime + add_column :groups, :plan, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index d792c77f3..1dcf9bcf9 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160415193307) do +ActiveRecord::Schema.define(version: 20160419140651) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -83,12 +83,32 @@ add_index "delayed_jobs", ["priority", "run_at"], name: "delayed_jobs_priority", using: :btree + create_table "events", force: :cascade do |t| + t.string "kind", limit: 255 + t.integer "eventable_id" + t.string "eventable_type" + t.integer "user_id" + t.integer "group_id" + t.integer "sequence_id" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "events", ["eventable_type", "eventable_id"], name: "index_events_on_eventable_type_and_eventable_id", using: :btree + add_index "events", ["group_id", "sequence_id"], name: "index_events_on_group_id_and_sequence_id", unique: true, using: :btree + add_index "events", ["group_id"], name: "index_events_on_group_id", using: :btree + add_index "events", ["sequence_id"], name: "index_events_on_sequence_id", using: :btree + add_index "events", ["user_id"], name: "index_events_on_user_id", using: :btree + create_table "groups", force: :cascade do |t| t.string "name" t.datetime "created_at" t.datetime "updated_at" t.string "currency_symbol", default: "$" t.string "currency_code", default: "USD" + t.string "customer_id" + t.datetime "trial_end" + t.string "plan" end create_table "memberships", force: :cascade do |t| @@ -107,10 +127,10 @@ create_table "subscription_trackers", force: :cascade do |t| t.integer "user_id", null: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false t.boolean "subscribed_to_email_notifications", default: false t.string "email_digest_delivery_frequency", default: "never" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end add_index "subscription_trackers", ["user_id"], name: "index_subscription_trackers_on_user_id", using: :btree @@ -142,4 +162,14 @@ add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree + create_table "webhooks", force: :cascade do |t| + t.integer "hookable_id" + t.string "hookable_type" + t.string "kind", null: false + t.string "uri", null: false + t.text "event_types", default: [], array: true + end + + add_index "webhooks", ["hookable_type", "hookable_id"], name: "index_webhooks_on_hookable_type_and_hookable_id", using: :btree + end