diff --git a/app/controllers/user_upgrades_controller.rb b/app/controllers/user_upgrades_controller.rb index c2e5f0160..e0e86e566 100644 --- a/app/controllers/user_upgrades_controller.rb +++ b/app/controllers/user_upgrades_controller.rb @@ -3,7 +3,8 @@ class UserUpgradesController < ApplicationController def create @user_upgrade = authorize UserUpgrade.create(recipient: recipient, purchaser: CurrentUser.user, status: "pending", upgrade_type: params[:upgrade_type]) - @checkout = @user_upgrade.create_checkout! + @country = params[:country] || CurrentUser.country || "US" + @checkout = @user_upgrade.create_checkout!(country: @country) respond_with(@user_upgrade) end diff --git a/app/models/user_upgrade.rb b/app/models/user_upgrade.rb index 9ed86e4ed..69f580f5c 100644 --- a/app/models/user_upgrade.rb +++ b/app/models/user_upgrade.rb @@ -76,32 +76,6 @@ class UserUpgrade < ApplicationRecord end end - def upgrade_price - case upgrade_type - when "gold" - UserUpgrade.gold_price - when "platinum" - UserUpgrade.platinum_price - when "gold_to_platinum" - UserUpgrade.gold_to_platinum_price - else - raise NotImplementedError - end - end - - def upgrade_description - case upgrade_type - when "gold" - "Upgrade to Gold" - when "platinum" - "Upgrade to Platinum" - when "gold_to_platinum" - "Upgrade Gold to Platinum" - else - raise NotImplementedError - end - end - def level_string User.level_string(level) end @@ -178,24 +152,25 @@ class UserUpgrade < ApplicationRecord end concerning :StripeMethods do - def create_checkout! + def create_checkout!(country: "US") + methods = payment_method_types(country) + currency = preferred_currency(country) + price_id = upgrade_price_id(currency) + checkout = Stripe::Checkout::Session.create( mode: "payment", success_url: Routes.user_upgrade_url(self), cancel_url: Routes.new_user_upgrade_url(user_id: recipient.id), client_reference_id: "user_upgrade_#{id}", customer_email: purchaser.email_address&.address, - payment_method_types: ["card"], + payment_method_types: methods, line_items: [{ - price_data: { - unit_amount: upgrade_price, - currency: "usd", - product_data: { - name: upgrade_description, - }, - }, + price: price_id, quantity: 1, }], + discounts: [{ + coupon: promotion_discount_id, + }], metadata: { user_upgrade_id: id, purchaser_id: purchaser.id, @@ -203,6 +178,7 @@ class UserUpgrade < ApplicationRecord purchaser_name: purchaser.name, recipient_name: recipient.name, upgrade_type: upgrade_type, + country: country, is_gift: is_gift?, level: level, }, @@ -252,6 +228,63 @@ class UserUpgrade < ApplicationRecord !pending? end + def promotion_discount_id + if Danbooru.config.is_promotion? + Danbooru.config.stripe_promotion_discount_id + end + end + + def upgrade_price_id(currency) + case [upgrade_type, currency] + when ["gold", "usd"] + Danbooru.config.stripe_gold_usd_price_id + when ["gold", "eur"] + Danbooru.config.stripe_gold_eur_price_id + when ["platinum", "usd"] + Danbooru.config.stripe_platinum_usd_price_id + when ["platinum", "eur"] + Danbooru.config.stripe_platinum_eur_price_id + when ["gold_to_platinum", "usd"] + Danbooru.config.stripe_gold_to_platinum_usd_price_id + when ["gold_to_platinum", "eur"] + Danbooru.config.stripe_gold_to_platinum_eur_price_id + else + raise NotImplementedError + end + end + + def payment_method_types(country) + case country.to_s.upcase + # Austria, https://stripe.com/docs/payments/bancontact + when "AT" + ["card", "eps"] + # Belgium, https://stripe.com/docs/payments/eps + when "BE" + ["card", "bancontact"] + # Germany, https://stripe.com/docs/payments/giropay + when "DE" + ["card", "giropay"] + # Netherlands, https://stripe.com/docs/payments/ideal + when "NL" + ["card", "ideal"] + # Poland, https://stripe.com/docs/payments/p24 + when "PL" + ["card", "p24"] + else + ["card"] + end + end + + def preferred_currency(country) + case country.to_s.upcase + # Austria, Belgium, Germany, Netherlands, Poland + when "AT", "BE", "DE", "NL", "PL" + "eur" + else + "usd" + end + end + class_methods do def register_webhook webhook = Stripe::WebhookEndpoint.create({ diff --git a/app/views/user_upgrades/new.html.erb b/app/views/user_upgrades/new.html.erb index 6f125ebed..31a85d34d 100644 --- a/app/views/user_upgrades/new.html.erb +++ b/app/views/user_upgrades/new.html.erb @@ -112,12 +112,12 @@ <%= link_to "Get #{Danbooru.config.canonical_app_name} Platinum", login_path(url: new_user_upgrade_path), class: "login-button" %> <% elsif @recipient.level == User::Levels::MEMBER %> - <%= button_to "Get #{Danbooru.config.canonical_app_name} Gold", user_upgrades_path(user_id: @recipient.id, upgrade_type: "gold"), remote: true, disable_with: "Redirecting..." %> - <%= button_to "Get #{Danbooru.config.canonical_app_name} Platinum", user_upgrades_path(user_id: @recipient.id, upgrade_type: "platinum"), remote: true, disable_with: "Redirecting..." %> + <%= button_to "Get #{Danbooru.config.canonical_app_name} Gold", user_upgrades_path(user_id: @recipient.id, upgrade_type: "gold", country: params[:country]), remote: true, disable_with: "Redirecting..." %> + <%= button_to "Get #{Danbooru.config.canonical_app_name} Platinum", user_upgrades_path(user_id: @recipient.id, upgrade_type: "platinum", country: params[:country]), remote: true, disable_with: "Redirecting..." %> <% elsif @recipient.level == User::Levels::GOLD %> <%= button_to "Get #{Danbooru.config.canonical_app_name} Gold", nil, disabled: true %> - <%= button_to "Get #{Danbooru.config.canonical_app_name} Platinum", user_upgrades_path(user_id: @recipient.id, upgrade_type: "gold_to_platinum"), remote: true, disable_with: "Redirecting..." %> + <%= button_to "Get #{Danbooru.config.canonical_app_name} Platinum", user_upgrades_path(user_id: @recipient.id, upgrade_type: "gold_to_platinum", country: params[:country]), remote: true, disable_with: "Redirecting..." %> <% else %> <%= button_to "Get #{Danbooru.config.canonical_app_name} Gold", nil, disabled: true %> @@ -150,7 +150,10 @@ What payment methods do you support?

We support all major credit and debit cards, including international - cards. Payments are securely handled by Stripe. + cards. We also support bank payments in several European countries, + including Austria, Belgium, Germany, the Netherlands, and Poland.

+ +

Payments are securely handled by Stripe. We don't support PayPal or Bitcoin at this time.

diff --git a/config/danbooru_default_config.rb b/config/danbooru_default_config.rb index 947dc75c1..3467e4dcc 100644 --- a/config/danbooru_default_config.rb +++ b/config/danbooru_default_config.rb @@ -375,6 +375,27 @@ module Danbooru def stripe_webhook_secret end + def stripe_gold_usd_price_id + end + + def stripe_platinum_usd_price_id + end + + def stripe_gold_to_platinum_usd_price_id + end + + def stripe_gold_eur_price_id + end + + def stripe_platinum_eur_price_id + end + + def stripe_gold_to_platinum_eur_price_id + end + + def stripe_promotion_discount_id + end + def twitter_api_key end diff --git a/test/functional/webhooks_controller_test.rb b/test/functional/webhooks_controller_test.rb index dd10f8df8..3ac99ae1e 100644 --- a/test/functional/webhooks_controller_test.rb +++ b/test/functional/webhooks_controller_test.rb @@ -1,7 +1,13 @@ require 'test_helper' class WebhooksControllerTest < ActionDispatch::IntegrationTest - mock_stripe! + setup do + StripeMock.start + end + + teardown do + StripeMock.stop + end def post_webhook(*args, payment_status: "paid", **metadata) event = StripeMock.mock_webhook_event(*args, payment_status: payment_status, metadata: metadata) diff --git a/test/test_helpers/stripe_test_helper.rb b/test/test_helpers/stripe_test_helper.rb index d1f0f7126..9bfc845f9 100644 --- a/test/test_helpers/stripe_test_helper.rb +++ b/test/test_helpers/stripe_test_helper.rb @@ -3,11 +3,11 @@ StripeMock.webhook_fixture_path = "test/fixtures/stripe-webhooks" module StripeTestHelper def mock_stripe! setup do - StripeMock.start + StripeMock.start unless UserUpgrade.enabled? end teardown do - StripeMock.stop + StripeMock.stop unless UserUpgrade.enabled? end end end diff --git a/test/unit/user_upgrade_test.rb b/test/unit/user_upgrade_test.rb index 38102d2bf..d73eee270 100644 --- a/test/unit/user_upgrade_test.rb +++ b/test/unit/user_upgrade_test.rb @@ -58,6 +58,116 @@ class UserUpgradeTest < ActiveSupport::TestCase end end end + + context "for each upgrade type" do + setup do + skip unless UserUpgrade.enabled? + end + + should "choose the right price in USD for a gold upgrade" do + @user_upgrade = create(:self_gold_upgrade) + @checkout = @user_upgrade.create_checkout!(country: "US") + + assert_equal(UserUpgrade.gold_price, @user_upgrade.payment_intent.amount) + assert_equal("usd", @user_upgrade.payment_intent.currency) + end + + should "choose the right price in USD for a platinum upgrade" do + @user_upgrade = create(:self_platinum_upgrade) + @checkout = @user_upgrade.create_checkout!(country: "US") + + assert_equal(UserUpgrade.platinum_price, @user_upgrade.payment_intent.amount) + assert_equal("usd", @user_upgrade.payment_intent.currency) + end + + should "choose the right price in USD for a gold to platinum upgrade" do + @user_upgrade = create(:self_gold_to_platinum_upgrade) + @checkout = @user_upgrade.create_checkout!(country: "US") + + assert_equal(UserUpgrade.gold_to_platinum_price, @user_upgrade.payment_intent.amount) + assert_equal("usd", @user_upgrade.payment_intent.currency) + end + + should "choose the right price in EUR for a gold upgrade" do + @user_upgrade = create(:self_gold_upgrade) + @checkout = @user_upgrade.create_checkout!(country: "DE") + + assert_equal(0.8 * UserUpgrade.gold_price, @user_upgrade.payment_intent.amount) + assert_equal("eur", @user_upgrade.payment_intent.currency) + end + + should "choose the right price in EUR for a platinum upgrade" do + @user_upgrade = create(:self_platinum_upgrade) + @checkout = @user_upgrade.create_checkout!(country: "DE") + + assert_equal(0.8 * UserUpgrade.platinum_price, @user_upgrade.payment_intent.amount) + assert_equal("eur", @user_upgrade.payment_intent.currency) + end + + should "choose the right price in EUR for a gold to platinum upgrade" do + @user_upgrade = create(:self_gold_to_platinum_upgrade) + @checkout = @user_upgrade.create_checkout!(country: "DE") + + assert_equal(0.8 * UserUpgrade.gold_to_platinum_price, @user_upgrade.payment_intent.amount) + assert_equal("eur", @user_upgrade.payment_intent.currency) + end + end + + context "for each country" do + setup do + @user_upgrade = create(:self_gold_upgrade) + skip unless UserUpgrade.enabled? + end + + should "choose the right payment methods for US" do + @checkout = @user_upgrade.create_checkout!(country: "US") + + assert_equal(["card"], @checkout.payment_method_types) + assert_equal("usd", @user_upgrade.payment_intent.currency) + end + + should "choose the right payment methods for AT" do + @checkout = @user_upgrade.create_checkout!(country: "AT") + + assert_equal(["card", "eps"], @checkout.payment_method_types) + assert_equal("eur", @user_upgrade.payment_intent.currency) + end + + should "choose the right payment methods for BE" do + @checkout = @user_upgrade.create_checkout!(country: "BE") + + assert_equal(["card", "bancontact"], @checkout.payment_method_types) + assert_equal("eur", @user_upgrade.payment_intent.currency) + end + + should "choose the right payment methods for DE" do + @checkout = @user_upgrade.create_checkout!(country: "DE") + + assert_equal(["card", "giropay"], @checkout.payment_method_types) + assert_equal("eur", @user_upgrade.payment_intent.currency) + end + + should "choose the right payment methods for NL" do + @checkout = @user_upgrade.create_checkout!(country: "NL") + + assert_equal(["card", "ideal"], @checkout.payment_method_types) + assert_equal("eur", @user_upgrade.payment_intent.currency) + end + + should "choose the right payment methods for PL" do + @checkout = @user_upgrade.create_checkout!(country: "PL") + + assert_equal(["card", "p24"], @checkout.payment_method_types) + assert_equal("eur", @user_upgrade.payment_intent.currency) + end + + should "choose the right payment methods for an unsupported country" do + @checkout = @user_upgrade.create_checkout!(country: "MX") + + assert_equal(["card"], @checkout.payment_method_types) + assert_equal("usd", @user_upgrade.payment_intent.currency) + end + end end context "the #receipt_url method" do @@ -65,6 +175,8 @@ class UserUpgradeTest < ActiveSupport::TestCase context "a pending upgrade" do should "not have a receipt" do + skip unless UserUpgrade.enabled? + @user_upgrade = create(:self_gold_upgrade, status: "pending") @user_upgrade.create_checkout!