From 351502694f5fed18fe29ceaca8cb46f5e6909a96 Mon Sep 17 00:00:00 2001 From: euglazer Date: Fri, 15 Apr 2016 12:47:54 -0700 Subject: [PATCH 01/10] add closed_help_card timestamps to memberships --- app/serializers/membership_serializer.rb | 4 +++- ...osed_help_card_timestamps_to_memberships.rb | 17 +++++++++++++++++ db/schema.rb | 18 ++++++++++-------- 3 files changed, 30 insertions(+), 9 deletions(-) create mode 100644 db/migrate/20160415193307_add_closed_help_card_timestamps_to_memberships.rb diff --git a/app/serializers/membership_serializer.rb b/app/serializers/membership_serializer.rb index eabd883f2..edc074f6a 100644 --- a/app/serializers/membership_serializer.rb +++ b/app/serializers/membership_serializer.rb @@ -5,7 +5,9 @@ class MembershipSerializer < ActiveModel::Serializer :created_at, :balance, :archived_at, - :raw_balance + :raw_balance, + :closed_admin_help_card_at, + :closed_member_help_card_at has_one :member, serializer: UserSerializer, root: 'users' has_one :group, serializer: GroupSerializer, root: 'groups' diff --git a/db/migrate/20160415193307_add_closed_help_card_timestamps_to_memberships.rb b/db/migrate/20160415193307_add_closed_help_card_timestamps_to_memberships.rb new file mode 100644 index 000000000..f52d9e94b --- /dev/null +++ b/db/migrate/20160415193307_add_closed_help_card_timestamps_to_memberships.rb @@ -0,0 +1,17 @@ +class AddClosedHelpCardTimestampsToMemberships < ActiveRecord::Migration + def up + add_column :memberships, :closed_member_help_card_at, :datetime + add_column :memberships, :closed_admin_help_card_at, :datetime + Membership.where(is_admin: false) + .joins(:member).where.not(users: {confirmed_at: nil}) + .update_all(closed_member_help_card_at: DateTime.now.utc) + Membership.where(is_admin: true) + .joins(:member).where.not(users: {confirmed_at: nil}) + .update_all(closed_admin_help_card_at: DateTime.now.utc) + end + + def down + remove_column :memberships, :closed_member_help_card_at + remove_column :memberships, :closed_admin_help_card_at + end +end diff --git a/db/schema.rb b/db/schema.rb index cd20d1124..d792c77f3 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: 20160412073353) do +ActiveRecord::Schema.define(version: 20160415193307) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -92,12 +92,14 @@ end create_table "memberships", force: :cascade do |t| - t.integer "group_id", null: false - t.integer "member_id", null: false - t.boolean "is_admin", default: false, null: false + t.integer "group_id", null: false + t.integer "member_id", null: false + t.boolean "is_admin", default: false, null: false t.datetime "created_at" t.datetime "updated_at" t.datetime "archived_at" + t.datetime "closed_member_help_card_at" + t.datetime "closed_admin_help_card_at" end add_index "memberships", ["group_id"], name: "index_memberships_on_group_id", using: :btree @@ -114,12 +116,12 @@ add_index "subscription_trackers", ["user_id"], name: "index_subscription_trackers_on_user_id", using: :btree create_table "users", force: :cascade do |t| - t.string "email", default: "", null: false - t.string "encrypted_password", default: "", null: false + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0, null: false + t.integer "sign_in_count", default: 0, null: false t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" t.string "current_sign_in_ip" @@ -134,7 +136,7 @@ t.integer "utc_offset" t.datetime "confirmed_at" t.datetime "joined_first_group_at" - t.boolean "is_super_admin", default: false + t.boolean "is_super_admin", default: false end add_index "users", ["email"], name: "index_users_on_email", unique: true, using: :btree From bedcd67ca40435e4e3ec6954c966ae7da2137a08 Mon Sep 17 00:00:00 2001 From: euglazer Date: Fri, 15 Apr 2016 14:13:17 -0700 Subject: [PATCH 02/10] add properties to membership and group serializer -- for help card management on the UI --- app/controllers/memberships_controller.rb | 2 +- app/models/group.rb | 4 ++++ app/serializers/group_serializer.rb | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/controllers/memberships_controller.rb b/app/controllers/memberships_controller.rb index 5df30ca48..1b866c759 100644 --- a/app/controllers/memberships_controller.rb +++ b/app/controllers/memberships_controller.rb @@ -82,7 +82,7 @@ def upload_review private def membership_params - params.require(:membership).permit(:is_admin) + params.require(:membership).permit(:is_admin, :closed_admin_help_card_at, :closed_member_help_card_at) end def membership diff --git a/app/models/group.rb b/app/models/group.rb index d4a9b495c..c632f4f40 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -28,6 +28,10 @@ def formatted_balance Money.new(balance * 100, currency_code).format end + def is_launched + members.any? && allocations.any? + end + def last_activity_at last_bucket = buckets.order(updated_at: :desc).limit(1).first last_bucket_updated_at = last_bucket ? last_bucket.updated_at : nil diff --git a/app/serializers/group_serializer.rb b/app/serializers/group_serializer.rb index 93a29889c..fde66c17c 100644 --- a/app/serializers/group_serializer.rb +++ b/app/serializers/group_serializer.rb @@ -4,5 +4,6 @@ class GroupSerializer < ActiveModel::Serializer :name, :balance, :currency_symbol, - :currency_code + :currency_code, + :is_launched end From 10a621e00e7e8b5ea6dcaafa4159b90420431b3d Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Apr 2016 11:58:08 -0400 Subject: [PATCH 03/10] routes for stripe --- Gemfile | 3 ++ Gemfile.lock | 18 ++++++++++ app/controllers/groups_controller.rb | 17 ++++++++- app/models/group.rb | 31 ++++++++++++++++ config/initializers/stripe.rb | 6 ++++ config/routes.rb | 8 ++++- .../20160419140651_add_customer_to_group.rb | 7 ++++ db/schema.rb | 36 +++++++++++++++++-- 8 files changed, 121 insertions(+), 5 deletions(-) create mode 100644 config/initializers/stripe.rb create mode 100644 db/migrate/20160419140651_add_customer_to_group.rb 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..6ed3b2ed3 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,22 @@ 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]) + group.add_card(params[:stripeEmail], params[:stripeToken]) + render status: 200, nothing: true + end + + api :POST, '/groups/:id/extend_trial', 'Extend the group trial by 30 days' + def extend_trial + group = Group.find(params[:id]) + group.extend_trial() + render status: 200, nothing: true + 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/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 baaf4eb18..43d7bf96d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -23,7 +23,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 From 6a2c126b691b445271f5b8406d16fec630f6a3ee Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Apr 2016 12:38:15 -0400 Subject: [PATCH 04/10] need these for the UI --- app/serializers/group_serializer.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 From a97fa3b351a5d02729a637c7803e99fa5630a21a Mon Sep 17 00:00:00 2001 From: Connor Turland Date: Tue, 19 Apr 2016 12:44:13 -0400 Subject: [PATCH 05/10] enforce admin --- app/controllers/groups_controller.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 6ed3b2ed3..b8f3ae8e1 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -35,15 +35,23 @@ def update api :POST, '/groups/:id/add_card', 'Add a credit card that pays for the group' def add_card group = Group.find(params[:id]) - group.add_card(params[:stripeEmail], params[:stripeToken]) - render status: 200, nothing: true + 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]) - group.extend_trial() - render status: 200, nothing: true + if current_user.is_admin_for?(group) + group.extend_trial() + render status: 200, nothing: true + else + render status: 403, nothing: true + end end private From 3783626ffe77a2c5afe87eaab9900242c39f5fb6 Mon Sep 17 00:00:00 2001 From: euglazer Date: Tue, 19 Apr 2016 09:52:49 -0700 Subject: [PATCH 06/10] add status to bucket_params_update --- app/controllers/buckets_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/buckets_controller.rb b/app/controllers/buckets_controller.rb index 809f4c25d..a67c1f3a4 100644 --- a/app/controllers/buckets_controller.rb +++ b/app/controllers/buckets_controller.rb @@ -60,6 +60,6 @@ def bucket_params_create end def bucket_params_update - params.require(:bucket).permit(:name, :description, :target) + params.require(:bucket).permit(:name, :description, :target, :status) end end From 12c9835ccf48b113d7b623165ad1943cf924df19 Mon Sep 17 00:00:00 2001 From: euglazer Date: Tue, 19 Apr 2016 10:48:36 -0700 Subject: [PATCH 07/10] in recent activity emails re: fully funded buckets, total contributions are displayed instead of target --- .../_funded_buckets_user_authored.html.erb | 2 +- .../recent_activity_partials/_new_funded_buckets.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/user_mailer/recent_activity_partials/_funded_buckets_user_authored.html.erb b/app/views/user_mailer/recent_activity_partials/_funded_buckets_user_authored.html.erb index 44eacf4d3..ed8b685c8 100644 --- a/app/views/user_mailer/recent_activity_partials/_funded_buckets_user_authored.html.erb +++ b/app/views/user_mailer/recent_activity_partials/_funded_buckets_user_authored.html.erb @@ -8,7 +8,7 @@
    <% buckets.each do |bucket| %>
  • - "><%= bucket.name %> (<%= bucket.formatted_target %>)
    + "><%= bucket.name %> (<%= bucket.formatted_total_contributions %>)
    <%= bucket.num_of_contributors %> funders
  • <% end %> diff --git a/app/views/user_mailer/recent_activity_partials/_new_funded_buckets.html.erb b/app/views/user_mailer/recent_activity_partials/_new_funded_buckets.html.erb index a1bc2a2e1..c1345f1af 100644 --- a/app/views/user_mailer/recent_activity_partials/_new_funded_buckets.html.erb +++ b/app/views/user_mailer/recent_activity_partials/_new_funded_buckets.html.erb @@ -8,7 +8,7 @@
      <% buckets.each do |bucket| %>
    • - "><%= bucket.name %> (<%= bucket.formatted_target %>)
      + "><%= bucket.name %> (<%= bucket.formatted_total_contributions %>)
      By <%= bucket.author_name %>
      <%= bucket.num_of_contributors %> funders
    • From 34c9d261ab3d7219d48464a1441f37bcacd7fe79 Mon Sep 17 00:00:00 2001 From: euglazer Date: Wed, 20 Apr 2016 11:35:41 -0700 Subject: [PATCH 08/10] gemfile lock --- Gemfile.lock | 3 --- 1 file changed, 3 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index 16f0d6f6e..ee0e3e67f 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -290,6 +290,3 @@ DEPENDENCIES spring stripe timecop - -BUNDLED WITH - 1.11.2 From 718d3a79559a30bac6dbc0cf98fec85b104008b1 Mon Sep 17 00:00:00 2001 From: euglazer Date: Wed, 20 Apr 2016 11:35:55 -0700 Subject: [PATCH 09/10] remove funding_closes_at from seeds --- db/seeds.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/db/seeds.rb b/db/seeds.rb index 03f99d663..f7cdb3305 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -86,7 +86,6 @@ target: rand(0..1000), status: 'live', created_at: Time.zone.now - rand(1..10).days, - funding_closes_at: Time.zone.now + rand(10..30).days, live_at: Time.now.utc ) rand(10).times { bucket.comments.create(user: group.members.sample, body: Faker::Lorem.sentence) } From 48054f14218e7d231fdc62876ac90ba299c03035 Mon Sep 17 00:00:00 2001 From: euglazer Date: Wed, 20 Apr 2016 12:12:56 -0700 Subject: [PATCH 10/10] create separate route for adding a customer to a group --- app/controllers/groups_controller.rb | 12 +++++++++++- config/routes.rb | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index b8f3ae8e1..a4ea778d5 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -11,7 +11,6 @@ def index def create group = Group.create(group_params) group.add_admin(current_user) - group.add_customer(current_user.email) render json: [group] end @@ -32,6 +31,17 @@ def update render json: [group] end + api :POST, '/groups/:id/add_customer', 'Add a credit card that pays for the group' + def add_customer + group = Group.find(params[:id]) + if current_user.is_admin_for(group) + group.add_customer(current_user.email) + render json: [group] + else + render nothing: true, status: 403 + end + end + api :POST, '/groups/:id/add_card', 'Add a credit card that pays for the group' def add_card group = Group.find(params[:id]) diff --git a/config/routes.rb b/config/routes.rb index d19495594..0af02796f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -27,6 +27,7 @@ resources :groups, only: [:index, :show, :create, :update] do member do + post :add_customer post :add_card post :extend_trial end