feat: add StripeClient.update_subscription_price

This commit is contained in:
Graham McIntire 2026-03-06 13:50:11 -06:00
parent 3e74ac5897
commit a813f56d1d
No known key found for this signature in database
2 changed files with 74 additions and 0 deletions

View file

@ -152,6 +152,25 @@ defmodule Towerops.Billing.StripeClient do
post("/v1/prices", params)
end
@doc """
Update subscription to use new price.
Fetches subscription to get item ID, then updates with proration_behavior: "none"
so price change takes effect at next billing cycle.
"""
def update_subscription_price(subscription_id, new_price_id) do
with {:ok, sub} <- get_subscription(subscription_id) do
item_id = sub |> get_in(["items", "data"]) |> List.first() |> Map.get("id")
params = %{
items: [%{id: item_id, price: new_price_id}],
proration_behavior: "none"
}
post("/v1/subscriptions/#{subscription_id}", params)
end
end
# HTTP Request Helpers
defp get(path) do

View file

@ -360,4 +360,59 @@ defmodule Towerops.Billing.StripeClientTest do
assert {:error, {:bad_request, "Invalid amount"}} = StripeClient.create_price("invalid")
end
end
describe "update_subscription_price/2" do
test "updates subscription to new price" do
# First stub: get subscription
Req.Test.stub(StripeClient, fn conn ->
if conn.method == "GET" and String.contains?(conn.request_path, "/v1/subscriptions/sub_test") do
conn
|> Plug.Conn.put_resp_content_type("application/json")
|> Plug.Conn.send_resp(
200,
Jason.encode!(%{
"id" => "sub_test",
"items" => %{
"data" => [%{"id" => "si_test123", "price" => %{"id" => "price_old"}}]
}
})
)
else
# Second stub: update subscription
{:ok, body, conn} = Plug.Conn.read_body(conn)
params = URI.decode_query(body)
assert params["items[0][id]"] == "si_test123"
assert params["items[0][price]"] == "price_new123"
assert params["proration_behavior"] == "none"
conn
|> Plug.Conn.put_resp_content_type("application/json")
|> Plug.Conn.send_resp(
200,
Jason.encode!(%{
"id" => "sub_test",
"items" => %{"data" => [%{"price" => %{"id" => "price_new123"}}]}
})
)
end
end)
assert {:ok, %{"id" => "sub_test"}} =
StripeClient.update_subscription_price("sub_test", "price_new123")
end
test "returns error when subscription not found" do
Req.Test.stub(StripeClient, fn conn ->
conn
|> Plug.Conn.put_resp_content_type("application/json")
|> Plug.Conn.send_resp(
404,
Jason.encode!(%{"error" => %{"message" => "No such subscription"}})
)
end)
assert {:error, _} = StripeClient.update_subscription_price("sub_invalid", "price_new")
end
end
end