-
Notifications
You must be signed in to change notification settings - Fork 356
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add support for Subscription Schedules (#480)
- Loading branch information
1 parent
28997ea
commit 43e3a39
Showing
4 changed files
with
358 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
defmodule Stripe.SubscriptionSchedule do | ||
@moduledoc """ | ||
Work with Stripe subscription schedule objects. | ||
""" | ||
|
||
use Stripe.Entity | ||
import Stripe.Request | ||
|
||
@type plans :: %{ | ||
plan: String.t(), | ||
quantity: pos_integer | ||
} | ||
|
||
@type phases :: %{ | ||
application_fee_percent: float | nil, | ||
end_date: Stripe.timestamp(), | ||
start_date: Stripe.timestamp(), | ||
tax_percent: float | nil, | ||
trial_end: Stripe.timestamp(), | ||
plans: list(plans) | ||
} | ||
|
||
@type t :: %__MODULE__{ | ||
id: Stripe.id(), | ||
object: String.t(), | ||
created: Stripe.timestamp(), | ||
canceled_at: Stripe.timestamp() | nil, | ||
released_at: Stripe.timestamp() | nil, | ||
completed_at: Stripe.timestamp() | nil, | ||
livemode: boolean, | ||
invoice_settings: %{ | ||
days_until_due: integer | ||
}, | ||
current_phase: %{ | ||
start_date: Stripe.timestamp(), | ||
end_date: Stripe.timestamp() | ||
}, | ||
renewal_behavior: String.t(), | ||
renewal_interval: String.t(), | ||
revision: String.t(), | ||
status: String.t(), | ||
subscription: Stripe.id() | Stripe.Subscription.t(), | ||
customer: Stripe.id() | Stripe.Customer.t(), | ||
released_subscription: Stripe.id() | Stripe.Subscription.t() | nil, | ||
phases: list(phases) | ||
} | ||
|
||
defstruct [ | ||
:id, | ||
:object, | ||
:billing, | ||
:created, | ||
:canceled_at, | ||
:completed_at, | ||
:current_phase, | ||
:customer, | ||
:phases, | ||
:released_at, | ||
:released_subscription, | ||
:status, | ||
:subscription, | ||
:invoice_settings, | ||
:livemode, | ||
:renewal_behavior, | ||
:renewal_interval, | ||
:revision | ||
] | ||
|
||
@plural_endpoint "subscription_schedules" | ||
|
||
@doc """ | ||
Create a subscription schedule. | ||
### Examples | ||
# Create subscription schedule from existing subscription | ||
Stripe.SubscriptionSchedule.create(%{from_subscription: "sub_1234"}) | ||
""" | ||
@spec create(params, Stripe.options()) :: {:ok, t} | {:error, Stripe.Error.t()} | ||
when params: %{ | ||
optional(:customer) => Stripe.id(), | ||
optional(:billing) => String.t(), | ||
optional(:from_subscription) => Stripe.id(), | ||
optional(:invoice_settings) => %{ | ||
optional(:days_until_due) => non_neg_integer | ||
}, | ||
optional(:phases) => [ | ||
%{ | ||
:plans => [ | ||
%{ | ||
:plan => Stripe.id() | Stripe.Plan.t(), | ||
optional(:quantity) => non_neg_integer | ||
} | ||
], | ||
optional(:application_fee_percent) => non_neg_integer, | ||
optional(:coupon) => String.t(), | ||
optional(:end_date) => Stripe.timestamp(), | ||
optional(:iterations) => non_neg_integer, | ||
optional(:start_date) => Stripe.timestamp(), | ||
optional(:tax_percent) => float, | ||
optional(:trial) => boolean(), | ||
optional(:trial_end) => Stripe.timestamp() | ||
} | ||
], | ||
optional(:renewal_behavior) => String.t(), | ||
optional(:renewal_interval) => %{ | ||
:renewal_interval => String.t(), | ||
:length => non_neg_integer | ||
}, | ||
optional(:start_date) => Stripe.timestamp() | ||
} | ||
def create(params, opts \\ []) do | ||
new_request(opts) | ||
|> put_endpoint(@plural_endpoint) | ||
|> put_params(params) | ||
|> put_method(:post) | ||
|> make_request() | ||
end | ||
|
||
@doc """ | ||
Retrieve a subscription schedule. | ||
""" | ||
@spec retrieve(Stripe.id() | t, Stripe.options()) :: {:ok, t} | {:error, Stripe.Error.t()} | ||
def retrieve(id, opts \\ []) do | ||
new_request(opts) | ||
|> put_endpoint(@plural_endpoint <> "/#{get_id!(id)}") | ||
|> put_method(:get) | ||
|> make_request() | ||
end | ||
|
||
@doc """ | ||
Update a subscription schedule. | ||
Takes the `id` and a map of changes. | ||
""" | ||
@spec update(Stripe.id() | t, params, Stripe.options()) :: {:ok, t} | {:error, Stripe.Error.t()} | ||
when params: %{ | ||
optional(:billing) => String.t(), | ||
optional(:invoice_settings) => %{ | ||
optional(:days_until_due) => non_neg_integer | ||
}, | ||
optional(:phases) => [ | ||
%{ | ||
:plans => [ | ||
%{ | ||
:plan => Stripe.id() | Stripe.Plan.t(), | ||
optional(:quantity) => non_neg_integer | ||
} | ||
], | ||
optional(:application_fee_percent) => non_neg_integer, | ||
optional(:coupon) => String.t(), | ||
optional(:end_date) => Stripe.timestamp(), | ||
optional(:iterations) => non_neg_integer, | ||
optional(:start_date) => Stripe.timestamp(), | ||
optional(:tax_percent) => float, | ||
optional(:trial) => boolean(), | ||
optional(:trial_end) => Stripe.timestamp() | ||
} | ||
], | ||
optional(:renewal_behavior) => String.t(), | ||
optional(:prorate) => boolean(), | ||
optional(:renewal_interval) => %{ | ||
:renewal_interval => String.t(), | ||
:length => non_neg_integer | ||
} | ||
} | ||
def update(id, params, opts \\ []) do | ||
new_request(opts) | ||
|> put_endpoint(@plural_endpoint <> "/#{get_id!(id)}") | ||
|> put_method(:post) | ||
|> put_params(params) | ||
|> make_request() | ||
end | ||
|
||
@doc """ | ||
Cancels a subscription schedule and its associated subscription immediately | ||
(if the subscription schedule has an active subscription). A subscription | ||
schedule can only be canceled if its status is not_started or active. | ||
Takes the subscription schedule `id`. | ||
""" | ||
|
||
@spec cancel(Stripe.id() | t, Stripe.options()) :: {:ok, t} | {:error, Stripe.Error.t()} | ||
def cancel(id, opts \\ []) when is_list(opts) do | ||
new_request(opts) | ||
|> put_endpoint(@plural_endpoint <> "/#{get_id!(id)}/cancel") | ||
|> put_method(:post) | ||
|> make_request() | ||
end | ||
|
||
@doc """ | ||
Releases a subscription schedule | ||
Takes the subscription schedule `id. | ||
""" | ||
|
||
@spec release(Stripe.id() | t, Stripe.options()) :: {:ok, t} | {:error, Stripe.Error.t()} | ||
def release(id, opts \\ []) when is_list(opts) do | ||
new_request(opts) | ||
|> put_endpoint(@plural_endpoint <> "/#{get_id!(id)}/release") | ||
|> put_method(:post) | ||
|> make_request() | ||
end | ||
|
||
@doc """ | ||
Retrieves the list of your subscription schedules. | ||
""" | ||
@spec list(params, Stripe.options()) :: {:ok, Stripe.List.t(t)} | {:error, Stripe.Error.t()} | ||
when params: %{ | ||
optional(:canceled_at) => Stripe.date_query(), | ||
optional(:completed_at) => Stripe.date_query(), | ||
optional(:created) => Stripe.date_query(), | ||
optional(:customer) => Stripe.Customer.t() | Stripe.id(), | ||
optional(:ending_before) => t | Stripe.id(), | ||
optional(:limit) => 1..100, | ||
optional(:released_at) => Stripe.date_query(), | ||
optional(:scheduled) => boolean(), | ||
optional(:starting_after) => t | Stripe.id() | ||
} | ||
def list(params \\ %{}, opts \\ []) do | ||
new_request(opts) | ||
|> prefix_expansions() | ||
|> put_endpoint(@plural_endpoint) | ||
|> put_method(:get) | ||
|> put_params(params) | ||
|> make_request() | ||
end | ||
end |
126 changes: 126 additions & 0 deletions
126
test/stripe/subscriptions/subscription_schedule_test.exs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
defmodule Stripe.SubscriptionScheduleTest do | ||
use Stripe.StripeCase, async: true | ||
|
||
@invalid_params %{ | ||
customer: "cus_123", | ||
renewal_behavior: "release", | ||
phases: [ | ||
%{ | ||
coupon: nil, | ||
default_tax_rates: [], | ||
end_date: 1_557_566_037, | ||
start_date: 1_554_974_037, | ||
tax_percent: 0 | ||
} | ||
] | ||
} | ||
describe "retrieve/2" do | ||
test "retrieves a subscription" do | ||
assert {:ok, %Stripe.SubscriptionSchedule{}} = | ||
Stripe.SubscriptionSchedule.retrieve("sub_sched_123") | ||
|
||
assert_stripe_requested(:get, "/v1/subscription_schedules/sub_sched_123") | ||
end | ||
end | ||
|
||
describe "create/2" do | ||
test "creates a subscription schedule" do | ||
params = %{ | ||
customer: "cus_123", | ||
renewal_behavior: "release", | ||
phases: [ | ||
%{ | ||
coupon: nil, | ||
default_tax_rates: [], | ||
end_date: 1_557_566_037, | ||
plans: [ | ||
%{ | ||
billing_thresholds: nil, | ||
plan: "some plan", | ||
quantity: 2, | ||
tax_rates: [] | ||
} | ||
], | ||
start_date: 1_554_974_037, | ||
tax_percent: 0 | ||
} | ||
] | ||
} | ||
|
||
assert {:ok, %Stripe.SubscriptionSchedule{}} = Stripe.SubscriptionSchedule.create(params) | ||
|
||
assert_stripe_requested(:post, "/v1/subscription_schedules") | ||
end | ||
|
||
test "fails with missing plans in phases" do | ||
assert {:error, %Stripe.Error{} = error} = | ||
Stripe.SubscriptionSchedule.create(@invalid_params) | ||
|
||
assert_stripe_requested(:post, "/v1/subscription_schedules") | ||
end | ||
end | ||
|
||
describe "update/2" do | ||
test "updates a subscription" do | ||
params = %{ | ||
renewal_behavior: "release", | ||
phases: [ | ||
%{ | ||
coupon: nil, | ||
default_tax_rates: [], | ||
end_date: 1_557_566_037, | ||
plans: [ | ||
%{ | ||
billing_thresholds: nil, | ||
plan: "some plan", | ||
quantity: 2, | ||
tax_rates: [] | ||
} | ||
], | ||
start_date: 1_554_974_037, | ||
tax_percent: 0 | ||
} | ||
] | ||
} | ||
|
||
assert {:ok, subscription} = Stripe.SubscriptionSchedule.update("sub_sched_123", params) | ||
assert_stripe_requested(:post, "/v1/subscription_schedules/#{subscription.id}") | ||
end | ||
|
||
test "fails with missing plans in phases" do | ||
assert {:error, %Stripe.Error{}} = | ||
Stripe.SubscriptionSchedule.update("sub_sched_123", @invalid_params) | ||
|
||
assert_stripe_requested(:post, "/v1/subscription_schedules/sub_sched_123") | ||
end | ||
end | ||
|
||
describe "list/2" do | ||
test "lists all subscriptions" do | ||
assert {:ok, %Stripe.List{data: subscriptions}} = Stripe.SubscriptionSchedule.list() | ||
assert_stripe_requested(:get, "/v1/subscription_schedules") | ||
assert is_list(subscriptions) | ||
assert %Stripe.SubscriptionSchedule{} = hd(subscriptions) | ||
end | ||
end | ||
|
||
describe "cancel/2" do | ||
test "cancels a subscription schedule" do | ||
{:ok, subscription} = Stripe.SubscriptionSchedule.retrieve("sub_sched_123") | ||
assert_stripe_requested(:get, "/v1/subscription_schedules/#{subscription.id}") | ||
|
||
assert {:ok, _} = Stripe.SubscriptionSchedule.cancel("sub_sched_123") | ||
assert_stripe_requested(:post, "/v1/subscription_schedules/#{subscription.id}/cancel") | ||
end | ||
end | ||
|
||
describe "release/2" do | ||
test "releases a subscription schedule" do | ||
{:ok, subscription} = Stripe.SubscriptionSchedule.retrieve("sub_sched_123") | ||
assert_stripe_requested(:get, "/v1/subscription_schedules/#{subscription.id}") | ||
|
||
assert {:ok, _} = Stripe.SubscriptionSchedule.release("sub_sched_123") | ||
assert_stripe_requested(:post, "/v1/subscription_schedules/#{subscription.id}/release") | ||
end | ||
end | ||
end |