aliquot 1.0.0 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0cdd20926e96b21c0fb8c494b13fe34995c59646285d71416c33045e907b830a
4
- data.tar.gz: 32d7e64f5ef20f37404bcc5293672dbaa43f6d299bd0701e49b859ad2e9c1a25
3
+ metadata.gz: de89d0021775b3047c345d9d41bdb90855fe9fac9e1a663b83e5f865fc407edf
4
+ data.tar.gz: ac219aa43dc4924729d1f29013e25d1cedd616c3f086734607bc35006182be8e
5
5
  SHA512:
6
- metadata.gz: 1b555a97041aaaf6e4cd3e3aaf82c17a94112627f36cc3e0835a4708c28eef8e8f6a4f20e51b978876cc77a1ced87b6a4b6066a7effd52e2a027c5d152a78995
7
- data.tar.gz: ee081263c8ad3869eaa53127ec7ed300f9bba4c67619c548ed83099932cf0978cf3e0ec48dbca47e054a01f79f747c261a0e96eb84d26e9665846dbfed5a2025
6
+ metadata.gz: 05102ee3be01374cd0b58d3dab85ddc6428d16df25a7dc7262989a3108691ab5c3c7e94b128f544c7d66be8f70cb428f8babc22d2fe805a31c1efb84912fcacc
7
+ data.tar.gz: 98a9aa9ae43295d177ed42c1d77f4e785f5eff0751c87fbf500fb52f48c98f684a9e9f331ad14e89dfd393b464585a61dc6b8c2377b2ebea7fa075b1f859fb59
data/lib/aliquot/error.rb CHANGED
@@ -29,5 +29,6 @@ module Aliquot
29
29
  # When shared_secret is invalid
30
30
  class InvalidSharedSecretError < Error; end
31
31
 
32
- class InvalidMerchantIDError < Error; end
32
+ # When recipient_id is invalid
33
+ class InvalidRecipientIDError < Error; end
33
34
  end
@@ -10,16 +10,16 @@ module Aliquot
10
10
  ##
11
11
  # A Payment represents a single payment using Google Pay.
12
12
  # It is used to verify/decrypt the supplied token by using the shared secret,
13
- # thus avoiding having knowledge of merchant primary keys.
13
+ # thus avoiding having knowledge of any private keys involved.
14
14
  class Payment
15
15
  SUPPORTED_PROTOCOL_VERSIONS = %w[ECv1 ECv2].freeze
16
16
  ##
17
17
  # Parameters:
18
18
  # token_string:: Google Pay token (JSON string)
19
19
  # shared_secret:: Base64 encoded shared secret
20
- # merchant_id:: Google Pay merchant ID
20
+ # recipient_id:: Google Pay recipient ID
21
21
  # signing_keys:: Signing keys fetched from Google
22
- def initialize(token_string, shared_secret, merchant_id,
22
+ def initialize(token_string, shared_secret, recipient_id,
23
23
  signing_keys: ENV['GOOGLE_SIGNING_KEYS'])
24
24
 
25
25
  begin
@@ -32,7 +32,7 @@ module Aliquot
32
32
  @token = validation.output
33
33
 
34
34
  @shared_secret = shared_secret
35
- @merchant_id = merchant_id
35
+ @recipient_id = recipient_id
36
36
  @signing_keys = signing_keys
37
37
  end
38
38
 
@@ -43,7 +43,7 @@ module Aliquot
43
43
  raise Error, "supported protocol versions are #{SUPPORTED_PROTOCOL_VERSIONS.join(', ')}"
44
44
  end
45
45
 
46
- @recipient_id = validate_merchant_id
46
+ @recipient_id = validate_recipient_id
47
47
 
48
48
  check_shared_secret
49
49
 
@@ -102,9 +102,10 @@ module Aliquot
102
102
  @intermediate_key[:keyExpiration].to_i < cur_millis
103
103
  end
104
104
 
105
- def validate_merchant_id
106
- raise InvalidMerchantIDError unless /\A[[:graph:]]+\z/ =~ @merchant_id
107
- "merchant:#{@merchant_id}"
105
+ def validate_recipient_id
106
+ raise InvalidRecipientIDError, 'recipient_id must be alphanumeric and punctuation' unless /\A[[:graph:]]+\z/ =~ @recipient_id
107
+
108
+ @recipient_id
108
109
  end
109
110
 
110
111
  def check_shared_secret
@@ -209,8 +210,8 @@ module Aliquot
209
210
 
210
211
  def decrypt(key, encrypted)
211
212
  c = new_cipher
212
- c.key = key
213
213
  c.decrypt
214
+ c.key = key
214
215
 
215
216
  c.update(Base64.strict_decode64(encrypted)) + c.final
216
217
  end
@@ -9,143 +9,221 @@ module Aliquot
9
9
  module Validator
10
10
  # Verified according to:
11
11
  # https://developers.google.com/pay/api/web/guides/resources/payment-data-cryptography#payment-method-token-structure
12
- module Predicates
13
- include Dry::Logic::Predicates
14
-
15
- CUSTOM_PREDICATE_ERRORS = {
16
- base64?: 'must be Base64',
17
- pan?: 'must be a pan',
18
- ec_public_key?: 'must be an EC public key',
19
- eci?: 'must be an ECI',
20
- json_string?: 'must be valid JSON',
21
- integer_string?: 'must be string encoded integer',
22
- month?: 'must be a month (1..12)',
23
- year?: 'must be a year (2000..3000)',
24
- base64_asn1?: 'must be base64 encoded asn1 value',
25
- json_object?: 'must be a JSON object',
26
-
27
- authMethodCryptogram3DS: 'authMethod CRYPTOGRAM_3DS requires eciIndicator',
28
- authMethodCard: 'eciIndicator/cryptogram must be omitted when PAN_ONLY',
29
- }.freeze
30
-
31
- # Support Ruby 2.3, but use the faster #match? when available.
32
- match_b = ''.respond_to?(:match?) ? ->(s, re) { s.match?(re) } : ->(s, re) { !!(s =~ re) }
33
-
34
- def self.to_bool(lbd)
35
- lbd.call
36
- true
37
- rescue
38
- false
39
- end
40
12
 
41
- predicate(:base64?) do |x|
42
- str?(x) &&
43
- match_b.call(x, /\A[=A-Za-z0-9+\/]*\z/) && # allowable chars
44
- x.length.remainder(4).zero? && # multiple of 4
45
- !match_b.call(x, /=[^$=]/) && # may only end with ='s
46
- !match_b.call(x, /===/) # at most 2 ='s
47
- end
13
+ CUSTOM_ERRORS = {
14
+ base64?: 'must be Base64',
15
+ pan?: 'must be a PAN',
16
+ ec_public_key?: 'must be an EC public key',
17
+ eci?: 'must be an ECI',
18
+ integer_string?: 'must be string encoded integer',
19
+ month?: 'must be a month (1..12)',
20
+ year?: 'must be a year (2000..3000)',
21
+ base64_asn1?: 'must be base64-encoded ANS.1 value',
22
+ json?: 'must be valid JSON',
23
+
24
+ is_authMethodCryptogram3DS: 'authMethod CRYPTOGRAM_3DS requires eciIndicator',
25
+ is_authMethodCard: 'eciIndicator/cryptogram must be omitted when PAN_ONLY',
26
+ }.freeze
27
+
28
+ def self.base64_check(value)
29
+ /\A[=A-Za-z0-9+\/]*\z/.match?(value) && # allowable chars
30
+ value.length.remainder(4).zero? && # multiple of 4
31
+ !/=[^$=]/.match?(value) && # may only end with ='s
32
+ !/===/.match?(value) # at most 2 ='s
33
+ end
48
34
 
49
- # We should figure out how strict we should be. Hopefully we can discard
50
- # the above Base64? predicate and use the following simpler one:
51
- #predicate(:strict_base64?) { |x| !!Base64.strict_decode64(x) rescue false }
35
+ Dry::Validation.register_macro(:base64?) do
36
+ if key?
37
+ unless Aliquot::Validator.base64_check(value)
38
+ key.failure(CUSTOM_ERRORS[:base64?])
39
+ end
40
+ end
41
+ end
52
42
 
53
- predicate(:pan?) { |x| match_b.call(x, /\A[1-9][0-9]{11,18}\z/) }
43
+ def self.ans1_check(value)
44
+ OpenSSL::ASN1.decode(Base64.strict_decode64(value)) rescue false
45
+ end
54
46
 
55
- predicate(:eci?) { |x| str?(x) && match_b.call(x, /\A\d{1,2}\z/) }
47
+ Dry::Validation.register_macro(:base64_asn1?) do
48
+ if key?
49
+ unless Aliquot::Validator.ans1_check(value)
50
+ key.failure(CUSTOM_ERRORS[:base64_asn1?])
51
+ end
52
+ end
53
+ end
56
54
 
57
- predicate(:ec_public_key?) { |x| base64?(x) && OpenSSL::PKey::EC.new(Base64.decode64(x)).check_key rescue false }
55
+ Dry::Validation.register_macro(:pan?) do
56
+ if key?
57
+ unless /\A[1-9][0-9]{11,18}\z/.match?(value)
58
+ key.failure(CUSTOM_ERRORS[:pan?])
59
+ end
60
+ end
61
+ end
58
62
 
59
- predicate(:json_string?) { |x| !!JSON.parse(x) rescue false }
63
+ Dry::Validation.register_macro(:ec_public_key?) do
64
+ if key?
65
+ begin
66
+ OpenSSL::PKey::EC.new(Base64.decode64(value)).check_key
67
+ rescue
68
+ key.failure(CUSTOM_ERRORS[:ec_public_key?])
69
+ end
70
+ end
71
+ end
60
72
 
61
- predicate(:integer_string?) { |x| str?(x) && match_b.call(x, /\A\d+\z/) }
73
+ Dry::Validation.register_macro(:eci?) do
74
+ if key?
75
+ unless /\A\d{1,2}\z/.match?(value)
76
+ key.failure(CUSTOM_ERRORS[:eci?])
77
+ end
78
+ end
79
+ end
62
80
 
63
- predicate(:month?) { |x| x.between?(1, 12) }
81
+ Dry::Validation.register_macro(:integer_string?) do
82
+ if key?
83
+ unless /\A\d+\z/.match?(value)
84
+ key.failure(CUSTOM_ERRORS[:integer_string?])
85
+ end
86
+ end
87
+ end
64
88
 
65
- predicate(:year?) { |x| x.between?(2000, 3000) }
89
+ Dry::Validation.register_macro(:json?) do
90
+ if key?
91
+ json = JSON.parse(value) rescue false
92
+ key.failure(CUSTOM_ERRORS[:json?]) unless json
93
+ end
94
+ end
66
95
 
67
- predicate(:base64_asn1?) { |x| OpenSSL::ASN1.decode(Base64.strict_decode64(x)) rescue false }
96
+ Dry::Validation.register_macro(:month?) do
97
+ if key?
98
+ unless value.between?(1, 12)
99
+ key.failure(CUSTOM_ERRORS[:month?])
100
+ end
101
+ end
102
+ end
68
103
 
69
- predicate(:json_object?) { |x| hash?(x) }
104
+ Dry::Validation.register_macro(:year?) do
105
+ if key?
106
+ unless value.between?(2000, 3000)
107
+ key.failure(CUSTOM_ERRORS[:year?])
108
+ end
109
+ end
70
110
  end
71
111
 
72
- # Base for DRY-Validation schemas used in Aliquot.
73
- class BaseSchema < Dry::Validation::Schema::JSON
74
- predicates(Predicates)
75
- def self.messages
76
- super.merge(en: { errors: Predicates::CUSTOM_PREDICATE_ERRORS })
112
+ class SignedKeyContract < Dry::Validation::Contract
113
+ json do
114
+ required(:keyExpiration).filled(:str?)
115
+ required(:keyValue).filled(:str?)
77
116
  end
117
+ rule(:keyExpiration).validate(:integer_string?)
118
+ rule(:keyValue).validate(:ec_public_key?)
78
119
  end
79
120
 
80
- # Schema used for the 'intermediateSigningKey' hash included in ECv2.
81
- IntermediateSigningKeySchema = Dry::Validation.Schema(BaseSchema) do
82
- required(:signedKey).filled(:str?, :json_string?)
121
+ SignedKeySchema = SignedKeyContract.new
83
122
 
84
- required(:signatures).filled(:array?) { each { base64? & base64_asn1? } }
123
+ # Schema used for the 'intermediateSigningKey' hash included in ECv2.
124
+ class IntermediateSigningKeyContract < Dry::Validation::Contract
125
+ json do
126
+ required(:signedKey).filled(:str?)
127
+ required(:signatures).array(:str?)
128
+ end
129
+ rule(:signedKey).validate(:json?)
130
+ rule(:signatures).each do
131
+ key.failure('must be Base64') unless Aliquot::Validator.base64_check(value) &&
132
+ Aliquot::Validator.ans1_check(value)
133
+ end
85
134
  end
86
135
 
87
- SignedKeySchema = Dry::Validation.Schema(BaseSchema) do
88
- required(:keyExpiration).filled(:integer_string?)
89
- required(:keyValue).filled(:ec_public_key?)
90
- end
136
+ IntermediateSigningKeySchema = IntermediateSigningKeyContract.new
91
137
 
92
138
  # DRY-Validation schema for Google Pay token
93
- TokenSchema = Dry::Validation.Schema(BaseSchema) do
94
- required(:signature).filled(:str?, :base64?, :base64_asn1?)
95
-
96
- required(:protocolVersion).filled(:str?).when(eql?: 'ECv2') do
97
- required(:intermediateSigningKey)
139
+ class TokenContract < Dry::Validation::Contract
140
+ json do
141
+ required(:signature).filled(:str?)
142
+ required(:signedMessage).filled(:str?)
143
+ required(:protocolVersion).filled(:str?)
144
+ optional(:intermediateSigningKey).hash(IntermediateSigningKeyContract.new.schema)
98
145
  end
146
+ rule(:signature).validate(:base64?, :base64_asn1?)
147
+ rule(:signedMessage).validate(:json?)
99
148
 
100
- required(:signedMessage).filled(:str?, :json_string?)
101
-
102
- optional(:intermediateSigningKey).value(:json_object?) { schema(IntermediateSigningKeySchema) }
149
+ rule(:intermediateSigningKey) do
150
+ key.failure('is missing') if 'ECv2'.eql?(values[:protocolVersion]) &&
151
+ values[:intermediateSigningKey].nil?
152
+ end
103
153
  end
104
154
 
155
+ TokenSchema = TokenContract.new
156
+
105
157
  # DRY-Validation schema for signedMessage component Google Pay token
106
- SignedMessageSchema = Dry::Validation.Schema(BaseSchema) do
107
- required(:encryptedMessage).filled(:str?, :base64?)
108
- required(:ephemeralPublicKey).filled(:str?, :base64?)
109
- required(:tag).filled(:str?, :base64?)
158
+ class SignedMessageContract < Dry::Validation::Contract
159
+ json do
160
+ required(:encryptedMessage).filled(:str?)
161
+ required(:ephemeralPublicKey).filled(:str?)
162
+ required(:tag).filled(:str?)
163
+ end
164
+ rule(:encryptedMessage).validate(:base64?)
165
+ rule(:ephemeralPublicKey).validate(:base64?)
166
+ rule(:tag).validate(:base64?)
110
167
  end
111
168
 
112
- # DRY-Validation schema for paymentMethodDetails component Google Pay token
113
- PaymentMethodDetailsSchema = Dry::Validation.Schema(BaseSchema) do
114
- required(:pan).filled(:integer_string?, :pan?)
115
- required(:expirationMonth).filled(:int?, :month?)
116
- required(:expirationYear).filled(:int?, :year?)
117
- required(:authMethod).filled(:str?, included_in?: %w[PAN_ONLY CRYPTOGRAM_3DS])
118
-
119
- optional(:cryptogram).filled(:str?)
120
- optional(:eciIndicator).filled(:str?, :eci?)
169
+ SignedMessageSchema = SignedMessageContract.new
121
170
 
122
- rule(cryptogram: %i[authMethod cryptogram]) do |method, cryptogram|
123
- method.eql?('CRYPTOGRAM_3DS') > required(:cryptogram)
171
+ # DRY-Validation schema for paymentMethodDetails component Google Pay token
172
+ class PaymentMethodDetailsContract < Dry::Validation::Contract
173
+ json do
174
+ required(:pan).filled(:str?)
175
+ required(:expirationMonth).filled(:int?)
176
+ required(:expirationYear).filled(:int?)
177
+ required(:authMethod).filled(:str?, included_in?: %w[PAN_ONLY CRYPTOGRAM_3DS])
178
+
179
+ optional(:cryptogram).filled(:str?)
180
+ optional(:eciIndicator).filled(:str?)
181
+ end
182
+ rule(:pan).validate(:integer_string?, :pan?)
183
+ rule(:expirationMonth).validate(:month?)
184
+ rule(:expirationYear).validate(:year?)
185
+ rule(:eciIndicator).validate(:eci?)
186
+
187
+ rule(:cryptogram) do
188
+ key.failure('is missing') if 'CRYPTOGRAM_3DS'.eql?(values[:authMethod]) &&
189
+ values[:cryptogram].nil?
124
190
  end
125
191
 
126
- rule(eciIndicator: %i[authMethod eciIndicator]) do |method, eci|
127
- method.eql?('PAN_ONLY').then(eci.none?)
192
+ rule(:cryptogram) do
193
+ key.failure('cannot be defined') if 'PAN_ONLY'.eql?(values[:authMethod]) &&
194
+ !values[:cryptogram].nil?
128
195
  end
129
196
 
130
- rule(cryptogram: %i[authMethod cryptogram]) do |method, cryptogram|
131
- method.eql?('PAN_ONLY').then(cryptogram.none?)
197
+ rule(:eciIndicator) do
198
+ key.failure('cannot be defined') if 'PAN_ONLY'.eql?(values[:authMethod]) &&
199
+ !values[:eciIndicator].nil?
132
200
  end
133
201
  end
134
202
 
203
+ PaymentMethodDetailsSchema = PaymentMethodDetailsContract.new
204
+
135
205
  # DRY-Validation schema for encryptedMessage component Google Pay token
136
- EncryptedMessageSchema = Dry::Validation.Schema(BaseSchema) do
137
- required(:messageExpiration).filled(:str?, :integer_string?)
138
- required(:messageId).filled(:str?)
139
- required(:paymentMethod).filled(:str?, eql?: 'CARD')
140
- required(:paymentMethodDetails).value(:json_object?) { schema PaymentMethodDetailsSchema }
206
+ class EncryptedMessageContract < Dry::Validation::Contract
207
+ json do
208
+ required(:messageExpiration).filled(:str?)
209
+ required(:messageId).filled(:str?)
210
+ required(:paymentMethod).filled(:str?)
211
+ required(:paymentMethodDetails).filled(:hash).schema(PaymentMethodDetailsContract.schema)
212
+ end
213
+ rule(:messageExpiration).validate(:integer_string?)
214
+ rule(:paymentMethod) do
215
+ key.failure('must be equal to CARD') unless 'CARD'.eql?(value)
216
+ end
141
217
  end
142
218
 
219
+ EncryptedMessageSchema = EncryptedMessageContract.new
220
+
143
221
  module InstanceMethods
144
222
  attr_reader :output
145
223
 
146
224
  def validate
147
225
  @validation ||= @schema.call(@input)
148
- @output = @validation.output
226
+ @output = @validation.to_h
149
227
  return true if @validation.success?
150
228
  raise Aliquot::ValidationError, "validation error(s), #{errors_formatted}"
151
229
  end
@@ -176,7 +254,9 @@ module Aliquot
176
254
  # Class for validating a Google Pay token
177
255
  class Token
178
256
  include InstanceMethods
257
+
179
258
  class Error < ::Aliquot::Error; end
259
+
180
260
  def initialize(input)
181
261
  @input = input
182
262
  @schema = TokenSchema
@@ -186,7 +266,9 @@ module Aliquot
186
266
  # Class for validating the SignedMessage component of a Google Pay token
187
267
  class SignedMessage
188
268
  include InstanceMethods
269
+
189
270
  class Error < ::Aliquot::Error; end
271
+
190
272
  def initialize(input)
191
273
  @input = input
192
274
  @schema = SignedMessageSchema
@@ -196,7 +278,9 @@ module Aliquot
196
278
  # Class for validating the encryptedMessage component of a Google Pay token
197
279
  class EncryptedMessageValidator
198
280
  include InstanceMethods
281
+
199
282
  class Error < ::Aliquot::Error; end
283
+
200
284
  def initialize(input)
201
285
  @input = input
202
286
  @schema = EncryptedMessageSchema
@@ -205,7 +289,9 @@ module Aliquot
205
289
 
206
290
  class SignedKeyValidator
207
291
  include InstanceMethods
292
+
208
293
  class Error < ::Aliquot::Error; end
294
+
209
295
  def initialize(input)
210
296
  @input = input
211
297
  @schema = SignedKeySchema
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: aliquot
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 2.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clearhaus
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-18 00:00:00.000000000 Z
11
+ date: 2022-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-validation
@@ -16,56 +16,56 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '1.8'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '1.8'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: excon
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
33
+ version: 0.71.0
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: 0.71.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: hkdf
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '0.3'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '0.3'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: aliquot-pay
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 1.0.0
61
+ version: 2.1.1
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 1.0.0
68
+ version: 2.1.1
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
82
  version: '3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.14.1
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.14.1
83
97
  description:
84
98
  email: hello@clearhaus.com
85
99
  executables: []
@@ -100,16 +114,16 @@ require_paths:
100
114
  - lib
101
115
  required_ruby_version: !ruby/object:Gem::Requirement
102
116
  requirements:
103
- - - ">="
117
+ - - "~>"
104
118
  - !ruby/object:Gem::Version
105
- version: '0'
119
+ version: '2.7'
106
120
  required_rubygems_version: !ruby/object:Gem::Requirement
107
121
  requirements:
108
122
  - - ">="
109
123
  - !ruby/object:Gem::Version
110
124
  version: '0'
111
125
  requirements: []
112
- rubygems_version: 3.0.2
126
+ rubygems_version: 3.1.6
113
127
  signing_key:
114
128
  specification_version: 4
115
129
  summary: Validates Google Pay tokens