From 3cc113973f1f7a67dea3c5063d77267e7309390a Mon Sep 17 00:00:00 2001 From: Blake Erickson Date: Mon, 8 Jul 2024 13:22:02 -0600 Subject: [PATCH] FEATURE: Site Setting to enable automatic tax (#218) FEATURE: Site Setting to enable automatic tax There is now a new site setting called `discourse_subscriptions_enable_automatic_tax` that can be enabled if you would like to automatically compute tax rates on all new subscriptions. --- .../subscribe_controller.rb | 29 ++-- config/locales/client.en.yml | 2 + config/settings.yml | 2 + spec/requests/subscribe_controller_spec.rb | 132 +++++++++++++++--- 4 files changed, 134 insertions(+), 31 deletions(-) diff --git a/app/controllers/discourse_subscriptions/subscribe_controller.rb b/app/controllers/discourse_subscriptions/subscribe_controller.rb index c062da6..9721698 100644 --- a/app/controllers/discourse_subscriptions/subscribe_controller.rb +++ b/app/controllers/discourse_subscriptions/subscribe_controller.rb @@ -85,14 +85,19 @@ module DiscourseSubscriptions promo_code_id = promo_code[:id] if promo_code - transaction = - ::Stripe::Subscription.create( - customer: customer[:id], - items: [{ price: params[:plan] }], - metadata: metadata_user, - trial_period_days: trial_days, - promotion_code: promo_code_id, - ) + subscription_params = { + customer: customer[:id], + items: [{ price: params[:plan] }], + metadata: metadata_user, + trial_period_days: trial_days, + promotion_code: promo_code_id, + } + + if SiteSetting.discourse_subscriptions_enable_automatic_tax + subscription_params[:automatic_tax] = { enabled: true } + end + + transaction = ::Stripe::Subscription.create(subscription_params) payment_intent = retrieve_payment_intent(transaction[:latest_invoice]) if transaction[ :status @@ -100,7 +105,13 @@ module DiscourseSubscriptions else coupon_id = promo_code[:coupon][:id] if promo_code && promo_code[:coupon] && promo_code[:coupon][:id] - invoice = ::Stripe::Invoice.create(customer: customer[:id]) + + invoice_params = { customer: customer[:id] } + if SiteSetting.discourse_subscriptions_enable_automatic_tax + invoice_params[:automatic_tax] = { enabled: true } + end + invoice = ::Stripe::Invoice.create(invoice_params) + invoice_item = ::Stripe::InvoiceItem.create( customer: customer[:id], diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index d326667..609a1d8 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -21,6 +21,8 @@ en: discourse_subscriptions_campaign_banner_bg_image: Upload a custom image to use as the background on the campaign banner. discourse_subscriptions_campaign_show_contributors: Show avatars of the most recent purchases of subscription products. discourse_subscriptions_campaign_product: The Stripe product id to send supporters when they click the button on the campaign. If this setting is blank, supporters will be directed to the main products page. + discourse_subscriptions_enable_automatic_tax: Enables automatic tax calculation which will automatically compute tax rates on all invoices generated by the subscription. + errors: discourse_patrons_amount_must_be_currency: "Currency amounts must be currencies without dollar symbol (eg 1.50)" js: diff --git a/config/settings.yml b/config/settings.yml index 8e77799..82c225a 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -80,3 +80,5 @@ discourse_subscriptions: discourse_subscriptions_campaign_group: default: "" hidden: true + discourse_subscriptions_enable_automatic_tax: + default: false \ No newline at end of file diff --git a/spec/requests/subscribe_controller_spec.rb b/spec/requests/subscribe_controller_spec.rb index e94a7c7..8392cd1 100644 --- a/spec/requests/subscribe_controller_spec.rb +++ b/spec/requests/subscribe_controller_spec.rb @@ -237,7 +237,8 @@ RSpec.describe DiscourseSubscriptions::SubscribeController do describe "#create" do before { ::Stripe::Customer.expects(:create).returns(id: "cus_1234") } - it "creates a subscription" do + it "creates a subscription without automatic_tax param" do + SiteSetting.discourse_subscriptions_enable_automatic_tax = false ::Stripe::Price.expects(:retrieve).returns( type: "recurring", product: "product_12345", @@ -247,18 +248,57 @@ RSpec.describe DiscourseSubscriptions::SubscribeController do }, ) + expected_subscription_params = { + customer: "cus_1234", + items: [{ price: "plan_1234" }], + metadata: { + user_id: user.id, + username: user.username_lower, + }, + trial_period_days: 0, + promotion_code: nil, + } + ::Stripe::Subscription .expects(:create) - .with( - customer: "cus_1234", - items: [price: "plan_1234"], - metadata: { - user_id: user.id, - username: user.username_lower, - }, + .with do |params| + params == expected_subscription_params && !params.key?(:automatic_tax) + end + .returns(status: "active", customer: "cus_1234") + + expect { + post "/s/create.json", params: { plan: "plan_1234", source: "tok_1234" } + }.to change { DiscourseSubscriptions::Customer.count } + end + + it "creates a subscription with automatic tax" do + SiteSetting.discourse_subscriptions_enable_automatic_tax = true + ::Stripe::Price.expects(:retrieve).returns( + type: "recurring", + product: "product_12345", + metadata: { + group_name: "awesome", trial_period_days: 0, - promotion_code: nil, - ) + }, + ) + + expected_subscription_params = { + customer: "cus_1234", + items: [{ price: "plan_1234" }], + metadata: { + user_id: user.id, + username: user.username_lower, + }, + trial_period_days: 0, + promotion_code: nil, + automatic_tax: { + enabled: true, + }, + } + + ::Stripe::Subscription + .expects(:create) + .with(expected_subscription_params) .returns(status: "active", customer: "cus_1234") expect { @@ -295,7 +335,8 @@ RSpec.describe DiscourseSubscriptions::SubscribeController do expect(response.status).to eq 422 end - it "creates a one time payment subscription" do + it "creates a one time payment subscription without automatic tax" do + SiteSetting.discourse_subscriptions_enable_automatic_tax = false ::Stripe::Price.expects(:retrieve).returns( type: "one_time", product: "product_12345", @@ -306,7 +347,52 @@ RSpec.describe DiscourseSubscriptions::SubscribeController do ::Stripe::InvoiceItem.expects(:create) - ::Stripe::Invoice.expects(:create).returns(status: "open", id: "in_123") + expected_one_time_params = { customer: "cus_1234" } + + ::Stripe::Invoice + .expects(:create) + .with { |params| params == expected_one_time_params && !params.key?(:automatic_tax) } + .returns(status: "open", id: "in_123") + + ::Stripe::Invoice.expects(:finalize_invoice).returns( + id: "in_123", + status: "open", + payment_intent: "pi_123", + ) + + ::Stripe::Invoice.expects(:retrieve).returns( + id: "in_123", + status: "open", + payment_intent: "pi_123", + ) + + ::Stripe::PaymentIntent.expects(:retrieve).returns(status: "successful") + + ::Stripe::Invoice.expects(:pay).returns(status: "paid", customer: "cus_1234") + + expect { + post "/s/create.json", params: { plan: "plan_1234", source: "tok_1234" } + }.to change { DiscourseSubscriptions::Customer.count } + end + + it "creates a one time payment subscription" do + SiteSetting.discourse_subscriptions_enable_automatic_tax = true + ::Stripe::Price.expects(:retrieve).returns( + type: "one_time", + product: "product_12345", + metadata: { + group_name: "awesome", + }, + ) + + ::Stripe::InvoiceItem.expects(:create) + + expected_one_time_params = { customer: "cus_1234", automatic_tax: { enabled: true } } + + ::Stripe::Invoice + .expects(:create) + .with(expected_one_time_params) + .returns(status: "open", id: "in_123") ::Stripe::Invoice.expects(:finalize_invoice).returns( id: "in_123", @@ -410,18 +496,20 @@ RSpec.describe DiscourseSubscriptions::SubscribeController do }, ) + expected_subscription_params = { + customer: "cus_1234", + items: [price: "plan_1234"], + metadata: { + user_id: user.id, + username: user.username_lower, + }, + trial_period_days: 0, + promotion_code: "promo123", + } + ::Stripe::Subscription .expects(:create) - .with( - customer: "cus_1234", - items: [price: "plan_1234"], - metadata: { - user_id: user.id, - username: user.username_lower, - }, - trial_period_days: 0, - promotion_code: "promo123", - ) + .with(expected_subscription_params) .returns(status: "active", customer: "cus_1234") expect {