Add support for multiplan subscriptions (#466)

* Add support for multiplan subscriptions:
Serialize indexed arrays into hashes with index keys in subscription create, subscription update, and upcoming invoice
Add a SubscriptionItem object that supports creation, deletion, update, listing, and retrieval

* Remove helpers that convert items array to indexed hash
This commit is contained in:
Jacqueline 2016-09-21 17:44:00 -07:00 committed by Kyle Conroy
parent 7637eb48bf
commit d2f783df34
7 changed files with 177 additions and 0 deletions

View File

@ -43,6 +43,7 @@ require 'stripe/recipient'
require 'stripe/bank_account'
require 'stripe/card'
require 'stripe/subscription'
require 'stripe/subscription_item'
require 'stripe/application_fee'
require 'stripe/refund'
require 'stripe/reversal'

View File

@ -0,0 +1,12 @@
module Stripe
class SubscriptionItem < APIResource
extend Stripe::APIOperations::Create
include Stripe::APIOperations::Delete
extend Stripe::APIOperations::List
include Stripe::APIOperations::Save
def self.resource_url
'/v1/subscription_items'
end
end
end

View File

@ -42,6 +42,7 @@ module Stripe
'recipient' => Recipient,
'refund' => Refund,
'subscription' => Subscription,
'subscription_item' => SubscriptionItem,
'file_upload' => FileUpload,
'token' => Token,
'transfer' => Transfer,

View File

@ -44,5 +44,23 @@ module Stripe
i.pay
assert_equal nil, i.next_payment_attempt
end
should "be able to retrieve upcoming invoices" do
base = "#{Stripe.api_base}/v1/invoices/upcoming?"
cus_sub = "customer=c_test_customer&subscription=s_test_subscription&"
item0 = "subscription_items[][plan]=gold&subscription_items[][quantity]=1&"
item1 = "subscription_items[][plan]=silver&subscription_items[][quantity]=2"
@mock.expects(:get).once.with(base + cus_sub + item0 + item1, nil, nil).
returns(make_response(make_invoice(:customer => 'c_test_customer', :subscription => 's_test_subscription')))
i = Stripe::Invoice.upcoming(
:customer => 'c_test_customer',
:subscription => 's_test_subscription',
:subscription_items => [{:plan => 'gold', :quantity =>1}, {:plan => 'silver', :quantity =>2}]
)
assert_equal 'c_test_customer', i.customer
assert_equal 's_test_subscription', i.subscription
end
end
end

View File

@ -0,0 +1,76 @@
require File.expand_path('../../test_helper', __FILE__)
module Stripe
class SubscriptionItemTest < Test::Unit::TestCase
should "subscription items should be retrievable" do
@mock.expects(:get).once.with("#{Stripe.api_base}/v1/subscription_items/si_test_subscription_item", nil, nil).
returns(make_response(make_subscription_item))
sub_item = Stripe::SubscriptionItem.retrieve('si_test_subscription_item')
assert sub_item.kind_of?(Stripe::SubscriptionItem)
end
should "subscription items should be listable" do
@mock.expects(:get).once.with("#{Stripe.api_base}/v1/subscription_items?subscription=s_test_subscription&limit=3", nil, nil).
returns(make_response(make_subscription_item_array))
sub_items = Stripe::SubscriptionItem.list(:subscription => 's_test_subscription', :limit => 3).data
assert sub_items.kind_of? Array
assert sub_items[0].kind_of? Stripe::SubscriptionItem
end
should "subscription items should be deletable" do
@mock.expects(:get).once.returns(make_response(make_subscription_item))
sub_item = Stripe::SubscriptionItem.retrieve('si_test_subscription_item')
@mock.expects(:delete).once.with("#{Stripe.api_base}/v1/subscription_items/#{sub_item.id}", nil, nil).
returns(make_response(make_subscription_item))
sub_item.delete
end
should "subscription items should be updateable" do
sid = 'si_test_subscription_item'
@mock.expects(:post).once.with do |url, api_key, params|
url == "#{Stripe.api_base}/v1/subscription_items/#{sid}" &&
api_key.nil? &&
CGI.parse(params) == {'plan' => ['silver'], 'quantity' => ['3']}
end.returns(make_response(make_subscription_item(:plan => 'silver', :quantity => 3)))
sub_item = Stripe::SubscriptionItem.update(sid, {:plan => 'silver', :quantity => 3})
assert_equal 'silver', sub_item.plan.id
assert_equal 3, sub_item.quantity
end
should "subscription items should be saveable" do
@mock.expects(:get).once.returns(make_response(make_subscription_item))
sub_item = Stripe::SubscriptionItem.retrieve('si_test_subscription_item')
@mock.expects(:post).once.with do |url, api_key, params|
url == "#{Stripe.api_base}/v1/subscription_items/#{sub_item.id}" &&
api_key.nil? &&
CGI.parse(params) == {'plan' => ['silver'], 'quantity' => ['3']}
end.returns(make_response(make_subscription_item(:plan => 'silver', :quantity => 3)))
sub_item.plan = 'silver'
sub_item.quantity = 3
sub_item.save
assert_equal 'silver', sub_item.plan.id
assert_equal 3, sub_item.quantity
end
should "create should return a new subscription item" do
@mock.expects(:post).once.with do |url, api_key, params|
url == "#{Stripe.api_base}/v1/subscription_items" && api_key.nil? &&
CGI.parse(params) == {'plan' => ['silver'], 'quantity' => ['3']}
end.returns(make_response(make_subscription_item(:plan => 'silver', :quantity => 3)))
sub_item = Stripe::SubscriptionItem.create(:plan => 'silver', :quantity => 3)
assert_equal 'silver', sub_item.plan.id
assert_equal 3, sub_item.quantity
end
end
end

View File

@ -87,6 +87,27 @@ module Stripe
assert_equal 'active', sub.status
end
should "subscription items should be updateable" do
sid = 's_test_subscription'
items = {:data => [{:plan => {:id =>'gold'}, :quantity => 1}, {:plan => {:id =>'silver'}, :quantity => 2}]}
@mock.expects(:post).once.with do |url, api_key, params|
url == "#{Stripe.api_base}/v1/subscriptions/#{sid}" &&
api_key.nil? &&
CGI.parse(params) == {
'items[][plan]'=>['gold', 'silver'],
'items[][quantity]'=>['1', '2'],
}
end.returns(make_response(make_subscription(:items => items)))
sub = Stripe::Subscription.update(sid, :items => [{:plan => 'gold', :quantity =>1}, {:plan => 'silver', :quantity =>2}])
assert_equal 'gold', sub.items.data[0].plan.id
assert_equal 1, sub.items.data[0].quantity
assert_equal 'silver', sub.items.data[1].plan.id
assert_equal 2, sub.items.data[1].quantity
end
should "subscriptions should be saveable" do
@mock.expects(:get).once.returns(make_response(make_subscription))
sub = Stripe::Subscription.retrieve('s_test_subscription')
@ -113,6 +134,28 @@ module Stripe
assert_equal 'gold', sub.plan.identifier
end
should "create with items should return a new subscription" do
items = {:data => [{:plan => {:id =>'gold'}, :quantity => 1}, {:plan => {:id =>'silver'}, :quantity => 2}]}
@mock.expects(:post).once.with do |url, api_key, params|
url == "#{Stripe.api_base}/v1/subscriptions" &&
api_key.nil? &&
CGI.parse(params) == {
'customer' => ['c_test_customer'],
'items[][plan]'=>['gold', 'silver'],
'items[][quantity]'=>['1', '2'],
}
end.returns(make_response(make_subscription(:items => items, :id => 'test_new_subscription')))
sub = Stripe::Subscription.create(:customer => 'c_test_customer', :items => [{:plan => 'gold', :quantity =>1}, {:plan => 'silver', :quantity =>2}])
assert_equal 'test_new_subscription', sub.id
assert_equal 'gold', sub.items.data[0].plan.id
assert_equal 1, sub.items.data[0].quantity
assert_equal 'silver', sub.items.data[1].plan.id
assert_equal 2, sub.items.data[1].quantity
end
should "be able to delete a subscriptions's discount" do
@mock.expects(:post).once.returns(make_response(make_subscription))
sub = Stripe::Subscription.create(:plan => 'gold', :customer => 'c_test_customer', coupon: 'forever')

View File

@ -277,6 +277,24 @@ module Stripe
}.merge(params)
end
def make_subscription_item(params = {})
plan = params.delete(:plan) || 'gold'
{
:id => "si_test_subscription_item",
:object => "subscription_item",
:created => 1473875521,
:plan => {
:id => plan,
:object => "plan",
:amount => 1000,
:created => 1468349629,
:currency => "usd",
:interval => "month",
},
:quantity => 1
}.merge(params)
end
def make_refund(params = {})
{
:object => 'refund',
@ -297,6 +315,14 @@ module Stripe
}
end
def make_subscription_item_array
{
:data => [make_subscription_item, make_subscription_item, make_subscription_item],
:object => 'list',
:resource_url => '/v1/subscription_items'
}
end
def make_customer_subscription_array(customer_id)
{
:data => [make_subscription, make_subscription, make_subscription],