Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stripe prices #8424

Open
wants to merge 4 commits into
base: stripe-prices-refactor
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions app/controllers/alaveteli_pro/plans_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ class AlaveteliPro::PlansController < AlaveteliPro::BaseController
before_action :authenticate, :check_has_current_subscription, only: [:show]

def index
@plans = AlaveteliPro::Plan.list
@prices = AlaveteliPro::Price.list
@pro_site_name = pro_site_name
end

def show
@plan = AlaveteliPro::Plan.retrieve(params[:id])
@plan || raise(ActiveRecord::RecordNotFound)
@price = AlaveteliPro::Price.retrieve(params[:id])
@price || raise(ActiveRecord::RecordNotFound)
end

private
Expand Down
18 changes: 9 additions & 9 deletions app/controllers/alaveteli_pro/subscriptions_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class AlaveteliPro::SubscriptionsController < AlaveteliPro::BaseController

before_action :check_allowed_to_subscribe_to_pro, only: [:create]
before_action :prevent_duplicate_submission, only: [:create]
before_action :load_plan, :load_coupon, only: [:create]
before_action :load_price, :load_coupon, only: [:create]

def index
@customer = current_user.pro_account.try(:stripe_customer)
Expand All @@ -28,8 +28,8 @@ def create
@pro_account.update_stripe_customer

attributes = {
plan: @plan.id,
tax_percent: @plan.tax_percent,
items: [{ price: @price.id }],
tax_percent: @price.tax_percent,
payment_behavior: 'allow_incomplete'
}
attributes[:coupon] = @coupon.id if @coupon
Expand Down Expand Up @@ -62,7 +62,7 @@ def create
end

if flash[:error]
json_redirect_to plan_path(@plan)
json_redirect_to plan_path(@price)
else
redirect_to authorise_subscription_path(@subscription.id)
end
Expand All @@ -89,7 +89,7 @@ def authorise
flash[:error] = _('There was a problem authorising your payment. You ' \
'have not been charged. Please try again.')

json_redirect_to plan_path(@subscription.plan)
json_redirect_to plan_path(@subscription.price)

elsif @subscription.active?
current_user.add_role(:pro)
Expand Down Expand Up @@ -168,16 +168,16 @@ def check_allowed_to_subscribe_to_pro
end

def check_has_current_subscription
# TODO: This doesn't take the plan in to account
# TODO: This doesn't take the price in to account
return if @user.pro_account.try(:subscription?)

flash[:notice] = _("You don't currently have a Pro subscription")
redirect_to pro_plans_path
end

def load_plan
@plan = AlaveteliPro::Plan.retrieve(params[:plan_id])
@plan || redirect_to(pro_plans_path)
def load_price
@price = AlaveteliPro::Price.retrieve(params[:price_id])
@price || redirect_to(pro_plans_path)
end

def load_coupon
Expand Down
45 changes: 0 additions & 45 deletions app/helpers/alaveteli_pro/plan_helper.rb

This file was deleted.

45 changes: 45 additions & 0 deletions app/helpers/alaveteli_pro/price_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
##
# Helper methods for formatting and displaying billing and price information
# in the Alaveteli Pro interface
#
module AlaveteliPro::PriceHelper
def billing_frequency(price)
if interval(price) == 'day' && interval_count(price) == 1
_('Billed: Daily')
elsif interval(price) == 'week' && interval_count(price) == 1
_('Billed: Weekly')
elsif interval(price) == 'month' && interval_count(price) == 1
_('Billed: Monthly')
elsif interval(price) == 'year' && interval_count(price) == 1
_('Billed: Annually')
else
_('Billed: every {{interval}}', interval: pluralize_interval(price))
end
end

def billing_interval(price)
if interval_count(price) == 1
_('per user, per {{interval}}', interval: interval(price))
else
_('per user, every {{interval}}', interval: pluralize_interval(price))
end
end

private

def pluralize_interval(price)
count = interval_count(price)
interval = interval(price)
return interval if count == 1

pluralize(count, interval)
end

def interval(price)
price.recurring['interval']
end

def interval_count(price)
price.recurring['interval_count']
end
end
32 changes: 0 additions & 32 deletions app/models/alaveteli_pro/plan.rb

This file was deleted.

33 changes: 33 additions & 0 deletions app/models/alaveteli_pro/price.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
##
# A wrapper for a Stripe::Price
#
class AlaveteliPro::Price < SimpleDelegator
include Taxable

tax :unit_amount

def self.list
AlaveteliConfiguration.stripe_prices.map do |(_key, id)|
retrieve(id)
end
end

def self.retrieve(id)
key = AlaveteliConfiguration.stripe_prices.key(id)
new(Stripe::Price.retrieve(key))
rescue Stripe::InvalidRequestError
nil
end

def to_param
AlaveteliConfiguration.stripe_prices[id] || id
end

# product
def product
@product ||= (
product_id = __getobj__.product
Stripe::Product.retrieve(product_id) if product_id
)
end
end
6 changes: 3 additions & 3 deletions app/models/alaveteli_pro/subscription.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ def delete
Stripe::Subscription.cancel(id)
end

# plan
def plan
@plan ||= AlaveteliPro::Plan.new(__getobj__.plan)
# price
def price
@price ||= AlaveteliPro::Price.new(items.first.price)
end

private
Expand Down
8 changes: 4 additions & 4 deletions app/views/alaveteli_pro/plans/_pricing_tiers.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="pricing__tiers">
<% @plans.each do |plan| %>
<% @prices.each do |price| %>
<div class="pricing__tier pricing__tier--primary">
<div class="pricing__tier__heading">
<h2><%= _('Professional') %></h2>
Expand All @@ -10,10 +10,10 @@

<p class="price-label">
<span class="price-label__amount">
<%= format_currency(plan.amount_with_tax, no_cents_if_whole: true) %>
<%= format_currency(price.unit_amount_with_tax, no_cents_if_whole: true) %>
</span>

<%= billing_interval(plan) %>
<%= billing_interval(price) %>
</p>
</div>

Expand All @@ -27,7 +27,7 @@
<li><%= _('Friendly support') %></li>
</ul>

<%= link_to _('Sign up'), plan_path(plan), class: 'button button-pop' %>
<%= link_to _('Sign up'), plan_path(price), class: 'button button-pop' %>
</div>
</div>
<% end %>
Expand Down
8 changes: 4 additions & 4 deletions app/views/alaveteli_pro/plans/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,11 @@
<h3><%= _('Selected plan') %></h3>
<div class="plan-overview">
<div class="plan-overview__desc">
<%= @plan.product.name %>
<%= @price.product.name %>
</div>
<div class="plan-overview__amount">
<%= format_currency(@plan.amount_with_tax) %>
<%= billing_frequency(@plan) %>
<%= format_currency(@price.unit_amount_with_tax) %>
<%= billing_frequency(@price) %>
</div>
</div>
</div>
Expand Down Expand Up @@ -65,7 +65,7 @@
</div>

<div class="settings__section">
<%= hidden_field_tag 'plan_id', @plan.id %>
<%= hidden_field_tag 'price_id', @price.id %>
<%= submit_tag _('Subscribe'), id: 'js-stripe-submit', disabled: true, data: { disable_with: 'Processing...' } %>
<%= link_to _('Cancel'), pro_plans_path, class: 'settings__cancel-button' %>
<p id="card-errors"></p>
Expand Down
6 changes: 3 additions & 3 deletions app/views/alaveteli_pro/subscriptions/_subscription.html.erb
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@

<div class="plan-overview">
<div class="plan-overview__desc">
<%= subscription.plan.product.name %>
<%= subscription.price.product.name %>
</div>

<div class="plan-overview__amount">
<%= format_currency(subscription.plan.amount_with_tax) %>
<%= billing_frequency(subscription.plan) %>
<%= format_currency(subscription.price.unit_amount_with_tax) %>
<%= billing_frequency(subscription.price) %>
<% if subscription.discounted? %>
<br>
<%= _('<strong>{{discounted_amount}}</strong> with discount ' \
Expand Down
21 changes: 21 additions & 0 deletions config/general.yml-example
Original file line number Diff line number Diff line change
Expand Up @@ -1202,6 +1202,27 @@ STRIPE_SECRET_KEY: ''
# ---
STRIPE_NAMESPACE: ''

# List of Stripe Prices which if a user signs up to will grant them access to
# Alaveteli Pro. Rendered on the Pro pricing pages in the order defined here.
#
# STRIPE_PRICES - Hash of objects with key equal to the Stripe Price IDs as the
# value equal to parameterise short human readable string.
#
# Note: Historical Stripe Price IDs listed here should include STRIPE_NAMESPACE.
#
# STRIPE_PRICES = Hash (default: { pro: 'pro' })
#
# Examples:
#
# STRIPE_PRICES:
# ALAVETELI-pro: pro
# price_123: pro-new-price
# price_456: pro-annual-billing
#
# ---
STRIPE_PRICES:
pro: pro

# Stripe.com webhook secret. Only required for Alaveteli Pro.
#
# STRIPE_WEBHOOK_SECRET: - String (default: '')
Expand Down
9 changes: 8 additions & 1 deletion doc/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Highlighted Features

* Migrated from Stripe Plans to Stripe Prices (Graeme Porteous)
* Upgrade Stripe API version (Graeme Porteous)
* Fix script/mailin when multiple EXCEPTION_NOTIFICATIONS_TO addresses are
specified (Graeme Porteous)
Expand Down Expand Up @@ -88,7 +89,6 @@
* Don't show users that have closed their account or been banned on leaderboards
(Chris Mytton)


## Upgrade Notes

* _Required:_ This upgrade requires upgrading Ruby from 3.0 to 3.1 or later.
Expand Down Expand Up @@ -132,6 +132,13 @@
version from `2017-01-27` to `2020-03-02`. No changes should be necessary to
your Stripe account.

* _Optional:_ We have moved from Stripe Plans to Stripe Prices. Previously we
hardcoded the Stripe Plan ID of `pro`, but with changes to the Stripe
dashboard this ID can no longer be created. Migration to the Prices API will
allow for more flexibly, pricing changes, and multiple price points - for
example annual pricing. For new prices you need to configure `STRIPE_PRICES`
in `config/general.yml`.

# 0.44.0.1

## Highlighted Features
Expand Down
9 changes: 8 additions & 1 deletion lib/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ module AlaveteliConfiguration
SMTP_MAILER_PORT: 25,
SMTP_MAILER_USER_NAME: '',
STRIPE_NAMESPACE: '',
STRIPE_PRICES: { pro: 'pro' },
STRIPE_PUBLISHABLE_KEY: '',
STRIPE_SECRET_KEY: '',
STRIPE_TAX_RATE: '0.20',
Expand Down Expand Up @@ -145,7 +146,13 @@ def self.get(key, default)
def self.method_missing(name)
key = name.to_s.upcase
if DEFAULTS.key?(key.to_sym)
get(key, DEFAULTS[key.to_sym])
value = get(key, DEFAULTS[key.to_sym])
case value
when Hash
value.with_indifferent_access
else
value
end
else
super
end
Expand Down
Loading
Loading