diff --git a/app/controllers/user_upgrades_controller.rb b/app/controllers/user_upgrades_controller.rb
index e2b8729d6..4a0b199c2 100644
--- a/app/controllers/user_upgrades_controller.rb
+++ b/app/controllers/user_upgrades_controller.rb
@@ -3,8 +3,8 @@ class UserUpgradesController < ApplicationController
respond_to :js, :html
def create
- @user_upgrade = UserUpgrade.new(recipient: user, purchaser: CurrentUser.user, level: params[:level].to_i)
- @checkout = @user_upgrade.create_checkout
+ @user_upgrade = authorize UserUpgrade.create(recipient: user, purchaser: CurrentUser.user, status: "pending", upgrade_type: params[:upgrade_type])
+ @checkout = @user_upgrade.create_checkout!
respond_with(@user_upgrade)
end
@@ -13,7 +13,8 @@ class UserUpgradesController < ApplicationController
end
def show
- authorize User, :upgrade?
+ @user_upgrade = authorize UserUpgrade.find(params[:id])
+ respond_with(@user_upgrade)
end
def user
diff --git a/app/models/user.rb b/app/models/user.rb
index 071c42d04..01730294c 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -99,6 +99,8 @@ class User < ApplicationRecord
has_many :post_votes
has_many :post_versions, foreign_key: :updater_id
has_many :bans, -> {order("bans.id desc")}
+ has_many :received_upgrades, class_name: "UserUpgrade", foreign_key: :recipient_id, dependent: :destroy
+ has_many :purchased_upgrades, class_name: "UserUpgrade", foreign_key: :purchaser_id, dependent: :destroy
has_one :recent_ban, -> {order("bans.id desc")}, :class_name => "Ban"
has_one :api_key
diff --git a/app/logical/user_upgrade.rb b/app/models/user_upgrade.rb
similarity index 61%
rename from app/logical/user_upgrade.rb
rename to app/models/user_upgrade.rb
index f9af14708..3fdc81045 100644
--- a/app/logical/user_upgrade.rb
+++ b/app/models/user_upgrade.rb
@@ -1,5 +1,18 @@
-class UserUpgrade
- attr_reader :recipient, :purchaser, :level
+class UserUpgrade < ApplicationRecord
+ belongs_to :recipient, class_name: "User"
+ belongs_to :purchaser, class_name: "User"
+
+ enum upgrade_type: {
+ gold: 0,
+ platinum: 10,
+ gold_to_platinum: 20
+ }, _suffix: "upgrade"
+
+ enum status: {
+ pending: 0,
+ processing: 10,
+ complete: 20
+ }
def self.stripe_publishable_key
Danbooru.config.stripe_publishable_key
@@ -21,51 +34,63 @@ class UserUpgrade
platinum_price - gold_price
end
- def initialize(recipient:, purchaser:, level:)
- @recipient, @purchaser, @level = recipient, purchaser, level.to_i
- end
-
- def upgrade_type
- if level == User::Levels::GOLD && recipient.level == User::Levels::MEMBER
- :gold_upgrade
- elsif level == User::Levels::PLATINUM && recipient.level == User::Levels::MEMBER
- :platinum_upgrade
- elsif level == User::Levels::PLATINUM && recipient.level == User::Levels::GOLD
- :gold_to_platinum_upgrade
+ def level
+ case upgrade_type
+ when "gold"
+ User::Levels::GOLD
+ when "platinum"
+ User::Levels::PLATINUM
+ when "gold_to_platinum"
+ User::Levels::PLATINUM
else
- raise ArgumentError, "Invalid upgrade"
+ raise NotImplementedError
end
end
def upgrade_price
case upgrade_type
- when :gold_upgrade
+ when "gold"
UserUpgrade.gold_price
- when :platinum_upgrade
+ when "platinum"
UserUpgrade.platinum_price
- when :gold_to_platinum_upgrade
+ when "gold_to_platinum"
UserUpgrade.gold_to_platinum_price
+ else
+ raise NotImplementedError
end
end
def upgrade_description
case upgrade_type
- when :gold_upgrade
+ when "gold"
"Upgrade to Gold"
- when :platinum_upgrade
+ when "platinum"
"Upgrade to Platinum"
- when :gold_to_platinum_upgrade
+ when "gold_to_platinum"
"Upgrade Gold to Platinum"
+ else
+ raise NotImplementedError
end
end
+ def level_string
+ User.level_string(level)
+ end
+
def is_gift?
recipient != purchaser
end
- def process_upgrade!
+ def process_upgrade!(payment_status)
recipient.with_lock do
- upgrade_recipient!
+ return if status == "complete"
+
+ if payment_status == "paid"
+ upgrade_recipient!
+ update!(status: :complete)
+ else
+ update!(status: :processing)
+ end
end
end
@@ -74,12 +99,12 @@ class UserUpgrade
end
concerning :StripeMethods do
- def create_checkout
- Stripe::Checkout::Session.create(
+ def create_checkout!
+ checkout = Stripe::Checkout::Session.create(
mode: "payment",
- success_url: Routes.user_upgrade_url(user_id: recipient.id),
+ success_url: Routes.user_upgrade_url(self),
cancel_url: Routes.new_user_upgrade_url(user_id: recipient.id),
- client_reference_id: "user_#{purchaser.id}",
+ client_reference_id: "user_upgrade_#{id}",
customer_email: recipient.email_address&.address,
payment_method_types: ["card"],
line_items: [{
@@ -93,6 +118,7 @@ class UserUpgrade
quantity: 1,
}],
metadata: {
+ user_upgrade_id: id,
purchaser_id: purchaser.id,
recipient_id: recipient.id,
purchaser_name: purchaser.name,
@@ -102,6 +128,9 @@ class UserUpgrade
level: level,
},
)
+
+ update!(stripe_id: checkout.id)
+ checkout
end
class_methods do
@@ -122,7 +151,7 @@ class UserUpgrade
event = build_event(request)
if event.type == "checkout.session.completed"
- checkout_session_completed(event)
+ checkout_session_completed(event.data.object)
end
end
@@ -132,13 +161,9 @@ class UserUpgrade
Stripe::Webhook.construct_event(payload, signature, stripe_webhook_secret)
end
- def checkout_session_completed(event)
- recipient = User.find(event.data.object.metadata.recipient_id)
- purchaser = User.find(event.data.object.metadata.purchaser_id)
- level = event.data.object.metadata.level
-
- user_upgrade = UserUpgrade.new(recipient: recipient, purchaser: purchaser, level: level)
- user_upgrade.process_upgrade!
+ def checkout_session_completed(checkout)
+ user_upgrade = UserUpgrade.find(checkout.metadata.user_upgrade_id)
+ user_upgrade.process_upgrade!(checkout.payment_status)
end
end
end
diff --git a/app/policies/user_upgrade_policy.rb b/app/policies/user_upgrade_policy.rb
new file mode 100644
index 000000000..78365557f
--- /dev/null
+++ b/app/policies/user_upgrade_policy.rb
@@ -0,0 +1,9 @@
+class UserUpgradePolicy < ApplicationPolicy
+ def create?
+ user.is_member?
+ end
+
+ def show?
+ record.recipient == user || record.purchaser == user || user.is_owner?
+ end
+end
diff --git a/app/views/user_upgrades/new.html.erb b/app/views/user_upgrades/new.html.erb
index 2ed284092..89d7ea4d6 100644
--- a/app/views/user_upgrades/new.html.erb
+++ b/app/views/user_upgrades/new.html.erb
@@ -101,11 +101,11 @@
You can pay with a credit or debit card. Safebooru uses Stripe
as a payment intermediary so none of your personal information will be stored on the site.
- <% if user.level < User::Levels::GOLD %>
- <%= button_to "Upgrade to Gold", user_upgrade_path(user_id: user.id, level: User::Levels::GOLD), remote: true, disable_with: "Redirecting..." %>
- <%= button_to "Upgrade to Platinum", user_upgrade_path(user_id: user.id, level: User::Levels::PLATINUM), remote: true, disable_with: "Redirecting..." %>
- <% elsif user.level < User::Levels::PLATINUM %>
- <%= button_to "Upgrade Gold to Platinum", user_upgrade_path(user_id: user.id, level: User::Levels::PLATINUM), remote: true, disable_with: "Redirecting..." %>
+ <% if user.level == User::Levels::MEMBER %>
+ <%= button_to "Upgrade to Gold", user_upgrades_path(user_id: user.id, upgrade_type: "gold"), remote: true, disable_with: "Redirecting..." %>
+ <%= button_to "Upgrade to Platinum", user_upgrades_path(user_id: user.id, upgrade_type: "platinum"), remote: true, disable_with: "Redirecting..." %>
+ <% elsif user.level == User::Levels::GOLD %>
+ <%= button_to "Upgrade Gold to Platinum", user_upgrades_path(user_id: user.id, upgrade_type: "gold_to_platinum"), remote: true, disable_with: "Redirecting..." %>
<% end %>
<% else %>
diff --git a/app/views/user_upgrades/show.html.erb b/app/views/user_upgrades/show.html.erb
index 014421551..7eeddffdf 100644
--- a/app/views/user_upgrades/show.html.erb
+++ b/app/views/user_upgrades/show.html.erb
@@ -1,22 +1,47 @@
-<% page_title "Account Upgraded" %>
+<% page_title "User Upgrade Status" %>
<%= render "users/secondary_links" %>
- <% if flash[:success] %>
-
Congratulations!
+
User Upgrade
- <% if user != CurrentUser.user %>
-
<%= user.name %> is now a <%= user.level_string %> user. Thanks for supporting the site!
+
+
+ -
+ Purchased
+ <%= time_ago_in_words_tagged @user_upgrade.updated_at %>
+ by <%= link_to_user @user_upgrade.purchaser %>
+ <% if @user_upgrade.is_gift? %>
+ for <%= link_to_user @user_upgrade.recipient %>
+ <% end %>
+
+ -
+ Upgrade Type
+ <%= @user_upgrade.upgrade_type.humanize %>
+
+ -
+ Status
+ <%= @user_upgrade.status.humanize %>
+
+
+
+
+ <% if @user_upgrade.status == "complete" %>
+ <% if @user_upgrade.is_gift? && CurrentUser.user == @user_upgrade.recipient %>
+
<%= link_to_user @user_upgrade.purchaser %> has upgraded your account to <%= @user_upgrade.level_string %>. Enjoy your new account!
+ <% elsif @user_upgrade.is_gift? && CurrentUser.user == @user_upgrade.purchaser %>
+
<%= link_to_user @user_upgrade.recipient %> is now a <%= @user_upgrade.level_string %> user. Thanks for supporting the site!
<% else %>
-
You are now a <%= user.level_string %> user. Thanks for supporting the site!
+
You are now a <%= @user_upgrade.level_string %> user. Thanks for supporting the site!
<% end %>
-
<%= link_to "Go back to #{Danbooru.config.canonical_app_name}", "https://danbooru.donmai.us" %> to start using your new account.
- <% elsif flash[:error] %>
-
An error occurred!
-
<%= flash[:error] %>
-
<%= link_to "Try again", new_user_upgrade_path %>
+
<%= link_to "Go back to #{Danbooru.config.canonical_app_name}", "https://danbooru.donmai.us" %> to continue using the site.
+ <% else %>
+ <%= content_for :html_header do %>
+
+ <% end %>
+
+
This order is still being processed. You will be notified as soon as the order is complete.
<% end %>
diff --git a/config/routes.rb b/config/routes.rb
index b027bb3a3..4bceb91fe 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -254,7 +254,7 @@ Rails.application.routes.draw do
get :custom_style
end
end
- resource :user_upgrade, :only => [:new, :create, :show]
+ resources :user_upgrades, only: [:new, :create, :show]
resources :user_feedbacks, except: [:destroy]
resources :user_name_change_requests, only: [:new, :create, :show, :index]
resources :webhooks do
diff --git a/db/migrate/20201224101208_create_user_upgrades.rb b/db/migrate/20201224101208_create_user_upgrades.rb
new file mode 100644
index 000000000..46b44f22a
--- /dev/null
+++ b/db/migrate/20201224101208_create_user_upgrades.rb
@@ -0,0 +1,20 @@
+class CreateUserUpgrades < ActiveRecord::Migration[6.1]
+ def change
+ create_table :user_upgrades do |t|
+ t.timestamps
+
+ t.references :recipient, index: true, null: false
+ t.references :purchaser, index: true, null: false
+ t.integer :upgrade_type, index: true, null: false
+ t.integer :status, index: true, null: false
+ t.string :stripe_id, index: true, null: true
+ end
+
+ # Reserve ID space for backfilling old upgrades.
+ reversible do |dir|
+ dir.up do
+ execute "SELECT setval('user_upgrades_id_seq', 25000, false)"
+ end
+ end
+ end
+end
diff --git a/db/structure.sql b/db/structure.sql
index 5f8a9b7da..f57281afa 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -3104,6 +3104,41 @@ CREATE SEQUENCE public.user_name_change_requests_id_seq
ALTER SEQUENCE public.user_name_change_requests_id_seq OWNED BY public.user_name_change_requests.id;
+--
+-- Name: user_upgrades; Type: TABLE; Schema: public; Owner: -
+--
+
+CREATE TABLE public.user_upgrades (
+ id bigint NOT NULL,
+ created_at timestamp(6) without time zone NOT NULL,
+ updated_at timestamp(6) without time zone NOT NULL,
+ recipient_id bigint NOT NULL,
+ purchaser_id bigint NOT NULL,
+ upgrade_type integer NOT NULL,
+ status integer NOT NULL,
+ stripe_id character varying
+);
+
+
+--
+-- Name: user_upgrades_id_seq; Type: SEQUENCE; Schema: public; Owner: -
+--
+
+CREATE SEQUENCE public.user_upgrades_id_seq
+ START WITH 1
+ INCREMENT BY 1
+ NO MINVALUE
+ NO MAXVALUE
+ CACHE 1;
+
+
+--
+-- Name: user_upgrades_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: -
+--
+
+ALTER SEQUENCE public.user_upgrades_id_seq OWNED BY public.user_upgrades.id;
+
+
--
-- Name: users_id_seq; Type: SEQUENCE; Schema: public; Owner: -
--
@@ -4172,6 +4207,13 @@ ALTER TABLE ONLY public.user_feedback ALTER COLUMN id SET DEFAULT nextval('publi
ALTER TABLE ONLY public.user_name_change_requests ALTER COLUMN id SET DEFAULT nextval('public.user_name_change_requests_id_seq'::regclass);
+--
+-- Name: user_upgrades id; Type: DEFAULT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.user_upgrades ALTER COLUMN id SET DEFAULT nextval('public.user_upgrades_id_seq'::regclass);
+
+
--
-- Name: users id; Type: DEFAULT; Schema: public; Owner: -
--
@@ -4537,6 +4579,14 @@ ALTER TABLE ONLY public.user_name_change_requests
ADD CONSTRAINT user_name_change_requests_pkey PRIMARY KEY (id);
+--
+-- Name: user_upgrades user_upgrades_pkey; Type: CONSTRAINT; Schema: public; Owner: -
+--
+
+ALTER TABLE ONLY public.user_upgrades
+ ADD CONSTRAINT user_upgrades_pkey PRIMARY KEY (id);
+
+
--
-- Name: users users_pkey; Type: CONSTRAINT; Schema: public; Owner: -
--
@@ -7045,6 +7095,41 @@ CREATE INDEX index_user_name_change_requests_on_original_name ON public.user_nam
CREATE INDEX index_user_name_change_requests_on_user_id ON public.user_name_change_requests USING btree (user_id);
+--
+-- Name: index_user_upgrades_on_purchaser_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_user_upgrades_on_purchaser_id ON public.user_upgrades USING btree (purchaser_id);
+
+
+--
+-- Name: index_user_upgrades_on_recipient_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_user_upgrades_on_recipient_id ON public.user_upgrades USING btree (recipient_id);
+
+
+--
+-- Name: index_user_upgrades_on_status; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_user_upgrades_on_status ON public.user_upgrades USING btree (status);
+
+
+--
+-- Name: index_user_upgrades_on_stripe_id; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_user_upgrades_on_stripe_id ON public.user_upgrades USING btree (stripe_id);
+
+
+--
+-- Name: index_user_upgrades_on_upgrade_type; Type: INDEX; Schema: public; Owner: -
+--
+
+CREATE INDEX index_user_upgrades_on_upgrade_type ON public.user_upgrades USING btree (upgrade_type);
+
+
--
-- Name: index_users_on_created_at; Type: INDEX; Schema: public; Owner: -
--
@@ -7437,6 +7522,7 @@ INSERT INTO "schema_migrations" (version) VALUES
('20200816175151'),
('20201201211748'),
('20201213052805'),
-('20201219201007');
+('20201219201007'),
+('20201224101208');
diff --git a/test/factories/user_upgrade.rb b/test/factories/user_upgrade.rb
new file mode 100644
index 000000000..a3b13c4fd
--- /dev/null
+++ b/test/factories/user_upgrade.rb
@@ -0,0 +1,38 @@
+FactoryBot.define do
+ factory(:user_upgrade) do
+ recipient { create(:member_user) }
+ purchaser { recipient }
+ upgrade_type { "gold" }
+ status { "pending" }
+ stripe_id { nil }
+
+ factory(:self_gold_upgrade) do
+ upgrade_type { "gold" }
+ end
+
+ factory(:self_platinum_upgrade) do
+ upgrade_type { "platinum" }
+ end
+
+ factory(:self_gold_to_platinum_upgrade) do
+ recipient { create(:gold_user) }
+ upgrade_type { "gold_to_platinum" }
+ end
+
+ factory(:gift_gold_upgrade) do
+ purchaser { create(:user) }
+ upgrade_type { "gold" }
+ end
+
+ factory(:gift_platinum_upgrade) do
+ purchaser { create(:user) }
+ upgrade_type { "platinum" }
+ end
+
+ factory(:gift_gold_to_platinum_upgrade) do
+ recipient { create(:gold_user) }
+ purchaser { create(:user) }
+ upgrade_type { "gold_to_platinum" }
+ end
+ end
+end
diff --git a/test/functional/user_upgrades_controller_test.rb b/test/functional/user_upgrades_controller_test.rb
index 58e92a8aa..ba6240730 100644
--- a/test/functional/user_upgrades_controller_test.rb
+++ b/test/functional/user_upgrades_controller_test.rb
@@ -3,38 +3,184 @@ require 'test_helper'
class UserUpgradesControllerTest < ActionDispatch::IntegrationTest
context "The user upgrades controller" do
context "new action" do
- should "render" do
+ should "render for a self upgrade" do
+ @user = create(:user)
+ get_auth new_user_upgrade_path, @user
+
+ assert_response :success
+ end
+
+ should "render for a gift upgrade" do
+ @recipient = create(:user)
+ get_auth new_user_upgrade_path(user_id: @recipient.id), create(:user)
+
+ assert_response :success
+ end
+
+ should "render for an anonymous user" do
get new_user_upgrade_path
+
assert_response :success
end
end
context "show action" do
- should "render" do
- get_auth user_upgrade_path, create(:user)
- assert_response :success
+ context "for a completed upgrade" do
+ should "render for a self upgrade" do
+ @user_upgrade = create(:self_gold_upgrade, status: "complete")
+ get_auth user_upgrade_path(@user_upgrade), @user_upgrade.purchaser
+
+ assert_response :success
+ end
+
+ should "render for a gift upgrade for the purchaser" do
+ @user_upgrade = create(:gift_gold_upgrade, status: "complete")
+ get_auth user_upgrade_path(@user_upgrade), @user_upgrade.purchaser
+
+ assert_response :success
+ end
+
+ should "render for a gift upgrade for the recipient" do
+ @user_upgrade = create(:gift_gold_upgrade, status: "complete")
+ get_auth user_upgrade_path(@user_upgrade), @user_upgrade.recipient
+
+ assert_response :success
+ end
+
+ should "render for the site owner" do
+ @user_upgrade = create(:self_gold_upgrade, status: "complete")
+ get_auth user_upgrade_path(@user_upgrade), create(:owner_user)
+
+ assert_response :success
+ end
+
+ should "be inaccessible to other users" do
+ @user_upgrade = create(:self_gold_upgrade, status: "complete")
+ get_auth user_upgrade_path(@user_upgrade), create(:user)
+
+ assert_response 403
+ end
+ end
+
+ context "for a pending upgrade" do
+ should "render" do
+ @user_upgrade = create(:self_gold_upgrade, status: "pending")
+ get_auth user_upgrade_path(@user_upgrade), @user_upgrade.purchaser
+
+ assert_response :success
+ end
end
end
context "create action" do
mock_stripe!
- context "for a self upgrade to Gold" do
- should "redirect the user to the Stripe checkout page" do
- user = create(:member_user)
- post_auth user_upgrade_path(user_id: user.id), user, params: { level: User::Levels::GOLD }, xhr: true
+ context "for a self upgrade" do
+ context "to Gold" do
+ should "create a pending upgrade" do
+ @user = create(:member_user)
- assert_response :success
+ post_auth user_upgrades_path(user_id: @user.id), @user, params: { upgrade_type: "gold" }, xhr: true
+ assert_response :success
+
+ @user_upgrade = @user.purchased_upgrades.last
+ assert_equal(@user, @user_upgrade.purchaser)
+ assert_equal(@user, @user_upgrade.recipient)
+ assert_equal("gold", @user_upgrade.upgrade_type)
+ assert_equal("pending", @user_upgrade.status)
+ assert_not_nil(@user_upgrade.stripe_id)
+ assert_match(/redirectToCheckout/, response.body)
+ end
+ end
+
+ context "to Platinum" do
+ should "create a pending upgrade" do
+ @user = create(:member_user)
+
+ post_auth user_upgrades_path(user_id: @user.id), @user, params: { upgrade_type: "platinum" }, xhr: true
+ assert_response :success
+
+ @user_upgrade = @user.purchased_upgrades.last
+ assert_equal(@user, @user_upgrade.purchaser)
+ assert_equal(@user, @user_upgrade.recipient)
+ assert_equal("platinum", @user_upgrade.upgrade_type)
+ assert_equal("pending", @user_upgrade.status)
+ assert_not_nil(@user_upgrade.stripe_id)
+ assert_match(/redirectToCheckout/, response.body)
+ end
+ end
+
+ context "from Gold to Platinum" do
+ should "create a pending upgrade" do
+ @user = create(:member_user)
+
+ post_auth user_upgrades_path(user_id: @user.id), @user, params: { upgrade_type: "gold_to_platinum" }, xhr: true
+ assert_response :success
+
+ @user_upgrade = @user.purchased_upgrades.last
+ assert_equal(@user, @user_upgrade.purchaser)
+ assert_equal(@user, @user_upgrade.recipient)
+ assert_equal("gold_to_platinum", @user_upgrade.upgrade_type)
+ assert_equal("pending", @user_upgrade.status)
+ assert_not_nil(@user_upgrade.stripe_id)
+ assert_match(/redirectToCheckout/, response.body)
+ end
end
end
- context "for a gifted upgrade to Gold" do
- should "redirect the user to the Stripe checkout page" do
- recipient = create(:member_user)
- purchaser = create(:member_user)
- post_auth user_upgrade_path(user_id: recipient.id), purchaser, params: { level: User::Levels::GOLD }, xhr: true
+ context "for a gifted upgrade" do
+ context "to Gold" do
+ should "create a pending upgrade" do
+ @recipient = create(:member_user)
+ @purchaser = create(:member_user)
- assert_response :success
+ post_auth user_upgrades_path(user_id: @recipient.id), @purchaser, params: { upgrade_type: "gold" }, xhr: true
+ assert_response :success
+
+ @user_upgrade = @purchaser.purchased_upgrades.last
+ assert_equal(@purchaser, @user_upgrade.purchaser)
+ assert_equal(@recipient, @user_upgrade.recipient)
+ assert_equal("gold", @user_upgrade.upgrade_type)
+ assert_equal("pending", @user_upgrade.status)
+ assert_not_nil(@user_upgrade.stripe_id)
+ assert_match(/redirectToCheckout/, response.body)
+ end
+ end
+
+ context "to Platinum" do
+ should "create a pending upgrade" do
+ @recipient = create(:member_user)
+ @purchaser = create(:member_user)
+
+ post_auth user_upgrades_path(user_id: @recipient.id), @purchaser, params: { upgrade_type: "platinum" }, xhr: true
+ assert_response :success
+
+ @user_upgrade = @purchaser.purchased_upgrades.last
+ assert_equal(@purchaser, @user_upgrade.purchaser)
+ assert_equal(@recipient, @user_upgrade.recipient)
+ assert_equal("platinum", @user_upgrade.upgrade_type)
+ assert_equal("pending", @user_upgrade.status)
+ assert_not_nil(@user_upgrade.stripe_id)
+ assert_match(/redirectToCheckout/, response.body)
+ end
+ end
+
+ context "from Gold to Platinum" do
+ should "create a pending upgrade" do
+ @recipient = create(:gold_user)
+ @purchaser = create(:member_user)
+
+ post_auth user_upgrades_path(user_id: @recipient.id), @purchaser, params: { upgrade_type: "gold_to_platinum" }, xhr: true
+ assert_response :success
+
+ @user_upgrade = @purchaser.purchased_upgrades.last
+ assert_equal(@purchaser, @user_upgrade.purchaser)
+ assert_equal(@recipient, @user_upgrade.recipient)
+ assert_equal("gold_to_platinum", @user_upgrade.upgrade_type)
+ assert_equal("pending", @user_upgrade.status)
+ assert_not_nil(@user_upgrade.stripe_id)
+ assert_match(/redirectToCheckout/, response.body)
+ end
end
end
end
diff --git a/test/functional/webhooks_controller_test.rb b/test/functional/webhooks_controller_test.rb
index afb596be4..dd10f8df8 100644
--- a/test/functional/webhooks_controller_test.rb
+++ b/test/functional/webhooks_controller_test.rb
@@ -3,8 +3,8 @@ require 'test_helper'
class WebhooksControllerTest < ActionDispatch::IntegrationTest
mock_stripe!
- def post_webhook(*args, **metadata)
- event = StripeMock.mock_webhook_event(*args, metadata: metadata)
+ def post_webhook(*args, payment_status: "paid", **metadata)
+ event = StripeMock.mock_webhook_event(*args, payment_status: payment_status, metadata: metadata)
signature = generate_stripe_signature(event)
headers = { "Stripe-Signature": signature }
@@ -58,105 +58,83 @@ class WebhooksControllerTest < ActionDispatch::IntegrationTest
end
context "for a checkout.session.completed event" do
+ context "for completed event with an unpaid payment status" do
+ should "not upgrade the user" do
+ @user_upgrade = create(:self_gold_upgrade)
+ post_webhook("checkout.session.completed", { user_upgrade_id: @user_upgrade.id, payment_status: "unpaid" })
+
+ assert_response 200
+ assert_equal("processing", @user_upgrade.reload.status)
+ assert_equal(User::Levels::MEMBER, @user_upgrade.recipient.reload.level)
+ end
+ end
+
context "for a self upgrade" do
- context "of a Member to Gold" do
+ context "to Gold" do
should "upgrade the user" do
- @user = create(:member_user)
-
- post_webhook("checkout.session.completed", {
- recipient_id: @user.id,
- purchaser_id: @user.id,
- upgrade_type: "gold_upgrade",
- level: User::Levels::GOLD,
- })
+ @user_upgrade = create(:self_gold_upgrade)
+ post_webhook("checkout.session.completed", { user_upgrade_id: @user_upgrade.id })
assert_response 200
- assert_equal(User::Levels::GOLD, @user.reload.level)
+ assert_equal("complete", @user_upgrade.reload.status)
+ assert_equal(User::Levels::GOLD, @user_upgrade.recipient.reload.level)
end
end
- context "of a Member to Platinum" do
+ context "to Platinum" do
should "upgrade the user" do
- @user = create(:member_user)
-
- post_webhook("checkout.session.completed", {
- recipient_id: @user.id,
- purchaser_id: @user.id,
- upgrade_type: "platinum_upgrade",
- level: User::Levels::PLATINUM,
- })
+ @user_upgrade = create(:self_platinum_upgrade)
+ post_webhook("checkout.session.completed", { user_upgrade_id: @user_upgrade.id })
assert_response 200
- assert_equal(User::Levels::PLATINUM, @user.reload.level)
+ assert_equal("complete", @user_upgrade.reload.status)
+ assert_equal(User::Levels::PLATINUM, @user_upgrade.recipient.reload.level)
end
end
- context "of a Gold user to Platinum" do
+ context "from Gold to Platinum" do
should "upgrade the user" do
- @user = create(:gold_user)
-
- post_webhook("checkout.session.completed", {
- recipient_id: @user.id,
- purchaser_id: @user.id,
- upgrade_type: "gold_to_platinum_upgrade",
- level: User::Levels::PLATINUM,
- })
+ @user_upgrade = create(:self_gold_to_platinum_upgrade)
+ post_webhook("checkout.session.completed", { user_upgrade_id: @user_upgrade.id })
assert_response 200
- assert_equal(User::Levels::PLATINUM, @user.reload.level)
+ assert_equal("complete", @user_upgrade.reload.status)
+ assert_equal(User::Levels::PLATINUM, @user_upgrade.recipient.reload.level)
end
end
end
context "for a gifted upgrade" do
- context "of a Member to Gold" do
+ context "to Gold" do
should "upgrade the user" do
- @recipient = create(:member_user)
- @purchaser = create(:member_user)
-
- post_webhook("checkout.session.completed", {
- recipient_id: @recipient.id,
- purchaser_id: @purchaser.id,
- upgrade_type: "gold_upgrade",
- level: User::Levels::GOLD,
- })
+ @user_upgrade = create(:gift_gold_upgrade)
+ post_webhook("checkout.session.completed", { user_upgrade_id: @user_upgrade.id })
assert_response 200
- assert_equal(User::Levels::GOLD, @recipient.reload.level)
+ assert_equal("complete", @user_upgrade.reload.status)
+ assert_equal(User::Levels::GOLD, @user_upgrade.recipient.reload.level)
end
end
- context "of a Member to Platinum" do
+ context "to Platinum" do
should "upgrade the user" do
- @recipient = create(:member_user)
- @purchaser = create(:member_user)
-
- post_webhook("checkout.session.completed", {
- recipient_id: @recipient.id,
- purchaser_id: @purchaser.id,
- upgrade_type: "platinum_upgrade",
- level: User::Levels::PLATINUM,
- })
+ @user_upgrade = create(:gift_platinum_upgrade)
+ post_webhook("checkout.session.completed", { user_upgrade_id: @user_upgrade.id })
assert_response 200
- assert_equal(User::Levels::PLATINUM, @recipient.reload.level)
+ assert_equal("complete", @user_upgrade.reload.status)
+ assert_equal(User::Levels::PLATINUM, @user_upgrade.recipient.reload.level)
end
end
- context "of a Gold user to Platinum" do
+ context "from Gold to Platinum" do
should "upgrade the user" do
- @recipient = create(:gold_user)
- @purchaser = create(:member_user)
-
- post_webhook("checkout.session.completed", {
- recipient_id: @recipient.id,
- purchaser_id: @purchaser.id,
- upgrade_type: "gold_to_platinum_upgrade",
- level: User::Levels::PLATINUM,
- })
+ @user_upgrade = create(:gift_gold_to_platinum_upgrade)
+ post_webhook("checkout.session.completed", { user_upgrade_id: @user_upgrade.id })
assert_response 200
- assert_equal(User::Levels::PLATINUM, @recipient.reload.level)
+ assert_equal("complete", @user_upgrade.reload.status)
+ assert_equal(User::Levels::PLATINUM, @user_upgrade.recipient.reload.level)
end
end
end
diff --git a/test/unit/user_upgrade_test.rb b/test/unit/user_upgrade_test.rb
index b6d228246..070fa2f48 100644
--- a/test/unit/user_upgrade_test.rb
+++ b/test/unit/user_upgrade_test.rb
@@ -3,37 +3,45 @@ require 'test_helper'
class UserUpgradeTest < ActiveSupport::TestCase
context "UserUpgrade:" do
context "the #process_upgrade! method" do
- setup do
- @user = create(:user)
- @user_upgrade = UserUpgrade.new(recipient: @user, purchaser: @user, level: User::Levels::GOLD)
- end
-
- should "update the user's level" do
- @user_upgrade.process_upgrade!
- assert_equal(User::Levels::GOLD, @user.reload.level)
- end
-
- should "log an account upgrade modaction" do
- assert_difference("ModAction.user_account_upgrade.count") do
- @user_upgrade.process_upgrade!
- end
- end
-
- should "send the user a dmail" do
- assert_difference("@user.dmails.received.count") do
- @user_upgrade.process_upgrade!
- end
- end
-
- context "for an upgrade for a user above Platinum level" do
- should "not demote the user" do
- @user.update!(level: User::Levels::BUILDER)
-
- assert_raise(User::PrivilegeError) do
- @user_upgrade.process_upgrade!
+ context "for a self upgrade" do
+ context "to Gold" do
+ setup do
+ @user_upgrade = create(:self_gold_upgrade)
end
- assert_equal(true, @user.reload.is_builder?)
+ should "update the user's level if the payment status is paid" do
+ @user_upgrade.process_upgrade!("paid")
+
+ assert_equal(User::Levels::GOLD, @user_upgrade.recipient.level)
+ assert_equal("complete", @user_upgrade.status)
+ end
+
+ should "not update the user's level if the payment is unpaid" do
+ @user_upgrade.process_upgrade!("unpaid")
+
+ assert_equal(User::Levels::MEMBER, @user_upgrade.recipient.level)
+ assert_equal("processing", @user_upgrade.status)
+ end
+
+ should "not update the user's level if the upgrade status is complete" do
+ @user_upgrade.update!(status: "complete")
+ @user_upgrade.process_upgrade!("paid")
+
+ assert_equal(User::Levels::MEMBER, @user_upgrade.recipient.level)
+ assert_equal("complete", @user_upgrade.status)
+ end
+
+ should "log an account upgrade modaction" do
+ assert_difference("ModAction.user_account_upgrade.count") do
+ @user_upgrade.process_upgrade!("paid")
+ end
+ end
+
+ should "send the recipient a dmail" do
+ assert_difference("@user_upgrade.recipient.dmails.received.count") do
+ @user_upgrade.process_upgrade!("paid")
+ end
+ end
end
end
end