elia_ruby 0.1.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.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +202 -0
- data/lib/elia/mcc/active_model_validator.rb +50 -0
- data/lib/elia/mcc/category.rb +107 -0
- data/lib/elia/mcc/code.rb +242 -0
- data/lib/elia/mcc/collection.rb +276 -0
- data/lib/elia/mcc/configuration.rb +85 -0
- data/lib/elia/mcc/data/.gitkeep +0 -0
- data/lib/elia/mcc/data/mcc_codes.yml +12809 -0
- data/lib/elia/mcc/data/ranges.yml +126 -0
- data/lib/elia/mcc/data/risk_categories.yml +293 -0
- data/lib/elia/mcc/errors.rb +48 -0
- data/lib/elia/mcc/railtie.rb +36 -0
- data/lib/elia/mcc/range.rb +158 -0
- data/lib/elia/mcc/serializer.rb +89 -0
- data/lib/elia/mcc/validator.rb +102 -0
- data/lib/elia/mcc/version.rb +7 -0
- data/lib/elia/mcc.rb +65 -0
- data/lib/elia_ruby/version.rb +5 -0
- data/lib/elia_ruby.rb +35 -0
- metadata +98 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Elia
|
|
4
|
+
module Mcc
|
|
5
|
+
# Serializer for MCC codes and related objects
|
|
6
|
+
#
|
|
7
|
+
# Provides consistent JSON/Hash representations for API responses.
|
|
8
|
+
class Serializer
|
|
9
|
+
DEFAULT_CODE_OPTIONS = {
|
|
10
|
+
include_all_descriptions: false,
|
|
11
|
+
include_categories: true,
|
|
12
|
+
include_range: true,
|
|
13
|
+
}.freeze
|
|
14
|
+
|
|
15
|
+
# Serializes a Code object
|
|
16
|
+
#
|
|
17
|
+
# @param code [Code] the code to serialize
|
|
18
|
+
# @param options [Hash] serialization options
|
|
19
|
+
# @option options [Boolean] :include_all_descriptions (false) include all source descriptions
|
|
20
|
+
# @option options [Boolean] :include_categories (true) include category data
|
|
21
|
+
# @option options [Boolean] :include_range (true) include range data
|
|
22
|
+
# @return [Hash] the serialized code
|
|
23
|
+
def self.serialize_code(code, options = {})
|
|
24
|
+
options = DEFAULT_CODE_OPTIONS.merge(options)
|
|
25
|
+
result = base_code_hash(code)
|
|
26
|
+
result.merge!(all_descriptions_hash(code)) if options[:include_all_descriptions]
|
|
27
|
+
result[:categories] = code.categories.map(&:id) if options[:include_categories]
|
|
28
|
+
result[:range] = code.range&.name if options[:include_range]
|
|
29
|
+
result
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.base_code_hash(code)
|
|
33
|
+
{ mcc: code.mcc, description: code.description,
|
|
34
|
+
stripe_code: code.stripe_code, irs_reportable: code.irs_reportable?, }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.all_descriptions_hash(code)
|
|
38
|
+
{ iso_description: code.iso_description, usda_description: code.usda_description,
|
|
39
|
+
stripe_description: code.stripe_description, visa_description: code.visa_description,
|
|
40
|
+
visa_clearing_name: code.visa_clearing_name, mastercard_description: code.mastercard_description,
|
|
41
|
+
amex_description: code.amex_description, alipay_description: code.alipay_description,
|
|
42
|
+
irs_description: code.irs_description, }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private_class_method :base_code_hash, :all_descriptions_hash
|
|
46
|
+
|
|
47
|
+
# Serializes a Category object
|
|
48
|
+
#
|
|
49
|
+
# @param category [Category] the category to serialize
|
|
50
|
+
# @param options [Hash] serialization options
|
|
51
|
+
# @return [Hash] the serialized category
|
|
52
|
+
def self.serialize_category(category, options = {})
|
|
53
|
+
result = {
|
|
54
|
+
id: category.id,
|
|
55
|
+
name: category.name,
|
|
56
|
+
description: category.description,
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
result[:codes] = category.codes if options[:include_codes]
|
|
60
|
+
|
|
61
|
+
result
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Serializes a Range object
|
|
65
|
+
#
|
|
66
|
+
# @param range [Range] the range to serialize
|
|
67
|
+
# @param options [Hash] serialization options
|
|
68
|
+
# @return [Hash] the serialized range
|
|
69
|
+
def self.serialize_range(range, _options = {})
|
|
70
|
+
{
|
|
71
|
+
start_code: range.start_code,
|
|
72
|
+
end_code: range.end_code,
|
|
73
|
+
name: range.name,
|
|
74
|
+
description: range.description,
|
|
75
|
+
reserved: range.reserved?,
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Serializes a collection of codes
|
|
80
|
+
#
|
|
81
|
+
# @param codes [Array<Code>] the codes to serialize
|
|
82
|
+
# @param options [Hash] serialization options
|
|
83
|
+
# @return [Array<Hash>] the serialized codes
|
|
84
|
+
def self.serialize_collection(codes, options = {})
|
|
85
|
+
codes.map { |code| serialize_code(code, options) }
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Elia
|
|
4
|
+
module Mcc
|
|
5
|
+
# Standalone validator for MCC codes
|
|
6
|
+
#
|
|
7
|
+
# Can be used independently of ActiveModel for basic validation.
|
|
8
|
+
#
|
|
9
|
+
# @example Basic usage
|
|
10
|
+
# validator = Elia::Mcc::Validator.new
|
|
11
|
+
# validator.valid?("5411") # => true
|
|
12
|
+
# validator.valid?("XXXX") # => false
|
|
13
|
+
#
|
|
14
|
+
# @example With category restrictions
|
|
15
|
+
# validator = Elia::Mcc::Validator.new(deny_categories: [:gambling, :adult])
|
|
16
|
+
# validator.valid?("7995") # => false (gambling)
|
|
17
|
+
class Validator
|
|
18
|
+
# Default error messages
|
|
19
|
+
MESSAGES = {
|
|
20
|
+
invalid_format: "must be a valid 4-digit MCC code",
|
|
21
|
+
not_found: "is not a recognized MCC code",
|
|
22
|
+
denied_category: "is in a denied category",
|
|
23
|
+
}.freeze
|
|
24
|
+
|
|
25
|
+
# @return [Hash] validation options
|
|
26
|
+
attr_reader :options
|
|
27
|
+
|
|
28
|
+
# Creates a new Validator instance
|
|
29
|
+
#
|
|
30
|
+
# @param options [Hash] validation options
|
|
31
|
+
# @option options [Boolean] :strict (true) require code to exist in registry
|
|
32
|
+
# @option options [Array<Symbol>] :deny_categories categories to reject
|
|
33
|
+
# @option options [Array<Symbol>] :allow_categories only allow these categories
|
|
34
|
+
def initialize(options = {})
|
|
35
|
+
@options = {
|
|
36
|
+
strict: true,
|
|
37
|
+
deny_categories: [],
|
|
38
|
+
allow_categories: nil,
|
|
39
|
+
}.merge(options)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Validates the given value
|
|
43
|
+
#
|
|
44
|
+
# @param value [String, Integer, nil] the value to validate
|
|
45
|
+
# @return [Array<String>] error messages (empty if valid)
|
|
46
|
+
def validate(value)
|
|
47
|
+
errors = []
|
|
48
|
+
|
|
49
|
+
return errors if value.nil?
|
|
50
|
+
|
|
51
|
+
# Check format
|
|
52
|
+
unless valid_format?(value)
|
|
53
|
+
errors << MESSAGES[:invalid_format]
|
|
54
|
+
return errors
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Check if code exists (strict mode)
|
|
58
|
+
if options[:strict]
|
|
59
|
+
code = Elia::Mcc.find(value)
|
|
60
|
+
if code.nil?
|
|
61
|
+
errors << MESSAGES[:not_found]
|
|
62
|
+
return errors
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Check category restrictions
|
|
66
|
+
if options[:deny_categories].any?
|
|
67
|
+
denied = options[:deny_categories].any? { |cat| code.in_category?(cat) }
|
|
68
|
+
errors << MESSAGES[:denied_category] if denied
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if options[:allow_categories]
|
|
72
|
+
allowed = options[:allow_categories].any? { |cat| code.in_category?(cat) }
|
|
73
|
+
errors << MESSAGES[:denied_category] unless allowed
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
errors
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Returns whether the value is valid
|
|
81
|
+
#
|
|
82
|
+
# @param value [String, Integer, nil] the value to validate
|
|
83
|
+
# @return [Boolean] true if valid
|
|
84
|
+
def valid?(value)
|
|
85
|
+
validate(value).empty?
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
private
|
|
89
|
+
|
|
90
|
+
# Checks if the value has a valid MCC format
|
|
91
|
+
#
|
|
92
|
+
# @param value [String, Integer, nil] the value to check
|
|
93
|
+
# @return [Boolean] true if the format is valid
|
|
94
|
+
def valid_format?(value)
|
|
95
|
+
return false if value.nil?
|
|
96
|
+
|
|
97
|
+
normalized = value.to_s.strip
|
|
98
|
+
normalized.match?(/\A\d{1,4}\z/)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
data/lib/elia/mcc.rb
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Elia
|
|
4
|
+
# MCC (Merchant Category Code) module provides tools for working with
|
|
5
|
+
# payment industry merchant category codes, including validation,
|
|
6
|
+
# categorization, and risk assessment.
|
|
7
|
+
module Mcc
|
|
8
|
+
class << self
|
|
9
|
+
# Returns the configuration object for the Mcc module
|
|
10
|
+
#
|
|
11
|
+
# @return [Configuration] the configuration instance
|
|
12
|
+
def configuration
|
|
13
|
+
@configuration ||= Configuration.new
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Yields the configuration object for block-based configuration
|
|
17
|
+
#
|
|
18
|
+
# @yield [Configuration] the configuration instance
|
|
19
|
+
# @return [Configuration] the configuration instance
|
|
20
|
+
def configure
|
|
21
|
+
yield(configuration) if block_given?
|
|
22
|
+
configuration
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Resets the configuration to defaults and clears the collection cache
|
|
26
|
+
#
|
|
27
|
+
# @return [Configuration] a new configuration instance
|
|
28
|
+
def reset!
|
|
29
|
+
@configuration = Configuration.new
|
|
30
|
+
@collection = nil
|
|
31
|
+
@configuration
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
alias reset_configuration! reset!
|
|
35
|
+
|
|
36
|
+
# Delegate query methods to Collection
|
|
37
|
+
delegate :all, :find, :find!, :[], :where, :in_range, :search,
|
|
38
|
+
:in_category, :valid?, :count, :size, :reload!,
|
|
39
|
+
to: :collection
|
|
40
|
+
|
|
41
|
+
# Returns all ISO 18245 ranges
|
|
42
|
+
#
|
|
43
|
+
# @return [Array<Range>] all ranges
|
|
44
|
+
def ranges
|
|
45
|
+
collection.all_ranges
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Returns all risk categories
|
|
49
|
+
#
|
|
50
|
+
# @return [Array<Category>] all categories
|
|
51
|
+
def categories
|
|
52
|
+
collection.all_categories
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
# Returns the collection instance, creating it if necessary
|
|
58
|
+
#
|
|
59
|
+
# @return [Collection] the collection instance
|
|
60
|
+
def collection
|
|
61
|
+
@collection ||= Collection.new
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
data/lib/elia_ruby.rb
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zeitwerk"
|
|
4
|
+
require "active_support"
|
|
5
|
+
require "active_support/core_ext/module/delegation"
|
|
6
|
+
require "active_support/core_ext/object/blank"
|
|
7
|
+
|
|
8
|
+
# Define the Elia module at the top level
|
|
9
|
+
module Elia
|
|
10
|
+
class Error < StandardError; end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
require_relative "elia_ruby/version"
|
|
14
|
+
|
|
15
|
+
# Set up Zeitwerk loader for Elia::Mcc namespace
|
|
16
|
+
loader = Zeitwerk::Loader.new
|
|
17
|
+
loader.tag = "elia_mcc"
|
|
18
|
+
loader.push_dir(File.expand_path("elia", __dir__), namespace: Elia)
|
|
19
|
+
|
|
20
|
+
# Ignore files that don't follow Zeitwerk conventions
|
|
21
|
+
loader.ignore(File.expand_path("elia/mcc/version.rb", __dir__))
|
|
22
|
+
loader.ignore(File.expand_path("elia/mcc/errors.rb", __dir__))
|
|
23
|
+
loader.ignore(File.expand_path("elia/mcc/railtie.rb", __dir__))
|
|
24
|
+
loader.ignore(File.expand_path("elia/mcc/active_model_validator.rb", __dir__))
|
|
25
|
+
loader.ignore(File.expand_path("elia/mcc/data", __dir__))
|
|
26
|
+
|
|
27
|
+
loader.setup
|
|
28
|
+
|
|
29
|
+
# Load files that don't follow Zeitwerk conventions manually
|
|
30
|
+
require_relative "elia/mcc/version"
|
|
31
|
+
require_relative "elia/mcc/errors"
|
|
32
|
+
require_relative "elia/mcc"
|
|
33
|
+
|
|
34
|
+
# Load the Railtie if Rails is present
|
|
35
|
+
require_relative "elia/mcc/railtie" if defined?(Rails::Railtie)
|
metadata
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: elia_ruby
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Elia Pay
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-01-28 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: activesupport
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '6.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '6.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: zeitwerk
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '2.6'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '2.6'
|
|
41
|
+
description: A comprehensive library for working with Merchant Category Codes (MCC),
|
|
42
|
+
including validation, categorization, and risk assessment.
|
|
43
|
+
email: support@eliapay.com
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- LICENSE.txt
|
|
49
|
+
- README.md
|
|
50
|
+
- lib/elia/mcc.rb
|
|
51
|
+
- lib/elia/mcc/active_model_validator.rb
|
|
52
|
+
- lib/elia/mcc/category.rb
|
|
53
|
+
- lib/elia/mcc/code.rb
|
|
54
|
+
- lib/elia/mcc/collection.rb
|
|
55
|
+
- lib/elia/mcc/configuration.rb
|
|
56
|
+
- lib/elia/mcc/data/.gitkeep
|
|
57
|
+
- lib/elia/mcc/data/mcc_codes.yml
|
|
58
|
+
- lib/elia/mcc/data/ranges.yml
|
|
59
|
+
- lib/elia/mcc/data/risk_categories.yml
|
|
60
|
+
- lib/elia/mcc/errors.rb
|
|
61
|
+
- lib/elia/mcc/railtie.rb
|
|
62
|
+
- lib/elia/mcc/range.rb
|
|
63
|
+
- lib/elia/mcc/serializer.rb
|
|
64
|
+
- lib/elia/mcc/validator.rb
|
|
65
|
+
- lib/elia/mcc/version.rb
|
|
66
|
+
- lib/elia_ruby.rb
|
|
67
|
+
- lib/elia_ruby/version.rb
|
|
68
|
+
homepage: https://github.com/eliapay/elia-ruby
|
|
69
|
+
licenses:
|
|
70
|
+
- MIT
|
|
71
|
+
metadata:
|
|
72
|
+
bug_tracker_uri: https://github.com/eliapay/elia-ruby/issues
|
|
73
|
+
changelog_uri: https://github.com/eliapay/elia-ruby/blob/main/CHANGELOG.md
|
|
74
|
+
documentation_uri: https://github.com/eliapay/elia-ruby#readme
|
|
75
|
+
github_repo: ssh://github.com/eliapay/elia-ruby
|
|
76
|
+
homepage_uri: https://github.com/eliapay/elia-ruby
|
|
77
|
+
source_code_uri: https://github.com/eliapay/elia-ruby
|
|
78
|
+
rubygems_mfa_required: 'true'
|
|
79
|
+
post_install_message:
|
|
80
|
+
rdoc_options: []
|
|
81
|
+
require_paths:
|
|
82
|
+
- lib
|
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: 2.7.0
|
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
89
|
+
requirements:
|
|
90
|
+
- - ">="
|
|
91
|
+
- !ruby/object:Gem::Version
|
|
92
|
+
version: '0'
|
|
93
|
+
requirements: []
|
|
94
|
+
rubygems_version: 3.5.22
|
|
95
|
+
signing_key:
|
|
96
|
+
specification_version: 4
|
|
97
|
+
summary: MCC (Merchant Category Code) library for payment processing
|
|
98
|
+
test_files: []
|