Skip to content

Commit

Permalink
Add Pro Coupon referral and retrieve methods
Browse files Browse the repository at this point in the history
Again simplify controllers by moving coupons loading into delegated
classes.
  • Loading branch information
gbp committed Nov 11, 2024
1 parent fe41065 commit 8e6f634
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 65 deletions.
15 changes: 6 additions & 9 deletions app/controllers/alaveteli_pro/subscriptions_controller.rb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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?)
Expand Down
26 changes: 26 additions & 0 deletions app/models/alaveteli_pro/coupon.rb
Original file line number Diff line number Diff line change
@@ -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
6 changes: 3 additions & 3 deletions app/views/alaveteli_pro/subscriptions/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@
<% end %>
</div>

<% if @discount_code && @discount_terms %>
<% if @referral = AlaveteliPro::Coupon.referral %>
<div class="settings-section">
<h3><%= _('Refer a friend') %></h3>
<p class="settings__item">
<%= _('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) %>
</p>
</div>
<% end %>
Expand Down
58 changes: 5 additions & 53 deletions spec/controllers/alaveteli_pro/subscriptions_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

before do
stripe_helper.create_coupon(
id: 'COUPON_CODE',
id: 'coupon_code',
amount_off: 1000,
currency: 'gbp'
)
Expand Down Expand Up @@ -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

Expand All @@ -172,7 +172,7 @@
and_return('alaveteli')

stripe_helper.create_coupon(
id: 'ALAVETELI-COUPON_CODE',
id: 'alaveteli-coupon_code',
amount_off: 1000,
currency: 'gbp'
)
Expand All @@ -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

Expand Down Expand Up @@ -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

Expand Down
81 changes: 81 additions & 0 deletions spec/models/alaveteli_pro/coupon_spec.rb
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 8e6f634

Please sign in to comment.