Skip to content
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
56 changes: 35 additions & 21 deletions lib/sanbase/stripe/stripe_api.ex
Original file line number Diff line number Diff line change
Expand Up @@ -200,10 +200,14 @@ defmodule Sanbase.StripeApi do

def upgrade_downgrade(db_subscription, plan) do
with {:ok, params} <- get_upgrade_downgrade_subscription_params(db_subscription, plan),
{:ok, stripe_subscription} <- retrieve_subscription(db_subscription.stripe_id),
# Remove coupon for free basic API subscription
{:ok, params} <- maybe_remove_coupon(params, db_subscription, plan),
{:ok, stripe_subscription} <- update_subscription(db_subscription.stripe_id, params) do
{:ok, stripe_subscription}
{:ok, params} <- maybe_remove_coupon(params, db_subscription, plan, stripe_subscription),
# Add SAN holder coupon if user has 1000+ SAN and no existing discount
{:ok, params} <- maybe_add_san_coupon(params, db_subscription, stripe_subscription),
{:ok, updated_stripe_subscription} <-
update_subscription(db_subscription.stripe_id, params) do
{:ok, updated_stripe_subscription}
end
end

Expand Down Expand Up @@ -257,24 +261,34 @@ defmodule Sanbase.StripeApi do

# Helpers

defp maybe_remove_coupon(params, db_subscription, _plan) do
with {:ok, stripe_subscription} <- retrieve_subscription(db_subscription.stripe_id) do
percent_off =
get_in(stripe_subscription, [
Access.key!(:discount),
Access.key!(:coupon),
Access.key!(:percent_off)
])

is_basic_plan? = fn plan_id ->
plan_id in Sanbase.Billing.Subscription.ProPlus.basic_api_plans()
end

if is_basic_plan?.(db_subscription.plan_id) and percent_off == 100.0 do
{:ok, Map.put(params, :coupon, nil)}
else
{:ok, params}
end
defp maybe_remove_coupon(params, db_subscription, _plan, stripe_subscription) do
percent_off =
get_in(stripe_subscription, [
Access.key(:discount),
Access.key(:coupon),
Access.key(:percent_off)
])

is_basic_plan? = fn plan_id ->
plan_id in Sanbase.Billing.Subscription.ProPlus.basic_api_plans()
end

if is_basic_plan?.(db_subscription.plan_id) and percent_off == 100.0 do
{:ok, Map.put(params, :coupon, nil)}
else
{:ok, params}
end
end

defp maybe_add_san_coupon(params, db_subscription, stripe_subscription) do
has_discount? = not is_nil(stripe_subscription.discount)
user = db_subscription.user
san_balance = User.san_balance_or_zero(user)

if not has_discount? and san_balance >= 1000 do
{:ok, Map.put(params, :coupon, "SAN_HOLDER_1000")}
else
{:ok, params}
end
end

Expand Down
107 changes: 107 additions & 0 deletions test/sanbase/billing/subscription_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,113 @@ defmodule Sanbase.Billing.SubscriptionTest do
end
end

describe "#update_subscription - SAN discount on upgrade" do
test "applies SAN discount when user has 1000+ SAN and no existing coupon", context do
user = insert(:user, email: "test@example.com", san_balance: Decimal.new(1000))
subscription = insert(:subscription_pro_sanbase, user: user, stripe_id: "sub_test_upgrade")
new_plan = context.plans.plan_pro_sanbase_yearly

stripe_sub_no_discount =
StripeApiTestResponse.retrieve_subscription_resp(stripe_id: "sub_test_upgrade") |> elem(1)

test_pid = self()

with_mocks([
{Stripe.Subscription, [],
[
retrieve: fn _stripe_id, _opts -> {:ok, stripe_sub_no_discount} end,
update: fn _stripe_id, params, _opts ->
send(test_pid, {:update_params, params})
StripeApiTestResponse.update_subscription_resp()
end
]}
]) do
{:ok, _updated} = Subscription.update_subscription(subscription, new_plan)

assert_receive {:update_params, params}
assert params[:coupon] == "SAN_HOLDER_1000"
end
end

test "does not apply coupon when subscription already has a discount", context do
user = insert(:staked_user, email: "test@example.com")
subscription = insert(:subscription_pro_sanbase, user: user, stripe_id: "sub_test_upgrade")
new_plan = context.plans.plan_pro_sanbase_yearly

stripe_sub_with_discount =
StripeApiTestResponse.retrieve_subscription_resp(stripe_id: "sub_test_upgrade")
|> elem(1)
|> Map.put(:discount, %{coupon: %{id: "existing_coupon", percent_off: 10}})

test_pid = self()

with_mocks([
{Stripe.Subscription, [],
[
retrieve: fn _stripe_id, _opts -> {:ok, stripe_sub_with_discount} end,
update: fn _stripe_id, params, _opts ->
send(test_pid, {:update_params, params})
StripeApiTestResponse.update_subscription_resp()
end
]}
]) do
{:ok, _updated} = Subscription.update_subscription(subscription, new_plan)

assert_receive {:update_params, params}
refute Map.has_key?(params, :coupon)
end
end

test "does not apply coupon when user has less than 1000 SAN", context do
user = insert(:user, email: "test@example.com", san_balance: Decimal.new(500))
subscription = insert(:subscription_pro_sanbase, user: user, stripe_id: "sub_test_upgrade")
new_plan = context.plans.plan_pro_sanbase_yearly

stripe_sub_no_discount =
StripeApiTestResponse.retrieve_subscription_resp(stripe_id: "sub_test_upgrade") |> elem(1)

test_pid = self()

with_mocks([
{Stripe.Subscription, [],
[
retrieve: fn _stripe_id, _opts -> {:ok, stripe_sub_no_discount} end,
update: fn _stripe_id, params, _opts ->
send(test_pid, {:update_params, params})
StripeApiTestResponse.update_subscription_resp()
end
]}
]) do
{:ok, _updated} = Subscription.update_subscription(subscription, new_plan)

assert_receive {:update_params, params}
refute Map.has_key?(params, :coupon)
end
end

test "upgrade works for user without SAN balance (no regression)", context do
user = insert(:user, email: "test@example.com")
subscription = insert(:subscription_pro_sanbase, user: user, stripe_id: "sub_test_upgrade")
new_plan = context.plans.plan_pro_sanbase_yearly

stripe_sub_no_discount =
StripeApiTestResponse.retrieve_subscription_resp(stripe_id: "sub_test_upgrade") |> elem(1)

with_mocks([
{Stripe.Subscription, [],
[
retrieve: fn _stripe_id, _opts -> {:ok, stripe_sub_no_discount} end,
update: fn _stripe_id, _params, _opts ->
StripeApiTestResponse.update_subscription_resp()
end
]}
]) do
result = Subscription.update_subscription(subscription, new_plan)
assert {:ok, _updated} = result
end
end
end

describe "#sync_subscription_with_stripe" do
setup(context) do
stripe_base_subscription =
Expand Down