paid 1.0.1 → 1.0.2
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.
- checksums.yaml +4 -4
- data/History.txt +6 -0
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/lib/paid.rb +32 -207
- data/lib/paid/account.rb +22 -10
- data/lib/paid/api_list.rb +56 -17
- data/lib/paid/api_method.rb +93 -0
- data/lib/paid/api_resource.rb +130 -8
- data/lib/paid/customer.rb +104 -40
- data/lib/paid/errors/api_connection_error.rb +1 -1
- data/lib/paid/errors/api_error.rb +25 -3
- data/lib/paid/errors/authentication_error.rb +1 -1
- data/lib/paid/errors/paid_error.rb +2 -9
- data/lib/paid/event.rb +28 -17
- data/lib/paid/event_data.rb +10 -0
- data/lib/paid/headers_builder.rb +75 -0
- data/lib/paid/invoice.rb +55 -16
- data/lib/paid/params_builder.rb +26 -0
- data/lib/paid/path_builder.rb +38 -0
- data/lib/paid/plan.rb +38 -13
- data/lib/paid/refund_list.rb +26 -0
- data/lib/paid/requester.rb +97 -0
- data/lib/paid/subscription.rb +47 -16
- data/lib/paid/transaction.rb +64 -16
- data/lib/paid/util.rb +14 -40
- data/paid.gemspec +1 -1
- data/test/paid/{api_class_test.rb → _api_resource_test.rb} +31 -17
- data/test/paid/account_test.rb +3 -3
- data/test/paid/api_list_test.rb +14 -8
- data/test/paid/api_method_test.rb +89 -0
- data/test/paid/customer_test.rb +20 -10
- data/test/paid/event_test.rb +3 -4
- data/test/paid/headers_builder_test.rb +39 -0
- data/test/paid/invoice_test.rb +3 -3
- data/test/paid/params_builder_test.rb +57 -0
- data/test/paid/path_builder_test.rb +67 -0
- data/test/paid/plan_test.rb +3 -3
- data/test/paid/requester_test.rb +86 -0
- data/test/paid/subscription_test.rb +3 -3
- data/test/paid/transaction_test.rb +4 -4
- data/test/paid/util_test.rb +36 -35
- data/test/test_data.rb +9 -2
- data/test/test_helper.rb +14 -14
- metadata +23 -19
- data/lib/paid/api_class.rb +0 -338
- data/lib/paid/api_singleton.rb +0 -5
- data/lib/paid/errors/invalid_request_error.rb +0 -10
- data/test/mock_resource.rb +0 -69
- data/test/paid/api_resource_test.rb +0 -28
- data/test/paid/api_singleton_test.rb +0 -12
- data/test/paid/authentication_test.rb +0 -50
- data/test/paid/status_codes_test.rb +0 -63
data/lib/paid/api_resource.rb
CHANGED
@@ -1,16 +1,138 @@
|
|
1
1
|
module Paid
|
2
|
-
class APIResource
|
3
|
-
attribute :id
|
4
|
-
attribute :object
|
2
|
+
class APIResource
|
5
3
|
|
6
|
-
|
4
|
+
class << self
|
5
|
+
# The default :id method is deprecated and isn't useful to us
|
6
|
+
if method_defined?(:id)
|
7
|
+
undef :id
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# The default :id method is deprecated and isn't useful to us
|
12
|
+
if method_defined?(:id)
|
13
|
+
undef :id
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :api_method
|
17
|
+
attr_reader :json
|
18
|
+
|
19
|
+
def initialize(json=nil, api_method=nil)
|
20
|
+
refresh_from(json)
|
21
|
+
end
|
22
|
+
|
23
|
+
def refresh_from(json={}, api_method=nil)
|
24
|
+
unless json.is_a?(Hash)
|
25
|
+
json = { :id => json }
|
26
|
+
end
|
27
|
+
json = Util.symbolize_keys(json)
|
28
|
+
|
29
|
+
# Clear or write over any old data
|
30
|
+
clear_api_attributes
|
31
|
+
@api_method = api_method
|
32
|
+
@json = Util.sorta_deep_clone(json)
|
33
|
+
|
34
|
+
# Use json (not the @json, the cloned copy)
|
35
|
+
json.each do |k, v|
|
36
|
+
if self.class.api_attribute_names.include?(k.to_sym)
|
37
|
+
instance_variable_set("@#{k}", determine_api_attribute_value(k, v))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
self
|
41
|
+
end
|
42
|
+
|
43
|
+
def inspect
|
44
|
+
id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
|
45
|
+
"#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> Attributes: " + JSON.pretty_generate(inspect_api_attributes)
|
46
|
+
end
|
47
|
+
|
48
|
+
def inspect_nested
|
49
|
+
id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
|
50
|
+
"#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}>"
|
51
|
+
end
|
52
|
+
|
53
|
+
def to_json(*args)
|
54
|
+
JSON.generate(api_attributes)
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.api_attribute_names
|
58
|
+
@api_attributes.map(&:first)
|
59
|
+
end
|
60
|
+
|
61
|
+
def api_attributes
|
62
|
+
ret = {}
|
63
|
+
self.class.api_attribute_names.each do |attribute|
|
64
|
+
ret[attribute] = self.send(attribute)
|
65
|
+
end
|
66
|
+
ret
|
67
|
+
end
|
68
|
+
|
69
|
+
def inspect_api_attributes
|
70
|
+
ret = {}
|
71
|
+
api_attributes.each do |k, v|
|
72
|
+
if v.is_a?(APIResource)
|
73
|
+
ret[k] = v.inspect_nested
|
74
|
+
else
|
75
|
+
ret[k] = v
|
76
|
+
end
|
77
|
+
end
|
78
|
+
ret
|
79
|
+
end
|
80
|
+
|
81
|
+
# TODO(joncalhoun): Make this work for nested class construction.
|
82
|
+
def changed_api_attributes
|
83
|
+
ret = {}
|
84
|
+
self.api_attributes.each do |name, value|
|
85
|
+
if @json[name] != value
|
86
|
+
ret[name] = value
|
87
|
+
end
|
88
|
+
end
|
89
|
+
ret
|
90
|
+
end
|
91
|
+
|
92
|
+
def clear_api_attributes
|
93
|
+
self.class.api_attribute_names.each do |name|
|
94
|
+
instance_variable_set("@#{name}", nil)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.determine_api_attribute_value(name, raw_value)
|
99
|
+
if @api_attributes[name] && @api_attributes[name].has_key?(:constructor)
|
100
|
+
klass = Util.constantize(@api_attributes[name][:constructor])
|
101
|
+
if(klass.respond_to?(:construct))
|
102
|
+
klass.construct(raw_value)
|
103
|
+
else
|
104
|
+
klass.new(raw_value)
|
105
|
+
end
|
106
|
+
else
|
107
|
+
raw_value
|
108
|
+
end
|
109
|
+
end
|
110
|
+
def determine_api_attribute_value(name, raw_value)
|
111
|
+
self.class.determine_api_attribute_value(name, raw_value)
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
def self.api_subclasses
|
116
|
+
return @api_subclasses ||= Set.new
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.api_subclass_fetch(name)
|
120
|
+
@api_subclasses_hash ||= {}
|
121
|
+
if @api_subclasses_hash.has_key?(name)
|
122
|
+
@api_subclasses_hash[name]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.register_api_subclass(subclass, name=nil)
|
127
|
+
@api_subclasses ||= Set.new
|
128
|
+
@api_subclasses << subclass
|
7
129
|
|
8
|
-
|
9
|
-
|
10
|
-
|
130
|
+
unless name.nil?
|
131
|
+
@api_subclasses_hash ||= {}
|
132
|
+
@api_subclasses_hash[name] = subclass
|
11
133
|
end
|
12
|
-
"#{base}/#{id}"
|
13
134
|
end
|
14
135
|
|
136
|
+
@api_attributes = {}
|
15
137
|
end
|
16
138
|
end
|
data/lib/paid/customer.rb
CHANGED
@@ -1,44 +1,108 @@
|
|
1
1
|
module Paid
|
2
2
|
class Customer < APIResource
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
3
|
+
attr_reader :id
|
4
|
+
attr_reader :object
|
5
|
+
attr_accessor :name
|
6
|
+
attr_accessor :email
|
7
|
+
attr_accessor :description
|
8
|
+
attr_accessor :external_id
|
9
|
+
attr_accessor :phone
|
10
|
+
attr_accessor :address_line1
|
11
|
+
attr_accessor :address_line2
|
12
|
+
attr_accessor :address_city
|
13
|
+
attr_accessor :address_state
|
14
|
+
attr_accessor :address_zip
|
15
|
+
attr_accessor :allow_ach
|
16
|
+
attr_accessor :allow_wire
|
17
|
+
attr_accessor :allow_check
|
18
|
+
attr_accessor :allow_credit_card
|
19
|
+
attr_accessor :terms
|
20
|
+
attr_accessor :billing_type
|
21
|
+
attr_accessor :billing_cycle
|
22
|
+
attr_accessor :stripe_customer_id
|
23
|
+
|
24
|
+
def self.all(params={}, headers={})
|
25
|
+
method = APIMethod.new(:get, "/customers", params, headers, self)
|
26
|
+
APIList.new(self, method.execute, method)
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.retrieve(id, params={}, headers={})
|
30
|
+
params = ParamsBuilder.merge(params, {
|
31
|
+
:id => id
|
32
|
+
})
|
33
|
+
method = APIMethod.new(:get, "/customers/:id", params, headers, self)
|
34
|
+
self.new(method.execute, method)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.by_external_id(external_id, params={}, headers={})
|
38
|
+
params = ParamsBuilder.merge(params, {
|
39
|
+
:external_id => external_id
|
40
|
+
})
|
41
|
+
method = APIMethod.new(:get, "/customers/by_external_id", params, headers, self)
|
42
|
+
self.new(method.execute, method)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.create(params={}, headers={})
|
46
|
+
method = APIMethod.new(:post, "/customers", params, headers, self)
|
47
|
+
self.new(method.execute, method)
|
48
|
+
end
|
49
|
+
|
50
|
+
def refresh(params={}, headers={})
|
51
|
+
method = APIMethod.new(:get, "/customers/:id", params, headers, self)
|
52
|
+
self.refresh_from(method.execute, method)
|
53
|
+
end
|
54
|
+
|
55
|
+
def save(params={}, headers={})
|
56
|
+
params = ParamsBuilder.merge(params, changed_api_attributes)
|
57
|
+
method = APIMethod.new(:put, "/customers/:id", params, headers, self)
|
58
|
+
self.refresh_from(method.execute, method)
|
59
|
+
end
|
60
|
+
|
61
|
+
def generate_invoice(params={}, headers={})
|
62
|
+
method = APIMethod.new(:post, "/customers/:id/generate_invoice", params, headers, self)
|
63
|
+
Util.constantize(:Invoice).new(method.execute, method)
|
64
|
+
end
|
65
|
+
|
66
|
+
def invoices(params={}, headers={})
|
67
|
+
params = ParamsBuilder.merge(params, {
|
68
|
+
:customer => self.id,
|
69
|
+
})
|
70
|
+
method = APIMethod.new(:get, "/invoices", params, headers, self)
|
71
|
+
APIList.new(:Invoice, method.execute, method)
|
72
|
+
end
|
73
|
+
|
74
|
+
def transactions(params={}, headers={})
|
75
|
+
params = ParamsBuilder.merge(params, {
|
76
|
+
:customer => self.id,
|
77
|
+
})
|
78
|
+
method = APIMethod.new(:get, "/transactions", params, headers, self)
|
79
|
+
APIList.new(:Transaction, method.execute, method)
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
# Everything below here is used behind the scenes.
|
84
|
+
APIResource.register_api_subclass(self, "customer")
|
85
|
+
@api_attributes = {
|
86
|
+
:id => { :readonly => true },
|
87
|
+
:object => { :readonly => true },
|
88
|
+
:name => nil,
|
89
|
+
:email => nil,
|
90
|
+
:description => nil,
|
91
|
+
:external_id => nil,
|
92
|
+
:phone => nil,
|
93
|
+
:address_line1 => nil,
|
94
|
+
:address_line2 => nil,
|
95
|
+
:address_city => nil,
|
96
|
+
:address_state => nil,
|
97
|
+
:address_zip => nil,
|
98
|
+
:allow_ach => nil,
|
99
|
+
:allow_wire => nil,
|
100
|
+
:allow_check => nil,
|
101
|
+
:allow_credit_card => nil,
|
102
|
+
:terms => nil,
|
103
|
+
:billing_type => nil,
|
104
|
+
:billing_cycle => nil,
|
105
|
+
:stripe_customer_id => nil
|
106
|
+
}
|
43
107
|
end
|
44
108
|
end
|
@@ -1,10 +1,32 @@
|
|
1
1
|
module Paid
|
2
2
|
class APIError < PaidError
|
3
|
+
attr_reader :api_method
|
3
4
|
|
4
|
-
def
|
5
|
-
|
6
|
-
|
5
|
+
def initialize(message=nil, api_method=nil)
|
6
|
+
@message = message
|
7
|
+
@api_method = api_method
|
7
8
|
end
|
8
9
|
|
10
|
+
def code
|
11
|
+
@api_method.response_code if @api_method
|
12
|
+
end
|
13
|
+
|
14
|
+
def body
|
15
|
+
@api_method.response_body if @api_method
|
16
|
+
end
|
17
|
+
|
18
|
+
def json
|
19
|
+
begin
|
20
|
+
@api_method.response_json if @api_method
|
21
|
+
rescue APIError
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
prefix = code.nil? ? "" : "(Status #{code}) "
|
28
|
+
"#{prefix}#{@message}"
|
29
|
+
end
|
9
30
|
end
|
10
31
|
end
|
32
|
+
|
@@ -1,20 +1,13 @@
|
|
1
1
|
module Paid
|
2
2
|
class PaidError < StandardError
|
3
3
|
attr_reader :message
|
4
|
-
attr_reader :http_status
|
5
|
-
attr_reader :http_body
|
6
|
-
attr_reader :json_body
|
7
4
|
|
8
|
-
def initialize(message=nil
|
5
|
+
def initialize(message=nil)
|
9
6
|
@message = message
|
10
|
-
@http_status = http_status
|
11
|
-
@http_body = http_body
|
12
|
-
@json_body = json_body
|
13
7
|
end
|
14
8
|
|
15
9
|
def to_s
|
16
|
-
|
17
|
-
"#{status_string}#{@message}"
|
10
|
+
"#{@message}"
|
18
11
|
end
|
19
12
|
end
|
20
13
|
end
|
data/lib/paid/event.rb
CHANGED
@@ -1,26 +1,37 @@
|
|
1
1
|
module Paid
|
2
2
|
class Event < APIResource
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
# attributes :id and :object inherited from APIResource
|
12
|
-
attribute :created_at
|
13
|
-
attribute :type
|
14
|
-
attribute :data, Data
|
3
|
+
attr_reader :id
|
4
|
+
attr_reader :object
|
5
|
+
attr_reader :created_at
|
6
|
+
attr_reader :type
|
7
|
+
attr_reader :data
|
15
8
|
|
16
|
-
|
17
|
-
|
9
|
+
def self.all(params={}, headers={})
|
10
|
+
method = APIMethod.new(:get, "/events", params, headers, self)
|
11
|
+
APIList.new(self, method.execute, method)
|
12
|
+
end
|
18
13
|
|
14
|
+
def self.retrieve(id, params={}, headers={})
|
15
|
+
params = ParamsBuilder.merge(params, {
|
16
|
+
:id => id
|
17
|
+
})
|
18
|
+
method = APIMethod.new(:get, "/events/:id", params, headers, self)
|
19
|
+
self.new(method.execute, method)
|
20
|
+
end
|
19
21
|
|
20
|
-
def
|
21
|
-
"/
|
22
|
+
def refresh(params={}, headers={})
|
23
|
+
method = APIMethod.new(:get, "/events/:id", params, headers, self)
|
24
|
+
self.refresh_from(method.execute, method)
|
22
25
|
end
|
23
26
|
|
24
|
-
|
27
|
+
# Everything below here is used behind the scenes.
|
28
|
+
APIResource.register_api_subclass(self, "event")
|
29
|
+
@api_attributes = {
|
30
|
+
:id => { :readonly => true },
|
31
|
+
:object => { :readonly => true },
|
32
|
+
:created_at => { :readonly => true },
|
33
|
+
:type => { :readonly => true },
|
34
|
+
:data => { :constructor => :EventData, :readonly => true },
|
35
|
+
}
|
25
36
|
end
|
26
37
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Paid
|
2
|
+
module HeadersBuilder
|
3
|
+
|
4
|
+
def self.build(headers, api_key=nil, auth_key=nil)
|
5
|
+
headers ||= {}
|
6
|
+
if auth_key
|
7
|
+
temp = default_headers.merge(custom_auth_header(auth_key, api_key))
|
8
|
+
else
|
9
|
+
temp = default_headers.merge(basic_auth_header(api_key))
|
10
|
+
end
|
11
|
+
temp.merge(headers)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.default_headers
|
15
|
+
headers = {
|
16
|
+
:user_agent => "Paid/#{Paid.api_version} RubyBindings/#{Paid::VERSION}",
|
17
|
+
:content_type => 'application/x-www-form-urlencoded'
|
18
|
+
}
|
19
|
+
|
20
|
+
begin
|
21
|
+
headers.update(:x_paid_client_user_agent => JSON.generate(user_agent))
|
22
|
+
rescue => e
|
23
|
+
headers.update(:x_paid_client_raw_user_agent => user_agent.inspect,
|
24
|
+
:error => "#{e} (#{e.class})")
|
25
|
+
end
|
26
|
+
headers
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.custom_auth_header(key, api_key)
|
30
|
+
unless api_key
|
31
|
+
raise AuthenticationError.new('No API key provided. Set your API key using "Paid.api_key = <API-KEY>".')
|
32
|
+
end
|
33
|
+
|
34
|
+
{
|
35
|
+
key => api_key,
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.basic_auth_header(api_key)
|
40
|
+
unless api_key
|
41
|
+
raise AuthenticationError.new('No API key provided. Set your API key using "Paid.api_key = <API-KEY>".')
|
42
|
+
end
|
43
|
+
|
44
|
+
base_64_key = Base64.encode64("#{api_key}:")
|
45
|
+
{
|
46
|
+
"Authorization" => "Basic #{base_64_key}",
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.user_agent
|
51
|
+
lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
|
52
|
+
|
53
|
+
{
|
54
|
+
:bindings_version => Paid::VERSION,
|
55
|
+
:lang => 'ruby',
|
56
|
+
:lang_version => lang_version,
|
57
|
+
:platform => RUBY_PLATFORM,
|
58
|
+
:publisher => 'paid',
|
59
|
+
:uname => get_uname
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.get_uname
|
64
|
+
`uname -a 2>/dev/null`.strip if RUBY_PLATFORM =~ /linux|darwin/i
|
65
|
+
rescue Errno::ENOMEM => ex # couldn't create subprocess
|
66
|
+
"uname lookup failed"
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
|