emv_qr_code_processor 1.1.5

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 182fc45c04647e1cc189c4af19d2094e8c0fa2e002923e1983a601e953e0d89a
4
+ data.tar.gz: ab7f560ccfec4c744976978e9505811589b0f49bf3ff2db7829fb072bcce096b
5
+ SHA512:
6
+ metadata.gz: 7b35c4b86f0a8593c46ceeed2b5c0377c0a120d91d31de2a0321430e38d4d541bd1502ce53a6be6c83b84f65d47612df322ecbb57ab0c5814dd879c40b6fd010
7
+ data.tar.gz: f5d72bfc2a0bc3a849af83767b79b0a7e92eb22f1e87ebb9c83f758f22315ac946927adb3247ceec068dfc4c60b60b255d629e46258bb875dc90edb74d78961b
@@ -0,0 +1,56 @@
1
+ require 'digest/crc16_ccitt'
2
+
3
+ module EmvQrCodeProcessor
4
+ class Decoder
5
+ attr_reader :raw_qr_code
6
+
7
+ def initialize(raw_qr_code)
8
+ @raw_qr_code = raw_qr_code
9
+ end
10
+
11
+ def decode(validate_crc: true)
12
+ result = decode_data_objects(raw_qr_code, Schema)
13
+ validate_crc(result.fetch(:crc)) if validate_crc
14
+
15
+ result
16
+ end
17
+
18
+ private
19
+
20
+ def decode_data_objects(raw_data, template)
21
+ result = {}
22
+
23
+ each_data_object(raw_data) do |data_object_id, value|
24
+ data_object_type = find_data_object_type(data_object_id, template)
25
+ raise Errors::InvalidQRCodePayload if data_object_type.nil?
26
+
27
+ value = decode_data_objects(value, data_object_type) unless data_object_type.primitive?
28
+ result[data_object_type.name] = value
29
+ end
30
+
31
+ result
32
+ end
33
+
34
+ def each_data_object(raw_data)
35
+ scanner = StringScanner.new(raw_data)
36
+ until scanner.eos?
37
+ data_object_id = scanner.scan(/\d{2}/)
38
+ value_length = scanner.scan(/\d{2}/).to_i
39
+ value = scanner.scan(/.{#{value_length}}/)
40
+
41
+ yield(data_object_id, value)
42
+ end
43
+ end
44
+
45
+ def find_data_object_type(id, template)
46
+ template.data_object_types.find { |d| d.id == id }
47
+ end
48
+
49
+ def validate_crc(crc)
50
+ payload_without_crc = raw_qr_code[0..-9]
51
+ calculated_crc = Schema::CRC.calculate(payload_without_crc)
52
+
53
+ raise Errors::InvalidCRC if calculated_crc != crc
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,41 @@
1
+ require 'digest/crc16_ccitt'
2
+
3
+ module EmvQrCodeProcessor
4
+ class Encoder
5
+ attr_reader :payload
6
+
7
+ def initialize(payload)
8
+ @payload = payload
9
+ end
10
+
11
+ def encode
12
+ result = encode_data_objects(payload, Schema)
13
+ result << calculate_crc(result)
14
+ result
15
+ end
16
+
17
+ private
18
+
19
+ def calculate_crc(data)
20
+ format(Schema::CRC.id, Schema::CRC.calculate(data))
21
+ end
22
+
23
+ def encode_data_objects(data, template)
24
+ sorted = data.sort_by { |name, _| find_data_object_type(name, template).id }
25
+
26
+ sorted.each_with_object("") do |(name, value), result|
27
+ data_object_type = find_data_object_type(name, template)
28
+ value = encode_data_objects(value, data_object_type) unless data_object_type.primitive?
29
+ result << format(data_object_type.id, value)
30
+ end
31
+ end
32
+
33
+ def format(id, value)
34
+ [id, value.length.to_s.rjust(2, "0"), value].join
35
+ end
36
+
37
+ def find_data_object_type(name, template)
38
+ template.data_object_types.find { |d| d.name == name }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,7 @@
1
+ module EmvQrCodeProcessor
2
+ module Errors
3
+ class EMVQRCodeError < StandardError; end
4
+ class InvalidCRC < EMVQRCodeError; end
5
+ class InvalidQRCodePayload < EMVQRCodeError; end
6
+ end
7
+ end
@@ -0,0 +1,86 @@
1
+ module EmvQrCodeProcessor
2
+ module Schema
3
+ DataObjectType = Struct.new(:id, :name, keyword_init: true) do
4
+ def primitive?
5
+ true
6
+ end
7
+ end
8
+
9
+ TemplateType = Struct.new(:id, :name, :data_object_types, keyword_init: true) do
10
+ def primitive?
11
+ false
12
+ end
13
+ end
14
+
15
+ class CRCType < DataObjectType
16
+ LENGTH = "04".freeze
17
+
18
+ def initialize
19
+ super(id: "63", name: :crc)
20
+ end
21
+
22
+ def calculate(data)
23
+ Digest::CRC16CCITT.new.update([data, id, LENGTH].join).to_s.upcase
24
+ end
25
+ end
26
+
27
+ class AdditionalDataFieldType < TemplateType
28
+ PLACEHOLDER = "***".freeze
29
+
30
+ def initialize
31
+ data_object_types = [
32
+ DataObjectType.new(id: "01", name: :bill_number),
33
+ DataObjectType.new(id: "02", name: :mobile_number),
34
+ DataObjectType.new(id: "03", name: :store_label),
35
+ DataObjectType.new(id: "04", name: :loyalty_number),
36
+ DataObjectType.new(id: "05", name: :reference_label),
37
+ DataObjectType.new(id: "06", name: :customer_label),
38
+ DataObjectType.new(id: "07", name: :terminal_label),
39
+ DataObjectType.new(id: "08", name: :transaction_purpose)
40
+ ]
41
+
42
+ ("09".."99").each do |id|
43
+ data_object_types << DataObjectType.new(id: id, name: :"reserved_#{id}")
44
+ end
45
+
46
+ super(
47
+ id: "62",
48
+ name: :additional_data_field,
49
+ data_object_types: data_object_types
50
+ )
51
+ end
52
+ end
53
+
54
+ CRC = CRCType.new
55
+ ADDITIONAL_DATA_FIELD = AdditionalDataFieldType.new
56
+
57
+ DATA_OBJECT_TYPES = [
58
+ DataObjectType.new(id: "00", name: :payload_format_indicator),
59
+ DataObjectType.new(id: "01", name: :point_of_initiation_method),
60
+ DataObjectType.new(id: "15", name: :"merchant_account_information.union_pay"),
61
+ DataObjectType.new(id: "52", name: :merchant_category_code),
62
+ DataObjectType.new(id: "53", name: :transaction_currency),
63
+ DataObjectType.new(id: "54", name: :transaction_amount),
64
+ DataObjectType.new(id: "55", name: :tip_or_convenience_fee_indicator),
65
+ DataObjectType.new(id: "56", name: :value_of_convenience_fee_fixed),
66
+ DataObjectType.new(id: "57", name: :value_of_convenience_fee_percentage),
67
+ DataObjectType.new(id: "58", name: :country_code),
68
+ DataObjectType.new(id: "59", name: :merchant_name),
69
+ DataObjectType.new(id: "60", name: :merchant_city),
70
+ DataObjectType.new(id: "61", name: :postal_code),
71
+ ADDITIONAL_DATA_FIELD,
72
+ CRC,
73
+ DataObjectType.new(id: "64", name: :merchant_information_language)
74
+ ]
75
+
76
+ ("00".."99").each do |id|
77
+ next if DATA_OBJECT_TYPES.any? { |d| d.id == id }
78
+
79
+ DATA_OBJECT_TYPES << DataObjectType.new(id: id, name: :"reserved_#{id}")
80
+ end
81
+
82
+ def self.data_object_types
83
+ DATA_OBJECT_TYPES
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,11 @@
1
+ module EmvQrCodeProcessor
2
+ module VERSION
3
+ # Semantic versioning
4
+ MAJOR = 1
5
+ MINOR = 1
6
+ TINY = 5
7
+
8
+ # Full version number
9
+ STRING = [MAJOR, MINOR, TINY].compact.join(".")
10
+ end
11
+ end
@@ -0,0 +1,10 @@
1
+ require "emv_qr_code_processor/schema"
2
+ require "emv_qr_code_processor/encoder"
3
+ require "emv_qr_code_processor/decoder"
4
+ require "emv_qr_code_processor/errors"
5
+ require "emv_qr_code_processor/version"
6
+
7
+ module EmvQrCodeProcessor
8
+ module Schema
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: emv_qr_code_processor
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.5
5
+ platform: ruby
6
+ authors:
7
+ - Sokmesa KHIEV
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-01-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: digest-crc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.6.3
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.6.3
27
+ description: 'Process to generate and read data EMV QRCode '
28
+ email:
29
+ - ksokmesa@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/emv_qr_code_processor.rb
35
+ - lib/emv_qr_code_processor/decoder.rb
36
+ - lib/emv_qr_code_processor/encoder.rb
37
+ - lib/emv_qr_code_processor/errors.rb
38
+ - lib/emv_qr_code_processor/schema.rb
39
+ - lib/emv_qr_code_processor/version.rb
40
+ homepage: https://github.com/sokmesakhiev/emv_qr_code_processor
41
+ licenses:
42
+ - MIT
43
+ metadata: {}
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubygems_version: 3.0.3
60
+ signing_key:
61
+ specification_version: 4
62
+ summary: EMV QRCode reader and writer
63
+ test_files: []