├── spec
├── fixtures
│ ├── credit_success.xml
│ ├── invalid_update.xml
│ ├── error_string.txt
│ ├── free_plan_not_set.xml
│ ├── invalid_subscriber.xml
│ ├── free_plan_not_elligable.xml
│ ├── free_plan_not_free.xml
│ ├── subscriber_not_found.xml
│ ├── existing_subscriber.xml
│ ├── payment_already_paid.xml
│ ├── payment_invalid.xml
│ ├── no_plans.xml
│ ├── no_transactions.xml
│ ├── payment_not_found.xml
│ ├── invoice_invalid.xml
│ ├── no_subscribers.xml
│ ├── plan_not_found.xml
│ ├── complimentary_failed_active.xml
│ ├── complimentary_failed_inactive.xml
│ ├── plan_disabled.xml
│ ├── error.xml
│ ├── feature_level_blank.xml
│ ├── complimentary_not_valid.xml
│ ├── errors.xml
│ ├── credit_not_valid.xml
│ ├── create_subscriber.xml
│ ├── free_plan_success.xml
│ ├── complimentary_success.xml
│ ├── lifetime_subscription_success.xml
│ ├── invoice_created.xml
│ ├── payment_success.xml
│ ├── transactions.xml
│ ├── subscribers.xml
│ ├── subscription_plan_list.xml
│ └── subscriber.xml
├── spec_helper.rb
├── transaction_spec.rb
├── subscription_plan_spec.rb
├── base_spec.rb
├── config_spec.rb
├── invoice_spec.rb
└── subscriber_spec.rb
├── .gitignore
├── .document
├── lib
├── rspreedly
│ ├── credit.rb
│ ├── line_item.rb
│ ├── lifetime_complimentary_subscription.rb
│ ├── complimentary_time_extension.rb
│ ├── complimentary_subscription.rb
│ ├── payment_method.rb
│ ├── error.rb
│ ├── transaction.rb
│ ├── subscription_plan.rb
│ ├── invoice.rb
│ ├── config.rb
│ ├── base.rb
│ └── subscriber.rb
└── rspreedly.rb
├── LICENSE
├── Rakefile
├── README.rdoc
└── rspreedly.gemspec
/spec/fixtures/credit_success.xml:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sw?
2 | .DS_Store
3 | coverage
4 | rdoc
5 | pkg
6 | .idea
--------------------------------------------------------------------------------
/spec/fixtures/invalid_update.xml:
--------------------------------------------------------------------------------
1 | You must specify subscriber parameters
--------------------------------------------------------------------------------
/spec/fixtures/error_string.txt:
--------------------------------------------------------------------------------
1 | Some error back from the response as a string
--------------------------------------------------------------------------------
/spec/fixtures/free_plan_not_set.xml:
--------------------------------------------------------------------------------
1 | You must specify the subscription plan id
--------------------------------------------------------------------------------
/spec/fixtures/invalid_subscriber.xml:
--------------------------------------------------------------------------------
1 | undefined method `delete' for nil:NilClass
--------------------------------------------------------------------------------
/spec/fixtures/free_plan_not_elligable.xml:
--------------------------------------------------------------------------------
1 | The subscriber is not eligible for a free trial
--------------------------------------------------------------------------------
/spec/fixtures/free_plan_not_free.xml:
--------------------------------------------------------------------------------
1 | The subscription plan must be a free trial plan.
--------------------------------------------------------------------------------
/spec/fixtures/subscriber_not_found.xml:
--------------------------------------------------------------------------------
1 | There is no subscriber with a customer id of 401
--------------------------------------------------------------------------------
/.document:
--------------------------------------------------------------------------------
1 | README.rdoc
2 | lib/**/*.rb
3 | bin/*
4 | features/**/*.feature
5 | LICENSE
6 |
--------------------------------------------------------------------------------
/spec/fixtures/existing_subscriber.xml:
--------------------------------------------------------------------------------
1 | A subscriber with a customer-id of 8889 already exists.
--------------------------------------------------------------------------------
/spec/fixtures/payment_already_paid.xml:
--------------------------------------------------------------------------------
1 | Unable to pay an invoice which has already been closed.
--------------------------------------------------------------------------------
/spec/fixtures/payment_invalid.xml:
--------------------------------------------------------------------------------
1 | You must specify credit card parameters for the payment.
--------------------------------------------------------------------------------
/spec/fixtures/no_plans.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/spec/fixtures/no_transactions.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/spec/fixtures/payment_not_found.xml:
--------------------------------------------------------------------------------
1 | Unable to find invoice "5b1af186651dd988865c6573921ec87fa4bec23b8"
--------------------------------------------------------------------------------
/spec/fixtures/invoice_invalid.xml:
--------------------------------------------------------------------------------
1 | You must specify both the subscription plan id and subscriber information.
--------------------------------------------------------------------------------
/spec/fixtures/no_subscribers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/spec/fixtures/plan_not_found.xml:
--------------------------------------------------------------------------------
1 | There is no subscription plan with an ID of 9 for the site you're accessing
--------------------------------------------------------------------------------
/spec/fixtures/complimentary_failed_active.xml:
--------------------------------------------------------------------------------
1 | Complimentary subscriptions cannot be given to active subscribers.
--------------------------------------------------------------------------------
/lib/rspreedly/credit.rb:
--------------------------------------------------------------------------------
1 | module RSpreedly
2 | class Credit < Base
3 | attr_accessor :amount
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/spec/fixtures/complimentary_failed_inactive.xml:
--------------------------------------------------------------------------------
1 | Complimentary subscriptions cannot be given to inactive subscribers.
--------------------------------------------------------------------------------
/spec/fixtures/plan_disabled.xml:
--------------------------------------------------------------------------------
1 | The subscription plan is temporarily unavailable for purchase. Please try again later.
--------------------------------------------------------------------------------
/spec/fixtures/error.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Email can't be blank
4 |
5 |
--------------------------------------------------------------------------------
/spec/fixtures/feature_level_blank.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Feature level can't be blank
4 |
5 |
--------------------------------------------------------------------------------
/lib/rspreedly/line_item.rb:
--------------------------------------------------------------------------------
1 | module RSpreedly
2 | class LineItem < Base
3 | attr_accessor :price, :notes, :amount, :description, :currency_code
4 | end
5 | end
--------------------------------------------------------------------------------
/spec/fixtures/complimentary_not_valid.xml:
--------------------------------------------------------------------------------
1 | Duration units must be either 'days' or 'months'
2 | Duration quantity can't be blank
3 | Duration quantity is not a number
--------------------------------------------------------------------------------
/lib/rspreedly/lifetime_complimentary_subscription.rb:
--------------------------------------------------------------------------------
1 | module RSpreedly
2 | class LifetimeComplimentarySubscription < Base
3 | attr_accessor :feature_level
4 | end
5 | end
--------------------------------------------------------------------------------
/lib/rspreedly/complimentary_time_extension.rb:
--------------------------------------------------------------------------------
1 | module RSpreedly
2 | class ComplimentaryTimeExtension < Base
3 | attr_accessor :duration_quantity, :duration_units
4 | end
5 | end
--------------------------------------------------------------------------------
/spec/fixtures/errors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Email can't be blank
4 | Name can't be blank
5 |
6 |
--------------------------------------------------------------------------------
/lib/rspreedly/complimentary_subscription.rb:
--------------------------------------------------------------------------------
1 | module RSpreedly
2 | class ComplimentarySubscription < Base
3 | attr_accessor :duration_quantity, :duration_units, :feature_level
4 | end
5 | end
--------------------------------------------------------------------------------
/spec/fixtures/credit_not_valid.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Amount is invalid. The total store credit for a subscriber must be greater than or equal to 0.
4 |
5 |
--------------------------------------------------------------------------------
/lib/rspreedly/payment_method.rb:
--------------------------------------------------------------------------------
1 | module RSpreedly
2 | module PaymentMethod
3 | class CreditCard < Base
4 | attr_accessor :number, :verification_value, :month,
5 | :year, :first_name, :last_name, :card_type,
6 | :address1, :address2, :city, :state, :zip, :country,
7 | :phone_number
8 | end
9 | class OnFile < Base
10 | def to_xml(opts=nil)
11 | " on-file "
12 | end
13 | end
14 | end
15 | end
--------------------------------------------------------------------------------
/lib/rspreedly/error.rb:
--------------------------------------------------------------------------------
1 | module RSpreedly
2 | module Error
3 |
4 | class Base < StandardError;
5 | attr_reader :response
6 |
7 | def initialize(response=nil)
8 | @response = response
9 | end
10 | end
11 |
12 | class AccessDenied < Base; end # 401 errors
13 | class Forbidden < Base; end # 403 errors
14 | class BadRequest < Base; end # 422 errors
15 | class NotFound < Base; end # 404 errors
16 | class GatewayTimeout < Base; end # 504 errors
17 | class ConnectionFailed< Base; end
18 | end
19 | end
20 |
--------------------------------------------------------------------------------
/spec/spec_helper.rb:
--------------------------------------------------------------------------------
1 | $LOAD_PATH.unshift(File.dirname(__FILE__))
2 | $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3 |
4 | require 'rubygems'
5 | require 'rspec'
6 | require 'webmock/rspec'
7 | require 'rspreedly'
8 |
9 | RSpec.configure do |config|
10 | config.before(:each) do
11 | RSpreedly::Config.reset
12 | end
13 | end
14 |
15 | def spreedly_url(path)
16 | "https://your-api-key:X@spreedly.com/api/v4/your-site-name#{path}"
17 | end
18 |
19 | def fixture(file)
20 | response_file = File.join(File.dirname(__FILE__), 'fixtures', file)
21 | File.read(response_file)
22 | end
--------------------------------------------------------------------------------
/lib/rspreedly.rb:
--------------------------------------------------------------------------------
1 | require 'logger'
2 | require 'httparty'
3 |
4 | require 'rspreedly/error'
5 | require 'rspreedly/config'
6 | require 'rspreedly/base'
7 | require 'rspreedly/credit'
8 | require 'rspreedly/invoice'
9 | require 'rspreedly/line_item'
10 | require 'rspreedly/subscriber'
11 | require 'rspreedly/subscription_plan'
12 | require 'rspreedly/payment_method'
13 | require 'rspreedly/complimentary_subscription'
14 | require 'rspreedly/complimentary_time_extension'
15 | require 'rspreedly/lifetime_complimentary_subscription'
16 | require 'rspreedly/transaction'
17 |
18 | module RSpreedly
19 |
20 | # a few utility methods
21 |
22 | # pretty much stolen from Rails
23 | def self.underscore(camel_cased_word)
24 | camel_cased_word.to_s.gsub(/\w+::/, '').
25 | gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
26 | gsub(/([a-z\d])([A-Z])/,'\1_\2').
27 | tr("-", "_").
28 | downcase
29 | end
30 |
31 | end
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2009 Richard Livsey
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/lib/rspreedly/transaction.rb:
--------------------------------------------------------------------------------
1 | module RSpreedly
2 |
3 | class Transaction < Base
4 |
5 | attr_accessor :active,
6 | :amount,
7 | :created_at,
8 | :currency_code,
9 | :description,
10 | :detail_type,
11 | :expires,
12 | :id,
13 | :invoice_id,
14 | :start_time,
15 | :terms,
16 | :updated_at,
17 | :price,
18 | :subscriber_customer_id,
19 | :detail
20 |
21 | class << self
22 |
23 | # Get a list of 50 transactions
24 | # Passing :since => id will get the 50 transactions since that one
25 | # GET /api/v4/[short site name]/transactions.xml
26 | def all(opts={})
27 | query_opts = {}
28 | if opts[:since]
29 | query_opts[:query] = {:since_id => opts[:since]}
30 | end
31 |
32 | response = api_request(:get, "/transactions.xml", query_opts)
33 | return [] unless response.has_key?("transactions")
34 | response["transactions"].collect{|data| Transaction.new(data)}
35 | end
36 |
37 | end
38 | end
39 | end
--------------------------------------------------------------------------------
/spec/transaction_spec.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2 | describe RSpreedly::Transaction do
3 |
4 | describe ".all" do
5 | it "should return an empty array if there are no transactions" do
6 | stub_request(:get, spreedly_url("/transactions.xml")).
7 | to_return(:body => fixture("no_transactions.xml"), :status => 200)
8 |
9 | RSpreedly::Transaction.all.should == []
10 | end
11 |
12 | it "should return an array of transactions if there are any to find" do
13 | stub_request(:get, spreedly_url("/transactions.xml")).
14 | to_return(:body => fixture("transactions.xml"), :status => 200)
15 |
16 | RSpreedly::Transaction.all.size.should == 3 # there are 3 in the fixture
17 | RSpreedly::Transaction.all.select{|x| x.is_a?(RSpreedly::Transaction )}.size.should == 3
18 | end
19 |
20 | it "should allow specifying the ID of the transaction to start (since_id)" do
21 | stub_request(:get, spreedly_url("/transactions.xml?since_id=123")).
22 | to_return(:body => fixture("transactions.xml"), :status => 200)
23 |
24 | RSpreedly::Transaction.all(:since => 123)
25 | WebMock.should have_requested(:get, spreedly_url("/transactions.xml?since_id=123"))
26 | end
27 | end
28 | end
--------------------------------------------------------------------------------
/spec/fixtures/create_subscriber.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 2009-10-28T22:16:20Z
7 | 8889
8 | true
9 |
10 |
11 | false
12 | false
13 | freddy
14 | 0.0
15 | USD
16 | 6af9994a57e420345897b1abb4c27a9db27fa4d0
17 | 2009-10-28T22:16:20Z
18 | false
19 | false
20 |
21 | false
22 |
23 | false
24 |
25 |
26 |
--------------------------------------------------------------------------------
/spec/fixtures/free_plan_success.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 2009-10-28T22:27:38Z
7 | 42
8 | true
9 | new@email.com
10 | 2013-05-02T00:07:37Z
11 | false
12 | false
13 | bob
14 | 0.0
15 | USD
16 | 81ba778a5849f461ebc0d77e78b9221af2210c6b
17 | 2009-10-28T22:55:37Z
18 | false
19 | false
20 |
21 | false
22 |
23 | true
24 |
25 |
26 |
--------------------------------------------------------------------------------
/spec/fixtures/complimentary_success.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 2010-02-21T19:04:28Z
4 |
5 |
6 | 2009-10-28T22:27:38Z
7 | 42
8 | true
9 | new@email.com
10 | 2013-05-02T00:07:37Z
11 | false
12 | false
13 | bob
14 | 0.0
15 | USD
16 | 81ba778a5849f461ebc0d77e78b9221af2210c6b
17 | 2009-10-28T22:55:37Z
18 | false
19 | false
20 |
21 | false
22 |
23 | false
24 | Pro
25 |
26 |
--------------------------------------------------------------------------------
/spec/fixtures/lifetime_subscription_success.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 2010-02-21T19:04:28Z
4 |
5 |
6 | 2009-10-28T22:27:38Z
7 | 42
8 | true
9 | new@email.com
10 | 2013-05-02T00:07:37Z
11 | true
12 | false
13 | bob
14 | 0.0
15 | USD
16 | 81ba778a5849f461ebc0d77e78b9221af2210c6b
17 | 2009-10-28T22:55:37Z
18 | false
19 | false
20 |
21 | false
22 |
23 | false
24 | Something
25 |
26 |
--------------------------------------------------------------------------------
/lib/rspreedly/subscription_plan.rb:
--------------------------------------------------------------------------------
1 | module RSpreedly
2 |
3 | class SubscriptionPlan < Base
4 |
5 | attr_accessor :amount,
6 | :charge_after_first_period,
7 | :charge_later_duration_quantity,
8 | :charge_later_duration_units,
9 | :created_at,
10 | :currency_code,
11 | :description,
12 | :duration_quantity,
13 | :duration_units,
14 | :enabled,
15 | :feature_level,
16 | :force_recurring,
17 | :id,
18 | :name,
19 | :needs_to_be_renewed,
20 | :plan_type,
21 | :price,
22 | :return_url,
23 | :terms,
24 | :updated_at
25 |
26 | # there's no API method for just getting one plan, so we fake it!
27 | def self.find(id)
28 | return all if id == :all
29 | all.find{|plan| plan.id == id.to_i}
30 | end
31 |
32 | # Get a list of all subscription plans (more)
33 | # GET /api/v4/[short site name]/subscription_plans.xml
34 | def self.all
35 | response = api_request(:get, "/subscription_plans.xml")
36 | return [] unless response.has_key?("subscription_plans")
37 | response["subscription_plans"].collect{|data| SubscriptionPlan.new(data)}
38 | end
39 |
40 | # Get a list of all active subscription plans (more)
41 | # GET /api/v4/[short site name]/subscription_plans.xml
42 | def self.active
43 | all.reject { |plan| !plan.enabled }
44 | end
45 |
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/lib/rspreedly/invoice.rb:
--------------------------------------------------------------------------------
1 | module RSpreedly
2 |
3 | class Invoice < Base
4 |
5 | attr_accessor :subscription_plan_id,
6 | :subscriber,
7 | :closed,
8 | :created_at,
9 | :token,
10 | :updated_at,
11 | :price,
12 | :amount,
13 | :currency_code,
14 | :line_items
15 |
16 | # Create an Invoice (more)
17 | # POST /api/v4/[short site name]/invoices.xml
18 | def create
19 | begin
20 | create!
21 | rescue RSpreedly::Error::Base
22 | nil
23 | end
24 | end
25 |
26 | def create!
27 | result = api_request(:post, "/invoices.xml", :body => self.to_xml)
28 | self.attributes = result["invoice"]
29 | true
30 | end
31 |
32 | alias_method :save, :create
33 | alias_method :save!, :create!
34 |
35 | def subscriber=(data)
36 | if data.is_a? Hash
37 | data = RSpreedly::Subscriber.new(data)
38 | end
39 | @subscriber = data
40 | end
41 |
42 | def line_items=(data)
43 | @line_items = []
44 | data.each do |item|
45 | if item.is_a? Hash
46 | item = RSpreedly::LineItem.new(item)
47 | end
48 | @line_items << item
49 | end
50 | end
51 |
52 | # Pay an Invoice (more)
53 | # PUT /api/v4/[short site name]/invoices/[invoice token]/pay.xml
54 | def pay(payment)
55 | begin
56 | pay!(payment)
57 | rescue RSpreedly::Error::Base
58 | nil
59 | end
60 | end
61 |
62 | def pay!(payment)
63 | result = api_request(:put, "/invoices/#{self.token}/pay.xml", :body => payment.to_xml(:outer => 'payment'))
64 | self.attributes = result["invoice"]
65 | true
66 | end
67 |
68 | end
69 | end
--------------------------------------------------------------------------------
/spec/fixtures/invoice_created.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 | 2009-10-29T02:04:10Z
5 | d06df8274ba261c203f62f7a64c2b18d6dff588e
6 | 2009-10-29T02:04:10Z
7 | $0.00
8 | 0.0
9 | USD
10 |
11 |
12 |
13 |
14 | 2009-10-29T02:04:10Z
15 | 44
16 | true
17 | joe@example.com
18 |
19 | false
20 | false
21 | joe
22 | 0.0
23 | USD
24 | 784d2d4199d6de062a27c2b84f0ad71aad1e14f1
25 | 2009-10-29T02:04:10Z
26 | false
27 | false
28 |
29 | false
30 |
31 | false
32 |
33 |
34 |
35 |
36 | 0.0
37 | USD
38 | Initial 1 month
39 | Subsequent Time Periods are $19.00 every 1 month
40 | $0.00
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/spec/fixtures/payment_success.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 | 2009-10-29T02:34:14Z
5 | 5b1f186651dd988865c6573921ec87fa4bec23b8
6 | 2009-10-29T02:42:47Z
7 | $0.00
8 | 0.0
9 | USD
10 |
11 | 2009-11-29T02:42:47Z
12 | Joe
13 | Bob
14 | 2009-10-29T02:34:14Z
15 | 400
16 | false
17 | test@example.com
18 | 2009-12-02T02:42:47Z
19 | false
20 | false
21 | bob
22 | 0.0
23 | USD
24 | c385a23a76d85400371d82cb24f383759cb28721
25 | 2009-10-29T02:42:47Z
26 | true
27 | false
28 | Personal
29 | true
30 | false
31 | false
32 | personal
33 |
34 |
35 |
36 | 0.0
37 | USD
38 | Initial 1 month
39 | Subsequent Time Periods are $19.00 every 1 month
40 | $0.00
41 |
42 |
43 |
--------------------------------------------------------------------------------
/lib/rspreedly/config.rb:
--------------------------------------------------------------------------------
1 | require 'pp'
2 | module RSpreedly
3 | module Config
4 | class << self
5 |
6 | # the configuration hash itself
7 | def configuration
8 | @configuration ||= defaults
9 | end
10 |
11 | def defaults
12 | {
13 | :logger => defined?(RAILS_DEFAULT_LOGGER) ? RAILS_DEFAULT_LOGGER : Logger.new(STDOUT),
14 | :debug => false,
15 | :site_name => "your-site-name",
16 | :api_key => "your-api-key"
17 | }
18 | end
19 |
20 | def [](key)
21 | configuration[key]
22 | end
23 |
24 | def []=(key, val)
25 | configuration[key] = val
26 | end
27 |
28 | # remove an item from the configuration
29 | def delete(key)
30 | configuration.delete(key)
31 | end
32 |
33 | # Return the value of the key, or the default if doesn't exist
34 | #
35 | # ==== Examples
36 | #
37 | # RSpreedly::Config.fetch(:monkey, false)
38 | # => false
39 | #
40 | def fetch(key, default)
41 | configuration.fetch(key, default)
42 | end
43 |
44 | def to_hash
45 | configuration
46 | end
47 |
48 | # Yields the configuration.
49 | #
50 | # ==== Examples
51 | # RSpreedly::Config.use do |config|
52 | # config[:debug] = true
53 | # config.something = false
54 | # end
55 | #
56 | def setup
57 | yield self
58 | nil
59 | end
60 |
61 | def clear
62 | @configuration = {}
63 | end
64 |
65 | def reset
66 | @configuration = defaults
67 | end
68 |
69 | # allow getting and setting properties via RSpreedly::Config.xxx
70 | #
71 | # ==== Examples
72 | # RSpreedly::Config.debug
73 | # RSpreedly::Config.debug = false
74 | #
75 | def method_missing(method, *args)
76 | if method.to_s[-1,1] == '='
77 | # splat with 1 item is just the item in 1.8, but an array in 1.9
78 | configuration[method.to_s.tr('=','').to_sym] = args.is_a?(Array) ? args.first : args
79 | else
80 | configuration[method]
81 | end
82 | end
83 |
84 | end
85 | end
86 | end
--------------------------------------------------------------------------------
/spec/subscription_plan_spec.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2 |
3 | describe RSpreedly::SubscriptionPlan do
4 |
5 | describe ".find" do
6 | describe "with the symbol :all" do
7 | it "should pass to RSpreedly::SubscriptionPlan.all" do
8 | RSpreedly::SubscriptionPlan.should_receive(:all)
9 | RSpreedly::SubscriptionPlan.find(:all)
10 | end
11 | end
12 |
13 | it "should return nil with an id for a plan which doesn't exist" do
14 | stub_request(:get, spreedly_url("/subscription_plans.xml")).
15 | to_return(:body => fixture("subscription_plan_list.xml"), :status => 200)
16 |
17 | RSpreedly::SubscriptionPlan.find(99).should be_nil
18 | end
19 |
20 | it "should return a SubscriptionPlan with an id for an existing plan" do
21 | stub_request(:get, spreedly_url("/subscription_plans.xml")).
22 | to_return(:body => fixture("subscription_plan_list.xml"), :status => 200)
23 |
24 | RSpreedly::SubscriptionPlan.find(42).should be_a(RSpreedly::SubscriptionPlan)
25 | end
26 | end
27 |
28 | describe ".all" do
29 | it "should return an empty array if there are no plans" do
30 | stub_request(:get, spreedly_url("/subscription_plans.xml")).
31 | to_return(:body => fixture("no_plans.xml"), :status => 200)
32 |
33 | RSpreedly::SubscriptionPlan.all.should == []
34 | end
35 |
36 | it "should return an array of SubscriptionPlans if there are any to find" do
37 | stub_request(:get, spreedly_url("/subscription_plans.xml")).
38 | to_return(:body => fixture("subscription_plan_list.xml"), :status => 200)
39 |
40 | RSpreedly::SubscriptionPlan.all.size.should == 4 # there are 4 in the fixture
41 | RSpreedly::SubscriptionPlan.all.select{|x| x.is_a?(RSpreedly::SubscriptionPlan )}.size.should == 4
42 | end
43 | end
44 |
45 | describe ".active" do
46 | it "should return an empty array if there are no plans at all" do
47 | stub_request(:get, spreedly_url("/subscription_plans.xml")).
48 | to_return(:body => fixture("no_plans.xml"), :status => 200)
49 | RSpreedly::SubscriptionPlan.active.should == []
50 | end
51 |
52 | it "should return an array of active SubscriptionPlans if there are any to find" do
53 | stub_request(:get, spreedly_url("/subscription_plans.xml")).
54 | to_return(:body => fixture("subscription_plan_list.xml"), :status => 200)
55 |
56 | RSpreedly::SubscriptionPlan.active.size.should == 2
57 | RSpreedly::SubscriptionPlan.active.select{|x| x.is_a?(RSpreedly::SubscriptionPlan )}.size.should == 2
58 | end
59 | end
60 |
61 | end
62 |
--------------------------------------------------------------------------------
/Rakefile:
--------------------------------------------------------------------------------
1 | require "rubygems"
2 | require "rubygems/package_task"
3 | require "rdoc/task"
4 |
5 | require "rspec"
6 | require "rspec/core/rake_task"
7 | RSpec::Core::RakeTask.new do |t|
8 | t.rspec_opts = %w(--format documentation --colour)
9 | end
10 |
11 |
12 | task :default => ["spec"]
13 |
14 | # This builds the actual gem. For details of what all these options
15 | # mean, and other ones you can add, check the documentation here:
16 | #
17 | # http://rubygems.org/read/chapter/20
18 | #
19 | spec = Gem::Specification.new do |s|
20 |
21 | # Change these as appropriate
22 | s.name = "rspreedly"
23 | s.version = "0.1.17"
24 | s.summary = "Ruby library for the Spreedly API"
25 | s.author = "Richard Livsey"
26 | s.email = "richard@livsey.org"
27 | s.homepage = "http://github.com/rlivsey/rspreedly"
28 |
29 | s.has_rdoc = true
30 | s.extra_rdoc_files = %w(README.rdoc)
31 | s.rdoc_options = %w(--main README.rdoc)
32 |
33 | # Add any extra files to include in the gem
34 | s.files = %w(LICENSE Rakefile README.rdoc rspreedly.gemspec) + Dir.glob("{spec,lib}/**/*")
35 | s.require_paths = ["lib"]
36 |
37 | # If you want to depend on other gems, add them here, along with any
38 | # relevant versions
39 | s.add_dependency("httparty", ">= 0.5.0")
40 |
41 | # If your tests use any gems, include them here
42 | s.add_development_dependency("rspec")
43 | s.add_development_dependency("webmock")
44 | end
45 |
46 | # This task actually builds the gem. We also regenerate a static
47 | # .gemspec file, which is useful if something (i.e. GitHub) will
48 | # be automatically building a gem for this project. If you're not
49 | # using GitHub, edit as appropriate.
50 | #
51 | # To publish your gem online, install the 'gemcutter' gem; Read more
52 | # about that here: http://gemcutter.org/pages/gem_docs
53 | Gem::PackageTask.new(spec) do |pkg|
54 | pkg.gem_spec = spec
55 | end
56 |
57 | desc "Build the gemspec file #{spec.name}.gemspec"
58 | task :gemspec do
59 | file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
60 | File.open(file, "w") {|f| f << spec.to_ruby }
61 | end
62 |
63 | # If you don't want to generate the .gemspec file, just remove this line. Reasons
64 | # why you might want to generate a gemspec:
65 | # - using bundler with a git source
66 | # - building the gem without rake (i.e. gem build blah.gemspec)
67 | # - maybe others?
68 | task :package => :gemspec
69 |
70 | # Generate documentation
71 | RDoc::Task.new do |rd|
72 | rd.main = "README.rdoc"
73 | rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
74 | rd.rdoc_dir = "rdoc"
75 | end
76 |
77 | desc 'Clear out RDoc and generated packages'
78 | task :clean => [:clobber_rdoc, :clobber_package] do
79 | rm "#{spec.name}.gemspec"
80 | end
81 |
--------------------------------------------------------------------------------
/spec/fixtures/transactions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 24.0
5 | 2009-09-26T03:06:30Z
6 | USD
7 | Subscription
8 | Subscription
9 | 2009-12-26T04:06:30Z
10 | 20
11 | 64
12 | 2009-09-26T03:06:30Z
13 | 3 months
14 | 2009-09-26T03:06:30Z
15 | $24.00
16 | 39053
17 |
18 | visa
19 | false
20 | example
21 |
22 |
23 |
24 | 24.0
25 | 2009-09-26T03:06:30Z
26 | USD
27 | Subscription
28 | Subscription
29 | 2009-12-26T04:06:30Z
30 | 20
31 | 64
32 | 2009-09-26T03:06:30Z
33 | 3 months
34 | 2009-09-26T03:06:30Z
35 | $24.00
36 | 39053
37 |
38 | visa
39 | false
40 | example
41 |
42 |
43 |
44 | 24.0
45 | 2009-09-26T03:06:30Z
46 | USD
47 | Subscription
48 | Subscription
49 | 2009-12-26T04:06:30Z
50 | 20
51 | 64
52 | 2009-09-26T03:06:30Z
53 | 3 months
54 | 2009-09-26T03:06:30Z
55 | $24.00
56 | 39053
57 |
58 | visa
59 | false
60 | example
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/README.rdoc:
--------------------------------------------------------------------------------
1 | = RSpreedly
2 |
3 | Ruby library to access the Spreedly API (all of it).
4 |
5 | == Why another one?
6 |
7 | There are a few of these knocking about, but I couldn't find one particular to my needs:
8 |
9 | * Well covered with specs - currently 100% coverage
10 | * Covers the entire API, not just parts of it
11 | * Rubyish/ActiveRecord style access
12 |
13 | == Installation
14 |
15 | It's on Gemcutter, so as long as you're setup to use that:
16 |
17 | gem install rspreedly
18 |
19 | Otherwise
20 |
21 | gem install gemcutter
22 | gem tumble
23 | gem install rspreedly
24 |
25 | == Usage
26 |
27 | Configure your API key and site name for your account:
28 |
29 | RSpreedly::Config.setup do |config|
30 | config.api_key = "your-api-key"
31 | config.site_name = "your-site-name"
32 | end
33 |
34 | List subscribers
35 |
36 | RSpreedly::Subscriber.find(:all).each do |subscriber|
37 | # do something
38 | end
39 |
40 | CRUD access to subscribers
41 |
42 | sub = RSpreedly::Subscriber.new(:customer_id => 42, :email => "somewhere@example.com")
43 | sub.token => nil
44 | sub.save
45 | sub.token => "6af9994a57e420345897b1abb4c27a9db27fa4d0"
46 |
47 | sub = RSpreedly::Subscriber.find(42)
48 | sub.email = "new-email@example.com"
49 | sub.save
50 |
51 | sub = RSpreedly::Subscriber.find(69)
52 | sub.destroy
53 |
54 | Comp subscriptions
55 |
56 | comp = RSpreedly::ComplimentarySubscription.new(:duration_quantity => 3, :duration_units => "months", :feature_level => "Pro")
57 | sub = RSpreedly::Subscriber.find(69)
58 | sub.comp_subscription(comp)
59 |
60 | View all plans
61 |
62 | plans = RSpreedly::SubscriptionPlan.find(:all)
63 |
64 | Raise an invoice, and pay it
65 |
66 | invoice = RSpreedly::Invoice.new(:subscription_plan_id => 5, :subscriber => sub)
67 | invoice.save
68 |
69 | payment = RSpreedly::PaymentMethod::CreditCard.new(:card_type => 'visa', :number => '123456', ...)
70 | invoice.pay(payment)
71 |
72 | Error messages
73 |
74 | invoice = RSpreedly::Invoice.new
75 | invoice.save => false
76 | invoice.errors => ["You must specify both the subscription plan id and subscriber information."]
77 |
78 | See the specs and API docs at Spreedly for more details and the rest of the things it can do.
79 |
80 | https://www.spreedly.com/manual/integration-reference/url-reference/
81 |
82 | == Todo
83 |
84 | * Better specs for data we send to Spreedly.
85 | * Better docs.
86 |
87 | == Thanks
88 |
89 | I studied the other Spreedly APIs and there may well be a line or three this library has in common with:
90 |
91 | * http://github.com/terralien/spreedly-gem
92 | * http://github.com/erbmicha/spreedly/
93 |
94 | == Copyright
95 |
96 | Copyright (c) 2009 Richard Livsey. See LICENSE for details.
97 |
--------------------------------------------------------------------------------
/spec/base_spec.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2 |
3 | class TestAPI < RSpreedly::Base
4 | attr_accessor :something
5 | end
6 |
7 | describe RSpreedly::Base do
8 |
9 | describe ".api_request" do
10 | it "should raise AccessDenied if a 401 is received" do
11 | stub_request(:put, spreedly_url("/")).to_return(:status => 401)
12 |
13 | lambda{
14 | RSpreedly::Base.api_request(:put, '/')
15 | }.should raise_error(RSpreedly::Error::AccessDenied)
16 | end
17 | end
18 |
19 | describe "attributes=" do
20 | before(:each) do
21 | @api = TestAPI.new
22 | end
23 |
24 | it "should assign attributes if they exist" do
25 | @api.attributes = {:something => "test"}
26 | @api.something.should == "test"
27 | end
28 |
29 | it "should not fail if an attribute does not exist" do
30 | lambda{
31 | @api.attributes = {:foo => true}
32 | }.should_not raise_error
33 | end
34 | end
35 |
36 | describe "error messages" do
37 |
38 | before(:each) do
39 | @api = TestAPI.new
40 | end
41 |
42 | def do_request
43 | @api.api_request(:post, "/")
44 | end
45 |
46 | def failing_request
47 | lambda{
48 | do_request
49 | }.should raise_error
50 | end
51 |
52 | it "should not set any errors on a successful request" do
53 | stub_request(:post, spreedly_url("/")).to_return(:status => 200)
54 | do_request
55 | @api.errors.should be_empty
56 | end
57 |
58 | it "should set one error in the error array if a string is return " do
59 | stub_request(:post, spreedly_url("/")).to_return(:body => fixture("error_string.txt"), :status => 422)
60 | failing_request
61 | @api.errors.should == ["Some error back from the response as a string"]
62 | end
63 |
64 | it "should set one error in the error array if an xml error with one item is returned" do
65 | stub_request(:post, spreedly_url("/")).to_return(:body => fixture("error.xml"), :status => 422)
66 | failing_request
67 | @api.errors.should == ["Email can't be blank"]
68 | end
69 |
70 | it "should set multiple errors in the error array if an xml error with multiple items is returned" do
71 | stub_request(:post, spreedly_url("/")).to_return(:body => fixture("errors.xml"), :status => 422)
72 | failing_request
73 | @api.errors.should == ["Email can't be blank", "Name can't be blank"]
74 | end
75 |
76 | it "should reset errors on each call" do
77 | # failing one first, which will generate some errors
78 | stub_request(:post, spreedly_url("/")).to_return(:body => fixture("errors.xml"), :status => 422)
79 | failing_request
80 | @api.errors.should_not be_empty
81 |
82 | # now a successful one should clear the errors
83 | stub_request(:post, spreedly_url("/")).to_return(:status => 200)
84 | do_request
85 | @api.errors.should be_empty
86 | end
87 | end
88 | end
--------------------------------------------------------------------------------
/rspreedly.gemspec:
--------------------------------------------------------------------------------
1 | # -*- encoding: utf-8 -*-
2 |
3 | Gem::Specification.new do |s|
4 | s.name = %q{rspreedly}
5 | s.version = "0.1.17"
6 |
7 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8 | s.authors = ["Richard Livsey"]
9 | s.date = %q{2011-08-17}
10 | s.email = %q{richard@livsey.org}
11 | s.extra_rdoc_files = ["README.rdoc"]
12 | s.files = ["LICENSE", "Rakefile", "README.rdoc", "rspreedly.gemspec", "spec/base_spec.rb", "spec/config_spec.rb", "spec/fixtures", "spec/fixtures/complimentary_failed_active.xml", "spec/fixtures/complimentary_failed_inactive.xml", "spec/fixtures/complimentary_not_valid.xml", "spec/fixtures/complimentary_success.xml", "spec/fixtures/create_subscriber.xml", "spec/fixtures/credit_not_valid.xml", "spec/fixtures/credit_success.xml", "spec/fixtures/error.xml", "spec/fixtures/error_string.txt", "spec/fixtures/errors.xml", "spec/fixtures/existing_subscriber.xml", "spec/fixtures/feature_level_blank.xml", "spec/fixtures/free_plan_not_elligable.xml", "spec/fixtures/free_plan_not_free.xml", "spec/fixtures/free_plan_not_set.xml", "spec/fixtures/free_plan_success.xml", "spec/fixtures/invalid_subscriber.xml", "spec/fixtures/invalid_update.xml", "spec/fixtures/invoice_created.xml", "spec/fixtures/invoice_invalid.xml", "spec/fixtures/lifetime_subscription_success.xml", "spec/fixtures/no_plans.xml", "spec/fixtures/no_subscribers.xml", "spec/fixtures/no_transactions.xml", "spec/fixtures/payment_already_paid.xml", "spec/fixtures/payment_invalid.xml", "spec/fixtures/payment_not_found.xml", "spec/fixtures/payment_success.xml", "spec/fixtures/plan_disabled.xml", "spec/fixtures/plan_not_found.xml", "spec/fixtures/subscriber.xml", "spec/fixtures/subscriber_not_found.xml", "spec/fixtures/subscribers.xml", "spec/fixtures/subscription_plan_list.xml", "spec/fixtures/transactions.xml", "spec/invoice_spec.rb", "spec/spec_helper.rb", "spec/subscriber_spec.rb", "spec/subscription_plan_spec.rb", "spec/transaction_spec.rb", "lib/rspreedly", "lib/rspreedly/base.rb", "lib/rspreedly/complimentary_subscription.rb", "lib/rspreedly/complimentary_time_extension.rb", "lib/rspreedly/config.rb", "lib/rspreedly/credit.rb", "lib/rspreedly/error.rb", "lib/rspreedly/invoice.rb", "lib/rspreedly/lifetime_complimentary_subscription.rb", "lib/rspreedly/line_item.rb", "lib/rspreedly/payment_method.rb", "lib/rspreedly/subscriber.rb", "lib/rspreedly/subscription_plan.rb", "lib/rspreedly/transaction.rb", "lib/rspreedly.rb"]
13 | s.homepage = %q{http://github.com/rlivsey/rspreedly}
14 | s.rdoc_options = ["--main", "README.rdoc"]
15 | s.require_paths = ["lib"]
16 | s.rubygems_version = %q{1.6.2}
17 | s.summary = %q{Ruby library for the Spreedly API}
18 |
19 | if s.respond_to? :specification_version then
20 | s.specification_version = 3
21 |
22 | if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
23 | s.add_runtime_dependency(%q, [">= 0.5.0"])
24 | s.add_development_dependency(%q, [">= 0"])
25 | s.add_development_dependency(%q, [">= 0"])
26 | else
27 | s.add_dependency(%q, [">= 0.5.0"])
28 | s.add_dependency(%q, [">= 0"])
29 | s.add_dependency(%q, [">= 0"])
30 | end
31 | else
32 | s.add_dependency(%q, [">= 0.5.0"])
33 | s.add_dependency(%q, [">= 0"])
34 | s.add_dependency(%q, [">= 0"])
35 | s.add_development_dependency(%q)
36 | s.add_development_dependency(%q)
37 | end
38 | end
39 |
--------------------------------------------------------------------------------
/lib/rspreedly/base.rb:
--------------------------------------------------------------------------------
1 | require 'pry'
2 |
3 | module RSpreedly
4 |
5 | class Base
6 | include HTTParty
7 | format :xml
8 | base_uri "https://spreedly.com/api/v4"
9 |
10 | attr_reader :errors
11 |
12 | def self.api_request(type, path, options={})
13 | site_name = RSpreedly::Config.site_name
14 | api_key = RSpreedly::Config.api_key
15 | path = "/#{site_name}#{path}"
16 |
17 | options.merge!({
18 | :basic_auth => {:username => api_key, :password => 'X'},
19 | :headers => {"Content-Type" => 'application/xml'}
20 | })
21 | self.do_request(type, path, options)
22 | end
23 |
24 | def self.do_request(type, path, options)
25 | begin
26 | response = self.send(type.to_s, path, options)
27 | rescue SocketError
28 | raise(RSpreedly::Error::ConnectionFailed.new, "Failed to connect to payment gateway.")
29 | end
30 |
31 | case response.code.to_i
32 | when 401
33 | raise(RSpreedly::Error::AccessDenied.new(response), response.body)
34 | when 403
35 | raise(RSpreedly::Error::Forbidden.new(response), response.body)
36 | when 422
37 | raise(RSpreedly::Error::BadRequest.new(response), response.body)
38 | when 404
39 | raise(RSpreedly::Error::NotFound.new(response), response.body)
40 | when 504
41 | raise(RSpreedly::Error::GatewayTimeout.new(response), response.body)
42 | end
43 |
44 | response
45 | end
46 |
47 | def initialize(attrs={})
48 | @errors = []
49 | self.attributes = attrs
50 | end
51 |
52 | def attributes=(attrs)
53 | attrs.each do |k, v|
54 | self.send(:"#{k}=", v) if self.respond_to?(:"#{k}=")
55 | end
56 | end
57 |
58 | def api_request(type, path, options={})
59 | @errors = []
60 | begin
61 | self.class.api_request(type, path, options)
62 | rescue RSpreedly::Error::Base => e
63 | if e.response.is_a?(Hash)
64 | if e.response.has_key?("errors")
65 | @errors = [*e.response["errors"]["error"]]
66 | else
67 | @errors = [e.response.body]
68 | end
69 | else
70 | @errors = [e.message]
71 | end
72 | raise
73 | end
74 | end
75 |
76 | # TODO - do this nicer
77 | # rather eew at the moment and hand made XML is not nice
78 | def to_xml(opts={})
79 | exclude = opts[:exclude] || []
80 | exclude << :errors
81 |
82 | tag = opts[:tag] || RSpreedly.underscore(self.class.to_s)
83 | inner = opts[:inner]
84 | outer = opts[:outer]
85 | no_tag = opts[:no_tag]
86 |
87 | xml = ""
88 | xml << "<#{outer}>" if outer
89 | xml << "<#{tag}>" unless no_tag
90 | xml << "<#{inner}>" if inner
91 | self.instance_variables.each do |var|
92 | name = var.to_s.gsub('@', '')
93 | next if exclude.include?(name.to_sym)
94 | value = self.instance_variable_get(var)
95 | if value.respond_to?(:to_xml)
96 | value = value.to_xml(:no_tag => (RSpreedly.underscore(value.class.to_s) == name))
97 | end
98 | xml << "<#{name}>#{value}#{name}>"
99 | end
100 | xml << "#{inner}>" if inner
101 | xml << "#{tag}>" unless no_tag
102 | xml << "#{outer}>" if outer
103 | xml
104 | end
105 | end
106 | end
--------------------------------------------------------------------------------
/spec/config_spec.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2 |
3 | describe RSpreedly::Config do
4 |
5 | before :each do
6 |
7 | RSpreedly::Config.clear
8 | RSpreedly::Config.setup do |c|
9 | c[:one] = 1
10 | c[:two] = 2
11 | end
12 |
13 | end
14 |
15 | describe "logger" do
16 |
17 | before :each do
18 | Object.send(:remove_const, :RAILS_DEFAULT_LOGGER) if defined?(RAILS_DEFAULT_LOGGER)
19 | end
20 |
21 | it "should default to RAILS_DEFAULT_LOGGER if defined" do
22 | RAILS_DEFAULT_LOGGER = "something"
23 | RSpreedly::Config.reset
24 | RSpreedly::Config.logger.should == "something"
25 | end
26 |
27 | it "should default to a Logger if RAILS_DEFAULT_LOGGER is not defined" do
28 | RSpreedly::Config.reset
29 | RSpreedly::Config.logger.should be_a(Logger)
30 | end
31 |
32 | end
33 |
34 | describe "configuration" do
35 |
36 | it "should return the configuration hash" do
37 | RSpreedly::Config.configuration.should == {:one => 1, :two => 2}
38 | end
39 |
40 | end
41 |
42 | describe "[]" do
43 |
44 | it "should return the config option matching the key" do
45 | RSpreedly::Config[:one].should == 1
46 | end
47 |
48 | it "should return nil if the key doesn't exist" do
49 | RSpreedly::Config[:monkey].should be_nil
50 | end
51 |
52 | end
53 |
54 | describe "[]=" do
55 |
56 | it "should set the config option for the key" do
57 | lambda{
58 | RSpreedly::Config[:banana] = :yellow
59 | }.should change(RSpreedly::Config, :banana).from(nil).to(:yellow)
60 | end
61 |
62 | end
63 |
64 | describe "delete" do
65 |
66 | it "should delete the config option for the key" do
67 | lambda{
68 | RSpreedly::Config.delete(:one)
69 | }.should change(RSpreedly::Config, :one).from(1).to(nil)
70 | end
71 |
72 | it "should leave the config the same if the key doesn't exist" do
73 | lambda{
74 | RSpreedly::Config.delete(:test)
75 | }.should_not change(RSpreedly::Config, :configuration)
76 | end
77 |
78 | end
79 |
80 | describe "fetch" do
81 |
82 | it "should return the config option matching the key if it exists" do
83 | RSpreedly::Config.fetch(:one, 100).should == 1
84 | end
85 |
86 | it "should return the config default if the key doesn't exist" do
87 | RSpreedly::Config.fetch(:other, 100).should == 100
88 | end
89 |
90 | end
91 |
92 | describe "to_hash" do
93 |
94 | it "should return a hash of the configuration" do
95 | RSpreedly::Config.to_hash.should == {:one => 1, :two => 2}
96 | end
97 |
98 | end
99 |
100 | describe "setup" do
101 |
102 | it "should yield self" do
103 | RSpreedly::Config.setup do |c|
104 | c.should == RSpreedly::Config
105 | end
106 | end
107 |
108 | it "should let you set items on the configuration object as a hash" do
109 | lambda{
110 | RSpreedly::Config.setup do |c|
111 | c[:bananas] = 100
112 | end
113 | }.should change(RSpreedly::Config, :bananas).from(nil).to(100)
114 | end
115 |
116 | it "should let you set items on the configuration object as a method" do
117 | lambda{
118 | RSpreedly::Config.setup do |c|
119 | c.monkeys = 100
120 | end
121 | }.should change(RSpreedly::Config, :monkeys).from(nil).to(100)
122 | end
123 |
124 | end
125 |
126 | describe "calling a missing method" do
127 |
128 | it "should retreive the config if the method matches a key" do
129 | RSpreedly::Config.one.should == 1
130 | end
131 |
132 | it "should retreive nil if the method doesn't match a key" do
133 | RSpreedly::Config.moo.should be_nil
134 | end
135 |
136 | it "should set the value of the config item matching the method name if it's an assignment" do
137 | lambda{
138 | RSpreedly::Config.trees = 3
139 | }.should change(RSpreedly::Config, :trees).from(nil).to(3)
140 | end
141 |
142 | end
143 |
144 | end
145 |
--------------------------------------------------------------------------------
/spec/fixtures/subscribers.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | 2009-10-28T22:16:20Z
8 | 8889
9 | true
10 |
11 |
12 | false
13 | false
14 | freddy
15 | 0.0
16 | USD
17 | 6af9994a57e420345897b1abb4c27a9db27fa4d0
18 | 2009-10-28T22:16:20Z
19 | false
20 | false
21 |
22 | false
23 |
24 | false
25 |
26 |
27 |
28 |
29 |
30 |
31 | 2009-10-28T22:26:57Z
32 | 1889
33 | true
34 |
35 |
36 | false
37 | false
38 | freddy
39 | 0.0
40 | USD
41 | 9638cba1d938e1bd8e18b8075e077775a0bb9d21
42 | 2009-10-28T22:26:57Z
43 | false
44 | false
45 |
46 | false
47 |
48 | false
49 |
50 |
51 |
52 |
53 |
54 |
55 | 2009-10-28T22:27:38Z
56 | 42
57 | true
58 | new@email.com
59 |
60 | false
61 | false
62 | bob
63 | 0.0
64 | USD
65 | 81ba778a5849f461ebc0d77e78b9221af2210c6b
66 | 2009-10-28T22:55:37Z
67 | false
68 | false
69 |
70 | false
71 |
72 | false
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/spec/fixtures/subscription_plan_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 19.0
5 | true
6 | 1
7 | months
8 | 2009-10-26T00:28:41Z
9 | USD
10 |
11 | 1
12 | months
13 | true
14 | personal
15 | true
16 | 42
17 | Personal
18 | true
19 | regular
20 | http://example.com
21 | 2009-10-26T00:28:41Z
22 | 1 month
23 | 19.0
24 |
25 |
26 | 49.0
27 | true
28 | 1
29 | months
30 | 2009-10-26T00:29:07Z
31 | USD
32 |
33 | 1
34 | months
35 | true
36 | team
37 | true
38 | 43
39 | Team
40 | true
41 | regular
42 | http://example.com
43 | 2009-10-26T00:29:07Z
44 | 1 month
45 | 49.0
46 |
47 |
48 | 99.0
49 | true
50 | 1
51 | months
52 | 2009-10-26T00:29:39Z
53 | USD
54 |
55 | 1
56 | months
57 | false
58 | corporate
59 | true
60 | 44
61 | Corporate
62 | true
63 | regular
64 | http://example.com
65 | 2009-10-26T00:29:39Z
66 | 1 month
67 | 99.0
68 |
69 |
70 | 0.0
71 | false
72 |
73 |
74 | 2009-10-29T00:31:15Z
75 | USD
76 |
77 | 0
78 | days
79 | false
80 | free
81 | false
82 | 45
83 | Free Trial
84 | false
85 | free_trial
86 | http://example.com
87 | 2009-10-29T00:31:15Z
88 | Lifetime
89 | 0.0
90 |
91 |
--------------------------------------------------------------------------------
/spec/fixtures/subscriber.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 2009-10-28T22:27:38Z
7 | 42
8 | true
9 | new@email.com
10 |
11 | false
12 | false
13 | bob
14 | 0.0
15 | USD
16 | 81ba778a5849f461ebc0d77e78b9221af2210c6b
17 | 2009-10-28T22:55:37Z
18 | false
19 | false
20 |
21 | false
22 |
23 | false
24 |
25 |
26 |
27 | true
28 | 2011-07-21T19:04:45Z
29 |
30 |
31 |
32 | 46089ee32310b4983c6acb0bc02dec955886d411
33 | 2011-07-21T19:05:07Z
34 | $0.05
35 | 0.05
36 | USD
37 |
38 |
39 | 99.0
40 | USD
41 | Every 1 month
42 |
43 | $99.00
44 |
45 |
46 | -48.99
47 | USD
48 | Credit from current subscription
49 |
50 | $-48.99
51 |
52 |
53 | -49.96
54 | USD
55 | Your store credit
56 |
57 | $-49.96
58 |
59 |
60 |
61 |
62 | true
63 | 2011-07-21T18:56:52Z
64 |
65 |
66 |
67 | 4730937c43a57b333b78ba474e6384bfb434bee3
68 | 2011-07-21T18:57:12Z
69 | $-49.96
70 | -49.96
71 | USD
72 |
73 |
74 | 49.0
75 | USD
76 | Every 1 month
77 |
78 | $49.00
79 |
80 |
81 | -98.96
82 | USD
83 | Credit from current subscription
84 |
85 | $-98.96
86 |
87 |
88 |
89 |
90 | true
91 | 2011-07-21T18:39:17Z
92 |
93 |
94 |
95 | 4bf20627d684dbaab231b349b1ed1ae6388f4ffc
96 | 2011-07-21T18:39:32Z
97 | $50.00
98 | 50.0
99 | USD
100 |
101 |
102 | 99.0
103 | USD
104 | Every 1 month
105 |
106 | $99.00
107 |
108 |
109 | -49.0
110 | USD
111 | Credit from current subscription
112 |
113 | $-49.00
114 |
115 |
116 |
117 |
118 | true
119 | 2011-07-21T18:15:11Z
120 |
121 |
122 |
123 | a5190437c9651aa77ac6e39e9833b579bff4658e
124 | 2011-07-21T18:15:36Z
125 | $49.00
126 | 49.0
127 | USD
128 |
129 |
130 | 49.0
131 | USD
132 | Every 1 month
133 |
134 | $49.00
135 |
136 |
137 |
138 |
139 | true
140 | 2011-07-21T18:14:07Z
141 |
142 |
143 |
144 | ea7a657f68908279fdc33c8cf1c7037c4a2eec2d
145 | 2011-07-21T18:14:32Z
146 | $0.00
147 | 0.0
148 | USD
149 |
150 |
151 | 0.0
152 | USD
153 | Initial 1 month
154 | Subsequent Time Periods are $49.00 every 1 month
155 | $0.00
156 |
157 |
158 |
159 |
160 |
161 |
--------------------------------------------------------------------------------
/spec/invoice_spec.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2 |
3 | describe RSpreedly::Invoice do
4 |
5 | describe "#create!" do
6 |
7 | before(:each) do
8 | @subscriber = RSpreedly::Subscriber.new(:customer_id => 42, :screen_name => "bob", :email => "test@example.com")
9 | @invoice = RSpreedly::Invoice.new(:subscriber => @subscriber, :subscription_plan_id => 2502)
10 | end
11 |
12 | it "should return true if successful" do
13 | stub_request(:post, spreedly_url("/invoices.xml")).
14 | to_return(:body => fixture("invoice_created.xml"), :status => 201)
15 |
16 | @invoice.create.should be_true
17 | end
18 |
19 | it "should update the invoice if successful" do
20 | stub_request(:post, spreedly_url("/invoices.xml")).
21 | to_return(:body => fixture("invoice_created.xml"), :status => 201)
22 |
23 | lambda{
24 | @invoice.create
25 | }.should change(@invoice, :price).from(nil).to("$0.00")
26 | end
27 |
28 | it "should setup line items in the invoice if successful" do
29 | stub_request(:post, spreedly_url("/invoices.xml")).
30 | to_return(:body => fixture("invoice_created.xml"), :status => 201)
31 |
32 | @invoice.create
33 | @invoice.line_items.size.should == 1
34 | @invoice.line_items[0].should be_a(RSpreedly::LineItem)
35 | end
36 |
37 | it "should raise NotFound if the plan doesn't exist" do
38 | stub_request(:post, spreedly_url("/invoices.xml")).
39 | to_return(:body => fixture("plan_not_found.xml"), :status => 404)
40 |
41 | lambda{
42 | @invoice.create!
43 | }.should raise_error(RSpreedly::Error::NotFound)
44 | end
45 |
46 | it "should raise BadRequest if the invoice is invalid" do
47 | stub_request(:post, spreedly_url("/invoices.xml")).
48 | to_return(:body => fixture("invoice_invalid.xml"), :status => 422)
49 |
50 | lambda{
51 | @invoice.create!
52 | }.should raise_error(RSpreedly::Error::BadRequest)
53 | end
54 |
55 | it "should raise Forbidden if the plan is disabled" do
56 | stub_request(:post, spreedly_url("/invoices.xml")).
57 | to_return(:body => fixture("plan_disabled.xml"), :status => 403)
58 |
59 | lambda{
60 | @invoice.create!
61 | }.should raise_error(RSpreedly::Error::Forbidden)
62 | end
63 | end
64 |
65 | describe "#create" do
66 |
67 | before(:each) do
68 | @subscriber = RSpreedly::Subscriber.new(:customer_id => 42, :screen_name => "bob", :email => "test@example.com")
69 | @invoice = RSpreedly::Invoice.new(:subscriber => @subscriber, :subscription_plan_id => 2502)
70 | end
71 |
72 | it "should return true if successful" do
73 | stub_request(:post, spreedly_url("/invoices.xml")).
74 | to_return(:body => fixture("invoice_created.xml"), :status => 201)
75 |
76 | @invoice.create.should be_true
77 | end
78 |
79 | it "should return nil if the plan doesn't exist" do
80 | stub_request(:post, spreedly_url("/invoices.xml")).
81 | to_return(:body => fixture("plan_not_found.xml"), :status => 404)
82 |
83 | @invoice.create.should be_nil
84 | end
85 |
86 | it "should return nil if the invoice is invalid" do
87 | stub_request(:post, spreedly_url("/invoices.xml")).
88 | to_return(:body => fixture("invoice_invalid.xml"), :status => 422)
89 |
90 | @invoice.create.should be_nil
91 | end
92 |
93 | it "should return nil if the plan is disabled" do
94 | stub_request(:post, spreedly_url("/invoices.xml")).
95 | to_return(:body => fixture("plan_disabled.xml"), :status => 403)
96 |
97 | @invoice.create.should be_nil
98 | end
99 | end
100 |
101 |
102 | describe "#pay" do
103 |
104 | before(:each) do
105 | @invoice = RSpreedly::Invoice.new(:token => "5b1f186651dd988865c6573921ec87fa4bec23b8")
106 | @payment = RSpreedly::PaymentMethod::CreditCard.new(:number => "4222222222222",
107 | :card_type => "visa",
108 | :verification_value => "234",
109 | :month => 1,
110 | :year => 2011,
111 | :first_name => "Joe",
112 | :last_name => "Bob")
113 | end
114 |
115 | it "should return true if successful" do
116 | stub_request(:put, spreedly_url("/invoices/5b1f186651dd988865c6573921ec87fa4bec23b8/pay.xml")).
117 | to_return(:body => fixture("payment_success.xml"), :status => 200)
118 |
119 | @invoice.pay(@payment).should be_true
120 | end
121 |
122 | it "should return nil if not successful" do
123 | stub_request(:put, spreedly_url("/invoices/5b1f186651dd988865c6573921ec87fa4bec23b8/pay.xml")).
124 | to_return(:body => fixture("payment_not_found.xml"), :status => 404)
125 |
126 | @invoice.pay(@payment).should be_nil
127 | end
128 |
129 | end
130 |
131 | describe "#pay!" do
132 |
133 | before(:each) do
134 | @invoice = RSpreedly::Invoice.new(:token => "5b1f186651dd988865c6573921ec87fa4bec23b8")
135 | @payment = RSpreedly::PaymentMethod::CreditCard.new(:number => "4222222222222",
136 | :card_type => "visa",
137 | :verification_value => "234",
138 | :month => 1,
139 | :year => 2011,
140 | :first_name => "Joe",
141 | :last_name => "Bob")
142 | end
143 |
144 | it "should return true if successful" do
145 | stub_request(:put, spreedly_url("/invoices/5b1f186651dd988865c6573921ec87fa4bec23b8/pay.xml")).
146 | to_return(:body => fixture("payment_success.xml"), :status => 200)
147 |
148 | @invoice.pay!(@payment).should be_true
149 | end
150 |
151 | it "should update the Invoice if successful" do
152 | stub_request(:put, spreedly_url("/invoices/5b1f186651dd988865c6573921ec87fa4bec23b8/pay.xml")).
153 | to_return(:body => fixture("payment_success.xml"), :status => 200)
154 |
155 | lambda{
156 | @invoice.pay!(@payment)
157 | }.should change(@invoice, :closed).to(true)
158 | end
159 |
160 | it "should raise NotFound if the invoice doesn't exist" do
161 | stub_request(:put, spreedly_url("/invoices/5b1f186651dd988865c6573921ec87fa4bec23b8/pay.xml")).
162 | to_return(:body => fixture("payment_not_found.xml"), :status => 404)
163 |
164 | lambda{
165 | @invoice.pay!(@payment)
166 | }.should raise_error(RSpreedly::Error::NotFound)
167 | end
168 |
169 | it "should raise GatewayTimeout if the payment gateway times out" do
170 | stub_request(:put, spreedly_url("/invoices/5b1f186651dd988865c6573921ec87fa4bec23b8/pay.xml")).
171 | to_return(:status => 504)
172 |
173 | lambda{
174 | @invoice.pay!(@payment)
175 | }.should raise_error(RSpreedly::Error::GatewayTimeout)
176 | end
177 |
178 | it "should raise BadRequest if the payment method is invalid" do
179 | stub_request(:put, spreedly_url("/invoices/5b1f186651dd988865c6573921ec87fa4bec23b8/pay.xml")).
180 | to_return(:body => fixture("payment_invalid.xml"), :status => 422)
181 |
182 | lambda{
183 | @invoice.pay!(@payment)
184 | }.should raise_error(RSpreedly::Error::BadRequest)
185 | end
186 |
187 | it "should raise Forbidden if the invoice is already paid" do
188 | stub_request(:put, spreedly_url("/invoices/5b1f186651dd988865c6573921ec87fa4bec23b8/pay.xml")).
189 | to_return(:body => fixture("payment_already_paid.xml"), :status => 403)
190 |
191 | lambda{
192 | @invoice.pay!(@payment)
193 | }.should raise_error(RSpreedly::Error::Forbidden)
194 | end
195 | end
196 | end
--------------------------------------------------------------------------------
/lib/rspreedly/subscriber.rb:
--------------------------------------------------------------------------------
1 | module RSpreedly
2 |
3 | class Subscriber < Base
4 |
5 | attr_accessor :active,
6 | :active_until,
7 | :billing_first_name,
8 | :billing_last_name,
9 | :card_expires_before_next_auto_renew,
10 | :created_at,
11 | :customer_id,
12 | :eligible_for_free_trial,
13 | :email,
14 | :feature_level,
15 | :grace_until,
16 | :in_grace_period,
17 | :lifetime_subscription,
18 | :new_customer_id,
19 | :on_trial,
20 | :payment_method,
21 | :ready_to_renew,
22 | :ready_to_renew_since,
23 | :recurring,
24 | :screen_name,
25 | :store_credit,
26 | :store_credit_currency_code,
27 | :subscription_plan_name,
28 | :token,
29 | :updated_at,
30 | :invoices
31 |
32 | class << self
33 |
34 | # Get a subscriber’s details
35 | # GET /api/v4/[short site name]/subscribers/[subscriber id].xml
36 | def find(id)
37 | return all if id == :all
38 |
39 | begin
40 | data = api_request(:get, "/subscribers/#{id}.xml")
41 | Subscriber.new(data["subscriber"])
42 | rescue RSpreedly::Error::NotFound
43 | nil
44 | end
45 | end
46 |
47 | # Get a list of all subscribers (more)
48 | # GET /api/v4/[short site name]/subscribers.xml
49 | def all
50 | response = api_request(:get, "/subscribers.xml")
51 | return [] unless response.has_key?("subscribers")
52 | response["subscribers"].collect{|data| Subscriber.new(data)}
53 | end
54 |
55 | # Find subscriber's by a given attribute and get their details
56 | def find_by_email(email)
57 | subscribers = []
58 | all.each do |subscriber|
59 | subscribers << subscriber if subscriber.email == email
60 | end
61 |
62 | subscribers.count > 1 ? subscribers : subscribers.first
63 | end
64 |
65 | # Clear all subscribers from a *test* site (more)
66 | # DELETE /api/v4/[short site name]/subscribers.xml
67 | def delete_all
68 | !! api_request(:delete, "/subscribers.xml")
69 | end
70 |
71 | alias_method :destroy_all, :delete_all
72 |
73 | end
74 |
75 | def invoices=(data)
76 | @invoices = []
77 | data.each do |item|
78 | if item.is_a? Hash
79 | item = RSpreedly::Invoice.new(item)
80 | end
81 | @invoices << item
82 | end
83 | end
84 |
85 | def new_record?
86 | !self.token
87 | end
88 |
89 | def save
90 | self.new_record? ? self.create : self.update
91 | end
92 |
93 | def save!
94 | self.new_record? ? self.create! : self.update!
95 | end
96 |
97 | # Create a subscriber (more)
98 | # POST /api/v4/[short site name]/subscribers.xml
99 | def create!
100 | result = api_request(:post, "/subscribers.xml", :body => self.to_xml)
101 | self.attributes = result["subscriber"]
102 | true
103 | end
104 |
105 | def create
106 | begin
107 | create!
108 | rescue RSpreedly::Error::Base
109 | # gulp those errors down
110 | # TODO - set self.errors or something?
111 | nil
112 | end
113 | end
114 |
115 | # Update a Subscriber (more)
116 | # PUT /api/v4/[short site name]/subscribers/[subscriber id].xml
117 | def update!
118 | !! api_request(:put, "/subscribers/#{self.customer_id}.xml", :body => self.to_xml(:exclude => [:customer_id]))
119 | end
120 |
121 | def update
122 | begin
123 | update!
124 | rescue RSpreedly::Error::Base
125 | # gulp those errors down
126 | # TODO - set self.errors or something?
127 | nil
128 | end
129 | end
130 |
131 | # Delete one subscriber from a *test* site (more)
132 | # DELETE /api/v4/[short site name]/subscribers/[subscriber id].xml
133 | def destroy
134 | begin
135 | !! api_request(:delete, "/subscribers/#{self.customer_id}.xml")
136 | rescue RSpreedly::Error::NotFound
137 | nil
138 | end
139 | end
140 | alias_method :delete, :destroy
141 |
142 | # Give a subscriber a complimentary subscription (more)
143 | # POST /api/v4/[short site name]/subscribers/[subscriber id]/complimentary_subscriptions.xml
144 | def comp_subscription(subscription)
145 | result = api_request(:post, "/subscribers/#{self.customer_id}/complimentary_subscriptions.xml", :body => subscription.to_xml)
146 | self.attributes = result["subscriber"]
147 | true
148 | end
149 |
150 | # Give a subscriber a complimentary time extension (more)
151 | # POST /api/v4/[short site name]/subscribers/[subscriber id]/complimentary_time_extension.xml
152 | def comp_time_extension(extension)
153 | result = api_request(:post, "/subscribers/#{self.customer_id}/complimentary_time_extensions.xml", :body => extension.to_xml)
154 | self.attributes = result["subscriber"]
155 | true
156 | end
157 |
158 | # Give a subscriber a credit (or reduce credit by supplying a negative value (more)
159 | # POST /api/v4[short site name]/subscribers/[subscriber id]/credits.xml
160 | def credit(amount)
161 | credit = Credit.new(:amount => amount)
162 | result = api_request(:post, "/subscribers/#{self.customer_id}/credits.xml", :body => credit.to_xml)
163 | self.store_credit = (self.store_credit || 0) + amount
164 | true
165 | end
166 |
167 | # Programatically Stopping Auto Renew of a Subscriber (more)
168 | # POST /api/v4/[short site name]/subscribers/[subscriber id]/stop_auto_renew.xml
169 | def stop_auto_renew
170 | !! api_request(:post, "/subscribers/#{self.customer_id}/stop_auto_renew.xml")
171 | end
172 |
173 | # Programatically Subscribe a Subscriber to a Free Trial Plan (more)
174 | # POST /api/v4/[short site name]/subscribers/[subscriber id]/subscribe_to_free_trial.xml
175 | def subscribe_to_free_trial(plan)
176 | result = api_request(:post, "/subscribers/#{self.customer_id}/subscribe_to_free_trial.xml", :body => plan.to_xml)
177 | self.attributes = result["subscriber"]
178 | true
179 | end
180 |
181 | # Programatically Allow Another Free Trial (more)
182 | # POST /api/v4/[short site name]/subscribers/[subscriber id]/allow_free_trial.xml
183 | def allow_free_trial
184 | result = api_request(:post, "/subscribers/#{self.customer_id}/allow_free_trial.xml")
185 | self.attributes = result["subscriber"]
186 | true
187 | end
188 |
189 | def grant_lifetime_subscription(feature_level)
190 | subscription = LifetimeComplimentarySubscription.new(:feature_level => feature_level)
191 | result = api_request(:post, "/subscribers/#{self.customer_id}/lifetime_complimentary_subscriptions.xml", :body => subscription.to_xml)
192 | self.attributes = result["subscriber"]
193 | true
194 | end
195 |
196 | def subscribe_link(subscription_plan_id, screen_name, return_url=nil)
197 | params = return_url.nil? ? "" : "?return_url=" + return_url
198 | "https://spreedly.com/#{RSpreedly::Config.site_name}/subscribers/#{self.customer_id}/subscribe/#{subscription_plan_id}/#{screen_name}#{params}"
199 | end
200 |
201 | def subscription_link(return_url=nil)
202 | params = return_url.nil? ? "" : "?return_url=" + return_url
203 | "https://spreedly.com/#{RSpreedly::Config.site_name}/subscriber_accounts/#{self.token}#{params}"
204 | end
205 |
206 | def to_xml(opts={})
207 |
208 | # the api doesn't let us send these things
209 | # so let's strip them out of the XML
210 | exclude = [
211 | :active, :active_until, :card_expires_before_next_auto_renew,
212 | :created_at, :eligible_for_free_trial, :feature_level,
213 | :grace_until, :in_grace_period, :lifetime_subscription,
214 | :on_trial, :ready_to_renew, :recurring,
215 | :store_credit, :store_credit_currency_code, :subscription_plan_name,
216 | :token, :updated_at, :ready_to_renew_since,
217 | :invoices
218 | ]
219 |
220 | opts[:exclude] ||= []
221 | opts[:exclude] |= exclude
222 |
223 | super(opts)
224 | end
225 | end
226 | end
--------------------------------------------------------------------------------
/spec/subscriber_spec.rb:
--------------------------------------------------------------------------------
1 | require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2 | describe RSpreedly::Subscriber do
3 |
4 | describe ".find" do
5 | describe "with the symbol :all" do
6 | it "should pass to RSpreedly::Subscriber.all" do
7 | RSpreedly::Subscriber.should_receive(:all)
8 | RSpreedly::Subscriber.find(:all)
9 | end
10 | end
11 |
12 | it "should return a Subscriber with an email for an existing subscriber" do
13 | stub_request(:get, spreedly_url("/subscribers.xml")).
14 | to_return(body: fixture("subscribers.xml"), status: 200)
15 |
16 | subscriber = RSpreedly::Subscriber.find_by_email "new@email.com"
17 | subscriber.email.should == "new@email.com"
18 | end
19 |
20 | it "should return an array of Subscriber filtered by email" do
21 | stub_request(:get, spreedly_url("/subscribers.xml")).
22 | to_return(body: fixture("subscribers.xml"), status: 200)
23 |
24 | subscribers = RSpreedly::Subscriber.find_by_email nil
25 | subscribers.class.should == Array
26 | end
27 |
28 | it "should return nil with an id for a subscriber who doesn't exist" do
29 | stub_request(:get, spreedly_url("/subscribers/42.xml")).
30 | to_return(:body => fixture("subscriber_not_found.xml"), :status => 404)
31 |
32 | RSpreedly::Subscriber.find(42).should be_nil
33 | end
34 |
35 | it "should return a Subscriber with an id for an existing subscriber" do
36 | stub_request(:get, spreedly_url("/subscribers/42.xml")).
37 | to_return(:body => fixture("subscriber.xml"), :status => 200)
38 |
39 | RSpreedly::Subscriber.find(42).should be_a(RSpreedly::Subscriber)
40 | end
41 |
42 | it "should include invoices on the Subscriber" do
43 | stub_request(:get, spreedly_url("/subscribers/42.xml")).
44 | to_return(:body => fixture("subscriber.xml"), :status => 200)
45 |
46 | subscriber = RSpreedly::Subscriber.find(42)
47 | subscriber.invoices.size.should == 5
48 | subscriber.invoices.select{|x| x.is_a?(RSpreedly::Invoice )}.size.should == 5
49 | end
50 | end
51 |
52 | describe ".all" do
53 | it "should return an empty array if there are no subscribers" do
54 | stub_request(:get, spreedly_url("/subscribers.xml")).
55 | to_return(:body => fixture("no_subscribers.xml"), :status => 200)
56 |
57 | RSpreedly::Subscriber.all.should == []
58 | end
59 |
60 | it "should return an array of subscribers if there are any to find" do
61 | stub_request(:get, spreedly_url("/subscribers.xml")).
62 | to_return(:body => fixture("subscribers.xml"), :status => 200)
63 |
64 | RSpreedly::Subscriber.all.size.should == 3 # there are 3 in the fixture
65 | RSpreedly::Subscriber.all.select{|x| x.is_a?(RSpreedly::Subscriber )}.size.should == 3
66 | end
67 | end
68 |
69 | describe ".destroy_all" do
70 | it "should return true if successful" do
71 | stub_request(:delete, spreedly_url("/subscribers.xml")).
72 | to_return(:status => 200)
73 |
74 | RSpreedly::Subscriber.destroy_all.should be_true
75 | end
76 | end
77 |
78 | describe "#to_xml" do
79 |
80 | before(:each) do
81 | # use the XML to build a subscriber
82 | stub_request(:get, spreedly_url("/subscribers/42.xml")).
83 | to_return(:body => fixture("subscriber.xml"), :status => 200)
84 |
85 | @subscriber = RSpreedly::Subscriber.find(42)
86 | end
87 |
88 | it "should strip fields the API can't handle" do
89 | fields = [
90 | :active, :active_until, :card_expires_before_next_auto_renew,
91 | :created_at, :eligible_for_free_trial, :feature_level,
92 | :grace_until, :in_grace_period, :lifetime_subscription,
93 | :on_trial, :ready_to_renew, :recurring,
94 | :store_credit, :store_credit_currency_code, :subscription_plan_name,
95 | :token, :updated_at
96 | ]
97 |
98 | xml = @subscriber.to_xml
99 | fields.each do |field|
100 | xml.should_not =~ /<#{field}>/
101 | end
102 | end
103 |
104 | it "should not strip fields the API can handle" do
105 | fields = [
106 | :billing_first_name,
107 | :billing_last_name,
108 | :customer_id, :email,
109 | :screen_name
110 | ]
111 |
112 | xml = @subscriber.to_xml
113 | fields.each do |field|
114 | xml.should =~ /<#{field}>/
115 | end
116 | end
117 | end
118 |
119 | describe "#new_record?" do
120 | before(:each) do
121 | @subscriber = RSpreedly::Subscriber.new
122 | end
123 |
124 | it "should return true if the subscriber doesn't have a token" do
125 | @subscriber.new_record?.should be_true
126 | end
127 |
128 | it "should return false if the subscriber has a token" do
129 | @subscriber.token = "something"
130 | @subscriber.new_record?.should be_false
131 | end
132 | end
133 |
134 | describe "#save" do
135 | before(:each) do
136 | @subscriber = RSpreedly::Subscriber.new
137 | end
138 |
139 | it "should call #create for a non existing subscriber" do
140 | @subscriber.should_receive(:create)
141 | @subscriber.save
142 | end
143 |
144 | it "should call update for an existing subscriber" do
145 | @subscriber.token = "something"
146 | @subscriber.should_receive(:update)
147 | @subscriber.save
148 | end
149 | end
150 |
151 | describe "#save!" do
152 | before(:each) do
153 | @subscriber = RSpreedly::Subscriber.new
154 | end
155 |
156 | it "should call #create! for a non existing subscriber" do
157 | @subscriber.should_receive(:create!)
158 | @subscriber.save!
159 | end
160 |
161 | it "should call update! for an existing subscriber" do
162 | @subscriber.token = "something"
163 | @subscriber.should_receive(:update!)
164 | @subscriber.save!
165 | end
166 | end
167 |
168 | describe "#create!" do
169 | it "should return true if successful" do
170 | stub_request(:post, spreedly_url("/subscribers.xml")).
171 | to_return(:body => fixture("create_subscriber.xml"), :status => 201)
172 |
173 | @subscriber = RSpreedly::Subscriber.new(:customer_id => 42, :screen_name => "bob")
174 | @subscriber.create!.should be_true
175 | end
176 |
177 | it "should raise a BadRequest error if the data is invalid" do
178 | stub_request(:post, spreedly_url("/subscribers.xml")).
179 | to_return(:body => fixture("invalid_subscriber.xml"), :status => 422)
180 |
181 | @subscriber = RSpreedly::Subscriber.new
182 | lambda{
183 | @subscriber.create!
184 | }.should raise_error(RSpreedly::Error::BadRequest)
185 | end
186 |
187 | it "should update the subscriber if successful" do
188 | stub_request(:post, spreedly_url("/subscribers.xml")).
189 | to_return(:body => fixture("create_subscriber.xml"), :status => 200)
190 |
191 | @subscriber = RSpreedly::Subscriber.new(:customer_id => 42, :screen_name => "bob")
192 | lambda{
193 | @subscriber.create!
194 | }.should change(@subscriber, :token).to("6af9994a57e420345897b1abb4c27a9db27fa4d0")
195 | end
196 |
197 | it "should raise a Forbidden error if there is already a subscriber with that ID" do
198 | stub_request(:post, spreedly_url("/subscribers.xml")).
199 | to_return(:body => fixture("existing_subscriber.xml"), :status => 403)
200 |
201 | @subscriber = RSpreedly::Subscriber.new(:customer_id => 42, :screen_name => "bob")
202 | lambda{
203 | @subscriber.create!
204 | }.should raise_error(RSpreedly::Error::Forbidden)
205 | end
206 | end
207 |
208 | describe "#create" do
209 | it "should return true if successful" do
210 | stub_request(:post, spreedly_url("/subscribers.xml")).
211 | to_return(:body => fixture("create_subscriber.xml"), :status => 201)
212 |
213 | @subscriber = RSpreedly::Subscriber.new
214 | @subscriber.customer_id = 42
215 | @subscriber.screen_name = "bob"
216 | @subscriber.create.should be_true
217 | end
218 |
219 | it "should return nil if the data is invalid" do
220 | stub_request(:post, spreedly_url("/subscribers.xml")).
221 | to_return(:body => fixture("invalid_subscriber.xml"), :status => 422)
222 |
223 | @subscriber = RSpreedly::Subscriber.new
224 | @subscriber.create.should be_nil
225 | end
226 |
227 | it "should return nil if there is already a subscriber with that ID" do
228 | stub_request(:post, spreedly_url("/subscribers.xml")).
229 | to_return(:body => fixture("existing_subscriber.xml"), :status => 403)
230 |
231 | @subscriber = RSpreedly::Subscriber.new
232 | @subscriber.customer_id = 42
233 | @subscriber.screen_name = "bob"
234 | @subscriber.create.should be_nil
235 | end
236 | end
237 |
238 | describe "#update!" do
239 | before(:each) do
240 | @subscriber = RSpreedly::Subscriber.new
241 | end
242 |
243 | describe "with basic information" do
244 | it "should return true if successful" do
245 | stub_request(:put, spreedly_url("/subscribers/42.xml")).
246 | to_return(:status => 200)
247 |
248 | @subscriber.customer_id = 42
249 | @subscriber.email = "new@email.com"
250 | @subscriber.update!.should be_true
251 | end
252 |
253 | it "should raise NotFound if the subscriber doesn't exist" do
254 | stub_request(:put, spreedly_url("/subscribers/400.xml")).
255 | to_return(:body => fixture("subscriber_not_found.xml"), :status => 404)
256 |
257 | @subscriber.customer_id = 400
258 | lambda{
259 | @subscriber.update!
260 | }.should raise_error(RSpreedly::Error::NotFound)
261 | end
262 |
263 | it "should raise BadRequest if the data is invalid" do
264 | stub_request(:put, spreedly_url("/subscribers/42.xml")).
265 | to_return(:body => fixture("invalid_update.xml"), :status => 422)
266 |
267 | @subscriber.customer_id = 42
268 | lambda{
269 | @subscriber.update!
270 | }.should raise_error(RSpreedly::Error::BadRequest)
271 | end
272 | end
273 |
274 | describe "with new_customer_id" do
275 | it "should return true if successful" do
276 | stub_request(:put, spreedly_url("/subscribers/42.xml")).
277 | to_return(:status => 200)
278 |
279 | @subscriber.customer_id = 42
280 | @subscriber.new_customer_id = 43
281 | @subscriber.email = "new@email.com"
282 | @subscriber.update!.should be_true
283 | end
284 |
285 | it "should raise NotFound if the subscriber doesn't exist" do
286 | stub_request(:put, spreedly_url("/subscribers/400.xml")).
287 | to_return(:body => fixture("subscriber_not_found.xml"), :status => 404)
288 |
289 | @subscriber.customer_id = 400
290 | @subscriber.new_customer_id = 401
291 | lambda{
292 | @subscriber.update!
293 | }.should raise_error(RSpreedly::Error::NotFound)
294 | end
295 |
296 | it "should raise BadRequest if the data is invalid" do
297 | stub_request(:put, spreedly_url("/subscribers/.xml")).
298 | to_return(:body => fixture("invalid_update.xml"), :status => 422)
299 |
300 | @subscriber.new_customer_id = 43
301 | lambda{
302 | @subscriber.update!
303 | }.should raise_error(RSpreedly::Error::BadRequest)
304 | end
305 | end
306 |
307 | describe "with payment information" do
308 | before(:each) do
309 | @subscriber.customer_id = 42
310 | @subscriber.payment_method = RSpreedly::PaymentMethod::CreditCard.new(:number => "1233445", :verification_value => "234")
311 | end
312 |
313 | it "should return true if successful" do
314 | stub_request(:put, spreedly_url("/subscribers/42.xml")).
315 | to_return(:status => 200)
316 |
317 | @subscriber.update!.should be_true
318 | end
319 |
320 | it "should raise NotFound if the subscriber doesn't exist" do
321 | stub_request(:put, spreedly_url("/subscribers/42.xml")).
322 | to_return(:body => fixture("subscriber_not_found.xml"), :status => 404)
323 |
324 | lambda{
325 | @subscriber.update!
326 | }.should raise_error(RSpreedly::Error::NotFound)
327 | end
328 |
329 | it "should raise BadRequest if the data is invalid" do
330 | stub_request(:put, spreedly_url("/subscribers/42.xml")).
331 | to_return(:body => fixture("invalid_update.xml"), :status => 422)
332 |
333 | lambda{
334 | @subscriber.update!
335 | }.should raise_error(RSpreedly::Error::BadRequest)
336 | end
337 |
338 | it "should raise Forbidden if the data is invalid" do
339 | stub_request(:put, spreedly_url("/subscribers/42.xml")).
340 | to_return(:body => fixture("invalid_update.xml"), :status => 403)
341 |
342 | lambda{
343 | @subscriber.update!
344 | }.should raise_error(RSpreedly::Error::Forbidden)
345 | end
346 | end
347 | end
348 |
349 | describe "#update" do
350 | before(:each) do
351 | @subscriber = RSpreedly::Subscriber.new
352 | end
353 |
354 | describe "with basic information" do
355 | it "should return true if successful" do
356 | stub_request(:put, spreedly_url("/subscribers/42.xml")).
357 | to_return(:status => 200)
358 |
359 | @subscriber.customer_id = 42
360 | @subscriber.email = "new@email.com"
361 | @subscriber.update!.should be_true
362 | end
363 |
364 | it "should return nil if the subscriber doesn't exist" do
365 | stub_request(:put, spreedly_url("/subscribers/400.xml")).
366 | to_return(:body => fixture("subscriber_not_found.xml"), :status => 404)
367 |
368 | @subscriber.customer_id = 400
369 | @subscriber.update.should be_nil
370 | end
371 |
372 | it "should return nil if the data is invalid" do
373 | stub_request(:put, spreedly_url("/subscribers/42.xml")).
374 | to_return(:body => fixture("invalid_update.xml"), :status => 422)
375 |
376 | @subscriber.customer_id = 42
377 | @subscriber.update.should be_nil
378 | end
379 | end
380 | end
381 |
382 | describe "#destroy" do
383 | before(:each) do
384 | @subscriber = RSpreedly::Subscriber.new(:customer_id => 42)
385 | end
386 |
387 | it "should return nil if the subscriber doesn't exist" do
388 | stub_request(:delete, spreedly_url("/subscribers/42.xml")).
389 | to_return(:body => fixture("subscriber_not_found.xml"), :status => 404)
390 |
391 | @subscriber.destroy.should be_nil
392 | end
393 |
394 | it "should return true if successful" do
395 | stub_request(:delete, spreedly_url("/subscribers/42.xml")).
396 | to_return(:status => 200)
397 |
398 | @subscriber.destroy.should be_true
399 | end
400 | end
401 |
402 | describe "#comp_subscription" do
403 | before(:each) do
404 | @subscriber = RSpreedly::Subscriber.new(:customer_id => 42, :feature_level => "Lowly")
405 | @subscription = RSpreedly::ComplimentarySubscription.new(:duration_quantity => 42, :duration_units => "months", :feature_level => "Pro")
406 | end
407 |
408 | it "should return true if successful" do
409 | stub_request(:post, spreedly_url("/subscribers/42/complimentary_subscriptions.xml")).
410 | to_return(:body => fixture("complimentary_success.xml"), :status => 201)
411 |
412 | @subscriber.comp_subscription(@subscription).should be_true
413 | end
414 |
415 | it "should update the subscriber if successful" do
416 | stub_request(:post, spreedly_url("/subscribers/42/complimentary_subscriptions.xml")).
417 | to_return(:body => fixture("complimentary_success.xml"), :status => 201)
418 |
419 | lambda{
420 | @subscriber.comp_subscription(@subscription)
421 | }.should change(@subscriber, :feature_level).to("Pro")
422 | end
423 |
424 | it "should raise NotFound if the subscriber doesn't exist" do
425 | stub_request(:post, spreedly_url("/subscribers/42/complimentary_subscriptions.xml")).
426 | to_return(:body => fixture("subscriber_not_found.xml"), :status => 404)
427 |
428 | lambda{
429 | @subscriber.comp_subscription(@subscription)
430 | }.should raise_error(RSpreedly::Error::NotFound)
431 | end
432 |
433 | it "should raise BadRequest if validation fails on the subscription" do
434 | stub_request(:post, spreedly_url("/subscribers/42/complimentary_subscriptions.xml")).
435 | to_return(:body => fixture("complimentary_not_valid.xml"), :status => 422)
436 |
437 | lambda{
438 | @subscriber.comp_subscription(@subscription)
439 | }.should raise_error(RSpreedly::Error::BadRequest)
440 | end
441 |
442 | it "should raise Forbidden if the subscriber is active" do
443 | stub_request(:post, spreedly_url("/subscribers/42/complimentary_subscriptions.xml")).
444 | to_return(:body => fixture("complimentary_failed_active.xml"), :status => 403)
445 |
446 | lambda{
447 | @subscriber.comp_subscription(@subscription)
448 | }.should raise_error(RSpreedly::Error::Forbidden)
449 | end
450 | end
451 |
452 | describe "#comp_time_extension" do
453 | before(:each) do
454 | @subscriber = RSpreedly::Subscriber.new(:customer_id => 42, :feature_level => "Lowly")
455 | @subscription = RSpreedly::ComplimentaryTimeExtension.new(:duration_quantity => 42, :duration_units => "months")
456 | end
457 |
458 | it "should return true if successful" do
459 | stub_request(:post, spreedly_url("/subscribers/42/complimentary_time_extensions.xml")).
460 | to_return(:body => fixture("complimentary_success.xml"), :status => 201)
461 |
462 | @subscriber.comp_time_extension(@subscription).should be_true
463 | end
464 |
465 | it "should update the subscriber if successful" do
466 | stub_request(:post, spreedly_url("/subscribers/42/complimentary_time_extensions.xml")).
467 | to_return(:body => fixture("complimentary_success.xml"), :status => 201)
468 |
469 | lambda{
470 | @subscriber.comp_time_extension(@subscription)
471 | }.should change(@subscriber, :active_until).to(Time.parse("Sun Feb 21 19:04:28 UTC 2010"))
472 | end
473 |
474 | it "should raise NotFound if the subscriber doesn't exist" do
475 | stub_request(:post, spreedly_url("/subscribers/42/complimentary_time_extensions.xml")).
476 | to_return(:body => fixture("subscriber_not_found.xml"), :status => 404)
477 |
478 | lambda{
479 | @subscriber.comp_time_extension(@subscription)
480 | }.should raise_error(RSpreedly::Error::NotFound)
481 | end
482 |
483 | it "should raise BadRequest if validation fails on the subscription" do
484 | stub_request(:post, spreedly_url("/subscribers/42/complimentary_time_extensions.xml")).
485 | to_return(:body => fixture("complimentary_not_valid.xml"), :status => 422)
486 |
487 | lambda{
488 | @subscriber.comp_time_extension(@subscription)
489 | }.should raise_error(RSpreedly::Error::BadRequest)
490 | end
491 |
492 | it "should raise Forbidden if the subscriber is inactive" do
493 | stub_request(:post, spreedly_url("/subscribers/42/complimentary_time_extensions.xml")).
494 | to_return(:body => fixture("complimentary_failed_inactive.xml"), :status => 403)
495 |
496 | lambda{
497 | @subscriber.comp_time_extension(@subscription)
498 | }.should raise_error(RSpreedly::Error::Forbidden)
499 | end
500 | end
501 |
502 | describe "#credit" do
503 | before(:each) do
504 | @subscriber = RSpreedly::Subscriber.new(:customer_id => 42, :feature_level => "Lowly")
505 | @credit_amount = 5
506 | end
507 |
508 | it "should return true if successful" do
509 | stub_request(:post, spreedly_url("/subscribers/42/credit.xml")).
510 | to_return(:body => fixture("credit_success.xml"), :status => 201)
511 |
512 | @subscriber.credit(@credit_amount).should be_true
513 | @subscriber.store_credit.should == @credit_amount
514 | end
515 |
516 | it "should raise NotFound if the subscriber doesn't exist" do
517 | stub_request(:post, spreedly_url("/subscribers/42/credit.xml")).
518 | to_return(:body => fixture("subscriber_not_found.xml"), :status => 404)
519 |
520 | lambda{
521 | @subscriber.credit(@credit_amount)
522 | }.should raise_error(RSpreedly::Error::NotFound)
523 | end
524 |
525 | it "should raise BadRequest if validation fails on the subscription" do
526 | stub_request(:post, spreedly_url("/subscribers/42/credit.xml")).
527 | to_return(:body => fixture("credit_not_valid.xml"), :status => 422)
528 |
529 | lambda{
530 | @subscriber.credit(@credit_amount)
531 | }.should raise_error(RSpreedly::Error::BadRequest)
532 | end
533 |
534 | it "should increment store_credit" do
535 | stub_request(:post, spreedly_url("/subscribers/42/credit.xml")).
536 | to_return(:body => fixture("credit_success.xml"), :status => 201)
537 |
538 | @subscriber.store_credit = 5
539 | @subscriber.credit(5).should be_true
540 | @subscriber.store_credit.should == 10
541 | end
542 |
543 | it "should decrement store_credit" do
544 | stub_request(:post, spreedly_url("/subscribers/42/credit.xml")).
545 | to_return(:body => fixture("credit_success.xml"), :status => 201)
546 |
547 | @subscriber.store_credit = 5
548 | @subscriber.credit(-5).should be_true
549 | @subscriber.store_credit.should == 0
550 | end
551 | end
552 |
553 | describe "#stop_auto_renew" do
554 | before(:each) do
555 | @subscriber = RSpreedly::Subscriber.new(:customer_id => 42)
556 | end
557 |
558 | it "should return true if successful" do
559 | stub_request(:post, spreedly_url("/subscribers/42/stop_auto_renew.xml")).
560 | to_return(:status => 200)
561 |
562 | @subscriber.stop_auto_renew.should be_true
563 | end
564 |
565 | it "should raise NotFound if the subscriber doesn't exist" do
566 | stub_request(:post, spreedly_url("/subscribers/42/stop_auto_renew.xml")).
567 | to_return(:body => fixture("subscriber_not_found.xml"), :status => 404)
568 |
569 | lambda{
570 | @subscriber.stop_auto_renew
571 | }.should raise_error(RSpreedly::Error::NotFound)
572 | end
573 | end
574 |
575 | describe "#subscribe_to_free_trial" do
576 | before(:each) do
577 | @subscriber = RSpreedly::Subscriber.new(:customer_id => 42)
578 | @plan = RSpreedly::SubscriptionPlan.new(:id => 2533)
579 | end
580 |
581 | it "should return true if successful" do
582 | stub_request(:post, spreedly_url("/subscribers/42/subscribe_to_free_trial.xml")).
583 | to_return(:body => fixture("free_plan_success.xml"), :status => 200)
584 |
585 | @subscriber.subscribe_to_free_trial(@plan).should be_true
586 | end
587 |
588 | it "should update the subscriber if successful" do
589 | stub_request(:post, spreedly_url("/subscribers/42/subscribe_to_free_trial.xml")).
590 | to_return(:body => fixture("free_plan_success.xml"), :status => 200)
591 |
592 | lambda{
593 | @subscriber.subscribe_to_free_trial(@plan)
594 | }.should change(@subscriber, :grace_until).to(Time.parse("2013-05-02T00:07:37Z"))
595 | end
596 |
597 | it "should raise NotFound if the subscriber doesn't exist" do
598 | stub_request(:post, spreedly_url("/subscribers/42/subscribe_to_free_trial.xml")).
599 | to_return(:body => fixture("subscriber_not_found.xml"), :status => 404)
600 |
601 | lambda{
602 | @subscriber.subscribe_to_free_trial(@plan)
603 | }.should raise_error(RSpreedly::Error::NotFound)
604 | end
605 |
606 | it "should raise NotFound if the plan doesn't exist" do
607 | stub_request(:post, spreedly_url("/subscribers/42/subscribe_to_free_trial.xml")).
608 | to_return(:body => fixture("plan_not_found.xml"), :status => 404)
609 |
610 | lambda{
611 | @subscriber.subscribe_to_free_trial(@plan)
612 | }.should raise_error(RSpreedly::Error::NotFound)
613 | end
614 |
615 | it "should raise BadRequest if no plan is specified" do
616 | stub_request(:post, spreedly_url("/subscribers/42/subscribe_to_free_trial.xml")).
617 | to_return(:body => fixture("free_plan_not_set.xml"), :status => 422)
618 |
619 | @plan.id = nil
620 | lambda{
621 | @subscriber.subscribe_to_free_trial(@plan)
622 | }.should raise_error(RSpreedly::Error::BadRequest)
623 | end
624 |
625 | it "should raise Forbidden if the subscriber isn't elligable" do
626 | stub_request(:post, spreedly_url("/subscribers/42/subscribe_to_free_trial.xml")).
627 | to_return(:body => fixture("free_plan_not_elligable.xml"), :status => 403)
628 |
629 | lambda{
630 | @subscriber.subscribe_to_free_trial(@plan)
631 | }.should raise_error(RSpreedly::Error::Forbidden)
632 | end
633 |
634 | it "should raise Forbidden if the plan isn't a free trial" do
635 | stub_request(:post, spreedly_url("/subscribers/42/subscribe_to_free_trial.xml")).
636 | to_return(:body => fixture("free_plan_not_free.xml"), :status => 403)
637 |
638 | lambda{
639 | @subscriber.subscribe_to_free_trial(@plan)
640 | }.should raise_error(RSpreedly::Error::Forbidden)
641 | end
642 | end
643 |
644 | describe "#allow_free_trial" do
645 | before(:each) do
646 | @subscriber = RSpreedly::Subscriber.new(:customer_id => 42)
647 | end
648 |
649 | it "should return true if successful" do
650 | stub_request(:post, spreedly_url("/subscribers/42/allow_free_trial.xml")).
651 | to_return(:body => fixture("free_plan_success.xml"), :status => 200)
652 |
653 | @subscriber.allow_free_trial.should be_true
654 | end
655 |
656 | it "should update the subscriber if successful" do
657 | stub_request(:post, spreedly_url("/subscribers/42/allow_free_trial.xml")).
658 | to_return(:body => fixture("free_plan_success.xml"), :status => 200)
659 |
660 | lambda{
661 | @subscriber.allow_free_trial
662 | }.should change(@subscriber, :grace_until).to(Time.parse("2013-05-02T00:07:37Z"))
663 | end
664 |
665 | it "should raise NotFound if the subscriber doesn't exist" do
666 | stub_request(:post, spreedly_url("/subscribers/42/allow_free_trial.xml")).
667 | to_return(:body => fixture("subscriber_not_found.xml"), :status => 404)
668 |
669 | lambda{
670 | @subscriber.allow_free_trial
671 | }.should raise_error(RSpreedly::Error::NotFound)
672 | end
673 | end
674 |
675 | describe "#grant_lifetime_subscription" do
676 |
677 | before(:each) do
678 | @subscriber = RSpreedly::Subscriber.new(:customer_id => 42)
679 | end
680 |
681 | it "should return true if successful" do
682 | stub_request(:post, spreedly_url("/subscribers/42/lifetime_complimentary_subscriptions.xml")).
683 | to_return(:body => fixture("lifetime_subscription_success.xml"), :status => 200)
684 |
685 | @subscriber.grant_lifetime_subscription("Something").should be_true
686 | end
687 |
688 | it "should update the subscriber's lifetime_subscription if successful" do
689 | stub_request(:post, spreedly_url("/subscribers/42/lifetime_complimentary_subscriptions.xml")).
690 | to_return(:body => fixture("lifetime_subscription_success.xml"), :status => 200)
691 |
692 | lambda{
693 | @subscriber.grant_lifetime_subscription("Something")
694 | }.should change(@subscriber, :lifetime_subscription).to(true)
695 | end
696 |
697 | it "should update the subscriber's feature_level if successful" do
698 | stub_request(:post, spreedly_url("/subscribers/42/lifetime_complimentary_subscriptions.xml")).
699 | to_return(:body => fixture("lifetime_subscription_success.xml"), :status => 200)
700 |
701 | lambda{
702 | @subscriber.grant_lifetime_subscription("Something")
703 | }.should change(@subscriber, :feature_level).to("Something")
704 | end
705 |
706 | it "should raise NotFound if the subscriber doesn't exist" do
707 | stub_request(:post, spreedly_url("/subscribers/42/lifetime_complimentary_subscriptions.xml")).
708 | to_return(:body => fixture("subscriber_not_found.xml"), :status => 404)
709 |
710 | lambda{
711 | @subscriber.grant_lifetime_subscription("Something")
712 | }.should raise_error(RSpreedly::Error::NotFound)
713 | end
714 |
715 | it "should raise BadRequest if the feature level is blank" do
716 | stub_request(:post, spreedly_url("/subscribers/42/lifetime_complimentary_subscriptions.xml")).
717 | to_return(:body => fixture("feature_level_blank.xml"), :status => 422)
718 |
719 | lambda{
720 | @subscriber.grant_lifetime_subscription("Something")
721 | }.should raise_error(RSpreedly::Error::BadRequest)
722 | end
723 |
724 | it "should return subscribe_link for subscriber without return_url" do
725 | @subscriber.subscribe_link('99', 'spreedster').should == "https://spreedly.com/your-site-name/subscribers/42/subscribe/99/spreedster"
726 | end
727 |
728 | it "should return subscribe_link for subscriber with return_url" do
729 | return_url = "http://mydomain.com/response"
730 | @subscriber.subscribe_link('99', 'spreedster', return_url).should == "https://spreedly.com/your-site-name/subscribers/42/subscribe/99/spreedster?return_url=http://mydomain.com/response"
731 | end
732 |
733 | it "should return subscription_link for subscriber without return_url" do
734 | @subscriber.stub(:token).and_return("my-token")
735 | @subscriber.subscription_link.should == "https://spreedly.com/your-site-name/subscriber_accounts/my-token"
736 | end
737 |
738 | it "should return subscription_link for subscriber with return_url" do
739 | @subscriber.stub(:token).and_return("my-token")
740 | return_url = "http://mydomain.com/response"
741 | @subscriber.subscription_link(return_url).should == "https://spreedly.com/your-site-name/subscriber_accounts/my-token?return_url=http://mydomain.com/response"
742 | end
743 | end
744 | end
745 |
--------------------------------------------------------------------------------