conekta 0.3.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.
Files changed (43) hide show
  1. data/.gitignore +18 -0
  2. data/CONTRIBUTORS +9 -0
  3. data/Gemfile +2 -0
  4. data/Gemfile.lock +57 -0
  5. data/History.txt +4 -0
  6. data/LICENSE +23 -0
  7. data/README.rdoc +22 -0
  8. data/Rakefile +14 -0
  9. data/VERSION +1 -0
  10. data/bin/conekta-console +7 -0
  11. data/conekta.gemspec +27 -0
  12. data/gemfiles/default-with-activesupport.gemfile +3 -0
  13. data/gemfiles/json.gemfile +4 -0
  14. data/gemfiles/yajl.gemfile +4 -0
  15. data/lib/conekta.rb +292 -0
  16. data/lib/conekta/account.rb +4 -0
  17. data/lib/conekta/api_operations/create.rb +16 -0
  18. data/lib/conekta/api_operations/delete.rb +11 -0
  19. data/lib/conekta/api_operations/list.rb +16 -0
  20. data/lib/conekta/api_operations/update.rb +16 -0
  21. data/lib/conekta/api_resource.rb +33 -0
  22. data/lib/conekta/charge.rb +43 -0
  23. data/lib/conekta/conekta_object.rb +158 -0
  24. data/lib/conekta/errors/api_connection_error.rb +4 -0
  25. data/lib/conekta/errors/api_error.rb +4 -0
  26. data/lib/conekta/errors/authentication_error.rb +4 -0
  27. data/lib/conekta/errors/card_error.rb +11 -0
  28. data/lib/conekta/errors/conekta_error.rb +20 -0
  29. data/lib/conekta/errors/malformed_request_error.rb +10 -0
  30. data/lib/conekta/errors/parameter_validation_error.rb +10 -0
  31. data/lib/conekta/errors/resource_not_found_error.rb +10 -0
  32. data/lib/conekta/event.rb +5 -0
  33. data/lib/conekta/json.rb +21 -0
  34. data/lib/conekta/list_object.rb +35 -0
  35. data/lib/conekta/log.rb +5 -0
  36. data/lib/conekta/singleton_api_resource.rb +20 -0
  37. data/lib/conekta/util.rb +99 -0
  38. data/lib/conekta/version.rb +3 -0
  39. data/lib/data/ca-certificates.crt +3918 -0
  40. data/spec/conekta_spec.rb +743 -0
  41. data/spec/conekta_with_active_support_spec.rb +3 -0
  42. data/spec/test_helper.rb +302 -0
  43. metadata +177 -0
@@ -0,0 +1,4 @@
1
+ module Conekta
2
+ class Account < SingletonAPIResource
3
+ end
4
+ end
@@ -0,0 +1,16 @@
1
+ module Conekta
2
+ module APIOperations
3
+ module Create
4
+ module ClassMethods
5
+ def create(params={}, api_key=nil)
6
+ response, api_key = Conekta.request(:post, self.url, api_key, params)
7
+ Util.convert_to_conekta_object(response, api_key)
8
+ end
9
+ end
10
+
11
+ def self.included(base)
12
+ base.extend(ClassMethods)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ module Conekta
2
+ module APIOperations
3
+ module Delete
4
+ def delete
5
+ response, api_key = Conekta.request(:delete, url, @api_key)
6
+ refresh_from(response, api_key)
7
+ self
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ module Conekta
2
+ module APIOperations
3
+ module List
4
+ module ClassMethods
5
+ def all(filters={}, api_key=nil)
6
+ response, api_key = Conekta.request(:get, url, api_key, filters)
7
+ Util.convert_to_conekta_object(response, api_key)
8
+ end
9
+ end
10
+
11
+ def self.included(base)
12
+ base.extend(ClassMethods)
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Conekta
2
+ module APIOperations
3
+ module Update
4
+ def save
5
+ if @unsaved_values.length > 0
6
+ values = {}
7
+ @unsaved_values.each { |k| values[k] = @values[k] }
8
+ values.delete(:id)
9
+ response, api_key = Conekta.request(:post, url, @api_key, values)
10
+ refresh_from(response, api_key)
11
+ end
12
+ self
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ module Conekta
2
+ class APIResource < ConektaObject
3
+ def self.class_name
4
+ self.name.split('::')[-1]
5
+ end
6
+
7
+ def self.url()
8
+ if self == APIResource
9
+ raise NotImplementedError.new('APIResource is an abstract class. You should perform actions on its subclasses (Charge, Customer, etc.)')
10
+ end
11
+ "/#{CGI.escape(class_name.downcase)}s"
12
+ end
13
+
14
+ def url
15
+ unless id = self['id']
16
+ raise ParameterValidationError.new("Could not determine which URL to request: #{self.class} instance has invalid ID: #{id.inspect}", 'id')
17
+ end
18
+ "#{self.class.url}/#{CGI.escape(id)}"
19
+ end
20
+
21
+ def refresh
22
+ response, api_key = Conekta.request(:get, url, @api_key, @retrieve_options)
23
+ refresh_from(response, api_key)
24
+ self
25
+ end
26
+
27
+ def self.retrieve(id, api_key=nil)
28
+ instance = self.new(id, api_key)
29
+ instance.refresh
30
+ instance
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,43 @@
1
+ module Conekta
2
+ class Charge < APIResource
3
+ include Conekta::APIOperations::List
4
+ include Conekta::APIOperations::Create
5
+ include Conekta::APIOperations::Update
6
+
7
+ def refund(params={})
8
+ response, api_key = Conekta.request(:post, refund_url, @api_key, params)
9
+ refresh_from(response, api_key)
10
+ self
11
+ end
12
+
13
+ def capture(params={})
14
+ response, api_key = Conekta.request(:post, capture_url, @api_key, params)
15
+ refresh_from(response, api_key)
16
+ self
17
+ end
18
+
19
+ def update_dispute(params)
20
+ response, api_key = Conekta.request(:post, dispute_url, @api_key, params)
21
+ refresh_from({ :dispute => response }, api_key, true)
22
+ dispute
23
+ end
24
+
25
+ private
26
+
27
+ def refund_url
28
+ url + '/refund'
29
+ end
30
+
31
+ def capture_url
32
+ url + '/capture'
33
+ end
34
+
35
+ def dispute_url
36
+ url + '/dispute'
37
+ end
38
+
39
+ def charges
40
+ Event.all({ :charge_id => id }, @api_key)
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,158 @@
1
+ module Conekta
2
+ class ConektaObject
3
+ include Enumerable
4
+
5
+ attr_accessor :api_key
6
+ @@permanent_attributes = Set.new([:api_key, :id])
7
+
8
+ # The default :id method is deprecated and isn't useful to us
9
+ if method_defined?(:id)
10
+ undef :id
11
+ end
12
+
13
+ def initialize(id=nil, api_key=nil)
14
+ # parameter overloading!
15
+ if id.kind_of?(Hash)
16
+ @retrieve_options = id.dup
17
+ @retrieve_options.delete(:id)
18
+ id = id[:id]
19
+ else
20
+ @retrieve_options = {}
21
+ end
22
+
23
+ @api_key = api_key
24
+ @values = {}
25
+ # This really belongs in APIResource, but not putting it there allows us
26
+ # to have a unified inspect method
27
+ @unsaved_values = Set.new
28
+ @transient_values = Set.new
29
+ self.id = id if id
30
+ end
31
+
32
+ def self.construct_from(values, api_key=nil)
33
+ obj = self.new(values[:id], api_key)
34
+ obj.refresh_from(values, api_key)
35
+ obj
36
+ end
37
+
38
+ def to_s(*args)
39
+ Conekta::JSON.dump(@values, :pretty => true)
40
+ end
41
+
42
+ def inspect()
43
+ id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
44
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> JSON: " + Conekta::JSON.dump(@values, :pretty => true)
45
+ end
46
+
47
+ def refresh_from(values, api_key, partial=false)
48
+ @api_key = api_key
49
+
50
+ removed = partial ? Set.new : Set.new(@values.keys - values.keys)
51
+ added = Set.new(values.keys - @values.keys)
52
+ # Wipe old state before setting new. This is useful for e.g. updating a
53
+ # customer, where there is no persistent card parameter. Mark those values
54
+ # which don't persist as transient
55
+
56
+ instance_eval do
57
+ remove_accessors(removed)
58
+ add_accessors(added)
59
+ end
60
+ removed.each do |k|
61
+ @values.delete(k)
62
+ @transient_values.add(k)
63
+ @unsaved_values.delete(k)
64
+ end
65
+ values.each do |k, v|
66
+ @values[k] = Util.convert_to_conekta_object(v, api_key)
67
+ @transient_values.delete(k)
68
+ @unsaved_values.delete(k)
69
+ end
70
+ end
71
+
72
+ def [](k)
73
+ @values[k.to_sym]
74
+ end
75
+
76
+ def []=(k, v)
77
+ send(:"#{k}=", v)
78
+ end
79
+
80
+ def keys
81
+ @values.keys
82
+ end
83
+
84
+ def values
85
+ @values.values
86
+ end
87
+
88
+ def to_json(*a)
89
+ Conekta::JSON.dump(@values)
90
+ end
91
+
92
+ def as_json(*a)
93
+ @values.as_json(*a)
94
+ end
95
+
96
+ def to_hash
97
+ @values
98
+ end
99
+
100
+ def each(&blk)
101
+ @values.each(&blk)
102
+ end
103
+
104
+ protected
105
+
106
+ def metaclass
107
+ class << self; self; end
108
+ end
109
+
110
+ def remove_accessors(keys)
111
+ metaclass.instance_eval do
112
+ keys.each do |k|
113
+ next if @@permanent_attributes.include?(k)
114
+ k_eq = :"#{k}="
115
+ remove_method(k) if method_defined?(k)
116
+ remove_method(k_eq) if method_defined?(k_eq)
117
+ end
118
+ end
119
+ end
120
+
121
+ def add_accessors(keys)
122
+ metaclass.instance_eval do
123
+ keys.each do |k|
124
+ next if @@permanent_attributes.include?(k)
125
+ k_eq = :"#{k}="
126
+ define_method(k) { @values[k] }
127
+ define_method(k_eq) do |v|
128
+ @values[k] = v
129
+ @unsaved_values.add(k)
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ def method_missing(name, *args)
136
+ # TODO: only allow setting in updateable classes.
137
+ if name.to_s.end_with?('=')
138
+ attr = name.to_s[0...-1].to_sym
139
+ @values[attr] = args[0]
140
+ @unsaved_values.add(attr)
141
+ add_accessors([attr])
142
+ return
143
+ else
144
+ return @values[name] if @values.has_key?(name)
145
+ end
146
+
147
+ begin
148
+ super
149
+ rescue NoMethodError => e
150
+ if @transient_values.include?(name)
151
+ raise NoMethodError.new(e.message + ". HINT: The '#{name}' attribute was set in the past, however. It was then wiped when refreshing the object with the result returned by Conekta's API, probably as a result of a save(). The attributes currently available on this object are: #{@values.keys.join(', ')}")
152
+ else
153
+ raise
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,4 @@
1
+ module Conekta
2
+ class APIConnectionError < ConektaError
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Conekta
2
+ class APIError < ConektaError
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Conekta
2
+ class AuthenticationError < ConektaError
3
+ end
4
+ end
@@ -0,0 +1,11 @@
1
+ module Conekta
2
+ class CardError < ConektaError
3
+ attr_reader :param, :code
4
+
5
+ def initialize(message, param, code, http_status=nil, http_body=nil, json_body=nil)
6
+ super(message, http_status, http_body, json_body)
7
+ @param = param
8
+ @code = code
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,20 @@
1
+ module Conekta
2
+ class ConektaError < StandardError
3
+ attr_reader :message
4
+ attr_reader :http_status
5
+ attr_reader :http_body
6
+ attr_reader :json_body
7
+
8
+ def initialize(message=nil, http_status=nil, http_body=nil, json_body=nil)
9
+ @message = message
10
+ @http_status = http_status
11
+ @http_body = http_body
12
+ @json_body = json_body
13
+ end
14
+
15
+ def to_s
16
+ status_string = @http_status.nil? ? "" : "(Status #{@http_status}) "
17
+ "#{status_string}#{@message}"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ module Conekta
2
+ class MalformedRequestError < ConektaError
3
+ attr_accessor :param
4
+
5
+ def initialize(message, param, http_status=nil, http_body=nil, json_body=nil)
6
+ super(message, http_status, http_body, json_body)
7
+ @param = param
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Conekta
2
+ class ParameterValidationError < ConektaError
3
+ attr_accessor :param
4
+
5
+ def initialize(message, param, http_status=nil, http_body=nil, json_body=nil)
6
+ super(message, http_status, http_body, json_body)
7
+ @param = param
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ module Conekta
2
+ class ResourceNotFoundError < ConektaError
3
+ attr_accessor :param
4
+
5
+ def initialize(message, param, http_status=nil, http_body=nil, json_body=nil)
6
+ super(message, http_status, http_body, json_body)
7
+ @param = param
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ module Conekta
2
+ class Event < APIResource
3
+ include Conekta::APIOperations::List
4
+ end
5
+ end
@@ -0,0 +1,21 @@
1
+ module Conekta
2
+ module JSON
3
+ if MultiJson.respond_to?(:dump)
4
+ def self.dump(*args)
5
+ MultiJson.dump(*args)
6
+ end
7
+
8
+ def self.load(*args)
9
+ MultiJson.load(*args)
10
+ end
11
+ else
12
+ def self.dump(*args)
13
+ MultiJson.encode(*args)
14
+ end
15
+
16
+ def self.load(*args)
17
+ MultiJson.decode(*args)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ module Conekta
2
+ class ListObject < ConektaObject
3
+
4
+ def [](k)
5
+ case k
6
+ when String, Symbol
7
+ super
8
+ else
9
+ raise ArgumentError.new("You tried to access the #{k.inspect} index, but ListObject types only support String keys. (HINT: List calls return an object with a 'data' (which is the data array). You likely want to call #data[#{k.inspect}])")
10
+ end
11
+ end
12
+
13
+ def each(&blk)
14
+ self.data.each(&blk)
15
+ end
16
+
17
+ def retrieve(id, api_key=nil)
18
+ api_key ||= @api_key
19
+ response, api_key = Conekta.request(:get,"#{url}/#{CGI.escape(id)}", api_key)
20
+ Util.convert_to_conekta_object(response, api_key)
21
+ end
22
+
23
+ def create(params={}, api_key=nil)
24
+ api_key ||= @api_key
25
+ response, api_key = Conekta.request(:post, url, api_key, params)
26
+ Util.convert_to_conekta_object(response, api_key)
27
+ end
28
+
29
+ def all(params={}, api_key=nil)
30
+ api_key ||= @api_key
31
+ response, api_key = Conekta.request(:get, url, api_key, params)
32
+ Util.convert_to_conekta_object(response, api_key)
33
+ end
34
+ end
35
+ end