diff --git a/app/controllers/alaveteli_pro/subscriptions_controller.rb b/app/controllers/alaveteli_pro/subscriptions_controller.rb index 794bbe7ead..2334b1beae 100644 --- a/app/controllers/alaveteli_pro/subscriptions_controller.rb +++ b/app/controllers/alaveteli_pro/subscriptions_controller.rb @@ -1,13 +1,11 @@ class AlaveteliPro::SubscriptionsController < AlaveteliPro::BaseController - include AlaveteliPro::StripeNamespace - skip_before_action :html_response, only: [:create, :authorise] skip_before_action :pro_user_authenticated?, only: [:create, :authorise] before_action :authenticate, only: [:create, :authorise] before_action :check_allowed_to_subscribe_to_pro, only: [:create] before_action :prevent_duplicate_submission, only: [:create] - before_action :load_plan, only: [:create] + before_action :load_plan, :load_coupon, only: [:create] before_action :check_has_current_subscription, only: [:index] def index @@ -19,11 +17,6 @@ def index @customer. sources.select { |card| card.id == @customer.default_source }.first end - - if referral_coupon - @discount_code = remove_stripe_namespace(referral_coupon.id) - @discount_terms = referral_coupon.metadata.humanized_terms - end end # TODO: remove reminder of Stripe params once shipped @@ -53,7 +46,7 @@ def create tax_percent: @plan.tax_percent, payment_behavior: 'allow_incomplete' } - attributes[:coupon] = coupon_code if coupon_code? + attributes[:coupon] = @coupon.id if @coupon @subscription = @pro_account.subscriptions.create(attributes) @@ -222,6 +215,10 @@ def load_plan @plan || redirect_to(pro_plans_path) end + def load_coupon + @coupon = AlaveteliPro::Coupon.retrieve(params[:coupon_code]) + end + def prevent_duplicate_submission # TODO: This doesn't take the plan in to account return unless @user.pro_account.try(:subscription?) diff --git a/app/models/alaveteli_pro/coupon.rb b/app/models/alaveteli_pro/coupon.rb new file mode 100644 index 0000000000..e6a0dacfee --- /dev/null +++ b/app/models/alaveteli_pro/coupon.rb @@ -0,0 +1,26 @@ +## +# A wrapper for a Stripe::Coupon +# +class AlaveteliPro::Coupon < SimpleDelegator + extend AlaveteliPro::StripeNamespace + include AlaveteliPro::StripeNamespace + + def self.referral + id = add_stripe_namespace(AlaveteliConfiguration.pro_referral_coupon) + retrieve(id) if id + end + + def self.retrieve(id) + new(Stripe::Coupon.retrieve(add_stripe_namespace(id))) + rescue Stripe::InvalidRequestError + nil + end + + def to_param + remove_stripe_namespace(id) + end + + def terms + metadata.humanized_terms || name + end +end diff --git a/app/views/alaveteli_pro/subscriptions/index.html.erb b/app/views/alaveteli_pro/subscriptions/index.html.erb index e6c434d774..21a382f72c 100644 --- a/app/views/alaveteli_pro/subscriptions/index.html.erb +++ b/app/views/alaveteli_pro/subscriptions/index.html.erb @@ -42,14 +42,14 @@ <% end %> - <% if @discount_code && @discount_terms %> + <% if @referral = AlaveteliPro::Coupon.referral %>

<%= _('Refer a friend') %>

<%= _('Share the code {{discount_code}} with a friend to give ' \ 'them {{discount_terms}} on signup.', - discount_code: content_tag(:strong, @discount_code), - discount_terms: @discount_terms) %> + discount_code: content_tag(:strong, @referral.to_param), + discount_terms: @referral.terms) %>

<% end %> diff --git a/spec/controllers/alaveteli_pro/subscriptions_controller_spec.rb b/spec/controllers/alaveteli_pro/subscriptions_controller_spec.rb index 9c1b51a6ed..dad74b45bb 100644 --- a/spec/controllers/alaveteli_pro/subscriptions_controller_spec.rb +++ b/spec/controllers/alaveteli_pro/subscriptions_controller_spec.rb @@ -16,7 +16,7 @@ before do stripe_helper.create_coupon( - id: 'COUPON_CODE', + id: 'coupon_code', amount_off: 1000, currency: 'gbp' ) @@ -156,7 +156,7 @@ include_examples 'successful example' it 'uses coupon code' do - expect(assigns(:subscription).discount.coupon.id).to eq('COUPON_CODE') + expect(assigns(:subscription).discount.coupon.id).to eq('coupon_code') end end @@ -172,7 +172,7 @@ and_return('alaveteli') stripe_helper.create_coupon( - id: 'ALAVETELI-COUPON_CODE', + id: 'alaveteli-coupon_code', amount_off: 1000, currency: 'gbp' ) @@ -187,8 +187,8 @@ include_examples 'successful example' it 'uses namespaced coupon code' do - expect(assigns(:subscription).discount.coupon.id).to eq( - 'ALAVETELI-COUPON_CODE') + expect(assigns(:subscription).discount.coupon.id). + to eq('alaveteli-coupon_code') end end @@ -777,54 +777,6 @@ get :index expect(assigns[:card].id).to eq(customer.default_source) end - - context 'if a PRO_REFERRAL_COUPON is blank' do - it 'does not assign the discount code' do - get :index - expect(assigns[:discount_code]).to be_nil - end - - it 'does not assign the discount terms' do - get :index - expect(assigns[:discount_terms]).to be_nil - end - end - - context 'if a PRO_REFERRAL_COUPON is set' do - before do - allow(AlaveteliConfiguration). - to receive(:pro_referral_coupon).and_return('PROREFERRAL') - allow(AlaveteliConfiguration). - to receive(:stripe_namespace).and_return('ALAVETELI') - end - - let!(:coupon) do - stripe_helper.create_coupon( - percent_off: 50, - duration: 'repeating', - duration_in_months: 1, - id: 'ALAVETELI-PROREFERRAL', - metadata: { humanized_terms: '50% off for 1 month' } - ) - end - - it 'assigns the discount code, stripping the stripe namespace' do - get :index - expect(assigns[:discount_code]).to eq('PROREFERRAL') - end - - it 'assigns the discount terms' do - get :index - expect(assigns[:discount_terms]).to eq('50% off for 1 month') - end - - it 'rescues from any stripe error' do - error = Stripe::InvalidRequestError.new('Coupon expired', 'param') - StripeMock.prepare_error(error, :get_coupon) - get :index - expect(assigns[:discount_code]).to be_nil - end - end end end diff --git a/spec/models/alaveteli_pro/coupon_spec.rb b/spec/models/alaveteli_pro/coupon_spec.rb new file mode 100644 index 0000000000..658b6ae4df --- /dev/null +++ b/spec/models/alaveteli_pro/coupon_spec.rb @@ -0,0 +1,81 @@ +require 'spec_helper' + +RSpec.describe AlaveteliPro::Coupon do + describe '.referral' do + before { described_class.instance_variable_set(:@referral, nil) } + + it 'returns a coupon for the referral configuration' do + stripe_coupon = double('Stripe::Coupon') + allow(AlaveteliConfiguration).to receive(:pro_referral_coupon). + and_return('REFERRAL') + allow(described_class).to receive(:add_stripe_namespace). + and_return('namespace_REFERRAL') + allow(Stripe::Coupon).to receive(:retrieve).with('namespace_REFERRAL'). + and_return(stripe_coupon) + + expect(described_class.referral).to be_an_instance_of(described_class) + expect(described_class.referral).to eq(stripe_coupon) + end + + it 'returns nil if no referral coupon is configured' do + allow(AlaveteliConfiguration).to receive(:pro_referral_coupon). + and_return(nil) + + expect(described_class.referral).to be_nil + end + end + + describe '.retrieve' do + it 'retrieves a Stripe coupon and wraps it' do + stripe_coupon = double('Stripe::Coupon') + allow(Stripe::Coupon).to receive(:retrieve).and_return(stripe_coupon) + allow(described_class).to receive(:add_stripe_namespace). + and_return('namespace_ID') + + coupon = described_class.retrieve('ID') + expect(coupon).to be_a(described_class) + expect(coupon.__getobj__).to eq(stripe_coupon) + end + + it 'returns nil if the coupon is not found' do + allow(Stripe::Coupon).to receive(:retrieve). + and_raise(Stripe::InvalidRequestError.new('', '')) + + expect(described_class.retrieve('NONEXISTENT')).to be_nil + end + end + + describe '#to_param' do + it 'removes the stripe namespace from the id' do + coupon = described_class.new(double('Stripe::Coupon', id: 'namespace_ID')) + allow(coupon).to receive(:remove_stripe_namespace).and_return('ID') + + expect(coupon.to_param).to eq('ID') + end + end + + describe '#terms' do + it 'returns humanized terms from metadata if present' do + coupon = described_class.new( + double( + 'Stripe::Coupon', + metadata: double(humanized_terms: 'Human readable terms') + ) + ) + + expect(coupon.terms).to eq('Human readable terms') + end + + it 'returns name if humanized terms are not present' do + coupon = described_class.new( + double( + 'Stripe::Coupon', + metadata: double(humanized_terms: nil), + name: 'Coupon Name' + ) + ) + + expect(coupon.terms).to eq('Coupon Name') + end + end +end