judopay 1.0.2.pre → 2.0.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 (63) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -0
  3. data/CHANGELOG.md +3 -0
  4. data/README.md +2 -0
  5. data/Rakefile +17 -2
  6. data/judopay.gemspec +11 -10
  7. data/lib/faraday/raise_http_exception.rb +1 -18
  8. data/lib/judopay.rb +12 -9
  9. data/lib/judopay/connection.rb +28 -18
  10. data/lib/judopay/core_ext/string.rb +4 -3
  11. data/lib/judopay/error.rb +55 -97
  12. data/lib/judopay/model.rb +49 -40
  13. data/lib/judopay/models/card_payment.rb +5 -10
  14. data/lib/judopay/models/card_preauth.rb +1 -1
  15. data/lib/judopay/models/collection.rb +1 -1
  16. data/lib/judopay/models/refund.rb +1 -1
  17. data/lib/judopay/models/register_card.rb +28 -0
  18. data/lib/judopay/models/save_card.rb +26 -0
  19. data/lib/judopay/models/token_payment.rb +1 -0
  20. data/lib/judopay/models/void.rb +19 -0
  21. data/lib/judopay/models/web_payments/payment.rb +3 -0
  22. data/lib/judopay/models/web_payments/web_payment_operation.rb +8 -0
  23. data/lib/judopay/request.rb +1 -1
  24. data/lib/judopay/serializer.rb +1 -5
  25. data/lib/judopay/version.rb +2 -1
  26. data/spec/factories.rb +64 -19
  27. data/spec/faraday/response_spec.rb +10 -12
  28. data/spec/fixtures/card_payments/create_bad_request.json +11 -4
  29. data/spec/fixtures/transactions/register_card.json +29 -0
  30. data/spec/fixtures/transactions/save_card.json +28 -0
  31. data/spec/fixtures/transactions/void.json +27 -0
  32. data/spec/judopay/card_address_spec.rb +1 -1
  33. data/spec/judopay/card_payment_spec.rb +1 -2
  34. data/spec/judopay/collection_spec.rb +1 -1
  35. data/spec/judopay/error_spec.rb +36 -15
  36. data/spec/judopay/judopay_spec.rb +1 -1
  37. data/spec/judopay/market/collection_spec.rb +1 -1
  38. data/spec/judopay/market/payment_spec.rb +1 -1
  39. data/spec/judopay/market/preauth_spec.rb +1 -1
  40. data/spec/judopay/market/refund_spec.rb +1 -1
  41. data/spec/judopay/market/transaction_spec.rb +1 -1
  42. data/spec/judopay/payment_spec.rb +1 -1
  43. data/spec/judopay/preauth_spec.rb +1 -1
  44. data/spec/judopay/refund_spec.rb +1 -1
  45. data/spec/judopay/register_card_spec.rb +24 -0
  46. data/spec/judopay/save_card_spec.rb +23 -0
  47. data/spec/judopay/transaction_spec.rb +1 -1
  48. data/spec/judopay/void_spec.rb +24 -0
  49. data/spec/spec_helper.rb +6 -2
  50. data/test/authentication_test.rb +23 -0
  51. data/test/base/integration_base.rb +20 -0
  52. data/test/base/payments_tests.rb +40 -0
  53. data/test/base/token_payment_tests.rb +91 -0
  54. data/test/card_details_test.rb +21 -0
  55. data/test/configuration_test.rb +35 -0
  56. data/test/helper/assertion_helper.rb +29 -0
  57. data/test/payment_test.rb +10 -0
  58. data/test/preauth_test.rb +10 -0
  59. data/test/register_card_test.rb +37 -0
  60. data/test/token_payment_test.rb +10 -0
  61. data/test/token_preauth_test.rb +10 -0
  62. data/test/void_test.rb +44 -0
  63. metadata +107 -50
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 690b26307f78ab7d86d45dbf2bc0f1fec0d1a685
4
- data.tar.gz: 76f31267f21171e651f091097372dcd76f40c1c3
3
+ metadata.gz: adc77d2a9f1b25747948b90476f9b72610823d58
4
+ data.tar.gz: 3747bc655c3d24a90883970037ce3f746f9a115d
5
5
  SHA512:
6
- metadata.gz: 331a700e95c41ec494a61a49888bf731ceb033688fb4d3721219994ee1f70d8a23c468de15264e3a627fad3c703bc7667a1420bcbb6a4e6bf7a23c528b03599b
7
- data.tar.gz: 02ffa1528b30fb99e46b429a6c158e1287b0cb3653b83584abe7bec4d974e1867d7ecc0ca265948e15fc810343f59f380d40e1e31ff9f4c845afacd65b2a2b6b
6
+ metadata.gz: e75fa57d16570440e16117e37fb6a47fcb9b991c69970009f105efe0224ce225369eb5bcaaeff9f34b37777fe87ac742eba66c40653644385d5a4ab39abdea12
7
+ data.tar.gz: af7d3f3ce3d06e1cd7942c28fdcfa257f61a9bb759de1be709a15b9e985db30974f86b314d736e6a09c75d607e49bb971f25d4df0b82091dbc0876f82ffbc9d2
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ - 2.1.2
5
+ - 2.0.0
6
+ sudo: false
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ # Version 4.0.0
2
+
3
+ First public release. Numbered to match current version of JudoPay API.
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  The Judopay gem supports Ruby 1.9.3 and above (including 2.0.x and 2.1.x).
4
4
 
5
+ [![Build Status](https://travis-ci.org/JudoPay/RubySdk.svg?branch=master)](https://travis-ci.org/JudoPay/RubySdk)
6
+
5
7
  ## Installation
6
8
 
7
9
  Add this line to your application's Gemfile:
data/Rakefile CHANGED
@@ -1,7 +1,22 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rspec/core/rake_task'
3
+ require 'rake/testtask'
3
4
 
4
5
  RSpec::Core::RakeTask.new
5
6
 
6
- task :default => :spec
7
- task :test => :spec
7
+ Rake::TestTask.new do |t|
8
+ raise 'Please setup JUDO_* environment vars' unless ENV['JUDO_API_ID'] && ENV['JUDO_API_TOKEN'] && ENV['JUDO_API_SECRET']
9
+ t.libs << 'test'
10
+ t.test_files = FileList['test/*_test.rb']
11
+ t.verbose = true
12
+ end
13
+
14
+ task :code_check do
15
+ sh 'rubocop'
16
+ Rake::Task['spec'].invoke
17
+ Rake::Task['test'].invoke
18
+ end
19
+
20
+ # check code before build
21
+ task :build => :code_check
22
+ task :default => :build
data/judopay.gemspec CHANGED
@@ -5,7 +5,7 @@ require 'judopay/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = 'judopay'
8
- spec.version = Judopay::VERSION
8
+ spec.version = Judopay::SDK_VERSION
9
9
  spec.authors = ['judoPay']
10
10
  spec.email = ['developersupport@judopay.com']
11
11
  spec.summary = 'Ruby SDK wrapper for the Judopay REST API'
@@ -19,19 +19,20 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ['lib']
20
20
 
21
21
  spec.required_ruby_version = '>= 1.9.3'
22
- spec.add_development_dependency 'bundler'
23
- spec.add_development_dependency 'rake'
24
22
  spec.add_development_dependency 'rspec'
25
23
  spec.add_development_dependency 'yard'
24
+ spec.add_development_dependency 'test-unit'
26
25
  spec.add_development_dependency 'yard-xml'
27
26
  spec.add_development_dependency 'factory_girl'
28
27
  spec.add_development_dependency 'rubocop'
29
28
  spec.add_development_dependency 'webmock'
30
- spec.add_dependency 'virtus', '=1.0.2'
31
- spec.add_dependency 'httpclient', '=2.4.0'
32
- spec.add_dependency 'activemodel', '=4.1.1'
33
- spec.add_dependency 'faraday', '=0.9.0'
34
- spec.add_dependency 'faraday_middleware', '=0.9.1'
35
- spec.add_dependency 'rash', '=0.4.0'
36
- spec.add_dependency 'addressable', '=2.3.6'
29
+ spec.add_dependency 'bundler'
30
+ spec.add_dependency 'rake'
31
+ spec.add_dependency 'virtus', '~> 1.0.2'
32
+ spec.add_dependency 'httpclient', '~> 2.4.0'
33
+ spec.add_dependency 'activemodel', '~> 4.1.1'
34
+ spec.add_dependency 'faraday', '~> 0.9.0'
35
+ spec.add_dependency 'faraday_middleware', '~> 0.9.1'
36
+ spec.add_dependency 'rash', '~> 0.4.0'
37
+ spec.add_dependency 'addressable', '~> 2.3.6'
37
38
  end
@@ -7,24 +7,7 @@ module FaradayMiddleware
7
7
  class RaiseHttpException < Faraday::Middleware
8
8
  def call(env)
9
9
  @app.call(env).on_complete do |response|
10
- case response.status.to_i
11
- when 400
12
- fail Judopay::BadRequest, response
13
- when 401, 403
14
- fail Judopay::NotAuthorized, response
15
- when 404
16
- fail Judopay::NotFound, response
17
- when 409
18
- fail Judopay::Conflict, response
19
- when 500
20
- fail Judopay::InternalServerError, response
21
- when 502
22
- fail Judopay::BadGateway, response
23
- when 503
24
- fail Judopay::ServiceUnavailable, response
25
- when 504
26
- fail Judopay::GatewayTimeout, response
27
- end
10
+ raise Judopay::APIError.factory(response) unless response.status.to_i == 200
28
11
  end
29
12
  end
30
13
 
data/lib/judopay.rb CHANGED
@@ -25,15 +25,13 @@ module Judopay
25
25
  logger.add(log_level) { message }
26
26
  end
27
27
 
28
- protected
29
-
30
28
  # Based on the use_production flag, which endpoint should we use?
31
29
  def self.configure_endpoint_for_environment
32
- if self.configuration.use_production == true
33
- self.configuration.endpoint_url = self.configuration.api_endpoints[:production]
34
- else
35
- self.configuration.endpoint_url = self.configuration.api_endpoints[:sandbox]
36
- end
30
+ self.configuration.endpoint_url = if self.configuration.use_production
31
+ self.configuration.api_endpoints[:production]
32
+ else
33
+ self.configuration.api_endpoints[:sandbox]
34
+ end
37
35
  end
38
36
 
39
37
  class Configuration
@@ -52,10 +50,10 @@ module Judopay
52
50
 
53
51
  # Set sensible configuration defaults
54
52
  def initialize
55
- @api_version = '4.0.0'
53
+ @api_version = Judopay::API_VERSION
56
54
  @format = 'json'
57
55
  @use_production = false
58
- @user_agent = 'Judopay Ruby SDK gem v' + Judopay::VERSION
56
+ @user_agent = "Judopay Ruby (#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}) SDK gem v#{Judopay::SDK_VERSION}"
59
57
  @logger = Judopay::NullLogger.new
60
58
  @api_endpoints = {
61
59
  :sandbox => 'https://partnerapi.judopay-sandbox.com',
@@ -63,5 +61,10 @@ module Judopay
63
61
  }.freeze
64
62
  @endpoint_url = @api_endpoints[:sandbox]
65
63
  end
64
+
65
+ def validate
66
+ return true unless judo_id.to_s.empty? || api_token.to_s.empty? || api_secret.to_s.empty?
67
+ raise Judopay::ValidationError, 'SDK configuration variables missing'
68
+ end
66
69
  end
67
70
  end
@@ -10,31 +10,29 @@ module Judopay
10
10
  private
11
11
 
12
12
  def connection(raw = false)
13
- options = {
14
- :headers => request_headers,
15
- :url => Judopay.configuration.endpoint_url,
16
- :ssl => {
17
- :ca_file => File.dirname(File.dirname(__FILE__)) + '/certs/rapidssl_ca.crt',
18
- :cert_store => false,
19
- :verify => true
20
- }
21
- }
22
-
23
- connection = Faraday::Connection.new(options) do |faraday|
24
- faraday.adapter :httpclient
13
+ connection = Faraday::Connection.new(default_connection_options) do |faraday|
14
+ # When adapter set to "httpclient" SSL error emerges after bunch of requests:
15
+ # "Faraday::SSLError: SSL_connect SYSCALL returned=5 errno=0 state=SSLv2/v3 read server hello A"
16
+ faraday.adapter :net_http
25
17
  faraday.use Faraday::Request::UrlEncoded
26
18
  faraday.use Faraday::Response::Logger, Judopay.configuration.logger
27
- faraday.use FaradayMiddleware::JudoMashify unless raw
28
- unless raw
29
- case Judopay.configuration.format.to_s
30
- when 'json' then faraday.use Faraday::Response::ParseJson
31
- end
32
- end
19
+ define_format(faraday, raw)
33
20
  faraday.use FaradayMiddleware::RaiseHttpException
34
21
  end
35
22
 
23
+ define_auth(connection)
24
+ end
25
+
26
+ def define_format(faraday, raw)
27
+ return if raw
28
+ faraday.use FaradayMiddleware::JudoMashify
29
+ faraday.use Faraday::Response::ParseJson if Judopay.configuration.format.to_s == 'json'
30
+ end
31
+
32
+ def define_auth(connection)
36
33
  # Authentication with basic auth if there is no OAuth2 access token
37
34
  if Judopay.configuration.oauth_access_token.nil?
35
+ Judopay.configuration.validate
38
36
  connection.basic_auth(
39
37
  Judopay.configuration.api_token,
40
38
  Judopay.configuration.api_secret
@@ -45,6 +43,18 @@ module Judopay
45
43
  connection
46
44
  end
47
45
 
46
+ def default_connection_options
47
+ {
48
+ :headers => request_headers,
49
+ :url => Judopay.configuration.endpoint_url,
50
+ :ssl => {
51
+ :ca_file => File.dirname(File.dirname(__FILE__)) + '/certs/rapidssl_ca.crt',
52
+ :cert_store => false,
53
+ :verify => true
54
+ }
55
+ }
56
+ end
57
+
48
58
  def request_headers
49
59
  format = Judopay.configuration.format
50
60
 
@@ -4,7 +4,7 @@ class String
4
4
  # "foo_bar".camel_case #=> "fooBar"
5
5
  def camel_case
6
6
  return self if self !~ /_/ && self =~ /[A-Z]+.*/
7
- split('_').map { |e| e.capitalize }.join.uncapitalize
7
+ split('_').map(&:capitalize).join.uncapitalize
8
8
  end
9
9
 
10
10
  # Convert first letter to lower case
@@ -19,8 +19,9 @@ class String
19
19
  #
20
20
  # "BananaMan".underscore #=> "banana_man"
21
21
  def underscore
22
- to_s.strip
23
- .gsub(' ', '_')
22
+ to_s
23
+ .strip
24
+ .tr(' ', '_')
24
25
  .gsub(/::/, '/')
25
26
  .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
26
27
  .gsub(/([a-z\d])([A-Z])/, '\1_\2')
data/lib/judopay/error.rb CHANGED
@@ -1,133 +1,91 @@
1
1
  require 'json'
2
2
 
3
3
  module Judopay
4
- # Base error class
5
- class Error < StandardError
6
- attr_writer :message
7
-
8
- def initialize(message = nil)
4
+ # Single field error model
5
+ class FieldError
6
+ attr_accessor :message, :code, :field_name, :detail
7
+
8
+ def initialize(message, code, field_name, detail)
9
+ @detail = detail
10
+ @field_name = field_name
11
+ @code = code
9
12
  @message = message
10
13
  end
11
14
 
12
- def message
13
- @message || self.class.name
14
- end
15
-
16
15
  def to_s
17
- @message
16
+ "Field \"#{@field_name}\" (code #{@code}): #{@message}"
18
17
  end
19
18
  end
20
19
 
21
20
  # Custom error class for rescuing from all API errors
22
21
  class APIError < StandardError
23
- attr_accessor :response, :error_type, :model_errors, :message, :parsed_body
24
-
25
- def initialize(response = nil)
26
- @response = response
27
-
28
- # Log the error
29
- Judopay.log(Logger::ERROR, self.class.name + ' ' + @message.to_s)
22
+ CATEGORY_UNKNOWN = 0
23
+ CATEGORY_REQUEST = 1
24
+ CATEGORY_MODEL = 2
25
+ CATEGORY_CONFIG = 3
26
+ CATEGORY_PROCESSING = 4
27
+ CATEGORY_EXCEPTION = 5
28
+
29
+ attr_accessor :message, :error_code, :status_code, :category, :field_errors
30
+
31
+ class << self
32
+ def factory(response)
33
+ parsed_body = JSON.parse(response.body)
34
+
35
+ new(
36
+ parsed_body['message'],
37
+ parsed_body['code'],
38
+ response.status.to_i,
39
+ parsed_body['category'],
40
+ extract_field_errors(parsed_body['details'])
41
+ )
42
+ end
30
43
 
31
- # If we got a JSON response body, set variables
32
- return if parsed_body.nil?
44
+ protected
33
45
 
34
- @message = body_attribute('errorMessage')
35
- @error_type = body_attribute('errorType').to_i
36
- api_model_errors = body_attribute('modelErrors')
37
- return if api_model_errors.nil?
46
+ def extract_field_errors(field_errors)
47
+ result = []
38
48
 
39
- process_api_model_errors(api_model_errors)
40
- end
49
+ field_errors.to_a.each do |field_error|
50
+ result << FieldError.new(field_error['message'], field_error['code'], field_error['fieldName'], field_error['detail'])
51
+ end
41
52
 
42
- def http_status
43
- @response.status.to_i if @response
53
+ result
54
+ end
44
55
  end
45
56
 
46
- def http_body
47
- @response.body if @response
57
+ def initialize(message, error_code = 0, status_code = 0, category = CATEGORY_UNKNOWN, field_errors = [])
58
+ @message = message
59
+ @error_code = error_code
60
+ @status_code = status_code
61
+ @category = category
62
+ @field_errors = field_errors
48
63
  end
49
64
 
50
65
  def to_s
51
- return @message if model_errors.nil?
52
-
53
- summary = []
54
- model_errors.each do |_key, value|
55
- summary.push(value.join('; '))
56
- end
57
-
58
- @message + ' (' + summary.join('; ') + ')'
66
+ "JudoPay ApiException (status code #{@status_code}, error code #{@error_code}, category #{@category}) #{message}"
59
67
  end
60
68
 
61
69
  def message
62
- @message || self.class.name
63
- end
64
-
65
- def parsed_body
66
- @parsed_body ||= parse_body
70
+ (@message || self.class.name) + field_errors_message
67
71
  end
68
72
 
69
73
  protected
70
74
 
71
- def parse_body
72
- return unless @response.respond_to?('response_headers')
73
- return unless @response.response_headers.include?('Content-Type')
74
- return unless @response.response_headers['Content-Type'].include?('application/json')
75
-
76
- ::JSON.parse(@response.body)
77
- end
78
-
79
- def body_attribute(attribute)
80
- return nil if parsed_body.nil? || !parsed_body.include?(attribute)
81
- parsed_body[attribute]
82
- end
83
-
84
- # Turn API model errors into a more ActiveRecord-like format
85
- def process_api_model_errors(api_model_errors)
86
- @model_errors = {}
87
- api_model_errors.each do |api_model_error|
88
- next unless api_model_error.is_a?(Hash)
89
- field_name = api_model_error['fieldName'].underscore.to_sym
90
- @model_errors[field_name] = [] if @model_errors[field_name].nil?
91
- @model_errors[field_name].push(api_model_error['errorMessage'])
92
- end
75
+ def field_errors_message
76
+ return '' if @field_errors.empty?
77
+ "\nFields errors:\n#{@field_errors.join("\n")}"
93
78
  end
94
79
  end
95
80
 
96
- # Raised when API returns the HTTP status code 400
97
- class BadRequest < APIError; end
98
-
99
- # Raised when API returns the HTTP status code 401/403
100
- class NotAuthorized < APIError
101
- def message
102
- 'Authorization has been denied for this request'
103
- end
104
- end
105
-
106
- # Raised when API returns the HTTP status code 404
107
- class NotFound < APIError; end
108
-
109
- # Raised when API returns the HTTP status code 409
110
- class Conflict < APIError; end
111
-
112
- # Raised when API returns the HTTP status code 500
113
- class InternalServerError < APIError; end
114
-
115
- # Raised when API returns the HTTP status code 502
116
- class BadGateway < APIError; end
117
-
118
- # Raised when API returns the HTTP status code 503
119
- class ServiceUnavailable < APIError; end
120
-
121
- # Raised when API returns the HTTP status code 504
122
- class GatewayTimeout < APIError; end
123
-
124
81
  # A validation error that hasn't reached the API
125
82
  class ValidationError < StandardError
126
83
  attr_accessor :errors, :message
127
84
 
128
- def initialize(errors)
85
+ def initialize(message, errors = nil)
129
86
  @errors = errors
130
- @message = 'Missing required fields' + model_errors_summary
87
+ @message = message
88
+ @message += model_errors_summary unless @errors.nil?
131
89
  end
132
90
 
133
91
  def to_s
@@ -142,12 +100,12 @@ module Judopay
142
100
  protected
143
101
 
144
102
  def model_errors_summary
145
- summary = []
103
+ summary = ["\nField errors:"]
146
104
  model_errors.each do |key, value|
147
- summary.push(key.to_s + ' ' + value.join('; '))
105
+ summary.push("#{key}: #{value.join('; ')}")
148
106
  end
149
107
 
150
- ' (' + summary.join('; ') + ')'
108
+ summary.join("\n")
151
109
  end
152
110
  end
153
111
  end