spreedly_subscriptions 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/HISTORY.md ADDED
@@ -0,0 +1,69 @@
1
+ # Changelog
2
+
3
+ ## 1.4.0
4
+
5
+ * Add accessor for invoices
6
+ * Add accessor for last successful invoice
7
+
8
+ ## 1.3.6
9
+
10
+ * Modernize packaging
11
+ * Improve handling of bad credentials
12
+
13
+ ## 1.3.5
14
+
15
+ * Move to Spreedly repo.
16
+
17
+ ## 1.3.4
18
+
19
+ * Add result body to comp validation failure. [ntalbott]
20
+
21
+ ## 1.3.3
22
+
23
+ * Add support for return_url in Spreedly.edit_subscriber_url [jjthrash]
24
+ * Add support for return_url in Spreedly.subscribe_url [jjthrash]
25
+
26
+ ## 1.3.2
27
+
28
+ * Added methods on subscriber: update, allow_free_trial,
29
+ add_fee [mateuszzawisza]
30
+ * Fixing tests: generate spreedly url and edit url working with all
31
+ site names [mateuszzawisza]
32
+ * Add support for pre-population of the subscribe page via the subscribe
33
+ url. [davemcp]
34
+
35
+ ## 1.3.1
36
+
37
+ * Handle new error reporting when creating a subscriber.
38
+ * Use a more sane way of setting attributes in the mock, and make plans have a
39
+ plan type [seancribbs].
40
+ * Allow optional arguments to be passed when creating subscribers [jaknowlden].
41
+
42
+ ## 1.3.0 / 2009-06-04
43
+
44
+ * Properly handle invalid Subscriber lookup [karnowski].
45
+ * Added support for stopping auto renew [scottmotte, dsimard].
46
+
47
+ ## 1.2.2 / 2009-05-11
48
+
49
+ * Fixed an error in the README [karnowski].
50
+
51
+ ## 1.2.1 / 2009-04-30
52
+
53
+ * A few documentation tweaks.
54
+
55
+ ## 1.2.0 / 2009-04-30
56
+
57
+ * Added mock support for Subscriber#activate_free_trial [scottmotte].
58
+ * Added tests for Subscriber#activate_free_trial.
59
+
60
+ ## 1.1.0 / 2009-04-24
61
+
62
+ * Compatibility with the latest hoe and HTTParty [seancribbs].
63
+ * Added Subscriber.delete! [scottmotte].
64
+ * Added Subscriber#activate_free_trial [scottmotte].
65
+
66
+ ## 1.0.0 / 2009-03-17
67
+
68
+ * Initial release.
69
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2009-2013 Spreedly, Inc.. All Rights Reserved.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ 'Software'), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,42 @@
1
+ # Spreedly gem
2
+
3
+ * https://github.com/spreedly/spreedly_subscriptions_gem
4
+
5
+ ## Description
6
+
7
+ The spreedly_subscriptions gem provides a convenient Ruby wrapper for the Spreedly
8
+ Subscriptions API.
9
+
10
+ ## Features
11
+
12
+ * Makes it easy to get started.
13
+ * Fully tested.
14
+ * (Mostly) fully substitutable mock implementation for fast tests.
15
+ * Great example code.
16
+
17
+ ## Synopsis
18
+
19
+ # For real
20
+ require 'spreedly/subscriptions'
21
+ Spreedly::Subscriptions.configure('site short name', 'crazy hash token')
22
+ url = Spreedly::Subscriptions.subscribe_url('customer id', 'plan id')
23
+ subscriber = Spreedly::Subscriptions::Subscriber.find('customer id')
24
+ subscriber.active?
25
+
26
+ # For fast tests
27
+ require 'spreedly/subscriptions/mock'
28
+ Spreedly::Subscriptions.configure('site short name', 'crazy hash token')
29
+ url = Spreedly::Subscriptions.subscribe_url('customer id', 'plan id')
30
+ subscriber = Spreedly::Subscriptions::Subscriber.find('customer id')
31
+ subscriber.active?
32
+
33
+ Yup, they're exactly the same except for the require and the speed!
34
+
35
+ ## Requirements
36
+
37
+ * A Spreedly Subscriptions account.
38
+ * HTTParty
39
+
40
+ ## Install
41
+
42
+ $ gem install spreedly_subscriptions
@@ -0,0 +1,48 @@
1
+ Dir[File.dirname(__FILE__) + '/../../vendor/*'].each do |directory|
2
+ next unless File.directory?(directory)
3
+ $LOAD_PATH.unshift File.expand_path(directory + '/lib')
4
+ end
5
+
6
+ require 'uri'
7
+ require 'bigdecimal'
8
+
9
+ require 'spreedly/subscriptions/version'
10
+
11
+ module Spreedly
12
+ module Subscriptions
13
+
14
+ # Generates a subscribe url for the given user id and plan.
15
+ # Options:
16
+ # :screen_name => a screen name for the user (shows up in the admin UI)
17
+ # :email => pre-populate the email field
18
+ # :first_name => pre-populate the first name field
19
+ # :last_name => pre-populate the last name field
20
+ def self.subscribe_url(id, plan, options={})
21
+ %w(screen_name email first_name last_name return_url).each do |option|
22
+ options[option.to_sym] &&= URI.escape(options[option.to_sym])
23
+ end
24
+
25
+ screen_name = options.delete(:screen_name)
26
+ params = %w(email first_name last_name return_url).select{|e| options[e.to_sym]}.collect{|e| "#{e}=#{options[e.to_sym]}"}.join('&')
27
+
28
+ url = "https://spreedly.com/#{site_name}/subscribers/#{id}/subscribe/#{plan}"
29
+ url << "/#{screen_name}" if screen_name
30
+ url << '?' << params unless params == ''
31
+
32
+ url
33
+ end
34
+
35
+ # Generates an edit subscriber for the given subscriber token. The
36
+ # token is returned with the subscriber info (i.e. by
37
+ # Subscriber.find).
38
+ def self.edit_subscriber_url(token, return_url = nil)
39
+ "https://spreedly.com/#{site_name}/subscriber_accounts/#{token}" +
40
+ if return_url
41
+ "?return_url=#{URI.escape(return_url)}"
42
+ else
43
+ ''
44
+ end
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,224 @@
1
+ require 'spreedly/subscriptions/common'
2
+
3
+ raise "Real Spreedly already required!" if defined?(Spreedly::REAL)
4
+
5
+ module Spreedly
6
+ module Subscriptions
7
+ MOCK = "mock"
8
+
9
+ def self.configure(name, token)
10
+ @site_name = name
11
+ end
12
+
13
+ def self.site_name
14
+ @site_name
15
+ end
16
+
17
+ class Resource
18
+ def self.attributes
19
+ @attributes ||= {}
20
+ end
21
+
22
+ def self.attributes=(value)
23
+ @attributes = value
24
+ end
25
+
26
+ attr_reader :attributes
27
+ def initialize(params={})
28
+ @attributes = self.class.attributes.inject({}){|a,(k,v)| a[k.to_sym] = v.call; a}
29
+ params.each {|k,v| @attributes[k.to_sym] = v }
30
+ end
31
+
32
+ def id
33
+ @attributes[:id]
34
+ end
35
+
36
+ def method_missing(method, *args)
37
+ if method.to_s =~ /\?$/
38
+ send(method.to_s[0..-2], *args)
39
+ elsif @attributes.include?(method)
40
+ @attributes[method]
41
+ else
42
+ super
43
+ end
44
+ end
45
+ end
46
+
47
+ class Subscriber < Resource
48
+ self.attributes = {
49
+ :created_at => proc{Time.now},
50
+ :token => proc{(rand * 1000).round},
51
+ :active => proc{false},
52
+ :store_credit => proc{BigDecimal("0.0")},
53
+ :active_until => proc{nil},
54
+ :feature_level => proc{""},
55
+ :on_trial => proc{false},
56
+ :recurring => proc{false},
57
+ :eligible_for_free_trial => proc{false}
58
+ }
59
+
60
+ def self.wipe! # :nodoc: all
61
+ @subscribers = nil
62
+ end
63
+
64
+ def self.create!(id, *args) # :nodoc: all
65
+ optional_attrs = args.last.is_a?(::Hash) ? args.pop : {}
66
+ email, screen_name = args
67
+ sub = new({:customer_id => id, :email => email, :screen_name => screen_name}.merge(optional_attrs))
68
+
69
+ if subscribers[sub.id]
70
+ raise "Could not create subscriber: already exists."
71
+ end
72
+
73
+ subscribers[sub.id] = sub
74
+ sub
75
+ end
76
+
77
+ def self.delete!(id)
78
+ subscribers.delete(id)
79
+ end
80
+
81
+ def self.find(id)
82
+ subscribers[id]
83
+ end
84
+
85
+ def self.subscribers
86
+ @subscribers ||= {}
87
+ end
88
+
89
+ def self.all
90
+ @subscribers.values
91
+ end
92
+
93
+ def initialize(params={})
94
+ super
95
+ if !id || id == ''
96
+ raise "Could not create subscriber: Customer ID can't be blank."
97
+ end
98
+ @invoices ||= []
99
+ end
100
+
101
+ def id
102
+ @attributes[:customer_id]
103
+ end
104
+
105
+ def update(args)
106
+ args.each_pair do |key, value|
107
+ if @attributes.has_key?(key)
108
+ @attributes[key] = value
109
+ end
110
+ end
111
+ end
112
+
113
+ def comp(quantity, units, feature_level=nil)
114
+ raise "Could not comp subscriber: no longer exists." unless self.class.find(id)
115
+ raise "Could not comp subscriber: validation failed." unless units && quantity
116
+ current_active_until = (active_until || Time.now)
117
+ @attributes[:active_until] = case units
118
+ when 'days'
119
+ current_active_until + (quantity.to_i * 86400)
120
+ when 'months'
121
+ current_active_until + (quantity.to_i * 30 * 86400)
122
+ end
123
+ @attributes[:feature_level] = feature_level if feature_level
124
+ @attributes[:active] = true
125
+ end
126
+
127
+ def activate_free_trial(plan_id)
128
+ raise "Could not activate free trial for subscriber: validation failed. missing subscription plan id" unless plan_id
129
+ raise "Could not active free trial for subscriber: subscriber or subscription plan no longer exists." unless self.class.find(id) && SubscriptionPlan.find(plan_id)
130
+ raise "Could not activate free trial for subscriber: subscription plan either 1) isn't a free trial, 2) the subscriber is not eligible for a free trial, or 3) the subscription plan is not enabled." if (on_trial? and !eligible_for_free_trial?)
131
+ @attributes[:on_trial] = true
132
+ plan = SubscriptionPlan.find(plan_id)
133
+ comp(plan.duration_quantity, plan.duration_units, plan.feature_level)
134
+ end
135
+
136
+ def allow_free_trial
137
+ @attributes[:eligible_for_free_trial] = true
138
+ end
139
+
140
+ def stop_auto_renew
141
+ raise "Could not stop auto renew for subscriber: subscriber does not exist." unless self.class.find(id)
142
+ @attributes[:recurring] = false
143
+ end
144
+
145
+ def subscribe(plan_id, card_number="4222222222222")
146
+ plan = SubscriptionPlan.find(plan_id)
147
+ @invoices.unshift(Invoice.new(
148
+ amount: (@invoices.select{|invoice| invoice.closed?}.size > 0 ? 0 : plan.amount),
149
+ closed: false
150
+ ))
151
+
152
+ return unless card_number == "4222222222222"
153
+
154
+ @invoices.first.attributes[:closed] = true
155
+ @attributes[:recurring] = true
156
+ comp(plan.duration_quantity, plan.duration_units, plan.feature_level)
157
+ end
158
+
159
+ def add_fee(args)
160
+ raise "Unprocessable Entity" unless (args.keys & [:amount, :group, :name]).size == 3
161
+ raise "Unprocessable Entity" unless active?
162
+ nil
163
+ end
164
+
165
+ def invoices
166
+ @invoices
167
+ end
168
+
169
+ def last_successful_invoice
170
+ @invoices.detect{|invoice| invoice.closed?}
171
+ end
172
+ end
173
+
174
+ class Invoice < Resource
175
+ end
176
+
177
+ class SubscriptionPlan < Resource
178
+ self.attributes = {
179
+ :plan_type => proc{'regular'},
180
+ :feature_level => proc{''}
181
+ }
182
+
183
+ def self.all
184
+ plans.values
185
+ end
186
+
187
+ def self.find(id)
188
+ plans[id.to_i]
189
+ end
190
+
191
+ def self.plans
192
+ @plans ||= {
193
+ 1 => new(
194
+ :id => 1,
195
+ :name => 'Default mock plan',
196
+ :duration_quantity => 1,
197
+ :duration_units => 'days',
198
+ :amount => 6
199
+ ),
200
+ 2 => new(
201
+ :id => 2,
202
+ :name => 'Test Free Trial Plan',
203
+ :plan_type => 'free_trial',
204
+ :duration_quantity => 1,
205
+ :duration_units => 'days',
206
+ :amount => 11
207
+ ),
208
+ 3 => new(
209
+ :id => 3,
210
+ :name => 'Test Regular Plan',
211
+ :duration_quantity => 1,
212
+ :duration_units => 'days',
213
+ :amount => 17
214
+ )
215
+ }
216
+ end
217
+
218
+ def trial?
219
+ (plan_type == "free_trial")
220
+ end
221
+ end
222
+
223
+ end
224
+ end
@@ -0,0 +1,29 @@
1
+ require 'mechanize'
2
+
3
+ module Spreedly
4
+ module Subscriptions
5
+ class Subscriber
6
+ # This method is *strictly* for use when testing, and will
7
+ # probably only work against a test Spreedly site anyhow.
8
+ def subscribe(plan_id, card_number="4222222222222")
9
+ agent = Mechanize.new
10
+ page = agent.get(Spreedly::Subscriptions.subscribe_url(id, plan_id))
11
+ page = page.forms.first.submit
12
+ form = page.forms.first
13
+ form['credit_card[first_name]'] = 'Joe'
14
+ form['credit_card[last_name]'] = 'Bob'
15
+ form['subscriber[email]'] = 'joe@example.com'
16
+ form['credit_card[number]'] = card_number
17
+ form['credit_card[card_type]'] = 'visa'
18
+ form['credit_card[verification_value]'] = '234'
19
+ form['credit_card[month]'] = '1'
20
+ form['credit_card[year]'] = '2024'
21
+ page = form.click_button
22
+
23
+ if card_number == "4222222222222"
24
+ raise "Subscription didn't go through" unless page.title == "Thank you!"
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,5 @@
1
+ module Spreedly
2
+ module Subscriptions
3
+ VERSION = "2.0.0"
4
+ end
5
+ end
@@ -0,0 +1,289 @@
1
+ require 'spreedly/subscriptions/common'
2
+ require 'httparty'
3
+
4
+ raise "Mock Spreedly already required!" if defined?(Spreedly::MOCK)
5
+
6
+ =begin rdoc
7
+ Provides a convenient wrapper around the https://spreedly.com/info/integration
8
+ API. Instead of mucking around with http you can just
9
+ Spreedly::Subscriptions.configure and Spreedly::Subscriber.find. Much of the
10
+ functionality is hung off of the Spreedly::Subscriptions::Subscriber class, and there's also a
11
+ Spreedly::SubscriptionPlan class.
12
+
13
+ One of the goals of this wrapper is to keep your tests fast while
14
+ also keeping your app working. It does this by providing a drop-in
15
+ replacement for the real Spreedly functionality that skips the
16
+ network and gives you a simple (some might call it stupid)
17
+ implementation that will work for 90% of your tests. At least we
18
+ hope so.
19
+
20
+ Help us make the mock work better by telling us when it doesn't work
21
+ so we can improve it. Thanks!
22
+
23
+ ==Example mock usage:
24
+
25
+ if ENV["SPREEDLY"] == "REAL"
26
+ require 'spreedly/subscriptions'
27
+ else
28
+ require 'spreedly/subscriptions/mock'
29
+ end
30
+ =end
31
+
32
+ module Spreedly
33
+ module Subscriptions
34
+ REAL = "real" # :nodoc:
35
+
36
+ include HTTParty
37
+ headers 'Accept' => 'text/xml'
38
+ headers 'Content-Type' => 'text/xml'
39
+ format :xml
40
+
41
+ # Call this before you start using the API to set things up.
42
+ def self.configure(site_name, token)
43
+ base_uri "https://spreedly.com/api/v4/#{site_name}"
44
+ basic_auth token, 'X'
45
+ @site_name = site_name
46
+ end
47
+
48
+ def self.site_name # :nodoc:
49
+ @site_name
50
+ end
51
+
52
+ def self.to_xml_params(hash) # :nodoc:
53
+ hash.collect do |key, value|
54
+ tag = key.to_s.tr('_', '-')
55
+ result = "<#{tag}>"
56
+ if value.is_a?(Hash)
57
+ result << to_xml_params(value)
58
+ else
59
+ result << value.to_s
60
+ end
61
+ result << "</#{tag}>"
62
+ result
63
+ end.join('')
64
+ end
65
+
66
+ class Resource # :nodoc: all
67
+ def initialize(data)
68
+ @data = data
69
+ end
70
+
71
+ def id
72
+ @data["id"]
73
+ end
74
+
75
+ def method_missing(method, *args, &block)
76
+ if method.to_s =~ /\?$/
77
+ send(method.to_s[0..-2])
78
+ elsif @data.include?(method.to_s)
79
+ @data[method.to_s]
80
+ else
81
+ super
82
+ end
83
+ end
84
+ end
85
+
86
+ class Subscriber < Resource
87
+ # This will DELETE all the subscribers from the site.
88
+ #
89
+ # Only works for test sites (enforced on the Spreedly side).
90
+ def self.wipe!
91
+ Spreedly::Subscriptions.delete('/subscribers.xml')
92
+ end
93
+
94
+ # This will DELETE individual subscribers from the site. Pass in the customer_id.
95
+ #
96
+ # Only works for test sites (enforced on the Spreedly side).
97
+ def self.delete!(id)
98
+ Spreedly::Subscriptions.delete("/subscribers/#{id}.xml")
99
+ end
100
+
101
+ # Creates a new subscriber on Spreedly. The subscriber will NOT
102
+ # be active - they have to pay or you have to comp them for that
103
+ # to happen.
104
+ #
105
+ # Usage:
106
+ # Spreedly::Subscriptions.Subscriber.create!(id, email)
107
+ # Spreedly::Subscriptions.Subscriber.create!(id, email, screen_name)
108
+ # Spreedly::Subscriptions.Subscriber.create!(id, :email => email, :screen_name => screen_name)
109
+ # Spreedly::Subscriptions.Subscriber.create!(id, email, screen_name, :billing_first_name => first_name)
110
+ def self.create!(id, *args)
111
+ optional_attrs = args.last.is_a?(::Hash) ? args.pop : {}
112
+ email, screen_name = args
113
+ subscriber = {:customer_id => id, :email => email, :screen_name => screen_name}.merge(optional_attrs)
114
+ result = Spreedly::Subscriptions.post('/subscribers.xml', :body => Spreedly.to_xml_params(:subscriber => subscriber))
115
+ case result.code.to_s
116
+ when /2../
117
+ new(result['subscriber'])
118
+ when '403'
119
+ raise "Could not create subscriber: already exists."
120
+ when '422'
121
+ errors = [*result['errors']].collect{|e| e.last}
122
+ raise "Could not create subscriber: #{errors.join(', ')}"
123
+ else
124
+ raise "Could not create subscriber: result code #{result.code}."
125
+ end
126
+ end
127
+
128
+ # Looks a subscriber up by id.
129
+ def self.find(id)
130
+ xml = Spreedly::Subscriptions.get("/subscribers/#{id}.xml")
131
+ if [200, 404].include?(xml.code)
132
+ (xml.nil? || xml.empty? ? nil : new(xml['subscriber']))
133
+ else
134
+ raise "Could not find subscriber: result code #{xml.code}, body '#{xml.body}'"
135
+ end
136
+ end
137
+
138
+ # Returns all the subscribers in your site.
139
+ def self.all
140
+ Spreedly::Subscriptions.get('/subscribers.xml')['subscribers'].collect{|data| new(data)}
141
+ end
142
+
143
+ # Spreedly calls your id for the user the "customer id". This
144
+ # gives you a handy alias so you can just call it "id".
145
+ def id
146
+ customer_id
147
+ end
148
+
149
+ # Allows you to give a complimentary subscription (if the
150
+ # subscriber is inactive) or a complimentary time extension (if
151
+ # the subscriber is active). Automatically figures out which
152
+ # to do.
153
+ #
154
+ # Note: units must be one of "days" or "months" (Spreedly
155
+ # enforced).
156
+ def comp(quantity, units, feature_level=nil)
157
+ params = {:duration_quantity => quantity, :duration_units => units}
158
+ params[:feature_level] = feature_level if feature_level
159
+ raise "Feature level is required to comp an inactive subscriber" if !active? and !feature_level
160
+ endpoint = (active? ? "complimentary_time_extensions" : "complimentary_subscriptions")
161
+ result = Spreedly::Subscriptions.post("/subscribers/#{id}/#{endpoint}.xml", :body => Spreedly.to_xml_params(endpoint[0..-2] => params))
162
+ case result.code.to_s
163
+ when /2../
164
+ when '404'
165
+ raise "Could not comp subscriber: no longer exists."
166
+ when '422'
167
+ raise "Could not comp subscriber: validation failed (#{result.body})."
168
+ when '403'
169
+ raise "Could not comp subscriber: invalid comp type (#{endpoint})."
170
+ else
171
+ raise "Could not comp subscriber: result code #{result.code}."
172
+ end
173
+ end
174
+
175
+ # Activates a free trial on the subscriber.
176
+ # Requires plan_id of the free trial plan
177
+ def activate_free_trial(plan_id)
178
+ result = Spreedly::Subscriptions.post("/subscribers/#{id}/subscribe_to_free_trial.xml", :body =>
179
+ Spreedly::Subscriptions.to_xml_params(:subscription_plan => {:id => plan_id}))
180
+ case result.code.to_s
181
+ when /2../
182
+ when '404'
183
+ raise "Could not active free trial for subscriber: subscriber or subscription plan no longer exists."
184
+ when '422'
185
+ raise "Could not activate free trial for subscriber: validation failed. missing subscription plan id"
186
+ when '403'
187
+ raise "Could not activate free trial for subscriber: subscription plan either 1) isn't a free trial, 2) the subscriber is not eligible for a free trial, or 3) the subscription plan is not enabled."
188
+ else
189
+ raise "Could not activate free trial for subscriber: result code #{result.code}."
190
+ end
191
+ end
192
+
193
+ # Stop the auto renew of the subscriber such that their recurring subscription will no longer be renewed.
194
+ # usage: @subscriber.stop_auto_renew
195
+ def stop_auto_renew
196
+ result = Spreedly::Subscriptions.post("/subscribers/#{id}/stop_auto_renew.xml")
197
+ case result.code.to_s
198
+ when /2../
199
+ when '404'
200
+ raise "Could not stop auto renew for subscriber: subscriber does not exist."
201
+ else
202
+ raise "Could not stop auto renew for subscriber: result code #{result.code}."
203
+ end
204
+ end
205
+
206
+ # Update a Subscriber
207
+ # usage: @subscriber.update(:email => email, :screen_name => screen_name)
208
+ def update(args)
209
+ result = Spreedly::Subscriptions.put("/subscribers/#{id}.xml", :body => Spreedly.to_xml_params(:subscriber => args))
210
+
211
+ case result.code.to_s
212
+ when /2../
213
+ when '403'
214
+ raise "Could not update subscriber: new-customer-id is already in use."
215
+ when '404'
216
+ raise "Could not update subscriber: subscriber not found"
217
+ else
218
+ raise "Could not update subscriber: result code #{result.code}."
219
+ end
220
+ end
221
+
222
+ # Allow Another Free Trial
223
+ # usage: @subscriber.allow_free_trial
224
+ def allow_free_trial
225
+ result = Spreedly::Subscriptions.post("/subscribers/#{id}/allow_free_trial.xml")
226
+
227
+ case result.code.to_s
228
+ when /2../
229
+ else
230
+ raise "Could not allow subscriber to another trial: result code #{result.code}."
231
+ end
232
+ end
233
+
234
+ # Add a Fee to a Subscriber
235
+ # usage: @subscriber.add_fee(:amount => amount, :group => group_name, :description => description, :name => name)
236
+ def add_fee(args)
237
+ result = Spreedly::Subscriptions.post("/subscribers/#{id}/fees.xml", :body => Spreedly::Subscriptions.to_xml_params(:fee => args))
238
+
239
+ case result.code.to_s
240
+ when /2../
241
+ when '404'
242
+ raise "Not Found"
243
+ when '422'
244
+ raise "Unprocessable Entity - #{result.body}"
245
+ else
246
+ raise "Could not add fee to subscriber: result code #{result.code}."
247
+ end
248
+ end
249
+
250
+ # Get the invoices for the subscriber
251
+ def invoices
252
+ @invoices ||= @data["invoices"].collect{|i| Invoice.new(i)}
253
+ end
254
+
255
+ # Get the last successful invoice
256
+ def last_successful_invoice
257
+ invoices.detect do |invoice|
258
+ invoice.closed?
259
+ end
260
+ end
261
+ end
262
+
263
+ class Invoice < Resource
264
+ end
265
+
266
+ class SubscriptionPlan < Resource
267
+ # Returns all of the subscription plans defined in your site.
268
+ def self.all
269
+ xml = Spreedly::Subscriptions.get('/subscription_plans.xml')
270
+ if xml.code == 200
271
+ xml['subscription_plans'].collect{|data| new(data)}
272
+ else
273
+ raise "Could not list subscription plans: result code #{xml.code}, body '#{xml.body}'"
274
+ end
275
+ end
276
+
277
+ # Returns the subscription plan with the given id.
278
+ def self.find(id)
279
+ all.detect{|e| e.id.to_s == id.to_s}
280
+ end
281
+
282
+ # Convenience method for determining if this plan is a free trial plan or not.
283
+ def trial?
284
+ (plan_type == 'free_trial')
285
+ end
286
+ end
287
+
288
+ end
289
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: spreedly_subscriptions
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 2.0.0
6
+ platform: ruby
7
+ authors:
8
+ - Spreedly
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-22 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ none: false
21
+ name: httparty
22
+ type: :runtime
23
+ prerelease: false
24
+ requirement: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ! '>='
27
+ - !ruby/object:Gem::Version
28
+ version: '0'
29
+ none: false
30
+ - !ruby/object:Gem::Dependency
31
+ version_requirements: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - ! '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '0'
36
+ none: false
37
+ name: shoulda
38
+ type: :development
39
+ prerelease: false
40
+ requirement: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ none: false
46
+ - !ruby/object:Gem::Dependency
47
+ version_requirements: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ~>
50
+ - !ruby/object:Gem::Version
51
+ version: 2.5.1
52
+ none: false
53
+ name: mechanize
54
+ type: :development
55
+ prerelease: false
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ version: 2.5.1
61
+ none: false
62
+ description: This gem provides a convenient Ruby wrapper for the Spreedly Subscriptions
63
+ API.
64
+ email:
65
+ - nathaniel@spreedly.com
66
+ executables: []
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - lib/spreedly/subscriptions/common.rb
71
+ - lib/spreedly/subscriptions/mock.rb
72
+ - lib/spreedly/subscriptions/test_hacks.rb
73
+ - lib/spreedly/subscriptions/version.rb
74
+ - lib/spreedly/subscriptions.rb
75
+ - README.md
76
+ - LICENSE.txt
77
+ - HISTORY.md
78
+ homepage: https://github.com/spreedly/spreedly_subscriptions_gem
79
+ licenses:
80
+ - MIT
81
+ post_install_message:
82
+ rdoc_options: []
83
+ require_paths:
84
+ - lib
85
+ required_ruby_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ none: false
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '1.8'
96
+ none: false
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 1.8.23
100
+ signing_key:
101
+ specification_version: 3
102
+ summary: Provides a Ruby wrapper for the Spreedly Subscriptions API.
103
+ test_files: []