synapse_pay 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.travis.yml +16 -0
  4. data/Gemfile +8 -0
  5. data/LICENSE +21 -0
  6. data/README.md +54 -0
  7. data/Rakefile +8 -0
  8. data/VERSION +1 -0
  9. data/bin/synapse_pay-console +7 -0
  10. data/gemfiles/default-with-activesupport.gemfile +10 -0
  11. data/gemfiles/json.gemfile +12 -0
  12. data/gemfiles/yajl.gemfile +12 -0
  13. data/lib/synapse_pay.rb +78 -0
  14. data/lib/synapse_pay/apibits/api_client.rb +29 -0
  15. data/lib/synapse_pay/apibits/api_endpoint.rb +9 -0
  16. data/lib/synapse_pay/apibits/api_list.rb +88 -0
  17. data/lib/synapse_pay/apibits/api_method.rb +97 -0
  18. data/lib/synapse_pay/apibits/api_object.rb +52 -0
  19. data/lib/synapse_pay/apibits/api_resource.rb +127 -0
  20. data/lib/synapse_pay/apibits/headers_builder.rb +47 -0
  21. data/lib/synapse_pay/apibits/params_builder.rb +30 -0
  22. data/lib/synapse_pay/apibits/path_builder.rb +38 -0
  23. data/lib/synapse_pay/apibits/requester.rb +104 -0
  24. data/lib/synapse_pay/apibits/util.rb +51 -0
  25. data/lib/synapse_pay/client.rb +89 -0
  26. data/lib/synapse_pay/endpoints/bank_endpoint.rb +47 -0
  27. data/lib/synapse_pay/endpoints/bank_mfa_device_endpoint.rb +5 -0
  28. data/lib/synapse_pay/endpoints/bank_mfa_questions_endpoint.rb +5 -0
  29. data/lib/synapse_pay/endpoints/bank_status_endpoint.rb +11 -0
  30. data/lib/synapse_pay/endpoints/card_endpoint.rb +26 -0
  31. data/lib/synapse_pay/endpoints/deposit_endpoint.rb +23 -0
  32. data/lib/synapse_pay/endpoints/mass_pay_endpoint.rb +26 -0
  33. data/lib/synapse_pay/endpoints/order_endpoint.rb +44 -0
  34. data/lib/synapse_pay/endpoints/user_endpoint.rb +26 -0
  35. data/lib/synapse_pay/endpoints/wire_endpoint.rb +29 -0
  36. data/lib/synapse_pay/endpoints/withdrawal_endpoint.rb +17 -0
  37. data/lib/synapse_pay/errors/api_connection_error.rb +4 -0
  38. data/lib/synapse_pay/errors/api_error.rb +32 -0
  39. data/lib/synapse_pay/errors/authentication_error.rb +4 -0
  40. data/lib/synapse_pay/errors/synapse_pay_error.rb +13 -0
  41. data/lib/synapse_pay/nested_list.rb +32 -0
  42. data/lib/synapse_pay/resources/bank.rb +46 -0
  43. data/lib/synapse_pay/resources/bank_mfa_device.rb +32 -0
  44. data/lib/synapse_pay/resources/bank_mfa_questions.rb +28 -0
  45. data/lib/synapse_pay/resources/bank_status.rb +21 -0
  46. data/lib/synapse_pay/resources/card.rb +32 -0
  47. data/lib/synapse_pay/resources/deposit.rb +25 -0
  48. data/lib/synapse_pay/resources/mass_pay.rb +38 -0
  49. data/lib/synapse_pay/resources/order.rb +63 -0
  50. data/lib/synapse_pay/resources/user.rb +80 -0
  51. data/lib/synapse_pay/resources/wire.rb +31 -0
  52. data/lib/synapse_pay/resources/withdrawal.rb +29 -0
  53. data/lib/synapse_pay/version.rb +3 -0
  54. data/synapse_pay.gemspec +29 -0
  55. data/test/synapse_pay/api_list_test.rb +23 -0
  56. data/test/synapse_pay/api_method_test.rb +89 -0
  57. data/test/synapse_pay/headers_builder_test.rb +39 -0
  58. data/test/synapse_pay/params_builder_test.rb +57 -0
  59. data/test/synapse_pay/path_builder_test.rb +50 -0
  60. data/test/synapse_pay/requester_test.rb +86 -0
  61. data/test/synapse_pay/util_test.rb +51 -0
  62. data/test/test_data.rb +72 -0
  63. data/test/test_helper.rb +41 -0
  64. metadata +220 -0
@@ -0,0 +1,52 @@
1
+ module SynapsePay
2
+ class APIObject
3
+ include Enumerable
4
+ attr_reader :json
5
+
6
+ def self.construct(json)
7
+ if json.is_a?(Array)
8
+ return json.map{ |a| APIObject.construct(a) }
9
+ elsif json.is_a?(Hash)
10
+ return APIObject.new(json)
11
+ else
12
+ return json
13
+ end
14
+ end
15
+
16
+ def initialize(json=nil)
17
+ refresh_from(json)
18
+ end
19
+
20
+ def refresh_from(json={})
21
+ @json = Util.sorta_deep_clone(json)
22
+ @json.each do |k, v|
23
+ @json[k] = APIObject.construct(v)
24
+ end
25
+ self
26
+ end
27
+
28
+ def inspect
29
+ @json.inspect
30
+ end
31
+
32
+ def to_json(*args)
33
+ JSON.generate(@json)
34
+ end
35
+
36
+ def method_missing(name, *args, &blk)
37
+ if name.to_s.end_with?('=')
38
+ attr = name.to_s[0...-1].to_sym
39
+ @json[attr] = args[0]
40
+ else
41
+ if @json.respond_to?(name)
42
+ @json.send(name, *args, &blk)
43
+ elsif @json.has_key?(name.to_sym)
44
+ return @json[name.to_sym]
45
+ else
46
+ super
47
+ end
48
+ end
49
+ end
50
+
51
+ end
52
+ end
@@ -0,0 +1,127 @@
1
+ module SynapsePay
2
+ class APIResource
3
+ attr_reader :api_method
4
+ attr_reader :json
5
+ attr_reader :client
6
+
7
+ def initialize(json=nil, api_method=nil, client=nil)
8
+ refresh_from(json, api_method, client)
9
+ end
10
+
11
+ def refresh_from(json={}, api_method=nil, client=nil)
12
+ unless json.is_a?(Hash)
13
+ json = { :id => json }
14
+ end
15
+ json = Util.symbolize_keys(json)
16
+
17
+ # Clear or write over any old data
18
+ clear_api_attributes
19
+ @api_method = api_method
20
+ @json = Util.sorta_deep_clone(json)
21
+ @client = client
22
+
23
+ # Use json (not the @json, the cloned copy)
24
+ json.each do |k, v|
25
+ if self.class.api_attribute_names.include?(k.to_sym)
26
+ instance_variable_set("@#{k}", determine_api_attribute_value(k, v))
27
+ end
28
+ end
29
+ self
30
+ end
31
+
32
+ def inspect
33
+ id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
34
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}> Attributes: " + JSON.pretty_generate(inspect_api_attributes)
35
+ end
36
+
37
+ def inspect_nested
38
+ id_string = (self.respond_to?(:id) && !self.id.nil?) ? " id=#{self.id}" : ""
39
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}#{id_string}>"
40
+ end
41
+
42
+ def to_json(*args)
43
+ JSON.generate(api_attributes)
44
+ end
45
+
46
+ def self.api_attribute_names
47
+ @api_attributes.map(&:first)
48
+ end
49
+
50
+ def api_attributes
51
+ ret = {}
52
+ self.class.api_attribute_names.each do |attribute|
53
+ ret[attribute] = self.send(attribute)
54
+ end
55
+ ret
56
+ end
57
+
58
+ def inspect_api_attributes
59
+ ret = {}
60
+ api_attributes.each do |k, v|
61
+ if v.is_a?(APIResource)
62
+ ret[k] = v.inspect_nested
63
+ else
64
+ ret[k] = v
65
+ end
66
+ end
67
+ ret
68
+ end
69
+
70
+ # TODO(joncalhoun): Make this work for nested class construction.
71
+ def changed_api_attributes
72
+ ret = {}
73
+ self.api_attributes.each do |name, value|
74
+ if @json[name] != value
75
+ ret[name] = value
76
+ end
77
+ end
78
+ ret
79
+ end
80
+
81
+ def clear_api_attributes
82
+ self.class.api_attribute_names.each do |name|
83
+ instance_variable_set("@#{name}", nil)
84
+ end
85
+ end
86
+
87
+ def self.determine_api_attribute_value(name, raw_value)
88
+ if @api_attributes[name] && @api_attributes[name].has_key?(:constructor)
89
+ klass = Util.constantize(@api_attributes[name][:constructor])
90
+ if(klass.respond_to?(:construct))
91
+ klass.construct(raw_value)
92
+ else
93
+ klass.new(raw_value)
94
+ end
95
+ else
96
+ APIObject.construct(raw_value)
97
+ end
98
+ end
99
+ def determine_api_attribute_value(name, raw_value)
100
+ self.class.determine_api_attribute_value(name, raw_value)
101
+ end
102
+
103
+
104
+ def self.api_subclasses
105
+ return @api_subclasses ||= Set.new
106
+ end
107
+
108
+ def self.api_subclass_fetch(name)
109
+ @api_subclasses_hash ||= {}
110
+ if @api_subclasses_hash.has_key?(name)
111
+ @api_subclasses_hash[name]
112
+ end
113
+ end
114
+
115
+ def self.register_api_subclass(subclass, name=nil)
116
+ @api_subclasses ||= Set.new
117
+ @api_subclasses << subclass
118
+
119
+ unless name.nil?
120
+ @api_subclasses_hash ||= {}
121
+ @api_subclasses_hash[name] = subclass
122
+ end
123
+ end
124
+
125
+ @api_attributes = {}
126
+ end
127
+ end
@@ -0,0 +1,47 @@
1
+ module SynapsePay
2
+ module HeadersBuilder
3
+
4
+ def self.build(headers)
5
+ headers ||= {}
6
+ default_headers.merge(headers)
7
+ end
8
+
9
+ def self.default_headers
10
+ headers = {
11
+ :user_agent => "SynapsePay/#{SynapsePay.api_version} RubyBindings/#{SynapsePay::VERSION}",
12
+ }.merge({
13
+ "Content-Type" => "application/json",
14
+ })
15
+
16
+ begin
17
+ headers.update(:x_synapse_pay_client_user_agent => JSON.generate(user_agent))
18
+ rescue => e
19
+ headers.update(:x_synapse_pay_client_raw_user_agent => user_agent.inspect,
20
+ :error => "#{e} (#{e.class})")
21
+ end
22
+ headers
23
+ end
24
+
25
+
26
+
27
+ def self.user_agent
28
+ lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
29
+
30
+ {
31
+ :bindings_version => SynapsePay::VERSION,
32
+ :lang => 'ruby',
33
+ :lang_version => lang_version,
34
+ :platform => RUBY_PLATFORM,
35
+ :publisher => 'synapse_pay',
36
+ :uname => get_uname
37
+ }
38
+ end
39
+
40
+ def self.get_uname
41
+ `uname -a 2>/dev/null`.strip if RUBY_PLATFORM =~ /linux|darwin/i
42
+ rescue Errno::ENOMEM => ex # couldn't create subprocess
43
+ "uname lookup failed"
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,30 @@
1
+ module SynapsePay
2
+ module ParamsBuilder
3
+
4
+ def self.clean(params)
5
+ Util.symbolize_keys(params || {})
6
+ end
7
+
8
+ # Clean the params, and the hash to_merge, and then merge them.
9
+ # This ensures that we dont get something like { "id" => 123, :id => 321 }.
10
+ def self.merge(*args)
11
+ ret = {}
12
+ args.each do |arg|
13
+ ret = ret.merge(clean(arg))
14
+ end
15
+ ret
16
+ end
17
+
18
+ def self.build(params, api_key=nil, auth_key=nil)
19
+ default_params.merge(clean(params))
20
+ end
21
+
22
+ def self.default_params
23
+ params = {
24
+ :client_id => SynapsePay.client_id,
25
+ :client_secret => SynapsePay.client_secret,
26
+ }
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,38 @@
1
+ module SynapsePay
2
+ module PathBuilder
3
+
4
+ # Take a path like:
5
+ # ":path/:id/dogs/:dog_id"
6
+ # and convert it to:
7
+ # "#{object.path}/#{object.id}/dogs/#{params[:id]}" => "/objects/1/dogs/2"
8
+ #
9
+ # Path priority is:
10
+ # 1. Object - this will be a class or an instance of a class.
11
+ # 2. Params - this is a hash of key values. All keys *must* be symbolized.
12
+ def self.build(path, object, params)
13
+ ret = path.dup
14
+ if ret.include?(":")
15
+ matches = ret.scan(/:([^\/]*)/).flatten.map(&:to_sym)
16
+ missing = Set.new(matches)
17
+
18
+ matches.each do |match|
19
+ value = determine_value(match, object, params)
20
+ missing.delete(match) unless value.nil?
21
+ ret.sub!(match.inspect, "#{value}")
22
+ end
23
+
24
+ if missing.any?
25
+ raise ArgumentError.new("Could not determine the full URL. The following values of the path are missing: #{missing.to_a.join(', ')}. Try setting them in your params.")
26
+ end
27
+ end
28
+ ret
29
+ end
30
+
31
+ def self.determine_value(match, object, params)
32
+ value = object.send(match) if object && object.respond_to?(match)
33
+ value ||= params[match] if params && params.has_key?(match)
34
+ value
35
+ end
36
+
37
+ end
38
+ end
@@ -0,0 +1,104 @@
1
+ module SynapsePay
2
+ module Requester
3
+
4
+ def self.request(method, url, params, headers)
5
+ method = method.to_sym
6
+ url, params = prepare_params(method, url, params, headers)
7
+ request_opts = {
8
+ :method => method,
9
+ :url => url,
10
+ :headers => headers,
11
+ :payload => params,
12
+
13
+ :verify_ssl => false,
14
+ :open_timeout => 30,
15
+ :timeout => 60
16
+ }
17
+
18
+ execute_request(request_opts)
19
+ end
20
+
21
+ def self.execute_request(opts)
22
+ RestClient::Request.execute(opts)
23
+ end
24
+
25
+ def self.get(url, params, headers)
26
+ self.request(:get, url, params, headers)
27
+ end
28
+
29
+ def self.delete(url, params, headers)
30
+ self.request(:delete, url, params, headers)
31
+ end
32
+
33
+ def self.put(url, params, headers)
34
+ self.request(:put, url, params, headers)
35
+ end
36
+
37
+ def self.post(url, params, headers)
38
+ self.request(:post, url, params, headers)
39
+ end
40
+
41
+ def self.prepare_params(method, url, params, headers)
42
+ if [:get, :head, :delete].include?(method)
43
+ unless params.empty?
44
+ url += URI.parse(url).query ? '&' : '?' + query_string(params)
45
+ end
46
+ params = nil
47
+ else
48
+ if headers["Content-Type"] == "application/json"
49
+ params = JSON.generate(params)
50
+ else
51
+ headers["Content-Type"] = "application/x-www-form-urlencoded"
52
+ if !RestClient::Payload.has_file?(params)
53
+ params = query_string(params)
54
+ end
55
+ end
56
+ end
57
+ [url, params]
58
+ end
59
+
60
+ def self.query_string(params)
61
+ params ||= {}
62
+ if params.any?
63
+ query_array(params).join('&')
64
+ else
65
+ ""
66
+ end
67
+ end
68
+
69
+ # Three major use cases (and nesting of them needs to be supported):
70
+ # { :a => { :b => "bvalue" } } => ["a[b]=bvalue"]
71
+ # { :a => [1, 2] } => ["a[]=1", "a[]=2"]
72
+ # { :a => "value" } => ["a=value"]
73
+ def self.query_array(params, key_prefix=nil)
74
+ ret = []
75
+ params.each do |key, value|
76
+ if params.is_a?(Array)
77
+ value = key
78
+ key = ''
79
+ end
80
+ key_suffix = escape(key)
81
+ full_key = key_prefix ? "#{key_prefix}[#{key_suffix}]" : key_suffix
82
+
83
+ if value.is_a?(Hash) || value.is_a?(Array)
84
+ # Handles the following cases:
85
+ # { :a => { :b => "bvalue" } } => ["a[b]=bvalue"]
86
+ # { :a => [1, 2] } => ["a[]=1", "a[]=2"]
87
+ ret += query_array(value, full_key)
88
+ elsif value.is_a?(APIObject)
89
+ ret += query_array(value.json, full_key)
90
+ else
91
+ # Handles the base case with just key and value:
92
+ # { :a => "value" } => ["a=value"]
93
+ ret << "#{full_key}=#{escape(value)}"
94
+ end
95
+ end
96
+ ret
97
+ end
98
+
99
+ def self.escape(val)
100
+ URI.escape(val.to_s, Regexp.new("[^#{URI::PATTERN::UNRESERVED}]"))
101
+ end
102
+
103
+ end
104
+ end
@@ -0,0 +1,51 @@
1
+ module SynapsePay
2
+ module Util
3
+
4
+ def self.symbolize_keys(obj)
5
+ if obj.is_a?(Hash)
6
+ ret = {}
7
+ obj.each do |key, value|
8
+ ret[(key.to_sym rescue key) || key] = symbolize_keys(value)
9
+ end
10
+ return ret
11
+ elsif obj.is_a?(Array)
12
+ return obj.map{ |value| symbolize_keys(value) }
13
+ else
14
+ return obj
15
+ end
16
+ end
17
+
18
+ def self.sorta_deep_clone(json)
19
+ if json.is_a?(Hash)
20
+ ret = {}
21
+ json.each do |k, v|
22
+ ret[k] = sorta_deep_clone(v)
23
+ end
24
+ ret
25
+ elsif json.is_a?(Array)
26
+ json.map{ |j| sorta_deep_clone(j) }
27
+ else
28
+ begin
29
+ json.dup
30
+ rescue
31
+ json
32
+ end
33
+ end
34
+ end
35
+
36
+ def self.constantize(str, prefix=false)
37
+ str = str.to_s
38
+ begin
39
+ str.split('::').reduce(Module, :const_get)
40
+ rescue NameError => e
41
+ if prefix
42
+ raise e
43
+ else
44
+ p = "#{self.name}".split("::").first
45
+ constantize("#{p}::#{str}", true)
46
+ end
47
+ end
48
+ end
49
+
50
+ end
51
+ end