issuer_response_codes 0.1.0 → 0.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f7dbb167d16f432e8a8113006dc1d2591b05c96ed6141fc0c1532c9b1e241ebf
4
- data.tar.gz: 3b5101d1027157edad887ee134551adffbd8a79210f238230d98091fd2b817df
3
+ metadata.gz: 02f7ca39c355badd05b7083f83789aa3d9271b0195d5955d2247548d8dd67d27
4
+ data.tar.gz: fad7b1f3758eb3211fd04a62155883d64939dc04382f4288ba45d2bc3aad8b59
5
5
  SHA512:
6
- metadata.gz: 1b4d8a1836429d54ee6ed64f408f3fa6c108047f8266e1f91253e362e7234805ff27f66ccd0ea953cdfe0124f6ec30b903ec74eec4a280e843aef4b0273578b8
7
- data.tar.gz: 1fb3f071324d4cdd7d1d63272ac82c86258f4509ba4cf2bf45cdb3245f85bd44ef7ae3ab70036213487dba5f4cbb1da0385ccb1ff9469e7eef185beb73baf396
6
+ metadata.gz: c3896b85d20055b822c6d07bb30ea0cff9f091e559166135339c3e6e2b8ebb5a29574b22446c2635cfc13790175510435ebc9f9d24975daf17a548cca3905140
7
+ data.tar.gz: 4806b6ace9f4c153aa9e0f56d25c7d940fc8033eab73a569511244d74b207b683f2dc9da056ba980abaeda98255eafcf4f5acdb3cbdd7ec681b4b5f0c966ba15
data/.gitignore CHANGED
@@ -6,3 +6,5 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+ .byebug_history
10
+ my_examples
data/.rubocop.yml CHANGED
@@ -1,6 +1,9 @@
1
1
  Style/StringLiterals:
2
2
  Enabled: false
3
3
 
4
+ Style/SingleArgumentDig:
5
+ Enabled: false
6
+
4
7
  Layout/LineLength:
5
8
  Enabled: false
6
9
 
data/Gemfile CHANGED
@@ -4,6 +4,3 @@ source "https://rubygems.org"
4
4
 
5
5
  # Specify your gem's dependencies in issuer_response_codes.gemspec
6
6
  gemspec
7
-
8
- gem "minitest", "~> 5.0"
9
- gem "rake", "~> 12.0"
data/Gemfile.lock CHANGED
@@ -1,27 +1,28 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- issuer_response_codes (0.1.0)
4
+ issuer_response_codes (0.2.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
9
  ast (2.4.2)
10
- backport (1.1.2)
10
+ backport (1.2.0)
11
11
  benchmark (0.1.1)
12
12
  bundler-audit (0.8.0)
13
13
  bundler (>= 1.2.0, < 3)
14
14
  thor (~> 1.0)
15
15
  byebug (11.1.3)
16
+ diff-lcs (1.4.4)
16
17
  e2mmap (0.1.0)
17
18
  jaro_winkler (1.5.4)
18
19
  kramdown (2.3.1)
19
20
  rexml
20
21
  kramdown-parser-gfm (1.1.0)
21
22
  kramdown (~> 2.0)
22
- mini_portile2 (2.5.1)
23
+ mini_portile2 (2.5.3)
23
24
  minitest (5.14.4)
24
- nokogiri (1.11.3)
25
+ nokogiri (1.11.7)
25
26
  mini_portile2 (~> 2.5.0)
26
27
  racc (~> 1.4)
27
28
  parallel (1.20.1)
@@ -34,22 +35,23 @@ GEM
34
35
  reverse_markdown (2.0.0)
35
36
  nokogiri
36
37
  rexml (3.2.5)
37
- rubocop (1.14.0)
38
+ rubocop (1.18.2)
38
39
  parallel (~> 1.10)
39
40
  parser (>= 3.0.0.0)
40
41
  rainbow (>= 2.2.2, < 4.0)
41
42
  regexp_parser (>= 1.8, < 3.0)
42
43
  rexml
43
- rubocop-ast (>= 1.5.0, < 2.0)
44
+ rubocop-ast (>= 1.7.0, < 2.0)
44
45
  ruby-progressbar (~> 1.7)
45
46
  unicode-display_width (>= 1.4.0, < 3.0)
46
- rubocop-ast (1.5.0)
47
+ rubocop-ast (1.7.0)
47
48
  parser (>= 3.0.1.1)
48
49
  ruby-progressbar (1.11.0)
49
- solargraph (0.40.4)
50
- backport (~> 1.1)
50
+ solargraph (0.42.3)
51
+ backport (~> 1.2)
51
52
  benchmark
52
53
  bundler (>= 1.17.2)
54
+ diff-lcs (~> 1.4)
53
55
  e2mmap
54
56
  jaro_winkler (~> 1.5)
55
57
  kramdown (~> 2.3)
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # IssuerResponseCodes
2
2
 
3
- This gem provides a quite comprehensive library of Issuer Response Code descriptions (for both cardholders and merchants) with suggested actions in 7 languages:
3
+ This gem provides a quite comprehensive library of Issuer Response Code and 3D-Secure Status Code descriptions (for both cardholders and merchants) with suggested actions in 7 languages:
4
4
  - Complete locales:
5
- - en
6
- - pl
5
+ - en
6
+ - pl
7
7
 
8
8
  - Incomplete locales:
9
9
  - da
@@ -59,11 +59,22 @@ def response_code_description
59
59
  ::ISSUER_RESPONSE_CODES.code(id: '43', locale: :pl).description
60
60
  #=> "Karta oznaczona jako skradziona. Skontaktuj się z Twoim bankiem w celu wyjaśnienia przyczyny problemu."
61
61
  end
62
+
63
+ def tds_code_description
64
+ ::ISSUER_RESPONSE_CODES.tds_code(id: '09').description
65
+ #=> "Security failure"
66
+
67
+ ::ISSUER_RESPONSE_CODES.tds_code(id: '09', target: :cardholder).description
68
+ #=> "Card authentication failed"
69
+
70
+ ::ISSUER_RESPONSE_CODES.tds_code(id: '09', locale: :pl).description
71
+ #=> "Niepowodzenie autoryzacji karty"
72
+ end
62
73
  ```
63
74
 
64
75
  ## Usage
65
76
 
66
- This gem provides an easy way of handling Issuer Response Codes. Whether you need detailed descriptions, short reasons or suggestions of what to do when a certain Code appeared, we've got you covered. Certain more explicit codes like `Stolen card' are masked behind more generic terms when you choose to target cardholders.
77
+ This gem provides an easy way of handling Issuer Response Codes and 3D-Secure Status Codes. Whether you need detailed descriptions, short reasons or suggestions of what to do when a certain Code appeared, we've got you covered. Certain more explicit codes like `Stolen card' are masked behind more generic terms when you choose to target cardholders.
67
78
 
68
79
  ### IssuerResponseCodes::Code
69
80
  #### Creation options
@@ -135,7 +146,7 @@ code.reason #=> "Karta utraciła ważność."
135
146
 
136
147
  #### Methods
137
148
 
138
- ##### Reason
149
+ ##### reason
139
150
 
140
151
  The `reason` method returns a relatively short description of the main reason why the Issuer Response Code appeared in the first place.
141
152
 
@@ -150,7 +161,7 @@ code = ::IssuerResponseCodes::Code.new(id: '59')
150
161
  code.reason #=> "Your bank has declined this transaction"
151
162
  ```
152
163
 
153
- ##### Behaviour
164
+ ##### behaviour
154
165
 
155
166
  The `behaviour` method returns a suggestion of what to do when the Issuer Response Code appeared. Mainly for cardholders.
156
167
 
@@ -165,9 +176,9 @@ code = ::IssuerResponseCodes::Code.new(id: '59')
165
176
  code.behaviour #=> "Please contact your card issuer to get more details and try again later."
166
177
  ```
167
178
 
168
- ##### Description
179
+ ##### description/humanize
169
180
 
170
- The `description` method is a combination of both the `reason` and `behaviour` of a Issuer Response Code
181
+ The `description` method (aliased as `humanize`) is a combination of both the `reason` and `behaviour` of a Issuer Response Code
171
182
 
172
183
  ```ruby
173
184
  code = ::IssuerResponseCodes::Code.new(id: '14')
@@ -180,6 +191,148 @@ code = ::IssuerResponseCodes::Code.new(id: '59')
180
191
  code.description #=> "Your bank has declined this transaction. Please contact your card issuer to get more details and try again later."
181
192
  ```
182
193
 
194
+ ##### fraudulent_code?
195
+
196
+ The `fraudulent_code?` method returns `true` when the Code indicates fraud.
197
+
198
+ ```ruby
199
+ code = ::IssuerResponseCodes::Code.new(id: '14')
200
+ code.fraudulent_code? #=> false
201
+
202
+ code = ::IssuerResponseCodes::Code.new(id: '04')
203
+ code.fraudulent_code? #=> true
204
+
205
+ code = ::IssuerResponseCodes::Code.new(id: '43')
206
+ code.fraudulent_code? #=> true
207
+ ```
208
+
209
+ ### IssuerResponseCodes::TdsCode
210
+ #### Creation options
211
+
212
+ ##### Targets
213
+
214
+ You can choose the main target of these descriptions (certain details are hidden for cardholders)
215
+
216
+ ```ruby
217
+ # Default values are as follows:
218
+ # target: :merchant, locale: :en
219
+
220
+ # fraud_notice is set to true by default when target = :merchant
221
+ code = ::IssuerResponseCodes::TdsCode.new(id: '11')
222
+ code.reason #=> "Suspected fraud."
223
+
224
+ code = ::IssuerResponseCodes::TdsCode.new(id: '11', target: :merchant)
225
+ code.reason #=> "Suspected fraud."
226
+
227
+ # fraud_notice is set to false by default when target = :cardholder
228
+ code = ::IssuerResponseCodes::TdsCode.new(id: '11', target: :cardholder)
229
+ code.reason #=> "Card authentication failed."
230
+ ```
231
+
232
+ ##### Fraud notice
233
+
234
+ Certain 3D-Secure status codes may signify that the transaction may be viewed as a fraud attempt. As such, this gem provides appropriate warnings. You can manually decide whether these warnings/notices should be added to descriptions or behaviour suggestions generated by this gem.
235
+
236
+ By default descriptions targeted at merchants have these warnings, while those targeted at cardholders omit them.
237
+
238
+ ```ruby
239
+ code = ::IssuerResponseCodes::TdsCode.new(id: '09', target: :merchant)
240
+ code.behaviour #=> "Please use a different card or contact issuer. Transactions with this code may be considered fraudulent."
241
+
242
+ # fraud_notice is set to false by default when target = :cardholder
243
+ code = ::IssuerResponseCodes::TdsCode.new(id: '09', target: :cardholder)
244
+ code.behaviour #=> "Please use a different card or contact issuer."
245
+ ```
246
+
247
+ This however can be overridden by explicitly passing the `fraud_notice` keyword parameter
248
+
249
+ ```ruby
250
+ # default options can be overridden
251
+ code = ::IssuerResponseCodes::TdsCode.new(id: '09', target: :merchant, fraud_notice: false)
252
+ code.behaviour #=> "Please use a different card or contact issuer."
253
+
254
+ # fraud_notice is set to false by default when target = :cardholder
255
+ code = ::IssuerResponseCodes::TdsCode.new(id: '09', target: :cardholder, fraud_notice: true)
256
+ code.behaviour #=> "Please use a different card or contact issuer. Transactions with this code may be considered fraudulent."
257
+ ```
258
+
259
+ ##### Locale
260
+
261
+ The default locale is `:en`. There are 7 in total: `%i[en pl da ee lt lv sv]`. Only the first two are complete, the rest are partially in English.
262
+
263
+ ```ruby
264
+ code = ::IssuerResponseCodes::TdsCode.new(id: '11', target: :cardholder)
265
+ code.reason #=> "Card authentication failed."
266
+
267
+ code = ::IssuerResponseCodes::TdsCode.new(id: '11', locale: :en, target: :cardholder)
268
+ code.reason #=> "Card authentication failed."
269
+
270
+ code = ::IssuerResponseCodes::TdsCode.new(id: '11', locale: :pl, target: :cardholder)
271
+ code.reason #=> "Negatywny wynik silnego uwierzytelnienia w systemie banku."
272
+ ```
273
+
274
+ #### Methods
275
+
276
+ ##### reason
277
+
278
+ The `reason` method returns a relatively short description of the main reason why the 3D-Secure status code appeared in the first place.
279
+
280
+ ```ruby
281
+ code = ::IssuerResponseCodes::TdsCode.new(id: '01')
282
+ code.reason #=> "Card authentication failed."
283
+
284
+ code = ::IssuerResponseCodes::TdsCode.new(id: '07')
285
+ code.reason #=> "Invalid transaction."
286
+
287
+ code = ::IssuerResponseCodes::TdsCode.new(id: '20')
288
+ code.reason #=> "Non-Payment transaction not supported."
289
+ ```
290
+
291
+ ##### behaviour
292
+
293
+ The `behaviour` method returns a suggestion of what to do when the 3D-Secure status code appeared. Mainly for cardholders.
294
+
295
+ ```ruby
296
+ code = ::IssuerResponseCodes::TdsCode.new(id: '01')
297
+ code.behaviour #=> "Check entered data and try again."
298
+
299
+ code = ::IssuerResponseCodes::TdsCode.new(id: '07')
300
+ code.behaviour #=> "Please check funds on your account and try again later."
301
+
302
+ code = ::IssuerResponseCodes::TdsCode.new(id: '20')
303
+ code.behaviour #=> "Please contact your card issuer to get more details and try again later."
304
+ ```
305
+
306
+ ##### description/humanize
307
+
308
+ The `description` method (aliased as `humanize`) is a combination of both the `reason` and `behaviour` of a Issuer Response Code
309
+
310
+ ```ruby
311
+ code = ::IssuerResponseCodes::TdsCode.new(id: '01')
312
+ code.description #=> "Invalid card number. Check entered data and try again."
313
+
314
+ code = ::IssuerResponseCodes::TdsCode.new(id: '07')
315
+ code.description #=> "Insufficient funds. Please check funds on your account and try again later."
316
+
317
+ code = ::IssuerResponseCodes::TdsCode.new(id: '20')
318
+ code.description #=> "Your bank has declined this transaction. Please contact your card issuer to get more details and try again later."
319
+ ```
320
+
321
+ ##### fraudulent_code?
322
+
323
+ The `fraudulent_code?` method returns `true` when the TdsCode indicates fraud.
324
+
325
+ ```ruby
326
+ code = ::IssuerResponseCodes::TdsCode.new(id: '11')
327
+ code.fraudulent_code? #=> true
328
+
329
+ code = ::IssuerResponseCodes::TdsCode.new(id: '22')
330
+ code.fraudulent_code? #=> false
331
+
332
+ code = ::IssuerResponseCodes::TdsCode.new(id: '26')
333
+ code.fraudulent_code? #=> false
334
+ ```
335
+
183
336
  ### Custom default configuration
184
337
 
185
338
  You can make use of the `Context` class to easily create your own default configurations. Contexts work as proxies which create codes for you.
@@ -194,12 +347,15 @@ ISSUER_RESPONSE_CODES = ::IssuerResponseCodes::Context.new(
194
347
  )
195
348
 
196
349
  # now just use this object as a proxy
197
- code = ISSUER_RESPONSE_CODE.code(id: '43')
350
+ code = ISSUER_RESPONSE_CODES.code(id: '43')
198
351
  code.reason #=> "Bank odrzucił autoryzację."
199
352
  code.behaviour #=> "Skontaktuj się z Twoim bankiem w celu wyjaśnienia przyczyny problemu. UWAGA: Nie należy powtarzać obciążeń dla tej karty! Może to zostać uznane za próbę oszustwa!"
200
353
 
354
+ tds_code = ISSUER_RESPONSE_CODES.tds_code(id: '11')
355
+ tds_code.reason #=> "Niepowodzenie autoryzacji karty"
356
+
201
357
  # these can always be overridden
202
- code = ISSUER_RESPONSE_CODE.code(id: '43', locale: :en, target: :merchant, fraud_notice: false)
358
+ code = ISSUER_RESPONSE_CODES.code(id: '43', locale: :en, target: :merchant, fraud_notice: false)
203
359
  code.reason #=> "Stolen card."
204
360
  code.behaviour #=> "Please contact your card issuer to get more details and try again later."
205
361
  ```
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
12
12
  spec.description = "A simple Ruby gem which provides Issuer Response Code descriptions and suggestions for cardholders and merchants"
13
13
  spec.homepage = "https://github.com/espago/issuer_response_codes"
14
14
  spec.license = "MIT"
15
- spec.required_ruby_version = Gem::Requirement.new(">= 2.7")
15
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.5")
16
16
 
17
17
  spec.metadata["homepage_uri"] = spec.homepage
18
18
  spec.metadata["source_code_uri"] = "https://github.com/espago/issuer_response_codes"
@@ -29,6 +29,8 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency 'bundler'
30
30
  spec.add_development_dependency 'bundler-audit'
31
31
  spec.add_development_dependency 'byebug'
32
+ spec.add_development_dependency 'minitest', '~> 5.0'
33
+ spec.add_development_dependency 'rake', '~> 12.0'
32
34
  spec.add_development_dependency 'rubocop'
33
35
  spec.add_development_dependency 'solargraph'
34
36
  end
@@ -4,13 +4,14 @@ require "issuer_response_codes/version"
4
4
  require "issuer_response_codes/locale_library"
5
5
  require "issuer_response_codes/context"
6
6
  require "issuer_response_codes/code"
7
+ require "issuer_response_codes/tds_code"
7
8
 
8
9
  module IssuerResponseCodes
9
10
  class IllegalTarget < StandardError; end
10
11
  class IllegalLocale < StandardError; end
11
12
 
12
13
  AVAILABLE_TARGETS = %i[merchant cardholder].freeze
13
- AVAILABLE_LOCALES = %i[en pl da ee lt lv sv].freeze
14
+ AVAILABLE_LOCALES = %i[en pl da de ee it lt lv sv].freeze
14
15
 
15
16
  LOCALE_LIBRARY = LocaleLibrary.new
16
17
  end
@@ -33,8 +33,14 @@ module IssuerResponseCodes
33
33
  end
34
34
 
35
35
  def behaviour
36
- fraud_notice_str = fraud_notice ? LOCALE_LIBRARY.dig(path: 'issuer_response_codes.fraud_notice') : ''
37
- LOCALE_LIBRARY.dig(path: id, substitute: fraud_notice_str, scope: "issuer_response_codes.behaviour", locale: locale, default: :unknown)
36
+ behaviour_str = LOCALE_LIBRARY.dig(path: id, scope: "issuer_response_codes.behaviour", locale: locale, default: :unknown)
37
+ return behaviour_str unless fraud_notice && fraudulent_code?
38
+
39
+ "#{behaviour_str} #{LOCALE_LIBRARY.dig(path: 'issuer_response_codes.fraud_notice')}"
40
+ end
41
+
42
+ def fraudulent_code?
43
+ @fraudulent_code ||= LOCALE_LIBRARY.dig(path: id, scope: "issuer_response_codes.fraudulent_codes", locale: locale)
38
44
  end
39
45
  end
40
46
  end
@@ -24,5 +24,9 @@ module IssuerResponseCodes
24
24
  def code(id:, target: default_target, locale: default_locale, fraud_notice: fraud_notice_by_default)
25
25
  Code.new(id: id, target: target, locale: locale, fraud_notice: fraud_notice)
26
26
  end
27
+
28
+ def tds_code(id:, target: default_target, locale: default_locale, fraud_notice: fraud_notice_by_default)
29
+ TdsCode.new(id: id, target: target, locale: locale, fraud_notice: fraud_notice)
30
+ end
27
31
  end
28
32
  end
@@ -18,6 +18,7 @@ module IssuerResponseCodes
18
18
  def dig(path:, locale: :en, scope: '', default: nil, substitute: '')
19
19
  result = __dig__(path: path, locale: locale, scope: scope, default: default)
20
20
  return result unless result
21
+ return result unless result.is_a? ::String
21
22
 
22
23
  result.gsub(/%{substitute}/, substitute)
23
24
  end
@@ -48,6 +49,8 @@ module IssuerResponseCodes
48
49
  end
49
50
 
50
51
  def dig_provided_path(path, scope, locale)
52
+ return if path.nil? || path.empty?
53
+
51
54
  full_path_array = [locale]
52
55
  full_path_array.append(*scope.split('.').map(&:to_sym))
53
56
  full_path_array.append(*path.split('.').map(&:to_sym))
@@ -58,7 +61,8 @@ module IssuerResponseCodes
58
61
  def load_locale(name)
59
62
  name_str = name.to_s
60
63
 
61
- single_locale_hash = self.class.symbolize_keys(::YAML.load_file("lib/locale/#{name_str}.yml")[name_str])
64
+ raw_hash = ::YAML.load_file(::File.join(::File.dirname(::File.expand_path(__FILE__)), "../locale/#{name_str}.yml"))[name_str]
65
+ single_locale_hash = self.class.symbolize_keys(raw_hash)
62
66
  @locale_hash[name] = single_locale_hash
63
67
  end
64
68
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module IssuerResponseCodes
4
+ class TdsCode < Code
5
+ def humanize
6
+ "#{reason} #{behaviour}"
7
+ end
8
+
9
+ alias description humanize
10
+
11
+ def reason
12
+ LOCALE_LIBRARY.dig(path: id, scope: "tds_status_codes.targeted.#{target}", locale: locale, default: :unknown)
13
+ end
14
+
15
+ def behaviour
16
+ behaviour_str = LOCALE_LIBRARY.dig(path: id, scope: "tds_status_codes.behaviour", locale: locale, default: :unknown)
17
+ return behaviour_str unless fraud_notice && fraudulent_code?
18
+
19
+ "#{behaviour_str} #{LOCALE_LIBRARY.dig(path: 'tds_status_codes.fraud_notice')}"
20
+ end
21
+
22
+ def fraudulent_code?
23
+ @fraudulent_code ||= LOCALE_LIBRARY.dig(path: id, scope: "tds_status_codes.fraudulent_codes", locale: locale)
24
+ end
25
+ end
26
+ end