doug-chargify 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,22 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+ dist
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
22
+ dist/*
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Wynn Netherland
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.
@@ -0,0 +1,31 @@
1
+ # chargify
2
+
3
+ Ruby wrapper for the chargify.com SAAS and billing API
4
+
5
+ ## Installation
6
+
7
+ sudo gem install chargify
8
+
9
+ ## Example Usage
10
+
11
+ ### Create, cancel, then reactivate subscription
12
+ attributes = { :product_handle => 'basic' ... }
13
+ @client = Chargify::Client.new('InDhcXAAAAAg7juDD', 'xxx-test')
14
+ subscription = @client.create_subscription(attributes)
15
+ @client.cancel_subscription(subscription[:id], "Canceled due to bad customer service.")
16
+ @client.reactivate_subscription(subscription[:id]) #Made him an offer he couldn't refuse!
17
+
18
+ ## Note on Patches/Pull Requests
19
+
20
+ * Fork the project.
21
+ * Make your feature addition or bug fix.
22
+ * Add tests for it. This is important so I don't break it in a
23
+ future version unintentionally.
24
+ * Commit, do not mess with rakefile, version, or history.
25
+ (if you want to have your own version, that is fine but
26
+ bump version in a commit by itself I can ignore when I pull)
27
+ * Send me a pull request. Bonus points for topic branches.
28
+
29
+ ### Copyright
30
+
31
+ Copyright (c) 2009 [Wynn Netherland](http://wynnnetherland.com). See LICENSE for details.
@@ -0,0 +1,15 @@
1
+ require "mg"
2
+ require 'active_support/inflector'
3
+ require 'shoulda/tasks'
4
+
5
+
6
+ MG.new("chargify.gemspec")
7
+
8
+ require 'rake/testtask'
9
+ Rake::TestTask.new(:test) do |test|
10
+ test.ruby_opts = ['-rubygems'] if defined? Gem
11
+ test.libs << 'lib' << 'test'
12
+ test.pattern = 'test/**/*_test.rb'
13
+ end
14
+
15
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.6
@@ -0,0 +1,21 @@
1
+ # Changelog
2
+
3
+ ## 0.2.6 May 27, 2010
4
+ * Fix #charge_subscription to submit it's body as json [@will](http://github.com/will) and [@ignat](http://github.com/ignat)
5
+ * API coverage for quantity components [@will](http://github.com/will)
6
+ * API coverage for site and subscription transactions [@ignat](http://github.com/ignat)
7
+
8
+ ## 0.2.5 May 24, 2010
9
+ * Require fix from [@will](http://github.com/will)
10
+
11
+ ## 0.2.4 May 20, 2010
12
+
13
+ * Substantial new API coverage from [@miksago](http://twitter.com/miksago)
14
+
15
+ ## 0.2.0 January 26, 2010
16
+
17
+ * Substantial fixes and convenience enhancements from [@nkabbara](http://github.com/nkabbara)
18
+
19
+ ## 0.1.0 November 18, 2009
20
+
21
+ * Initial version
@@ -0,0 +1,82 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{doug-chargify}
8
+ s.version = "0.2.5"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Doug Youch"]
12
+ s.date = %q{2010-05-24}
13
+ s.description = %q{Ruby wrapper for the chargify.com SAAS and billing API designed to be thread safe}
14
+ s.email = %q{dougyouch@yahoo.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.markdown"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.markdown",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "changelog.md",
27
+ "chargify.gemspec",
28
+ "lib/chargify.rb",
29
+ "lib/chargify/client.rb",
30
+ "test/fixtures/customer.json",
31
+ "test/fixtures/customers.json",
32
+ "test/fixtures/deleted_subscription.json",
33
+ "test/fixtures/invalid_subscription.json",
34
+ "test/fixtures/new_customer.json",
35
+ "test/fixtures/product.json",
36
+ "test/fixtures/products.json",
37
+ "test/fixtures/subscription.json",
38
+ "test/fixtures/subscription_not_found.json",
39
+ "test/fixtures/subscriptions.json",
40
+ "test/helper.rb",
41
+ "test/chargify_test.rb"
42
+ ]
43
+ s.homepage = %q{http://github.com/pengwynn/chargify}
44
+ s.rdoc_options = ["--charset=UTF-8"]
45
+ s.require_paths = ["lib"]
46
+ s.rubygems_version = %q{1.3.6}
47
+ s.summary = %q{Ruby wrapper for the chargify.com SAAS and billing API}
48
+ s.test_files = [
49
+ "test/helper.rb",
50
+ "test/chargify_test.rb"
51
+ ]
52
+
53
+ if s.respond_to? :specification_version then
54
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
55
+ s.specification_version = 3
56
+
57
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
58
+ s.add_runtime_dependency(%q<hashie>, ["~> 0.1.3"])
59
+ s.add_runtime_dependency(%q<httparty>, ["~> 0.5.2"])
60
+ s.add_development_dependency(%q<shoulda>, [">= 2.10.1"])
61
+ s.add_development_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
62
+ s.add_development_dependency(%q<mocha>, ["~> 0.9.8"])
63
+ s.add_development_dependency(%q<fakeweb>, [">= 1.2.5"])
64
+ s.add_development_dependency(%q<redgreen>, [">= 1.2.2"])
65
+ else
66
+ s.add_dependency(%q<hashie>, ["~> 0.1.3"])
67
+ s.add_dependency(%q<httparty>, ["~> 0.5.2"])
68
+ s.add_dependency(%q<shoulda>, [">= 2.10.1"])
69
+ s.add_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
70
+ s.add_dependency(%q<mocha>, ["~> 0.9.8"])
71
+ s.add_dependency(%q<fakeweb>, [">= 1.2.5"])
72
+ end
73
+ else
74
+ s.add_dependency(%q<hashie>, ["~> 0.1.3"])
75
+ s.add_dependency(%q<httparty>, ["~> 0.5.2"])
76
+ s.add_dependency(%q<shoulda>, [">= 2.10.1"])
77
+ s.add_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
78
+ s.add_dependency(%q<mocha>, ["~> 0.9.8"])
79
+ s.add_dependency(%q<fakeweb>, [">= 1.2.5"])
80
+ end
81
+ end
82
+
@@ -0,0 +1,11 @@
1
+ require 'hashie'
2
+ require 'httparty'
3
+ require 'json'
4
+
5
+ directory = File.expand_path(File.dirname(__FILE__))
6
+
7
+ require File.join(directory, 'chargify', 'client')
8
+
9
+ module Chargify
10
+ VERSION = "0.2.6".freeze
11
+ end
@@ -0,0 +1,229 @@
1
+ module Chargify
2
+ class UnexpectedResponseError < RuntimeError
3
+ end
4
+
5
+ class Parser < HTTParty::Parser
6
+ def parse
7
+ begin
8
+ Crack::JSON.parse(body)
9
+ rescue => e
10
+ raise UnexpectedResponseError, "Crack could not parse JSON. It said: #{e.message}. Chargify's raw response: #{body}"
11
+ end
12
+ end
13
+ end
14
+
15
+ class Client
16
+ include HTTParty
17
+
18
+ parser Chargify::Parser
19
+ headers 'Content-Type' => 'application/json'
20
+
21
+ attr_reader :api_key, :subdomain
22
+
23
+ # Your API key can be generated on the settings screen.
24
+ def initialize(api_key, subdomain)
25
+ @api_key = api_key
26
+ @subdomain = subdomain
27
+ end
28
+
29
+ # options: page
30
+ def list_customers(options={})
31
+ customers = get("/customers.json", :query => options)
32
+ customers.map{|c| Hashie::Mash.new c['customer']}
33
+ end
34
+
35
+ def customer_by_id(chargify_id)
36
+ request = get("/customers/#{chargify_id}.json")
37
+ success = request.code == 200
38
+ response = Hashie::Mash.new(request).customer if success
39
+ Hashie::Mash.new(response || {}).update(:success? => success)
40
+ end
41
+
42
+ def customer_by_reference(reference_id)
43
+ request = get("/customers/lookup.json?reference=#{reference_id}")
44
+ success = request.code == 200
45
+ response = Hashie::Mash.new(request).customer if success
46
+ Hashie::Mash.new(response || {}).update(:success? => success)
47
+ end
48
+
49
+ alias customer customer_by_reference
50
+
51
+
52
+ #
53
+ # * first_name (Required)
54
+ # * last_name (Required)
55
+ # * email (Required)
56
+ # * organization (Optional) Company/Organization name
57
+ # * reference (Optional, but encouraged) The unique identifier used within your own application for this customer
58
+ #
59
+ def create_customer(info={})
60
+ response = Hashie::Mash.new(post("/customers.json", :body => {:customer => info}))
61
+ return response.customer if response.customer
62
+ response
63
+ end
64
+
65
+ #
66
+ # * first_name (Required)
67
+ # * last_name (Required)
68
+ # * email (Required)
69
+ # * organization (Optional) Company/Organization name
70
+ # * reference (Optional, but encouraged) The unique identifier used within your own application for this customer
71
+ #
72
+ def update_customer(info={})
73
+ info.stringify_keys!
74
+ chargify_id = info.delete('id')
75
+ response = Hashie::Mash.new(put("/customers/#{chargify_id}.json", :body => {:customer => info}))
76
+ return response.customer unless response.customer.to_a.empty?
77
+ response
78
+ end
79
+
80
+ def customer_subscriptions(chargify_id)
81
+ subscriptions = get("/customers/#{chargify_id}/subscriptions.json")
82
+ subscriptions.map{|s| Hashie::Mash.new s['subscription']}
83
+ end
84
+
85
+ def subscription(subscription_id)
86
+ raw_response = get("/subscriptions/#{subscription_id}.json")
87
+ return nil if raw_response.code != 200
88
+ Hashie::Mash.new(raw_response).subscription
89
+ end
90
+
91
+ # Returns all elements outputted by Chargify plus:
92
+ # response.success? -> true if response code is 201, false otherwise
93
+ def create_subscription(subscription_attributes={})
94
+ raw_response = post("/subscriptions.json", :body => {:subscription => subscription_attributes})
95
+ created = true if raw_response.code == 201
96
+ response = Hashie::Mash.new(raw_response)
97
+ (response.subscription || response).update(:success? => created)
98
+ end
99
+
100
+ # Returns all elements outputted by Chargify plus:
101
+ # response.success? -> true if response code is 200, false otherwise
102
+ def update_subscription(sub_id, subscription_attributes = {})
103
+ raw_response = put("/subscriptions/#{sub_id}.json", :body => {:subscription => subscription_attributes})
104
+ updated = true if raw_response.code == 200
105
+ response = Hashie::Mash.new(raw_response)
106
+ (response.subscription || response).update(:success? => updated)
107
+ end
108
+
109
+ # Returns all elements outputted by Chargify plus:
110
+ # response.success? -> true if response code is 200, false otherwise
111
+ def cancel_subscription(sub_id, message="")
112
+ raw_response = delete("/subscriptions/#{sub_id}.json", :body => {:subscription => {:cancellation_message => message} })
113
+ deleted = true if raw_response.code == 200
114
+ response = Hashie::Mash.new(raw_response)
115
+ (response.subscription || response).update(:success? => deleted)
116
+ end
117
+
118
+ def reactivate_subscription(sub_id)
119
+ raw_response = put("/subscriptions/#{sub_id}/reactivate.json", :body => "")
120
+ reactivated = true if raw_response.code == 200
121
+ response = Hashie::Mash.new(raw_response) rescue Hashie::Mash.new
122
+ (response.subscription || response).update(:success? => reactivated)
123
+ end
124
+
125
+ def charge_subscription(sub_id, subscription_attributes={})
126
+ raw_response = post("/subscriptions/#{sub_id}/charges.json", :body => { :charge => subscription_attributes })
127
+ success = raw_response.code == 201
128
+ if raw_response.code == 404
129
+ raw_response = {}
130
+ end
131
+
132
+ response = Hashie::Mash.new(raw_response)
133
+ (response.charge || response).update(:success? => success)
134
+ end
135
+
136
+ def migrate_subscription(sub_id, product_id)
137
+ raw_response = post("/subscriptions/#{sub_id}/migrations.json", :body => {:product_id => product_id })
138
+ success = true if raw_response.code == 200
139
+ response = Hashie::Mash.new(raw_response)
140
+ (response.subscription || {}).update(:success? => success)
141
+ end
142
+
143
+ def list_products
144
+ products = get("/products.json")
145
+ products.map{|p| Hashie::Mash.new p['product']}
146
+ end
147
+
148
+ def product(product_id)
149
+ Hashie::Mash.new( get("/products/#{product_id}.json")).product
150
+ end
151
+
152
+ def product_by_handle(handle)
153
+ Hashie::Mash.new(get("/products/handle/#{handle}.json")).product
154
+ end
155
+
156
+ def list_subscription_usage(subscription_id, component_id)
157
+ raw_response = get("/subscriptions/#{subscription_id}/components/#{component_id}/usages.json")
158
+ success = raw_response.code == 200
159
+ response = Hashie::Mash.new(raw_response)
160
+ response.update(:success? => success)
161
+ end
162
+
163
+ def subscription_transactions(sub_id, options={})
164
+ transactions = get("/subscriptions/#{sub_id}/transactions.json", :query => options)
165
+ transactions.map{|t| Hashie::Mash.new t['transaction']}
166
+ end
167
+
168
+ def site_transactions(options={})
169
+ transactions = get("/transactions.json", :query => options)
170
+ transactions.map{|t| Hashie::Mash.new t['transaction']}
171
+ end
172
+
173
+ def list_components(subscription_id)
174
+ components = get("/subscriptions/#{subscription_id}/components.json")
175
+ components.map{|c| Hashie::Mash.new c['component']}
176
+ end
177
+
178
+ def subscription_component(subscription_id, component_id)
179
+ response = get("/subscriptions/#{subscription_id}/components/#{component_id}.json")
180
+ Hashie::Mash.new(response).component
181
+ end
182
+
183
+ def update_subscription_component_allocated_quantity(subscription_id, component_id, quantity)
184
+ response = put("/subscriptions/#{subscription_id}/components/#{component_id}.json", :body => {:component => {:allocated_quantity => quantity}})
185
+ response[:success?] = response.code == 200
186
+ Hashie::Mash.new(response)
187
+ end
188
+
189
+
190
+
191
+ private
192
+
193
+ def base_uri
194
+ @base_uri ||= "https://#{@subdomain}.chargify.com"
195
+ end
196
+
197
+ def full_uri(path)
198
+ "#{base_uri}#{path}"
199
+ end
200
+
201
+ def basic_auth
202
+ {:basic_auth => {:username => @api_key, :password => 'x'}}
203
+ end
204
+
205
+ def post(path, options={})
206
+ jsonify_body!(options)
207
+ self.class.post(full_uri(path), options.merge(basic_auth))
208
+ end
209
+
210
+ def put(path, options={})
211
+ jsonify_body!(options)
212
+ self.class.put(full_uri(path), options.merge(basic_auth))
213
+ end
214
+
215
+ def delete(path, options={})
216
+ jsonify_body!(options)
217
+ self.class.delete(full_uri(path), options.merge(basic_auth))
218
+ end
219
+
220
+ def get(path, options={})
221
+ jsonify_body!(options)
222
+ self.class.get(full_uri(path), options.merge(basic_auth))
223
+ end
224
+
225
+ def jsonify_body!(options)
226
+ options[:body] = options[:body].to_json if options[:body]
227
+ end
228
+ end
229
+ end
@@ -0,0 +1,350 @@
1
+ require 'helper'
2
+
3
+ class ChargifyTest < Test::Unit::TestCase
4
+ context "Chargify API client" do
5
+ setup do
6
+ @client = Chargify::Client.new('OU812', 'pengwynn')
7
+ end
8
+
9
+ should "return a list of customers" do
10
+ stub_get "https://OU812:x@pengwynn.chargify.com/customers.json", "customers.json"
11
+ customers = @client.list_customers
12
+ customers.size.should == 1
13
+ customers.first.reference.should == 'bradleyjoyce'
14
+ customers.first.organization.should == 'Squeejee'
15
+ end
16
+
17
+ context "when finding customers" do
18
+ should "be able to be found by a <reference_id>" do
19
+ stub_get "https://OU812:x@pengwynn.chargify.com/customers/lookup.json?reference=bradleyjoyce", "customer.json"
20
+ customer = @client.customer("bradleyjoyce")
21
+ customer.success?.should == true
22
+ end
23
+
24
+ should "be able to be found by a <chargify_id>" do
25
+ stub_get "https://OU812:x@pengwynn.chargify.com/customers/16.json", "customer.json"
26
+ customer = @client.customer_by_id(16)
27
+ customer.success?.should == true
28
+ end
29
+
30
+ should "return an empty Hash with success? set to false" do
31
+ stub_get "https://OU812:x@pengwynn.chargify.com/customers/16.json", "", 404
32
+ customer = @client.customer_by_id(16)
33
+ customer.success?.should == false
34
+ end
35
+ end
36
+
37
+ should "create a new customer" do
38
+ stub_post "https://OU812:x@pengwynn.chargify.com/customers.json", "new_customer.json"
39
+ info = {
40
+ :first_name => "Wynn",
41
+ :last_name => "Netherland",
42
+ :email => "wynn@example.com"
43
+ }
44
+ customer = @client.create_customer(info)
45
+ customer.first_name.should == "Wynn"
46
+ end
47
+
48
+ should "update a customer" do
49
+ stub_put "https://OU812:x@pengwynn.chargify.com/customers/16.json", "new_customer.json"
50
+ info = {
51
+ :id => 16,
52
+ :first_name => "Wynn",
53
+ :last_name => "Netherland",
54
+ :email => "wynn@example.com"
55
+ }
56
+ customer = @client.update_customer(info)
57
+ customer.first_name.should == "Wynn"
58
+ end
59
+
60
+ # Depends on Chargify:
61
+ # should_eventually "delete a customer" do
62
+ #
63
+ # end
64
+
65
+ should "raise UnexpectedResponseError when reponse is invalid JSON" do
66
+ stub_post "https://OU812:x@pengwynn.chargify.com/subscriptions.json", "invalid_subscription.json"
67
+ options = {
68
+ :product_handle => 'monthly',
69
+ :customer_reference => 'bradleyjoyce',
70
+ :customer_attributes => {
71
+ :first_name => "Wynn",
72
+ :last_name => "Netherland",
73
+ :email => "wynn@example.com"
74
+ }
75
+ }
76
+ assert_raise Chargify::UnexpectedResponseError do
77
+ @client.create_subscription(options)
78
+ end
79
+ end
80
+
81
+ should "return a list of customer subscriptions" do
82
+ stub_get "https://OU812:x@pengwynn.chargify.com/customers/16/subscriptions.json", "subscriptions.json"
83
+ subscriptions = @client.customer_subscriptions(16)
84
+ subscriptions.size.should == 1
85
+ subscriptions.first.customer.reference.should == "bradleyjoyce"
86
+ end
87
+
88
+
89
+ should "return info for a subscription" do
90
+ stub_get "https://OU812:x@pengwynn.chargify.com/subscriptions/13.json", "subscription.json"
91
+ subscription = @client.subscription(13)
92
+ subscription.customer.reference.should == 'bradleyjoyce'
93
+ end
94
+
95
+ should "return nil if a subscription is not found" do
96
+ stub_get "https://OU812:x@pengwynn.chargify.com/subscriptions/18.json", "subscription_not_found.json", 404
97
+ subscription = @client.subscription(18)
98
+ subscription.should == nil
99
+ end
100
+
101
+ should "update a customer subscription" do
102
+ stub_put "https://OU812:x@pengwynn.chargify.com/subscriptions/123.json", "subscription.json"
103
+ options = {
104
+ :product_handle => 'monthly',
105
+ :customer_reference => 'bradleyjoyce',
106
+ :customer_attributes => {
107
+ :first_name => "Wynn",
108
+ :last_name => "Netherland",
109
+ :email => "wynn@example.com"
110
+ }
111
+ }
112
+ subscription = @client.update_subscription(123, options)
113
+ subscription.customer.organization.should == 'Squeejee'
114
+ end
115
+
116
+ should "set success? to true when subscription is updated successfully" do
117
+ stub_put "https://OU812:x@pengwynn.chargify.com/subscriptions/123.json", "subscription.json", 200
118
+ options = {
119
+ :product_handle => 'monthly',
120
+ :customer_reference => 'bradleyjoyce',
121
+ :customer_attributes => {
122
+ :first_name => "Wynn",
123
+ :last_name => "Netherland",
124
+ :email => "wynn@example.com"
125
+ }
126
+ }
127
+ subscription = @client.update_subscription(123, options)
128
+ subscription.success?.should == true
129
+ end
130
+
131
+ should "set success? to false when subscription is not updated successfully" do
132
+ stub_put "https://OU812:x@pengwynn.chargify.com/subscriptions/123.json", "subscription.json", 500
133
+ options = {
134
+ :product_handle => 'monthly',
135
+ :customer_reference => 'bradleyjoyce',
136
+ :customer_attributes => {
137
+ :first_name => "Wynn",
138
+ :last_name => "Netherland",
139
+ :email => "wynn@example.com"
140
+ }
141
+ }
142
+ subscription = @client.update_subscription(123, options)
143
+ subscription.success?.should == nil
144
+ end
145
+
146
+ should "create a customer subscription" do
147
+ stub_post "https://OU812:x@pengwynn.chargify.com/subscriptions.json", "subscription.json"
148
+ options = {
149
+ :product_handle => 'monthly',
150
+ :customer_reference => 'bradleyjoyce',
151
+ :customer_attributes => {
152
+ :first_name => "Wynn",
153
+ :last_name => "Netherland",
154
+ :email => "wynn@example.com"
155
+ }
156
+ }
157
+ subscription = @client.create_subscription(options)
158
+ subscription.customer.organization.should == 'Squeejee'
159
+ end
160
+
161
+ should "create a customer subscription with a coupon code" do
162
+ stub_post "https://OU812:x@pengwynn.chargify.com/subscriptions.json", "subscription.json"
163
+ options = {
164
+ :product_handle => 'monthly',
165
+ :customer_reference => 'bradleyjoyce',
166
+ :customer_attributes => {
167
+ :first_name => "Wynn",
168
+ :last_name => "Netherland",
169
+ :email => "wynn@example.com"
170
+ },
171
+ :coupon_code => "EARLYBIRD"
172
+ }
173
+ subscription = @client.create_subscription(options)
174
+ #subscription.coupon.should == 'Squeejee'
175
+ end
176
+
177
+ should "set success? to true when subscription is created successfully" do
178
+ stub_post "https://OU812:x@pengwynn.chargify.com/subscriptions.json", "subscription.json", 201
179
+ options = {
180
+ :product_handle => 'monthly',
181
+ :customer_reference => 'bradleyjoyce',
182
+ :customer_attributes => {
183
+ :first_name => "Wynn",
184
+ :last_name => "Netherland",
185
+ :email => "wynn@example.com"
186
+ }
187
+ }
188
+ subscription = @client.create_subscription(options)
189
+ subscription.success?.should == true
190
+ end
191
+
192
+ should "set success? to nil when subscription is not created successfully" do
193
+ stub_post "https://OU812:x@pengwynn.chargify.com/subscriptions.json", "subscription.json", 400
194
+ options = {
195
+ :product_handle => 'monthly',
196
+ :customer_reference => 'bradleyjoyce',
197
+ :customer_attributes => {
198
+ :first_name => "Wynn",
199
+ :last_name => "Netherland",
200
+ :email => "wynn@example.com"
201
+ }
202
+ }
203
+ subscription = @client.create_subscription(options)
204
+ subscription.success?.should == nil
205
+ end
206
+
207
+ should "reactivate a subscription" do
208
+ stub_put "https://OU812:x@pengwynn.chargify.com/subscriptions/123/reactivate.json", "subscription.json", 200
209
+ subscription = @client.reactivate_subscription(123)
210
+
211
+ subscription.state.should == "active"
212
+ end
213
+
214
+ should "set success? to nil when subscription is not reactivated successfully" do
215
+ stub_put "https://OU812:x@pengwynn.chargify.com/subscriptions/123/reactivate.json", "subscription_not_found.json", 500
216
+ subscription = @client.reactivate_subscription(123)
217
+
218
+ subscription.success?.should == nil
219
+ end
220
+
221
+ should "set success? to false when subscription is reactivated successfully" do
222
+ stub_put "https://OU812:x@pengwynn.chargify.com/subscriptions/123/reactivate.json", "subscription.json", 200
223
+ subscription = @client.reactivate_subscription(123)
224
+
225
+ subscription.success?.should == true
226
+ end
227
+
228
+ should "cancel subscription" do
229
+ stub_delete "https://OU812:x@pengwynn.chargify.com/subscriptions/123.json", "deleted_subscription.json", 200
230
+ subscription = @client.cancel_subscription(123)
231
+
232
+ subscription.state.should == "canceled"
233
+ end
234
+
235
+ should "set success? to nil when subscription is not cancelled successfully" do
236
+ stub_delete "https://OU812:x@pengwynn.chargify.com/subscriptions/123.json", "deleted_subscription.json", 500
237
+ subscription = @client.cancel_subscription(123)
238
+
239
+ subscription.success?.should == nil
240
+ end
241
+
242
+ should "set success? to true when subscription is cancelled successfully" do
243
+ stub_delete "https://OU812:x@pengwynn.chargify.com/subscriptions/123.json", "deleted_subscription.json", 200
244
+ subscription = @client.cancel_subscription(123)
245
+
246
+ subscription.success?.should == true
247
+ end
248
+
249
+ context "when creating a one-off charge for a subscription" do
250
+ setup do
251
+ stub_post "https://OU812:x@pengwynn.chargify.com/subscriptions/123/charges.json", "charge_subscription.json", 201
252
+ @options = {
253
+ :memo => "This is the description of the one time charge.",
254
+ :amount => 1.00,
255
+ :amount_in_cents => 100
256
+ }
257
+ end
258
+
259
+ should "accept :amount as a parameter" do
260
+ subscription = @client.charge_subscription(123, @options)
261
+
262
+ subscription.amount_in_cents.should == @options[:amount]*100
263
+ subscription.success?.should == true
264
+ end
265
+
266
+ should "accept :amount_in_cents as a parameter" do
267
+ subscription = @client.charge_subscription(123, @options)
268
+
269
+ subscription.amount_in_cents.should == @options[:amount_in_cents]
270
+ subscription.success?.should == true
271
+ end
272
+
273
+ should "have success? as false if parameters are missing" do
274
+ stub_post "https://OU812:x@pengwynn.chargify.com/subscriptions/123/charges.json", "charge_subscription_missing_parameters.json", 422
275
+
276
+ subscription = @client.charge_subscription(123, {})
277
+ subscription.success?.should == false
278
+ end
279
+
280
+ should "have success? as false if the subscription is not found" do
281
+ stub_post "https://OU812:x@pengwynn.chargify.com/subscriptions/9999/charges.json", "", 404
282
+
283
+ subscription = @client.charge_subscription(9999, @options)
284
+ subscription.success?.should == false
285
+ end
286
+ end
287
+
288
+ context "for metered subscriptions" do
289
+ should_eventually "list usage for a subscription" do
290
+ stub_get "https://OU812:x@pengwynn.chargify.com/subscriptions/123/components/456/usages.json", "list_metered_subscriptions.json", 200
291
+
292
+ subscription = @client.list_subscription_usage(123, 456)
293
+ subscription.success?.should == true
294
+ end
295
+
296
+ should_eventually "record usage for a subscription" do
297
+
298
+ end
299
+ end
300
+
301
+ should "migrate a subscription from one product to another" do
302
+ stub_post "https://OU812:x@pengwynn.chargify.com/subscriptions/123/migrations.json", "migrate_subscription.json"
303
+
304
+ subscription = @client.migrate_subscription(123, 354);
305
+ subscription.success?.should == true
306
+ subscription.product.id.should == 354
307
+ end
308
+
309
+ should "return a list of products" do
310
+ stub_get "https://OU812:x@pengwynn.chargify.com/products.json", "products.json"
311
+ products = @client.list_products
312
+ products.first.accounting_code.should == 'TSMO'
313
+ end
314
+
315
+ should "return info for a product" do
316
+ stub_get "https://OU812:x@pengwynn.chargify.com/products/8.json", "product.json"
317
+ product = @client.product(8)
318
+ product.accounting_code.should == 'TSMO'
319
+ end
320
+
321
+ should "return info for a product by its handle" do
322
+ stub_get "https://OU812:x@pengwynn.chargify.com/products/handle/tweetsaver.json", "product.json"
323
+ product = @client.product_by_handle('tweetsaver')
324
+ product.accounting_code.should == 'TSMO'
325
+ end
326
+
327
+ context "for quantity based components" do
328
+ should "list components" do
329
+ stub_get "https://OU812:x@pengwynn.chargify.com/subscriptions/123/components.json", "components.json"
330
+ components = @client.list_components(123)
331
+ components.first.allocated_quantity.should == 42
332
+ components.last.allocated_quantity.should == 2
333
+ end
334
+
335
+ should "show a specific component" do
336
+ stub_get "https://OU812:x@pengwynn.chargify.com/subscriptions/123/components/16.json", "component.json"
337
+ component = @client.subscription_component 123, 16
338
+ component.name.should == "Extra Rubies"
339
+ component.allocated_quantity.should == 42
340
+ end
341
+
342
+ should "update the allocated_quantity for a component" do
343
+ stub_put "https://OU812:x@pengwynn.chargify.com/subscriptions/123/components/16.json", "component.json"
344
+ response = @client.update_subscription_component_allocated_quantity 123, 16, 20_000_000
345
+ response.success?.should == true
346
+ end
347
+ end
348
+
349
+ end
350
+ end
@@ -0,0 +1,12 @@
1
+ {
2
+ "customer": {
3
+ "reference": "bradleyjoyce",
4
+ "updated_at": "2009-10-07T11:10:27-04:00",
5
+ "id": 16,
6
+ "organization": "Squeejee",
7
+ "first_name": "Bradley",
8
+ "last_name": "Joyce",
9
+ "email": "bradley@squeejee.com",
10
+ "created_at": "2009-10-07T11:10:27-04:00"
11
+ }
12
+ }
@@ -0,0 +1,12 @@
1
+ [{
2
+ "customer": {
3
+ "reference": "bradleyjoyce",
4
+ "updated_at": "2009-10-07T11:10:27-04:00",
5
+ "id": 16,
6
+ "organization": "Squeejee",
7
+ "first_name": "Bradley",
8
+ "last_name": "Joyce",
9
+ "email": "bradley@squeejee.com",
10
+ "created_at": "2009-10-07T11:10:27-04:00"
11
+ }
12
+ }]
@@ -0,0 +1 @@
1
+ {"created_at":"Tue Jan 26 18:40:32 -0600 2010","activated_at":"Tue Jan 26 18:40:33 -0600 2010","expires_at":null,"cancellation_message":"Optional message","trial_ended_at":null,"updated_at":"Tue Jan 26 18:40:35 -0600 2010","credit_card":{"card_type":"bogus","expiration_year":2013,"masked_card_number":"XXXX-XXXX-XXXX-1","expiration_month":3,"last_name":"Indicisive","first_name":"John"},"id":3642,"current_period_ends_at":"Fri Feb 26 18:40:32 -0600 2010","product":{"product_family":{"name":"ZipZoomAuto","handle":"zipzoomauto","accounting_code":null,"id":268,"description":""},"name":"Basic Plan","handle":"basic","price_in_cents":5000,"accounting_code":"","id":689,"interval":1,"description":"","interval_unit":"month"},"success?":true,"customer":{"reference":"11654","created_at":"Tue Jan 26 18:40:32 -0600 2010","updated_at":"Tue Jan 26 18:40:32 -0600 2010","id":3546,"last_name":"Indicisive","organization":null,"email":"john@doe.com","first_name":"John"},"trial_started_at":null,"balance_in_cents":0,"current_period_started_at":"Tue Jan 26 18:40:32 -0600 2010","state":"canceled"}
@@ -0,0 +1,48 @@
1
+ "subscription": {
2
+ "customer": {
3
+ "reference": "bradleyjoyce",
4
+ "updated_at": "2009-10-07T11:10:27-04:00",
5
+ "id": 16,
6
+ "organization": "Squeejee",
7
+ "first_name": "Bradley",
8
+ "last_name": "Joyce",
9
+ "email": "bradley@squeejee.com",
10
+ "created_at": "2009-10-07T11:10:27-04:00"
11
+ },
12
+ "cancellation_message": null,
13
+ "updated_at": "2009-10-07T11:10:56-04:00",
14
+ "expires_at": null,
15
+ "activated_at": "2009-10-23T16:16:59-04:00",
16
+ "current_period_started_at": "2009-11-14T11:10:56-05:00",
17
+ "credit_card": {
18
+ "card_type": "bogus",
19
+ "expiration_year": 2015,
20
+ "masked_card_number": "XXXX-XXXX-XXXX-1",
21
+ "first_name": "Bradley",
22
+ "last_name": "Joyce",
23
+ "expiration_month": 7
24
+ },
25
+ "trial_ended_at": "2009-10-14T11:10:56-04:00",
26
+ "id": 14,
27
+ "product": {
28
+ "price_in_cents": 500,
29
+ "name": "Monthly",
30
+ "handle": "monthly",
31
+ "id": 8,
32
+ "accounting_code": "TSMO",
33
+ "product_family": {
34
+ "name": "TweetSaver",
35
+ "handle": "tweetsaver",
36
+ "id": 7,
37
+ "accounting_code": null
38
+ },
39
+ "interval_unit": "month",
40
+ "interval": 1
41
+ },
42
+ "current_period_ends_at": "2009-12-14T11:10:56-05:00",
43
+ "trial_started_at": "2009-10-07T11:10:56-04:00",
44
+ "balance_in_cents": 0,
45
+ "state": "active",
46
+ "created_at": "2009-10-07T11:10:56-04:00"
47
+ }
48
+ }
@@ -0,0 +1,12 @@
1
+ {
2
+ "customer": {
3
+ "created_at": "2009-11-18T10:56:22-05:00",
4
+ "email": "wynn.netherland@gmail.com",
5
+ "first_name": "Wynn",
6
+ "id": 333,
7
+ "last_name": "Netherland",
8
+ "organization": null,
9
+ "reference": null,
10
+ "updated_at": "2009-11-18T10:56:22-05:00"
11
+ }
12
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "product": {
3
+ "price_in_cents": 500,
4
+ "name": "Monthly",
5
+ "handle": "monthly",
6
+ "id": 8,
7
+ "accounting_code": "TSMO",
8
+ "product_family": {
9
+ "name": "TweetSaver",
10
+ "handle": "tweetsaver",
11
+ "id": 7,
12
+ "accounting_code": null
13
+ },
14
+ "interval_unit": "month",
15
+ "interval": 1
16
+ }
17
+ }
@@ -0,0 +1,17 @@
1
+ [{
2
+ "product": {
3
+ "price_in_cents": 500,
4
+ "name": "Monthly",
5
+ "handle": "monthly",
6
+ "id": 8,
7
+ "accounting_code": "TSMO",
8
+ "product_family": {
9
+ "name": "TweetSaver",
10
+ "handle": "tweetsaver",
11
+ "id": 7,
12
+ "accounting_code": null
13
+ },
14
+ "interval_unit": "month",
15
+ "interval": 1
16
+ }
17
+ }]
@@ -0,0 +1,49 @@
1
+ {
2
+ "subscription": {
3
+ "customer": {
4
+ "reference": "bradleyjoyce",
5
+ "updated_at": "2009-10-07T11:10:27-04:00",
6
+ "id": 16,
7
+ "organization": "Squeejee",
8
+ "first_name": "Bradley",
9
+ "last_name": "Joyce",
10
+ "email": "bradley@squeejee.com",
11
+ "created_at": "2009-10-07T11:10:27-04:00"
12
+ },
13
+ "cancellation_message": null,
14
+ "updated_at": "2009-10-07T11:10:56-04:00",
15
+ "expires_at": null,
16
+ "activated_at": "2009-10-23T16:16:59-04:00",
17
+ "current_period_started_at": "2009-11-14T11:10:56-05:00",
18
+ "credit_card": {
19
+ "card_type": "bogus",
20
+ "expiration_year": 2015,
21
+ "masked_card_number": "XXXX-XXXX-XXXX-1",
22
+ "first_name": "Bradley",
23
+ "last_name": "Joyce",
24
+ "expiration_month": 7
25
+ },
26
+ "trial_ended_at": "2009-10-14T11:10:56-04:00",
27
+ "id": 14,
28
+ "product": {
29
+ "price_in_cents": 500,
30
+ "name": "Monthly",
31
+ "handle": "monthly",
32
+ "id": 8,
33
+ "accounting_code": "TSMO",
34
+ "product_family": {
35
+ "name": "TweetSaver",
36
+ "handle": "tweetsaver",
37
+ "id": 7,
38
+ "accounting_code": null
39
+ },
40
+ "interval_unit": "month",
41
+ "interval": 1
42
+ },
43
+ "current_period_ends_at": "2009-12-14T11:10:56-05:00",
44
+ "trial_started_at": "2009-10-07T11:10:56-04:00",
45
+ "balance_in_cents": 0,
46
+ "state": "active",
47
+ "created_at": "2009-10-07T11:10:56-04:00"
48
+ }
49
+ }
@@ -0,0 +1,49 @@
1
+ [{
2
+ "subscription": {
3
+ "customer": {
4
+ "reference": "bradleyjoyce",
5
+ "updated_at": "2009-10-07T11:10:27-04:00",
6
+ "id": 16,
7
+ "organization": "Squeejee",
8
+ "first_name": "Bradley",
9
+ "last_name": "Joyce",
10
+ "email": "bradley@squeejee.com",
11
+ "created_at": "2009-10-07T11:10:27-04:00"
12
+ },
13
+ "cancellation_message": null,
14
+ "updated_at": "2009-10-07T11:10:56-04:00",
15
+ "expires_at": null,
16
+ "activated_at": "2009-10-23T16:16:59-04:00",
17
+ "current_period_started_at": "2009-11-14T11:10:56-05:00",
18
+ "credit_card": {
19
+ "card_type": "bogus",
20
+ "expiration_year": 2015,
21
+ "masked_card_number": "XXXX-XXXX-XXXX-1",
22
+ "first_name": "Bradley",
23
+ "last_name": "Joyce",
24
+ "expiration_month": 7
25
+ },
26
+ "trial_ended_at": "2009-10-14T11:10:56-04:00",
27
+ "id": 14,
28
+ "product": {
29
+ "price_in_cents": 500,
30
+ "name": "Monthly",
31
+ "handle": "monthly",
32
+ "id": 8,
33
+ "accounting_code": "TSMO",
34
+ "product_family": {
35
+ "name": "TweetSaver",
36
+ "handle": "tweetsaver",
37
+ "id": 7,
38
+ "accounting_code": null
39
+ },
40
+ "interval_unit": "month",
41
+ "interval": 1
42
+ },
43
+ "current_period_ends_at": "2009-12-14T11:10:56-05:00",
44
+ "trial_started_at": "2009-10-07T11:10:56-04:00",
45
+ "balance_in_cents": 0,
46
+ "state": "active",
47
+ "created_at": "2009-10-07T11:10:56-04:00"
48
+ }
49
+ }]
@@ -0,0 +1,52 @@
1
+ require 'test/unit'
2
+ require 'pathname'
3
+
4
+ require 'shoulda'
5
+ require 'matchy'
6
+ require 'mocha'
7
+ require 'fakeweb'
8
+ require 'redgreen'
9
+
10
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
11
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
12
+ require 'chargify'
13
+
14
+ class Test::Unit::TestCase
15
+ end
16
+
17
+ FakeWeb.allow_net_connect = false
18
+
19
+ def fixture_file(filename)
20
+ return '' if filename == ''
21
+ file_path = File.expand_path(File.dirname(__FILE__) + '/fixtures/' + filename)
22
+ File.read(file_path)
23
+ end
24
+
25
+
26
+ def stub_get(url, filename, status=nil)
27
+ options = {:body => fixture_file(filename)}
28
+ options.merge!({:status => status}) unless status.nil?
29
+
30
+ FakeWeb.register_uri(:get, url, options)
31
+ end
32
+
33
+ def stub_post(url, filename, status=nil)
34
+ options = {:body => fixture_file(filename)}
35
+ options.merge!({:status => status}) unless status.nil?
36
+
37
+ FakeWeb.register_uri(:post, url, options)
38
+ end
39
+
40
+ def stub_put(url, filename, status=nil)
41
+ options = {:body => fixture_file(filename)}
42
+ options.merge!({:status => status}) unless status.nil?
43
+
44
+ FakeWeb.register_uri(:put, url, options)
45
+ end
46
+
47
+ def stub_delete(url, filename, status=nil)
48
+ options = {:body => fixture_file(filename)}
49
+ options.merge!({:status => status}) unless status.nil?
50
+
51
+ FakeWeb.register_uri(:delete, url, options)
52
+ end
metadata ADDED
@@ -0,0 +1,147 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: doug-chargify
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.5
5
+ platform: ruby
6
+ authors:
7
+ - Doug Youch
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-05-24 00:00:00 +00:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hashie
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 0.1.3
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: httparty
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 0.5.2
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: shoulda
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.10.1
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: jnunemaker-matchy
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "="
52
+ - !ruby/object:Gem::Version
53
+ version: 0.4.0
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: mocha
57
+ type: :development
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ~>
62
+ - !ruby/object:Gem::Version
63
+ version: 0.9.8
64
+ version:
65
+ - !ruby/object:Gem::Dependency
66
+ name: fakeweb
67
+ type: :development
68
+ version_requirement:
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 1.2.5
74
+ version:
75
+ - !ruby/object:Gem::Dependency
76
+ name: redgreen
77
+ type: :development
78
+ version_requirement:
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 1.2.2
84
+ version:
85
+ description: Ruby wrapper for the chargify.com SAAS and billing API designed to be thread safe
86
+ email: dougyouch@yahoo.com
87
+ executables: []
88
+
89
+ extensions: []
90
+
91
+ extra_rdoc_files:
92
+ - LICENSE
93
+ - README.markdown
94
+ files:
95
+ - .document
96
+ - .gitignore
97
+ - LICENSE
98
+ - README.markdown
99
+ - Rakefile
100
+ - VERSION
101
+ - changelog.md
102
+ - chargify.gemspec
103
+ - lib/chargify.rb
104
+ - lib/chargify/client.rb
105
+ - test/fixtures/customer.json
106
+ - test/fixtures/customers.json
107
+ - test/fixtures/deleted_subscription.json
108
+ - test/fixtures/invalid_subscription.json
109
+ - test/fixtures/new_customer.json
110
+ - test/fixtures/product.json
111
+ - test/fixtures/products.json
112
+ - test/fixtures/subscription.json
113
+ - test/fixtures/subscription_not_found.json
114
+ - test/fixtures/subscriptions.json
115
+ - test/helper.rb
116
+ - test/chargify_test.rb
117
+ has_rdoc: true
118
+ homepage: http://github.com/pengwynn/chargify
119
+ licenses: []
120
+
121
+ post_install_message:
122
+ rdoc_options:
123
+ - --charset=UTF-8
124
+ require_paths:
125
+ - lib
126
+ required_ruby_version: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: "0"
131
+ version:
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - ">="
135
+ - !ruby/object:Gem::Version
136
+ version: "0"
137
+ version:
138
+ requirements: []
139
+
140
+ rubyforge_project:
141
+ rubygems_version: 1.3.5
142
+ signing_key:
143
+ specification_version: 3
144
+ summary: Ruby wrapper for the chargify.com SAAS and billing API
145
+ test_files:
146
+ - test/helper.rb
147
+ - test/chargify_test.rb