Recharge 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/happymapper/attribute.rb +3 -0
- data/lib/happymapper/element.rb +3 -0
- data/lib/happymapper/item.rb +179 -0
- data/lib/happymapper/version.rb +3 -0
- data/lib/happymapper.rb +315 -0
- data/lib/recharge/base.rb +45 -0
- data/lib/recharge/charge.rb +45 -0
- data/lib/recharge/client.rb +58 -0
- data/lib/recharge/customer.rb +53 -0
- data/lib/recharge/paymethod.rb +23 -0
- data/lib/recharge/product.rb +37 -0
- data/lib/recharge/response.rb +34 -0
- data/lib/recharge/transaction.rb +31 -0
- data/lib/recharge.rb +59 -0
- data/lib/test.rb +520 -0
- metadata +76 -0
@@ -0,0 +1,179 @@
|
|
1
|
+
module HappyMapper
|
2
|
+
class Item
|
3
|
+
attr_accessor :name, :type, :tag, :options, :namespace
|
4
|
+
|
5
|
+
Types = [String, Float, Time, Date, DateTime, Integer, Boolean]
|
6
|
+
|
7
|
+
# options:
|
8
|
+
# :deep => Boolean False to only parse element's children, True to include
|
9
|
+
# grandchildren and all others down the chain (// in expath)
|
10
|
+
# :namespace => String Element's namespace if it's not the global or inherited
|
11
|
+
# default
|
12
|
+
# :parser => Symbol Class method to use for type coercion.
|
13
|
+
# :raw => Boolean Use raw node value (inc. tags) when parsing.
|
14
|
+
# :single => Boolean False if object should be collection, True for single object
|
15
|
+
# :tag => String Element name if it doesn't match the specified name.
|
16
|
+
def initialize(name, type, o={})
|
17
|
+
self.name = name.to_s
|
18
|
+
self.type = type
|
19
|
+
self.tag = o.delete(:tag) || name.to_s
|
20
|
+
self.options = o
|
21
|
+
|
22
|
+
@xml_type = self.class.to_s.split('::').last.downcase
|
23
|
+
end
|
24
|
+
|
25
|
+
def constant
|
26
|
+
@constant ||= constantize(type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def from_xml_node(node, namespace)
|
30
|
+
if primitive?
|
31
|
+
find(node, namespace) do |n|
|
32
|
+
if n.respond_to?(:content)
|
33
|
+
typecast(n.content)
|
34
|
+
else
|
35
|
+
typecast(n.to_s)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
else
|
39
|
+
if options[:parser]
|
40
|
+
find(node, namespace) do |n|
|
41
|
+
if n.respond_to?(:content) && !options[:raw]
|
42
|
+
value = n.content
|
43
|
+
else
|
44
|
+
value = n.to_s
|
45
|
+
end
|
46
|
+
|
47
|
+
begin
|
48
|
+
constant.send(options[:parser].to_sym, value)
|
49
|
+
rescue
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
else
|
54
|
+
constant.parse(node, options)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def xpath(namespace = self.namespace)
|
60
|
+
xpath = ''
|
61
|
+
xpath += './/' if options[:deep]
|
62
|
+
xpath += "#{DEFAULT_NS}:" if namespace
|
63
|
+
xpath += tag
|
64
|
+
# puts "xpath: #{xpath}"
|
65
|
+
xpath
|
66
|
+
end
|
67
|
+
|
68
|
+
def primitive?
|
69
|
+
Types.include?(constant)
|
70
|
+
end
|
71
|
+
|
72
|
+
def element?
|
73
|
+
@xml_type == 'element'
|
74
|
+
end
|
75
|
+
|
76
|
+
def attribute?
|
77
|
+
!element?
|
78
|
+
end
|
79
|
+
|
80
|
+
def method_name
|
81
|
+
@method_name ||= name.tr('-', '_')
|
82
|
+
end
|
83
|
+
|
84
|
+
def typecast(value)
|
85
|
+
return value if value.kind_of?(constant) || value.nil?
|
86
|
+
begin
|
87
|
+
if constant == String then value.to_s
|
88
|
+
elsif constant == Float then value.to_f
|
89
|
+
elsif constant == Time then Time.parse(value.to_s)
|
90
|
+
elsif constant == Date then Date.parse(value.to_s)
|
91
|
+
elsif constant == DateTime then DateTime.parse(value.to_s)
|
92
|
+
elsif constant == Boolean then ['true', 't', '1'].include?(value.to_s.downcase)
|
93
|
+
elsif constant == Integer
|
94
|
+
# ganked from datamapper
|
95
|
+
value_to_i = value.to_i
|
96
|
+
if value_to_i == 0 && value != '0'
|
97
|
+
value_to_s = value.to_s
|
98
|
+
begin
|
99
|
+
Integer(value_to_s =~ /^(\d+)/ ? $1 : value_to_s)
|
100
|
+
rescue ArgumentError
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
else
|
104
|
+
value_to_i
|
105
|
+
end
|
106
|
+
else
|
107
|
+
value
|
108
|
+
end
|
109
|
+
rescue
|
110
|
+
value
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
def constantize(type)
|
116
|
+
if type.is_a?(String)
|
117
|
+
names = type.split('::')
|
118
|
+
constant = Object
|
119
|
+
names.each do |name|
|
120
|
+
constant = constant.const_defined?(name) ?
|
121
|
+
constant.const_get(name) :
|
122
|
+
constant.const_missing(name)
|
123
|
+
end
|
124
|
+
constant
|
125
|
+
else
|
126
|
+
type
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def find(node, namespace, &block)
|
131
|
+
if options[:namespace] == false
|
132
|
+
namespace = nil
|
133
|
+
elsif options[:namespace]
|
134
|
+
# from an element definition
|
135
|
+
namespace = "#{DEFAULT_NS}:#{options[:namespace]}"
|
136
|
+
elsif self.namespace
|
137
|
+
# this node has a custom namespace (that is present in the doc)
|
138
|
+
namespace = "#{DEFAULT_NS}:#{self.namespace}"
|
139
|
+
end
|
140
|
+
|
141
|
+
if element?
|
142
|
+
if(options[:single].nil? || options[:single])
|
143
|
+
result = node.find_first(xpath(namespace), namespace)
|
144
|
+
else
|
145
|
+
result = node.find(xpath(namespace))
|
146
|
+
end
|
147
|
+
# puts "vfxn: #{xpath} #{result.inspect}"
|
148
|
+
if result
|
149
|
+
if(options[:single].nil? || options[:single])
|
150
|
+
value = yield(result)
|
151
|
+
else
|
152
|
+
value = []
|
153
|
+
|
154
|
+
result.each do |res|
|
155
|
+
value << yield(res)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
if options[:attributes].is_a?(Hash)
|
159
|
+
result.attributes.each do |xml_attribute|
|
160
|
+
if attribute_options = options[:attributes][xml_attribute.name.to_sym]
|
161
|
+
attribute_value = Attribute.new(xml_attribute.name.to_sym, *attribute_options).from_xml_node(result, namespace)
|
162
|
+
result.instance_eval <<-EOV
|
163
|
+
def value.#{xml_attribute.name}
|
164
|
+
#{attribute_value.inspect}
|
165
|
+
end
|
166
|
+
EOV
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
value
|
171
|
+
else
|
172
|
+
nil
|
173
|
+
end
|
174
|
+
else
|
175
|
+
yield(node[tag])
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
data/lib/happymapper.rb
ADDED
@@ -0,0 +1,315 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'date'
|
3
|
+
require 'time'
|
4
|
+
require 'xml'
|
5
|
+
|
6
|
+
class Boolean; end
|
7
|
+
|
8
|
+
module HappyMapper
|
9
|
+
|
10
|
+
DEFAULT_NS = "happymapper"
|
11
|
+
|
12
|
+
def self.included(base)
|
13
|
+
base.instance_variable_set("@attributes", {})
|
14
|
+
base.instance_variable_set("@elements", {})
|
15
|
+
base.instance_variable_set("@registered_namespaces", {})
|
16
|
+
|
17
|
+
base.extend ClassMethods
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def attribute(name, type, options={})
|
22
|
+
attribute = Attribute.new(name, type, options)
|
23
|
+
@attributes[to_s] ||= []
|
24
|
+
@attributes[to_s] << attribute
|
25
|
+
attr_accessor attribute.method_name.intern
|
26
|
+
end
|
27
|
+
|
28
|
+
def attributes
|
29
|
+
@attributes[to_s] || []
|
30
|
+
end
|
31
|
+
|
32
|
+
def element(name, type, options={})
|
33
|
+
element = Element.new(name, type, options)
|
34
|
+
@elements[to_s] ||= []
|
35
|
+
@elements[to_s] << element
|
36
|
+
attr_accessor element.method_name.intern
|
37
|
+
end
|
38
|
+
|
39
|
+
def content(name)
|
40
|
+
@content = name
|
41
|
+
attr_accessor name
|
42
|
+
end
|
43
|
+
|
44
|
+
def after_parse_callbacks
|
45
|
+
@after_parse_callbacks ||= []
|
46
|
+
end
|
47
|
+
|
48
|
+
def after_parse(&block)
|
49
|
+
after_parse_callbacks.push(block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def elements
|
53
|
+
@elements[to_s] || []
|
54
|
+
end
|
55
|
+
|
56
|
+
def has_one(name, type, options={})
|
57
|
+
element name, type, {:single => true}.merge(options)
|
58
|
+
end
|
59
|
+
|
60
|
+
def has_many(name, type, options={})
|
61
|
+
element name, type, {:single => false}.merge(options)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Specify a namespace if a node and all its children are all namespaced
|
65
|
+
# elements. This is simpler than passing the :namespace option to each
|
66
|
+
# defined element.
|
67
|
+
def namespace(namespace = nil)
|
68
|
+
@namespace = namespace if namespace
|
69
|
+
@namespace
|
70
|
+
end
|
71
|
+
|
72
|
+
def register_namespace(namespace, ns)
|
73
|
+
@registered_namespaces.merge!(namespace => ns)
|
74
|
+
end
|
75
|
+
|
76
|
+
def tag(new_tag_name)
|
77
|
+
@tag_name = new_tag_name.to_s
|
78
|
+
end
|
79
|
+
|
80
|
+
def tag_name
|
81
|
+
@tag_name ||= to_s.split('::')[-1].downcase
|
82
|
+
end
|
83
|
+
|
84
|
+
def parse(xml, options = {})
|
85
|
+
if xml.is_a?(XML::Node)
|
86
|
+
node = xml
|
87
|
+
else
|
88
|
+
if xml.is_a?(XML::Document)
|
89
|
+
node = xml.root
|
90
|
+
else
|
91
|
+
node = XML::Parser.string(xml).parse.root
|
92
|
+
end
|
93
|
+
|
94
|
+
root = node.name == tag_name
|
95
|
+
end
|
96
|
+
|
97
|
+
namespace = @namespace || (node.namespaces && node.namespaces.default)
|
98
|
+
namespace = "#{DEFAULT_NS}:#{namespace}" if namespace
|
99
|
+
|
100
|
+
xpath = root ? '/' : './/'
|
101
|
+
xpath += "#{DEFAULT_NS}:" if namespace
|
102
|
+
xpath += tag_name
|
103
|
+
|
104
|
+
nodes = node.find(xpath, Array(namespace))
|
105
|
+
collection = nodes.collect do |n|
|
106
|
+
obj = new
|
107
|
+
|
108
|
+
attributes.each do |attr|
|
109
|
+
obj.send("#{attr.method_name}=",
|
110
|
+
attr.from_xml_node(n, namespace))
|
111
|
+
end
|
112
|
+
|
113
|
+
elements.each do |elem|
|
114
|
+
obj.send("#{elem.method_name}=",
|
115
|
+
elem.from_xml_node(n, namespace))
|
116
|
+
end
|
117
|
+
|
118
|
+
obj.send("#{@content}=", n.content) if @content
|
119
|
+
|
120
|
+
obj.class.after_parse_callbacks.each { |callback| callback.call(obj) }
|
121
|
+
|
122
|
+
obj
|
123
|
+
end
|
124
|
+
|
125
|
+
# per http://libxml.rubyforge.org/rdoc/classes/LibXML/XML/Document.html#M000354
|
126
|
+
nodes = nil
|
127
|
+
|
128
|
+
if options[:single] || root
|
129
|
+
collection.first
|
130
|
+
else
|
131
|
+
collection
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
#
|
138
|
+
# Create an xml representation of the specified class based on defined
|
139
|
+
# HappyMapper elements and attributes. The method is defined in a way
|
140
|
+
# that it can be called recursively by classes that are also HappyMapper
|
141
|
+
# classes, allowg for the composition of classes.
|
142
|
+
#
|
143
|
+
def to_xml(parent_node = nil, default_namespace = nil)
|
144
|
+
|
145
|
+
#
|
146
|
+
# Create a tag that uses the tag name of the class that has no contents
|
147
|
+
# but has the specified namespace or uses the default namespace
|
148
|
+
#
|
149
|
+
current_node = XML::Node.new(self.class.tag_name)
|
150
|
+
|
151
|
+
|
152
|
+
if parent_node
|
153
|
+
#
|
154
|
+
# if #to_xml has been called with a parent_node that means this method
|
155
|
+
# is being called recursively (or a special case) and we want to return
|
156
|
+
# the parent_node with the new node as a child
|
157
|
+
#
|
158
|
+
parent_node << current_node
|
159
|
+
else
|
160
|
+
#
|
161
|
+
# If #to_xml has been called without a Node (and namespace) that
|
162
|
+
# means we want to return an xml document
|
163
|
+
#
|
164
|
+
write_out_to_xml = true
|
165
|
+
end
|
166
|
+
|
167
|
+
#
|
168
|
+
# Add all the registered namespaces to the current node and the current node's
|
169
|
+
# root element. Without adding it to the root element it is not possible to
|
170
|
+
# parse or use xpath to find elements.
|
171
|
+
#
|
172
|
+
if self.class.instance_variable_get('@registered_namespaces')
|
173
|
+
|
174
|
+
# Given a node, continue moving up to parents until there are no more parents
|
175
|
+
find_root_node = lambda {|node| while node.parent? ; node = node.parent ; end ; node }
|
176
|
+
root_node = find_root_node.call(current_node)
|
177
|
+
|
178
|
+
# Add the registered namespace to the found root node only if it does not already have one defined
|
179
|
+
self.class.instance_variable_get('@registered_namespaces').each_pair do |prefix,href|
|
180
|
+
XML::Namespace.new(current_node,prefix,href)
|
181
|
+
XML::Namespace.new(root_node,prefix,href) unless root_node.namespaces.find_by_prefix(prefix)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
#
|
186
|
+
# Determine the tag namespace if one has been specified. This value takes
|
187
|
+
# precendence over one that is handed down to composed sub-classes.
|
188
|
+
#
|
189
|
+
tag_namespace = current_node.namespaces.find_by_prefix(self.class.namespace) || default_namespace
|
190
|
+
|
191
|
+
# Set the namespace of the current node to the specified namespace
|
192
|
+
current_node.namespaces.namespace = tag_namespace if tag_namespace
|
193
|
+
|
194
|
+
#
|
195
|
+
# Add all the attribute tags to the current node with their namespace, if one
|
196
|
+
# is defined, or the namespace handed down to the node.
|
197
|
+
#
|
198
|
+
self.class.attributes.each do |attribute|
|
199
|
+
attribute_namespace = current_node.namespaces.find_by_prefix(attribute.options[:namespace]) || default_namespace
|
200
|
+
|
201
|
+
value = send(attribute.method_name)
|
202
|
+
|
203
|
+
#
|
204
|
+
# If the attribute has a :on_save attribute defined that is a proc or
|
205
|
+
# a defined method, then call those with the current value.
|
206
|
+
#
|
207
|
+
if on_save_operation = attribute.options[:on_save]
|
208
|
+
if on_save_operation.is_a?(Proc)
|
209
|
+
value = on_save_operation.call(value)
|
210
|
+
elsif respond_to?(on_save_operation)
|
211
|
+
value = send(on_save_operation,value)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
current_node[ "#{attribute_namespace ? "#{attribute_namespace.prefix}:" : ""}#{attribute.tag}" ] = value
|
216
|
+
end
|
217
|
+
|
218
|
+
#
|
219
|
+
# All all the elements defined (e.g. has_one, has_many, element) ...
|
220
|
+
#
|
221
|
+
self.class.elements.each do |element|
|
222
|
+
|
223
|
+
tag = element.tag || element.name
|
224
|
+
|
225
|
+
element_namespace = current_node.namespaces.find_by_prefix(element.options[:namespace]) || tag_namespace
|
226
|
+
|
227
|
+
value = send(element.name)
|
228
|
+
|
229
|
+
#
|
230
|
+
# If the element defines an :on_save lambda/proc then we will call that
|
231
|
+
# operation on the specified value. This allows for operations to be
|
232
|
+
# performed to convert the value to a specific value to be saved to the xml.
|
233
|
+
#
|
234
|
+
if on_save_operation = element.options[:on_save]
|
235
|
+
if on_save_operation.is_a?(Proc)
|
236
|
+
value = on_save_operation.call(value)
|
237
|
+
elsif respond_to?(on_save_operation)
|
238
|
+
value = send(on_save_operation,value)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
#
|
243
|
+
# Normally a nil value would be ignored, however if specified then
|
244
|
+
# an empty element will be written to the xml
|
245
|
+
#
|
246
|
+
if value.nil? && element.options[:state_when_nil]
|
247
|
+
current_node << XML::Node.new(tag,nil,element_namespace)
|
248
|
+
end
|
249
|
+
|
250
|
+
#
|
251
|
+
# To allow for us to treat both groups of items and singular items
|
252
|
+
# equally we wrap the value and treat it as an array.
|
253
|
+
#
|
254
|
+
if value.nil?
|
255
|
+
values = []
|
256
|
+
elsif value.respond_to?(:to_ary) && !element.options[:single]
|
257
|
+
values = value.to_ary
|
258
|
+
else
|
259
|
+
values = [value]
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
values.each do |item|
|
264
|
+
|
265
|
+
if item.is_a?(HappyMapper)
|
266
|
+
|
267
|
+
#
|
268
|
+
# Other HappyMapper items that are convertable should not be called
|
269
|
+
# with the current node and the namespace defined for the element.
|
270
|
+
#
|
271
|
+
item.to_xml(current_node,element_namespace)
|
272
|
+
|
273
|
+
elsif item
|
274
|
+
|
275
|
+
#
|
276
|
+
# When a value exists we should append the value for the tag
|
277
|
+
#
|
278
|
+
current_node << XML::Node.new(tag,item.to_s,element_namespace)
|
279
|
+
|
280
|
+
else
|
281
|
+
|
282
|
+
#
|
283
|
+
# Normally a nil value would be ignored, however if specified then
|
284
|
+
# an empty element will be written to the xml
|
285
|
+
#
|
286
|
+
current_node << XML.Node.new(tag,nil,element_namespace) if element.options[:state_when_nil]
|
287
|
+
|
288
|
+
end
|
289
|
+
|
290
|
+
end
|
291
|
+
|
292
|
+
end
|
293
|
+
|
294
|
+
|
295
|
+
#
|
296
|
+
# Generate xml from a document if no node was passed as a parameter. Otherwise
|
297
|
+
# this method is being called recursively (or special case) and we should
|
298
|
+
# return the node with this node attached as a child.
|
299
|
+
#
|
300
|
+
if write_out_to_xml
|
301
|
+
document = XML::Document.new
|
302
|
+
document.root = current_node
|
303
|
+
document.to_s
|
304
|
+
else
|
305
|
+
parent_node
|
306
|
+
end
|
307
|
+
|
308
|
+
end
|
309
|
+
|
310
|
+
|
311
|
+
end
|
312
|
+
|
313
|
+
require File.dirname(__FILE__) + '/happymapper/item'
|
314
|
+
require File.dirname(__FILE__) + '/happymapper/attribute'
|
315
|
+
require File.dirname(__FILE__) + '/happymapper/element'
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Recharge
|
2
|
+
class Base
|
3
|
+
class << self
|
4
|
+
def get(uri)
|
5
|
+
client = Client.new()
|
6
|
+
return client.request('GET', uri)
|
7
|
+
end
|
8
|
+
|
9
|
+
def post(uri, data=nil)
|
10
|
+
client = Client.new()
|
11
|
+
return client.request('POST', uri, data)
|
12
|
+
end
|
13
|
+
|
14
|
+
def delete(uri)
|
15
|
+
client = Client.new()
|
16
|
+
return client.request('DELETE', uri)
|
17
|
+
end
|
18
|
+
|
19
|
+
def returnImportantXML(data)
|
20
|
+
require "rexml/document"
|
21
|
+
doc = REXML::Document.new(data)
|
22
|
+
|
23
|
+
if doc.elements[1].elements[3].length == 1
|
24
|
+
return doc.elements[1].elements[3].elements[1]
|
25
|
+
else
|
26
|
+
return doc.elements[1].elements[3]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def returnErrorXML(data)
|
31
|
+
require "rexml/document"
|
32
|
+
doc = REXML::Document.new(data)
|
33
|
+
|
34
|
+
doc.elements["/response/result/resultDescription"].text
|
35
|
+
end
|
36
|
+
|
37
|
+
def returnStatusXML(data)
|
38
|
+
require "rexml/document"
|
39
|
+
doc = REXML::Document.new(data)
|
40
|
+
|
41
|
+
doc.elements["/response/result/resultDescription"]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Recharge
|
2
|
+
class Charge
|
3
|
+
include HappyMapper
|
4
|
+
|
5
|
+
tag 'charge'
|
6
|
+
|
7
|
+
element :id, String
|
8
|
+
element :product, String
|
9
|
+
element :payMethod, String
|
10
|
+
element :billingStartDate, String
|
11
|
+
element :billingEndDate, String
|
12
|
+
element :billingEndAmount, String
|
13
|
+
element :price, String
|
14
|
+
element :intervalValue, String
|
15
|
+
element :intervalUnit, String
|
16
|
+
element :nextChargeDate, String
|
17
|
+
element :totalRevenue, String
|
18
|
+
element :pubID, String
|
19
|
+
element :oneTime, String
|
20
|
+
element :error, String
|
21
|
+
|
22
|
+
def initialize(id='')
|
23
|
+
@id = id
|
24
|
+
end
|
25
|
+
def self.find_all
|
26
|
+
responseXML = Recharge::Base.get('charges')
|
27
|
+
parse(responseXML.to_s)
|
28
|
+
end
|
29
|
+
def self.find (id)
|
30
|
+
responseXML = Recharge::Base.get('charges/'+id)
|
31
|
+
parse(responseXML.to_s)
|
32
|
+
end
|
33
|
+
def self.create (attributes = {})
|
34
|
+
responseXML = Recharge::Base.post('charges', attributes)
|
35
|
+
parse(responseXML.to_s)
|
36
|
+
end
|
37
|
+
def update (attributes = {})
|
38
|
+
responseXML = Recharge::Base.post('charges/'+self.id, attributes)
|
39
|
+
Charge.parse(responseXML.to_s)
|
40
|
+
end
|
41
|
+
def destroy
|
42
|
+
responseXML = Recharge::Base.delete('charges/'+self.id)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class Client
|
2
|
+
|
3
|
+
def baseURL
|
4
|
+
"https://www.rechargebilling.com/API/v2/"
|
5
|
+
end
|
6
|
+
|
7
|
+
def request(method, uri, data=nil)
|
8
|
+
response = sendRequest(method, uri, data)
|
9
|
+
response.assertValidResponse
|
10
|
+
if response.code.to_i == 200
|
11
|
+
if method != "DELETE"
|
12
|
+
Recharge::Base.returnImportantXML(response.body)
|
13
|
+
else
|
14
|
+
Recharge::Base.returnStatusXML(response.body)
|
15
|
+
end
|
16
|
+
else
|
17
|
+
Recharge::Base.returnErrorXML(response.body)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
def sendRequest(method, uri, data=nil)
|
23
|
+
require "net/https"
|
24
|
+
require "uri"
|
25
|
+
|
26
|
+
if !Recharge.api_key
|
27
|
+
puts "no api key"
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
31
|
+
uri = baseURL+uri
|
32
|
+
uri = URI.parse(uri)
|
33
|
+
|
34
|
+
if method == "GET"
|
35
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
36
|
+
end
|
37
|
+
|
38
|
+
if method == "POST"
|
39
|
+
request = Net::HTTP::Post.new(uri.request_uri)
|
40
|
+
request.set_form_data(data)
|
41
|
+
end
|
42
|
+
|
43
|
+
if method == "DELETE"
|
44
|
+
request = Net::HTTP::Delete.new(uri.request_uri)
|
45
|
+
end
|
46
|
+
|
47
|
+
request.basic_auth(Recharge.api_key, "")
|
48
|
+
|
49
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
50
|
+
http.use_ssl = true
|
51
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
52
|
+
|
53
|
+
response = http.request(request)
|
54
|
+
|
55
|
+
return Response.new(response.code, response.body)
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Recharge
|
2
|
+
class Customer
|
3
|
+
include HappyMapper
|
4
|
+
|
5
|
+
tag 'customer'
|
6
|
+
|
7
|
+
element :id, String
|
8
|
+
element :refID, String
|
9
|
+
element :firstName, String
|
10
|
+
element :lastName, String
|
11
|
+
element :email, String
|
12
|
+
element :company, String
|
13
|
+
element :phone, String
|
14
|
+
element :billingAddress1, String
|
15
|
+
element :billingAddress2, String
|
16
|
+
element :billingCity, String
|
17
|
+
element :billingState, String
|
18
|
+
element :billingZIP, String
|
19
|
+
element :shippingAddress1, String
|
20
|
+
element :shippingAddress2, String
|
21
|
+
element :shippingCity, String
|
22
|
+
element :shippingState, String
|
23
|
+
element :shippingZIP, String
|
24
|
+
element :emailOption, String
|
25
|
+
element :signupDate, String
|
26
|
+
|
27
|
+
has_many :payMethods, PayMethod
|
28
|
+
has_many :charges, Charge
|
29
|
+
|
30
|
+
def initialize(id='')
|
31
|
+
@id = id
|
32
|
+
end
|
33
|
+
def self.find_all
|
34
|
+
responseXML = Recharge::Base.get('customers')
|
35
|
+
parse(responseXML.to_s)
|
36
|
+
end
|
37
|
+
def self.find (id)
|
38
|
+
responseXML = Recharge::Base.get('customers/'+id)
|
39
|
+
parse(responseXML.to_s)
|
40
|
+
end
|
41
|
+
def self.create (attributes = {})
|
42
|
+
responseXML = Recharge::Base.post('customers', attributes)
|
43
|
+
parse(responseXML.to_s)
|
44
|
+
end
|
45
|
+
def update (attributes = {})
|
46
|
+
responseXML = Recharge::Base.post('customers/'+self.id, attributes)
|
47
|
+
Customer.parse(responseXML.to_s)
|
48
|
+
end
|
49
|
+
def destroy
|
50
|
+
responseXML = Recharge::Base.delete('customers/'+self.id)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|