signifyd 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,387 @@
1
+ # External Dependencies
2
+ require 'i18n'
3
+ require 'set'
4
+ require 'active_support'
5
+ require 'rest_client'
6
+ require 'uri'
7
+ require 'json'
8
+ require 'base64'
9
+ require 'openssl'
10
+
11
+ # Gem Version
12
+ require 'signifyd/version'
13
+
14
+ # API operations
15
+ require 'signifyd/util'
16
+ require 'signifyd/signifyd_object'
17
+ require 'signifyd/resource'
18
+ require 'signifyd/api/create'
19
+ require 'signifyd/api/list'
20
+ require 'signifyd/api/update'
21
+ require 'signifyd/case'
22
+
23
+ # Internal Dependencies
24
+ require 'signifyd/errors/signifyd_error'
25
+ require 'signifyd/errors/api_error'
26
+ require 'signifyd/errors/api_connection_error'
27
+ require 'signifyd/errors/authentication_error'
28
+ require 'signifyd/errors/invalid_request_error'
29
+ require 'signifyd/errors/not_implemented_error'
30
+
31
+ module Signifyd
32
+ # ssl_bundle_path
33
+ #
34
+ # Path to hold Signifyd.com's certificate
35
+ # @return: String[path to certificate file]
36
+ @@ssl_bundle_path = File.join(File.dirname(__FILE__), 'data/ca-certificates.crt')
37
+
38
+ # api_key
39
+ #
40
+ # Default point for where the application will hold the API_KEY
41
+ # for the current instance. If not set, must be passed in all calls
42
+ # as last parameter.
43
+ # @return String[unique identifier]
44
+ @@api_key = nil
45
+
46
+ # api_base
47
+ #
48
+ # Root url where the Signifyd API endpoints will live. This can be
49
+ # changed by setting Signifyd.test_mode = true and will default to
50
+ # staging env.
51
+ # @return: String[url to Signifyd's API]
52
+ @@api_base = 'https://api.signifyd.com'
53
+
54
+ # api_version
55
+ #
56
+ # Version right now will be the url structure, might change later.
57
+ # For now this is ok.
58
+ # @return: String[url path of our current api version]
59
+ @@api_version = '/v2'
60
+
61
+ # verify_ssl_certs
62
+ #
63
+ # When this is set to false, any request made will not be a verfied
64
+ # and supported request by Signifyd. This should be set to true and
65
+ # the library will use the Signifyd keys..not for now :/
66
+ # @return: Boolean
67
+ @@verify_ssl_certs = true
68
+
69
+ # test_mode
70
+ #
71
+ # When set to true, will default to Signifyd's staging environment.
72
+ # This as well should always be set to false.
73
+ # @return: Boolean
74
+ @@test_mode = false
75
+
76
+ # local_mode
77
+ #
78
+ # When set to true, will default to a local environment of localhost:9000.
79
+ # This as well should always be set to false.
80
+ # @return: Boolean
81
+ @@local_mode = false
82
+
83
+ # ssl_bundle_path
84
+ #
85
+ # Returns the path to the certificate store location
86
+ def self.ssl_bundle_path
87
+ @@ssl_bundle_path
88
+ end
89
+
90
+ # api_url
91
+ #
92
+ # This method is used to set the full url that the request will be made
93
+ # to. Ideally, pass in the version of the API and then the method that
94
+ # will be requested.
95
+ # An example retrun would be: 'https://signifyd.com/v2/cases
96
+ # @return: String[url for request to be made]
97
+ def self.api_url(url='')
98
+ @@api_base + url
99
+ end
100
+
101
+ # api_key=
102
+ #
103
+ # Setter method to set the API key. Set into class variable and used
104
+ # globally on all calls made.
105
+ # @return: String[api key]
106
+ def self.api_key=(api_key)
107
+ @@api_key = api_key
108
+ end
109
+
110
+ # api_key
111
+ #
112
+ # Getter method for the API key that has been set by the application.
113
+ # @return: String[api key]
114
+ def self.api_key
115
+ @@api_key
116
+ end
117
+
118
+ # api_version=
119
+ #
120
+ # Setter method to set the API version. Set into class variable and used
121
+ # globally on all calls made.
122
+ # @return: String[api url version]
123
+ def self.api_version=(api_version)
124
+ @@api_version = api_version
125
+ end
126
+
127
+ # api_version
128
+ #
129
+ # Getter method for the API version that has been set by the application.
130
+ # @return: String[api url version]
131
+ def self.api_version
132
+ @@api_version
133
+ end
134
+
135
+ # api_base=
136
+ #
137
+ # Setter method to set the API url base. Set into class variable and used
138
+ # globally on all calls made.
139
+ # @return: String[api base]
140
+ def self.api_base=(api_base)
141
+ @@api_base = api_base
142
+ end
143
+
144
+ # api_base
145
+ #
146
+ # Getter method for the API base that has been set by the application.
147
+ # @return: String[api base]
148
+ def self.api_base
149
+ @@api_base
150
+ end
151
+
152
+ # verify_ssl_certs=
153
+ #
154
+ # Setter method to set the API verify_ssl_certs. Set into class variable and
155
+ # used globally on all calls made.
156
+ # @return: Boolean
157
+ def self.verify_ssl_certs=(verify)
158
+ @@verify_ssl_certs = verify
159
+ end
160
+
161
+ # verify_ssl_certs
162
+ #
163
+ # Getter method for the API verify_ssl_certs that has been set by the application.
164
+ # @return: Boolean
165
+ def self.verify_ssl_certs
166
+ @@verify_ssl_certs
167
+ end
168
+
169
+ # test_mode=
170
+ #
171
+ # Setter method to set the API test_mode. Set into class variable and used
172
+ # globally on all calls made.
173
+ # @return: Boolean
174
+ def self.test_mode=(test_mode)
175
+ Signifyd.api_base = 'https://staging.signifyd.com' if test_mode && !self.local_mode
176
+ @@test_mode = test_mode
177
+ end
178
+
179
+ # test_mode
180
+ #
181
+ # Getter method for the API test_mode that has been set by the application.
182
+ # @return: Boolean
183
+ def self.test_mode
184
+ @@test_mode
185
+ end
186
+
187
+ # local_mode=
188
+ #
189
+ # Setter method to set the API local_mode. Set into class variable and used
190
+ # globally on all calls made.
191
+ # @return: Boolean
192
+ def self.local_mode=(local_mode)
193
+ Signifyd.api_base = 'http://localhost:9000' if local_mode && !self.test_mode
194
+ @@local_mode = local_mode
195
+ end
196
+
197
+ # local_mode
198
+ #
199
+ # Getter method for the API test_mode that has been set by the application.
200
+ # @return: Boolean
201
+ def self.local_mode
202
+ @@local_mode
203
+ end
204
+
205
+ # request
206
+ #
207
+ # Global method that will use RestClient to make all requests. Everything else
208
+ # is set between Signifyd.{setter} methods. This method is called from other
209
+ # methods so direct calls won't be necessary.
210
+ #
211
+ # @param[Req]: String[method] - :get, :post, :put, :delete
212
+ # @param[Req]: String[url] - '/cases'
213
+ # @param[Req]: Hash[params] - {transaction...}
214
+ # @param[Opt]: String[api_key] - 'YOUR-API-KEY'
215
+ # @param[Opt]: Hash[options] - optional parameters
216
+ # @return: Hash - containing response code, body, and other data
217
+ def self.request(method, url, params={}, api_key=nil, options={})
218
+ api_key = api_key.nil? ? @@api_key : api_key
219
+ raise AuthenticationError.new('No API key provided. Fix: Signifyd.api_key = \'Your API KEY\'') unless api_key
220
+
221
+ uname = (@@uname ||= RUBY_PLATFORM =~ /linux|darwin/i ? `uname -a 2>/dev/null`.strip : nil)
222
+ lang_version = "#{RUBY_VERSION} p#{RUBY_PATCHLEVEL} (#{RUBY_RELEASE_DATE})"
223
+ ua = {
224
+ :bindings_version => Signifyd::VERSION,
225
+ :lang => 'ruby',
226
+ :lang_version => lang_version,
227
+ :platform => RUBY_PLATFORM,
228
+ :publisher => 'signifyd',
229
+ :uname => uname
230
+ }
231
+
232
+ if @@verify_ssl_certs
233
+ ssl_opts = {
234
+ :verify_ssl => OpenSSL::SSL::VERIFY_PEER,
235
+ :ssl_ca_file => @@ssl_bundle_path
236
+ }
237
+ else
238
+ ssl_opts = {
239
+ :verify_ssl => OpenSSL::SSL::VERIFY_NONE
240
+ }
241
+ end
242
+
243
+ # Determine how to send the data and encode it based on what method we are sending. Some
244
+ # are necessary, some are not.
245
+ case method.to_s
246
+ when 'get'
247
+ if options.has_key?(:order_id)
248
+ url = url.gsub('cases', "orders/#{options[:order_id]}/case")
249
+ end
250
+ when 'post'
251
+
252
+ when 'put'
253
+ # we need to eject out the case_id from the params hash and append it to the url
254
+ if params.has_key?(:case_id) || params.has_key?('case_id')
255
+ case_id = params.delete(:case_id)
256
+ params.reject! { |k| k == :case_id || k == 'case_id' }
257
+ url << "/#{case_id}"
258
+ end
259
+ when 'delete'
260
+
261
+ end
262
+
263
+ # Create the full url here
264
+ url = self.api_url(url)
265
+
266
+ # Parse into valid json
267
+ payload = JSON.dump(params)
268
+
269
+ # Convert the key
270
+ authkey = api_key == {} || api_key == nil ? '' : Base64.encode64(api_key)
271
+
272
+ # Headers must contain these keys
273
+ headers = {
274
+ "Content-Length" => payload.size,
275
+ "Content-Type" => "application/json",
276
+ "Authorization" => "Basic #{authkey}",
277
+ 'User-Agent' => "Signifyd Ruby #{@@api_version.gsub('/', '')}"
278
+ }
279
+
280
+ # All necessary options must be set
281
+ opts = {
282
+ :method => method,
283
+ :url => url,
284
+ :headers => headers,
285
+ :open_timeout => 30,
286
+ :payload => payload,
287
+ :timeout => 80
288
+ }.merge(ssl_opts)
289
+
290
+ # Make the request
291
+ begin
292
+ response = execute_request(opts)
293
+ rescue SocketError => e
294
+ self.handle_restclient_error(e)
295
+ rescue NoMethodError => e
296
+ if e.message =~ /\WRequestFailed\W/
297
+ e = APIConnectionError.new('Unexpected HTTP response code')
298
+ self.handle_restclient_error(e)
299
+ else
300
+ raise
301
+ end
302
+ rescue RestClient::ExceptionWithResponse => e
303
+ if rcode = e.http_code and rbody = e.http_body
304
+ self.handle_api_error(rcode, rbody)
305
+ else
306
+ self.handle_restclient_error(e)
307
+ end
308
+ rescue RestClient::Exception, Errno::ECONNREFUSED => e
309
+ self.handle_restclient_error(e)
310
+ end
311
+
312
+ rbody = response.body
313
+ rcode = response.code
314
+ return {code: rcode, body: JSON.parse(rbody)}
315
+ end
316
+
317
+ # execute_request
318
+ #
319
+ # Handles the request, pass in opts hash, RestClient makes the call.
320
+ # @param: Hash[opts] - Configured options from Signifyd.request method.
321
+ # @return: RestClient::Request - the result of the request.
322
+ def self.execute_request(opts)
323
+ RestClient::Request.execute(opts)
324
+ end
325
+
326
+ # handle_restclient_error
327
+ #
328
+ # @param: RestClient[error] - could be many different types of errors.
329
+ # @return: String[error message]
330
+ def self.handle_restclient_error(e)
331
+ case e
332
+ when RestClient::ServerBrokeConnection, RestClient::RequestTimeout
333
+ message = "Could not connect to Signifyd (#{@@api_base}). Please check your internet connection and try again."
334
+ when RestClient::SSLCertificateNotVerified
335
+ message = "Could not verify Signifyd's SSL certificate. Please make sure that your network is not intercepting certificates. (Try going to https://api.signifyd.com/v2 in your browser.) If this problem persists, let us know at support@signifyd.com."
336
+ when SocketError
337
+ message = "Unexpected error communicating when trying to connect to Signifyd. HINT: You may be seeing this message because your DNS is not working. To check, try running 'host signifyd.com' from the command line."
338
+ else
339
+ message = "Unexpected error communicating with Signifyd. If this problem persists, let us know at support@signifyd.com."
340
+ end
341
+ message += "\n\n(Network error: #{e.message})"
342
+ raise APIConnectionError.new(message)
343
+ end
344
+
345
+ # handle_api_error
346
+ #
347
+ # @param: String
348
+ # @param: String
349
+ def self.handle_api_error(rcode, rbody)
350
+ error = {}
351
+ case rcode
352
+ when 400, 404
353
+ error[:message] = "Invalid request"
354
+ error[:param] = ""
355
+ raise invalid_request_error error, rcode, rbody
356
+ when 401
357
+ error[:message] = "Authentication error"
358
+ error[:param] = ""
359
+ raise authentication_error error, rcode, rbody
360
+ else
361
+ error[:message] = "API error"
362
+ error[:param] = ""
363
+ raise general_api_error rcode, rbody
364
+ end
365
+ end
366
+
367
+ def self.invalid_request_error(error, rcode, rbody)
368
+ raise InvalidRequestError.new(error[:message], error[:param], rcode, rbody)
369
+ end
370
+
371
+ def self.authentication_error(error, rcode, rbody)
372
+ raise AuthenticationError.new(error[:message], error[:param], rcode, rbody)
373
+ end
374
+
375
+ def self.general_api_error(rcode, rbody)
376
+ raise APIError.new("Invalid response object from API: #{rbody.inspect} (HTTP response code was #{rcode})", rcode, rbody)
377
+ end
378
+
379
+ private
380
+ # configured?
381
+ #
382
+ # Check to see if the API key has been set
383
+ # @return: Boolean
384
+ def self.configured?
385
+ !!@@api_key
386
+ end
387
+ end
@@ -0,0 +1,16 @@
1
+ module Signifyd
2
+ module API
3
+ module Create
4
+ module ClassMethods
5
+ def create(params={}, api_key=nil)
6
+ raise InvalidRequestError.new('You have passed invalid parameters to Case.create') if params == {}
7
+ Signifyd.request(:post, self.url, params, 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,20 @@
1
+ module Signifyd
2
+ module API
3
+ module List
4
+ module ClassMethods
5
+ def all(filters={}, api_key=nil)
6
+ Signifyd.request(:get, url, {}, api_key, filters)
7
+ end
8
+ # To retrieve case by orderId pass in {order_id: value}
9
+ # into options
10
+ def find(options={}, filters={},api_key=nil)
11
+ Signifyd.request(:get, url, filters, api_key, options)
12
+ end
13
+ end
14
+
15
+ def self.included(base)
16
+ base.extend(ClassMethods)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ module Signifyd
2
+ module API
3
+ module Update
4
+ module ClassMethods
5
+ def update(case_id, params={}, api_key=nil)
6
+ raise InvalidRequestError.new('You have passed invalid parameters to Case.create') if params == {} || case_id.nil?
7
+ Signifyd.request(:put, self.url, params.merge(case_id: case_id), 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,7 @@
1
+ module Signifyd
2
+ class Case < Resource
3
+ include Signifyd::API::Create
4
+ include Signifyd::API::List
5
+ include Signifyd::API::Update
6
+ end
7
+ end