diff --git a/app/controllers/user_upgrades_controller.rb b/app/controllers/user_upgrades_controller.rb index be7ee116a..e2b8729d6 100644 --- a/app/controllers/user_upgrades_controller.rb +++ b/app/controllers/user_upgrades_controller.rb @@ -1,11 +1,12 @@ class UserUpgradesController < ApplicationController helper_method :user - skip_before_action :verify_authenticity_token, only: [:create] + respond_to :js, :html def create - if params[:stripeToken] - create_stripe - end + @user_upgrade = UserUpgrade.new(recipient: user, purchaser: CurrentUser.user, level: params[:level].to_i) + @checkout = @user_upgrade.create_checkout + + respond_with(@user_upgrade) end def new @@ -22,38 +23,4 @@ class UserUpgradesController < ApplicationController CurrentUser.user end end - - private - - def create_stripe - @user = user - - if params[:desc] == "Upgrade to Gold" - level = User::Levels::GOLD - cost = UserUpgrade.gold_price - elsif params[:desc] == "Upgrade to Platinum" - level = User::Levels::PLATINUM - cost = UserUpgrade.platinum_price - elsif params[:desc] == "Upgrade Gold to Platinum" && @user.level == User::Levels::GOLD - level = User::Levels::PLATINUM - cost = UserUpgrade.upgrade_price - else - raise "Invalid desc" - end - - begin - charge = Stripe::Charge.create(amount: cost, currency: "usd", source: params[:stripeToken], description: params[:desc]) - @user.promote_to!(level, User.system, is_upgrade: true) - flash[:success] = true - rescue Stripe::StripeError => e - DanbooruLogger.log(e) - flash[:error] = e.message - end - - if @user == CurrentUser.user - redirect_to user_upgrade_path - else - redirect_to user_upgrade_path(user_id: params[:user_id]) - end - end end diff --git a/app/controllers/webhooks_controller.rb b/app/controllers/webhooks_controller.rb new file mode 100644 index 000000000..0ff5cdeae --- /dev/null +++ b/app/controllers/webhooks_controller.rb @@ -0,0 +1,13 @@ +class WebhooksController < ApplicationController + skip_forgery_protection only: :receive + rescue_with Stripe::SignatureVerificationError, status: 400 + + def receive + if params[:source] == "stripe" + UserUpgrade.receive_webhook(request) + head 200 + else + head 400 + end + end +end diff --git a/app/helpers/user_upgrades_helper.rb b/app/helpers/user_upgrades_helper.rb index 0b7c301aa..8d37de45d 100644 --- a/app/helpers/user_upgrades_helper.rb +++ b/app/helpers/user_upgrades_helper.rb @@ -1,24 +1,4 @@ module UserUpgradesHelper - def stripe_button(desc, cost, user) - html = %{ -
- - #{hidden_field_tag(:desc, desc)} - #{hidden_field_tag(:user_id, user.id)} - -
- } - - raw(html) - end - def cents_to_usd(cents) number_to_currency(cents / 100, precision: 0) end diff --git a/app/logical/user_upgrade.rb b/app/logical/user_upgrade.rb index b29909faf..f9af14708 100644 --- a/app/logical/user_upgrade.rb +++ b/app/logical/user_upgrade.rb @@ -1,13 +1,145 @@ class UserUpgrade + attr_reader :recipient, :purchaser, :level + + def self.stripe_publishable_key + Danbooru.config.stripe_publishable_key + end + + def self.stripe_webhook_secret + Danbooru.config.stripe_webhook_secret + end + def self.gold_price 2000 end def self.platinum_price - 4000 + 2 * gold_price end - def self.upgrade_price - 2000 + def self.gold_to_platinum_price + 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 + else + raise ArgumentError, "Invalid upgrade" + end + end + + def upgrade_price + case upgrade_type + when :gold_upgrade + UserUpgrade.gold_price + when :platinum_upgrade + UserUpgrade.platinum_price + when :gold_to_platinum_upgrade + UserUpgrade.gold_to_platinum_price + end + end + + def upgrade_description + case upgrade_type + when :gold_upgrade + "Upgrade to Gold" + when :platinum_upgrade + "Upgrade to Platinum" + when :gold_to_platinum_upgrade + "Upgrade Gold to Platinum" + end + end + + def is_gift? + recipient != purchaser + end + + def process_upgrade! + recipient.with_lock do + upgrade_recipient! + end + end + + def upgrade_recipient! + recipient.promote_to!(level, User.system, is_upgrade: true) + end + + concerning :StripeMethods do + def create_checkout + Stripe::Checkout::Session.create( + mode: "payment", + success_url: Routes.user_upgrade_url(user_id: recipient.id), + cancel_url: Routes.new_user_upgrade_url(user_id: recipient.id), + client_reference_id: "user_#{purchaser.id}", + customer_email: recipient.email_address&.address, + payment_method_types: ["card"], + line_items: [{ + price_data: { + unit_amount: upgrade_price, + currency: "usd", + product_data: { + name: upgrade_description, + }, + }, + quantity: 1, + }], + metadata: { + purchaser_id: purchaser.id, + recipient_id: recipient.id, + purchaser_name: purchaser.name, + recipient_name: recipient.name, + upgrade_type: upgrade_type, + is_gift: is_gift?, + level: level, + }, + ) + end + + class_methods do + def register_webhook + webhook = Stripe::WebhookEndpoint.create({ + url: Routes.webhook_user_upgrade_url(source: "stripe"), + enabled_events: [ + "payment_intent.created", + "payment_intent.payment_failed", + "checkout.session.completed", + ], + }) + + webhook.secret + end + + def receive_webhook(request) + event = build_event(request) + + if event.type == "checkout.session.completed" + checkout_session_completed(event) + end + end + + def build_event(request) + payload = request.body.read + signature = request.headers["Stripe-Signature"] + 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! + end + end end end diff --git a/app/views/user_upgrades/_stripe_payment.html.erb b/app/views/user_upgrades/_stripe_payment.html.erb deleted file mode 100644 index b9e43052d..000000000 --- a/app/views/user_upgrades/_stripe_payment.html.erb +++ /dev/null @@ -1,10 +0,0 @@ -
-

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 %> - <%= stripe_button("Upgrade to Gold", UserUpgrade.gold_price, user) %> - <%= stripe_button("Upgrade to Platinum", UserUpgrade.platinum_price, user) %> - <% elsif user.level < User::Levels::PLATINUM %> - <%= stripe_button("Upgrade Gold to Platinum", UserUpgrade.upgrade_price, user) %> - <% end %> -
diff --git a/app/views/user_upgrades/_stripe_payment_safebooru.html.erb b/app/views/user_upgrades/_stripe_payment_safebooru.html.erb deleted file mode 100644 index 730eab9f6..000000000 --- a/app/views/user_upgrades/_stripe_payment_safebooru.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -
-

You can pay with a credit or debit card on <%= link_to "Safebooru", new_user_upgrade_url(host: "safebooru.donmai.us", protocol: "https") %>. Your account will then also be upgraded on Danbooru. You can login to Safebooru with the same username and password you use on Danbooru.

-
diff --git a/app/views/user_upgrades/create.js.erb b/app/views/user_upgrades/create.js.erb new file mode 100644 index 000000000..e08bc3438 --- /dev/null +++ b/app/views/user_upgrades/create.js.erb @@ -0,0 +1,2 @@ +var stripe = Stripe("<%= j UserUpgrade.stripe_publishable_key %>"); +stripe.redirectToCheckout({ sessionId: "<%= j @checkout.id %>" }); diff --git a/app/views/user_upgrades/new.html.erb b/app/views/user_upgrades/new.html.erb index 4cd610bb3..2ed284092 100644 --- a/app/views/user_upgrades/new.html.erb +++ b/app/views/user_upgrades/new.html.erb @@ -1,5 +1,6 @@ <% page_title "Account Upgrade" %> -<% meta_description "Upgrade to a Gold or Platinum account on #{Danbooru.config.app_name}." %> +<% meta_description "Upgrade to a Gold or Platinum account." %> + <%= render "users/secondary_links" %> @@ -96,9 +97,24 @@ <% if CurrentUser.is_anonymous? %>

<%= link_to "Sign up", new_user_path %> or <%= link_to "login", login_path(url: new_user_upgrade_path) %> first to upgrade your account.

<% elsif CurrentUser.safe_mode? %> - <%= render "stripe_payment" %> +
+

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..." %>

+ <% end %> +
<% else %> - <%= render "stripe_payment_safebooru" %> +
+

You can pay with a credit or debit card on + <%= link_to "Safebooru", new_user_upgrade_url(user_id: user.id, host: "safebooru.donmai.us", protocol: "https") %>. + Your account will then also be upgraded on Danbooru. You can login to + Safebooru with the same username and password you use on Danbooru.

+
<% end %> <% end %> diff --git a/config/danbooru_default_config.rb b/config/danbooru_default_config.rb index 41fd594d9..75622de73 100644 --- a/config/danbooru_default_config.rb +++ b/config/danbooru_default_config.rb @@ -359,6 +359,9 @@ module Danbooru def stripe_publishable_key end + def stripe_webhook_secret + end + def twitter_api_key end diff --git a/config/initializers/stripe.rb b/config/initializers/stripe.rb index 43495044b..7a60018c9 100644 --- a/config/initializers/stripe.rb +++ b/config/initializers/stripe.rb @@ -1 +1,2 @@ Stripe.api_key = Danbooru.config.stripe_secret_key +Stripe.api_version = "2020-08-27" diff --git a/config/routes.rb b/config/routes.rb index 9ab7cacfa..b027bb3a3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -257,6 +257,9 @@ Rails.application.routes.draw do resource :user_upgrade, :only => [:new, :create, :show] resources :user_feedbacks, except: [:destroy] resources :user_name_change_requests, only: [:new, :create, :show, :index] + resources :webhooks do + post :receive, on: :collection + end resources :wiki_pages, id: /.+?(?=\.json|\.xml|\.html)|.+/ do put :revert, on: :member get :search, on: :collection diff --git a/test/fixtures/stripe-webhooks/checkout.session.completed.json b/test/fixtures/stripe-webhooks/checkout.session.completed.json new file mode 100644 index 000000000..975bb555d --- /dev/null +++ b/test/fixtures/stripe-webhooks/checkout.session.completed.json @@ -0,0 +1,55 @@ +{ + "id": "evt_000", + "object": "event", + "api_version": "2020-08-27", + "created": 1608705740, + "data": { + "object": { + "id": "cs_test_000", + "object": "checkout.session", + "allow_promotion_codes": null, + "amount_subtotal": 2000, + "amount_total": 2000, + "billing_address_collection": null, + "cancel_url": "http://localhost/user_upgrade/new", + "client_reference_id": "user_12345", + "currency": "usd", + "customer": "cus_000", + "customer_email": null, + "livemode": false, + "locale": null, + "metadata": { + "purchaser_id": "12345", + "recipient_id": "12345", + "purchaser_name": "user_12345", + "recipient_name": "user_12345", + "upgrade_type": "gold_upgrade", + "is_gift": "false", + "level": "30" + }, + "mode": "payment", + "payment_intent": "pi_000", + "payment_method_types": [ + "card" + ], + "payment_status": "paid", + "setup_intent": null, + "shipping": null, + "shipping_address_collection": null, + "submit_type": null, + "subscription": null, + "success_url": "http://localhost/user_upgrade?user_id=12345", + "total_details": { + "amount_discount": 0, + "amount_tax": 0 + } + } + }, + "livemode": false, + "pending_webhooks": 3, + "request": { + "id": null, + "idempotency_key": null + }, + "type": "checkout.session.completed" +} diff --git a/test/fixtures/stripe-webhooks/payment_intent.created.json b/test/fixtures/stripe-webhooks/payment_intent.created.json new file mode 100644 index 000000000..915aa9f90 --- /dev/null +++ b/test/fixtures/stripe-webhooks/payment_intent.created.json @@ -0,0 +1,67 @@ +{ + "id": "evt_000", + "object": "event", + "api_version": "2020-08-27", + "created": 1608705945, + "data": { + "object": { + "id": "pi_000", + "object": "payment_intent", + "amount": 2000, + "amount_capturable": 0, + "amount_received": 0, + "application": null, + "application_fee_amount": null, + "canceled_at": null, + "cancellation_reason": null, + "capture_method": "automatic", + "charges": { + "object": "list", + "data": [], + "has_more": false, + "total_count": 0, + "url": "/v1/charges?payment_intent=pi_000" + }, + "client_secret": "pi_000", + "confirmation_method": "automatic", + "created": 1608705945, + "currency": "usd", + "customer": null, + "description": null, + "invoice": null, + "last_payment_error": null, + "livemode": false, + "metadata": {}, + "next_action": null, + "on_behalf_of": null, + "payment_method": null, + "payment_method_options": { + "card": { + "installments": null, + "network": null, + "request_three_d_secure": "automatic" + } + }, + "payment_method_types": [ + "card" + ], + "receipt_email": null, + "review": null, + "setup_future_usage": null, + "shipping": null, + "source": null, + "statement_descriptor": null, + "statement_descriptor_suffix": null, + "status": "requires_payment_method", + "transfer_data": null, + "transfer_group": null + } + }, + "livemode": false, + "pending_webhooks": 3, + "request": { + "id": "req_000", + "idempotency_key": null + }, + "type": "payment_intent.created" +} diff --git a/test/functional/user_upgrades_controller_test.rb b/test/functional/user_upgrades_controller_test.rb index db88aa327..58e92a8aa 100644 --- a/test/functional/user_upgrades_controller_test.rb +++ b/test/functional/user_upgrades_controller_test.rb @@ -1,14 +1,6 @@ require 'test_helper' class UserUpgradesControllerTest < ActionDispatch::IntegrationTest - setup do - StripeMock.start - end - - teardown do - StripeMock.stop - end - context "The user upgrades controller" do context "new action" do should "render" do @@ -25,103 +17,24 @@ class UserUpgradesControllerTest < ActionDispatch::IntegrationTest end context "create action" do - setup do - @user = create(:user) - @token = StripeMock.generate_card_token - end + mock_stripe! - context "a self upgrade" do - should "upgrade a Member to Gold" do - post_auth user_upgrade_path, @user, params: { stripeToken: @token, desc: "Upgrade to Gold" } - - assert_redirected_to user_upgrade_path - assert_equal(true, @user.reload.is_gold?) - end - - should "upgrade a Member to Platinum" do - post_auth user_upgrade_path, @user, params: { stripeToken: @token, desc: "Upgrade to Platinum" } - - assert_redirected_to user_upgrade_path - assert_equal(true, @user.reload.is_platinum?) - end - - should "upgrade a Gold user to Platinum" do - @user.update!(level: User::Levels::GOLD) - post_auth user_upgrade_path, @user, params: { stripeToken: @token, desc: "Upgrade Gold to Platinum" } - - assert_redirected_to user_upgrade_path - assert_equal(true, @user.reload.is_platinum?) - end - - should "log an account upgrade modaction" do - assert_difference("ModAction.user_account_upgrade.count") do - post_auth user_upgrade_path, @user, params: { stripeToken: @token, desc: "Upgrade to Gold" } - end - end - - should "send the user a dmail" do - assert_difference("@user.dmails.received.count") do - post_auth user_upgrade_path, @user, params: { stripeToken: @token, desc: "Upgrade to Gold" } - end - end - end - - context "a gifted upgrade" do - should "upgrade the user to Gold" do - @other_user = create(:user) - post_auth user_upgrade_path, @user, params: { stripeToken: @token, desc: "Upgrade to Gold", user_id: @other_user.id } - - assert_redirected_to user_upgrade_path(user_id: @other_user.id) - assert_equal(true, @other_user.reload.is_gold?) - assert_equal(false, @user.reload.is_gold?) - end - end - - context "an upgrade for a user above Platinum level" do - should "not demote the user" do - @builder = create(:builder_user) - post_auth user_upgrade_path, @user, params: { stripeToken: @token, desc: "Upgrade to Gold", user_id: @builder.id } - - assert_response 403 - assert_equal(true, @builder.reload.is_builder?) - end - end - - context "an upgrade with a missing Stripe token" do - should "not upgrade the user" do - post_auth user_upgrade_path, @user, params: { desc: "Upgrade to Gold" } + 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 assert_response :success - assert_equal(true, @user.reload.is_member?) end end - context "an upgrade with an invalid Stripe token" do - should "not upgrade the user" do - post_auth user_upgrade_path, @user, params: { stripeToken: "garbage", desc: "Upgrade to Gold" } + 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 - assert_redirected_to user_upgrade_path - assert_equal(true, @user.reload.is_member?) - end - end - - context "an upgrade with an credit card that is declined" do - should "not upgrade the user" do - StripeMock.prepare_card_error(:card_declined) - post_auth user_upgrade_path, @user, params: { stripeToken: @token, desc: "Upgrade to Gold" } - - assert_redirected_to user_upgrade_path - assert_equal(true, @user.reload.is_member?) - end - end - - context "an upgrade with an credit card that is expired" do - should "not upgrade the user" do - StripeMock.prepare_card_error(:expired_card) - post_auth user_upgrade_path, @user, params: { stripeToken: @token, desc: "Upgrade to Gold" } - - assert_redirected_to user_upgrade_path - assert_equal(true, @user.reload.is_member?) + assert_response :success end end end diff --git a/test/functional/webhooks_controller_test.rb b/test/functional/webhooks_controller_test.rb new file mode 100644 index 000000000..afb596be4 --- /dev/null +++ b/test/functional/webhooks_controller_test.rb @@ -0,0 +1,167 @@ +require 'test_helper' + +class WebhooksControllerTest < ActionDispatch::IntegrationTest + mock_stripe! + + def post_webhook(*args, **metadata) + event = StripeMock.mock_webhook_event(*args, metadata: metadata) + signature = generate_stripe_signature(event) + headers = { "Stripe-Signature": signature } + + post receive_webhooks_path(source: "stripe"), headers: headers, params: event, as: :json + end + + # https://github.com/stripe-ruby-mock/stripe-ruby-mock/issues/467#issuecomment-634674913 + # https://stripe.com/docs/webhooks/signatures + def generate_stripe_signature(event) + time = Time.now + secret = UserUpgrade.stripe_webhook_secret + signature = Stripe::Webhook::Signature.compute_signature(time, event.to_json, secret) + Stripe::Webhook::Signature.generate_header(time, signature, scheme: Stripe::Webhook::Signature::EXPECTED_SCHEME) + end + + context "The webhooks controller" do + context "receive action" do + context "for a request from an unrecognized source" do + should "fail" do + post receive_webhooks_path(source: "blah") + assert_response 400 + end + end + + context "for a Stripe webhook" do + context "with a missing signature" do + should "fail" do + event = StripeMock.mock_webhook_event("payment_intent.created") + post receive_webhooks_path(source: "stripe"), params: event, as: :json + + assert_response 400 + end + end + + context "with an invalid signature" do + should "fail" do + event = StripeMock.mock_webhook_event("payment_intent.created") + headers = { "Stripe-Signature": "blah" } + post receive_webhooks_path(source: "stripe"), headers: headers, params: event, as: :json + + assert_response 400 + end + end + + context "for a payment_intent.created event" do + should "work" do + post_webhook("payment_intent.created") + + assert_response 200 + end + end + + context "for a checkout.session.completed event" do + context "for a self upgrade" do + context "of a Member 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, + }) + + assert_response 200 + assert_equal(User::Levels::GOLD, @user.reload.level) + end + end + + context "of a Member 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, + }) + + assert_response 200 + assert_equal(User::Levels::PLATINUM, @user.reload.level) + end + end + + context "of a Gold user 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, + }) + + assert_response 200 + assert_equal(User::Levels::PLATINUM, @user.reload.level) + end + end + end + + context "for a gifted upgrade" do + context "of a Member 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, + }) + + assert_response 200 + assert_equal(User::Levels::GOLD, @recipient.reload.level) + end + end + + context "of a Member 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, + }) + + assert_response 200 + assert_equal(User::Levels::PLATINUM, @recipient.reload.level) + end + end + + context "of a Gold user 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, + }) + + assert_response 200 + assert_equal(User::Levels::PLATINUM, @recipient.reload.level) + end + end + end + end + end + end + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 1af778120..ca6a4b4f2 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -26,6 +26,7 @@ class ActiveSupport::TestCase include DownloadTestHelper include IqdbTestHelper include UploadTestHelper + extend StripeTestHelper mock_post_version_service! mock_pool_version_service! diff --git a/test/test_helpers/stripe_test_helper.rb b/test/test_helpers/stripe_test_helper.rb new file mode 100644 index 000000000..d1f0f7126 --- /dev/null +++ b/test/test_helpers/stripe_test_helper.rb @@ -0,0 +1,13 @@ +StripeMock.webhook_fixture_path = "test/fixtures/stripe-webhooks" + +module StripeTestHelper + def mock_stripe! + setup do + StripeMock.start + end + + teardown do + StripeMock.stop + end + end +end diff --git a/test/unit/user_upgrade_test.rb b/test/unit/user_upgrade_test.rb new file mode 100644 index 000000000..b6d228246 --- /dev/null +++ b/test/unit/user_upgrade_test.rb @@ -0,0 +1,41 @@ +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! + end + + assert_equal(true, @user.reload.is_builder?) + end + end + end + end +end