aliquot 2.3.0 → 3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2770596cedb5e18b0d1abd71b16081826a9f78668a8b04994913a31f21c593ca
4
- data.tar.gz: 3b4525df139a05d3a963fb06eae0881d833b686a45a6bf06707ad6ff44b98b0d
3
+ metadata.gz: f8e2a9a7e324874ed68aeaacd8a072ea00dedfce4e6022494f7333c94b55ec1d
4
+ data.tar.gz: 5bfc71f786436d227100470dc37a0d8715196b59d474d0b39b6dd3afa730ff61
5
5
  SHA512:
6
- metadata.gz: 5e50437ca7ad2733ca13f8bfb4448fba30b8187a01f304b213da5d6261b485af2ca8aff141031c3d7ab031d8a25f7b295e863c582fa9a8de7112865d0d9e8d5a
7
- data.tar.gz: 23af415ed4ac86e67932d26220f9cbbe5664f4966db378aefc4bd563c521a399fa4659930b3c7f44a698baf574be10b12f6606a5d80a1b1aa85deeb63d0ad2b5
6
+ metadata.gz: c95f8f7f99e9bbb9dfa7685927521e135b480e8a9e098c265771d592e1ebbd9c16e75be9b11b03cb369d0290e6fbb5c860be7cfad2252543b84950448011e13b
7
+ data.tar.gz: 9983ede008609ae095ce8dc863e361c7f3c46e449efe59c7d0422331284b9e18ec8b1182d14da9a71b5361266435c68d6fb125750165968d82ea755365ec9ff3
@@ -66,6 +66,9 @@ module Aliquot
66
66
 
67
67
  begin
68
68
  @message = JSON.parse(decrypt(aes_key, @signed_message[:encryptedMessage]))
69
+ @message['paymentMethodDetails'].merge!(
70
+ 'threedsCryptogram' => @message['paymentMethodDetails']
71
+ .delete('3dsCryptogram')) if @message['paymentMethodDetails']['3dsCryptogram']
69
72
  rescue JSON::JSONError => e
70
73
  raise InputError, "encryptedMessage JSON is invalid, #{e.message}"
71
74
  rescue => e
@@ -221,7 +224,20 @@ module Aliquot
221
224
  validator.validate
222
225
 
223
226
  # Output is hashed with symbolized keys.
224
- validator.output
227
+ message_hash = validator.output
228
+
229
+ payment_method_details_message = message_hash[:paymentMethodDetails]
230
+ message_details_validator =
231
+ if message_hash[:paymentMethod] == 'TOKENIZED_CARD' ||
232
+ message_hash[:paymentMethodDetails]['authMethod'] == 'CRYPTOGRAM_3DS'
233
+ Aliquot::Validator::PaymentMethodDetailsValidator.new(payment_method_details_message, protocol_version, true)
234
+ else
235
+ Aliquot::Validator::PaymentMethodDetailsValidator.new(payment_method_details_message, protocol_version, false)
236
+ end
237
+ message_details_validator.validate
238
+ message_hash[:paymentMethodDetails] = message_details_validator.output
239
+
240
+ message_hash
225
241
  end
226
242
 
227
243
  ##
@@ -21,8 +21,8 @@ module Aliquot
21
21
  base64_asn1?: 'must be base64-encoded ANS.1 value',
22
22
  json?: 'must be valid JSON',
23
23
 
24
- is_authMethodCryptogram3DS: 'authMethod CRYPTOGRAM_3DS requires eciIndicator',
25
- is_authMethodCard: 'eciIndicator/cryptogram must be omitted when PAN_ONLY',
24
+ is_authMethodCryptogram3DS: 'authMethod CRYPTOGRAM_3DS or 3DS requires eciIndicator',
25
+ is_authMethodCard: 'eciIndicator/cryptogram/3dsCryptogram must be omitted when PAN_ONLY',
26
26
  }.freeze
27
27
 
28
28
  def self.base64_check(value)
@@ -118,8 +118,6 @@ module Aliquot
118
118
  rule(:keyValue).validate(:ec_public_key?)
119
119
  end
120
120
 
121
- SignedKeySchema = SignedKeyContract.new
122
-
123
121
  # Schema used for the 'intermediateSigningKey' hash included in ECv2.
124
122
  class IntermediateSigningKeyContract < Dry::Validation::Contract
125
123
  json do
@@ -129,12 +127,10 @@ module Aliquot
129
127
  rule(:signedKey).validate(:json?)
130
128
  rule(:signatures).each do
131
129
  key.failure('must be Base64') unless Aliquot::Validator.base64_check(value) &&
132
- Aliquot::Validator.ans1_check(value)
130
+ Aliquot::Validator.ans1_check(value)
133
131
  end
134
132
  end
135
133
 
136
- IntermediateSigningKeySchema = IntermediateSigningKeyContract.new
137
-
138
134
  # DRY-Validation schema for Google Pay token
139
135
  class TokenContract < Dry::Validation::Contract
140
136
  json do
@@ -147,13 +143,11 @@ module Aliquot
147
143
  rule(:signedMessage).validate(:json?)
148
144
 
149
145
  rule(:intermediateSigningKey) do
150
- key.failure('is missing') if 'ECv2'.eql?(values[:protocolVersion]) &&
151
- values[:intermediateSigningKey].nil?
146
+ key.failure('is missing') if values[:protocolVersion] == 'ECv2' &&
147
+ values[:intermediateSigningKey].nil?
152
148
  end
153
149
  end
154
150
 
155
- TokenSchema = TokenContract.new
156
-
157
151
  # DRY-Validation schema for signedMessage component Google Pay token
158
152
  class SignedMessageContract < Dry::Validation::Contract
159
153
  json do
@@ -166,41 +160,55 @@ module Aliquot
166
160
  rule(:tag).validate(:base64?)
167
161
  end
168
162
 
169
- SignedMessageSchema = SignedMessageContract.new
170
-
171
- # DRY-Validation schema for paymentMethodDetails component Google Pay token
172
- class PaymentMethodDetailsContract < Dry::Validation::Contract
163
+ class CommonPaymentMethodDetailsContract < Dry::Validation::Contract
173
164
  json do
174
- required(:pan).filled(:str?)
175
165
  required(:expirationMonth).filled(:int?)
176
166
  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
167
  end
182
- rule(:pan).validate(:integer_string?, :pan?)
183
168
  rule(:expirationMonth).validate(:month?)
184
169
  rule(:expirationYear).validate(:year?)
185
- rule(:eciIndicator).validate(:eci?)
170
+ end
186
171
 
187
- rule(:cryptogram) do
188
- key.failure('is missing') if 'CRYPTOGRAM_3DS'.eql?(values[:authMethod]) &&
189
- values[:cryptogram].nil?
172
+ class ECv1_PaymentMethodDetailsContract < CommonPaymentMethodDetailsContract
173
+ json(CommonPaymentMethodDetailsContract.schema) do
174
+ required(:pan).filled(:str?)
190
175
  end
191
176
 
192
- rule(:cryptogram) do
193
- key.failure('cannot be defined') if 'PAN_ONLY'.eql?(values[:authMethod]) &&
194
- !values[:cryptogram].nil?
177
+ rule(:pan).validate(:integer_string?, :pan?)
178
+ end
179
+
180
+ class ECv1_TokenizedPaymentMethodDetailsContract < CommonPaymentMethodDetailsContract
181
+ json(CommonPaymentMethodDetailsContract.schema) do
182
+ required(:dpan).filled(:str?)
183
+ required(:threedsCryptogram).filled(:str?)
184
+ required(:eciIndicator).filled(:str?)
185
+ required(:authMethod).filled(:str?, included_in?: %w[3DS])
195
186
  end
196
187
 
197
- rule(:eciIndicator) do
198
- key.failure('cannot be defined') if 'PAN_ONLY'.eql?(values[:authMethod]) &&
199
- !values[:eciIndicator].nil?
188
+ rule(:dpan).validate(:integer_string?, :pan?)
189
+ rule(:eciIndicator).validate(:eci?)
190
+ end
191
+
192
+ class ECv2_PaymentMethodDetailsContract < CommonPaymentMethodDetailsContract
193
+ json(CommonPaymentMethodDetailsContract.schema) do
194
+ required(:pan).filled(:str?)
195
+ required(:authMethod).filled(:str?, included_in?: %w[PAN_ONLY])
200
196
  end
197
+
198
+ rule(:pan).validate(:integer_string?, :pan?)
201
199
  end
202
200
 
203
- PaymentMethodDetailsSchema = PaymentMethodDetailsContract.new
201
+ class ECv2_TokenizedPaymentMethodDetailsContract < CommonPaymentMethodDetailsContract
202
+ json(CommonPaymentMethodDetailsContract.schema) do
203
+ required(:pan).filled(:str?)
204
+ required(:cryptogram).filled(:str?)
205
+ required(:eciIndicator).filled(:str?)
206
+ required(:authMethod).filled(:str?, included_in?: %w[CRYPTOGRAM_3DS])
207
+ end
208
+
209
+ rule(:pan).validate(:integer_string?, :pan?)
210
+ rule(:eciIndicator).validate(:eci?)
211
+ end
204
212
 
205
213
  # DRY-Validation schema for encryptedMessage component Google Pay token
206
214
  class EncryptedMessageContract < Dry::Validation::Contract
@@ -208,17 +216,40 @@ module Aliquot
208
216
  required(:messageExpiration).filled(:str?)
209
217
  required(:messageId).filled(:str?)
210
218
  required(:paymentMethod).filled(:str?)
211
- required(:paymentMethodDetails).filled(:hash).schema(PaymentMethodDetailsContract.schema)
219
+ required(:paymentMethodDetails).filled(:hash)
212
220
  optional(:gatewayMerchantId).filled(:str?)
213
221
  end
214
222
  rule(:messageExpiration).validate(:integer_string?)
223
+
224
+ rule(:paymentMethodDetails).validate do
225
+ contract =
226
+ if values[:protocolVersion] == 'ECv1'
227
+ if values[:paymentMethod] == 'TOKENIZED_CARD'
228
+ ECv1_TokenizedPaymentMethodDetailsContract.new
229
+ else
230
+ ECv1_PaymentMethodDetailsContract.new
231
+ end
232
+ else
233
+ if values[:authMethod] == 'CRYPTOGRAM_3DS'
234
+ ECv2_TokenizedPaymentMethodDetailsContract.new
235
+ else
236
+ ECv2_PaymentMethodDetailsContract.new
237
+ end
238
+ end
239
+ contract.call(values[:paymentMethodDetails])
240
+ end
241
+
215
242
  rule(:paymentMethod) do
216
- key.failure('must be equal to CARD') unless 'CARD'.eql?(value)
243
+ if values[:paymentMethodDetails].is_a?(Hash)
244
+ if '3DS'.eql?(values[:paymentMethodDetails]['authMethod']) # Tokenized ECv1
245
+ key.failure('must be equal to TOKENIZED_CARD') unless value == 'TOKENIZED_CARD'
246
+ else
247
+ key.failure('must be equal to CARD') unless value == 'CARD'
248
+ end
249
+ end
217
250
  end
218
251
  end
219
252
 
220
- EncryptedMessageSchema = EncryptedMessageContract.new
221
-
222
253
  module InstanceMethods
223
254
  attr_reader :output
224
255
 
@@ -226,7 +257,7 @@ module Aliquot
226
257
  @validation ||= @schema.call(@input)
227
258
  @output = @validation.to_h
228
259
  return true if @validation.success?
229
- raise Aliquot::ValidationError, "validation error(s), #{errors_formatted}"
260
+ raise Aliquot::ValidationError, "validation error(s): #{error_list.join(', ')}"
230
261
  end
231
262
 
232
263
  def valid?
@@ -241,13 +272,12 @@ module Aliquot
241
272
  @validation.errors
242
273
  end
243
274
 
244
- def errors_formatted(node = [errors])
245
- node.pop.flat_map do |key, value|
246
- if value.is_a?(Array)
247
- value.map { |error| "#{(node + [key]).join('.')} #{error}" }
248
- else
249
- errors_formatted(node + [key, value])
250
- end
275
+ def error_list(node = errors.to_h, path = '')
276
+ if node.is_a?(Array)
277
+ node.map { |error| "#{path} #{error}" }
278
+ elsif node.is_a?(Hash)
279
+ path = "#{path}." unless path.empty?
280
+ node.flat_map { |key, sub_node| error_list(sub_node, "#{path}#{key}") }
251
281
  end
252
282
  end
253
283
  end
@@ -260,7 +290,7 @@ module Aliquot
260
290
 
261
291
  def initialize(input)
262
292
  @input = input
263
- @schema = TokenSchema
293
+ @schema = TokenContract.new
264
294
  end
265
295
  end
266
296
 
@@ -272,7 +302,7 @@ module Aliquot
272
302
 
273
303
  def initialize(input)
274
304
  @input = input
275
- @schema = SignedMessageSchema
305
+ @schema = SignedMessageContract.new
276
306
  end
277
307
  end
278
308
 
@@ -284,7 +314,32 @@ module Aliquot
284
314
 
285
315
  def initialize(input)
286
316
  @input = input
287
- @schema = EncryptedMessageSchema
317
+ @schema = EncryptedMessageContract.new
318
+ end
319
+ end
320
+
321
+ # Class for validating the encryptedMessage component of a Google Pay token
322
+ class PaymentMethodDetailsValidator
323
+ include InstanceMethods
324
+
325
+ class Error < ::Aliquot::Error; end
326
+
327
+ def initialize(input, version, tokenized)
328
+ @input = input
329
+ @schema =
330
+ if version == 'ECv1'
331
+ if tokenized
332
+ Aliquot::Validator::ECv1_TokenizedPaymentMethodDetailsContract.new
333
+ else
334
+ Aliquot::Validator::ECv1_PaymentMethodDetailsContract.new
335
+ end
336
+ else
337
+ if tokenized
338
+ Aliquot::Validator::ECv2_TokenizedPaymentMethodDetailsContract.new
339
+ else
340
+ Aliquot::Validator::ECv2_PaymentMethodDetailsContract.new
341
+ end
342
+ end
288
343
  end
289
344
  end
290
345
 
@@ -295,7 +350,7 @@ module Aliquot
295
350
 
296
351
  def initialize(input)
297
352
  @input = input
298
- @schema = SignedKeySchema
353
+ @schema = SignedKeyContract.new
299
354
  end
300
355
  end
301
356
  end
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: 2.3.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Clearhaus
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-05 00:00:00.000000000 Z
11
+ date: 2024-02-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dry-validation
@@ -58,14 +58,14 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '3.0'
61
+ version: '4.0'
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: '3.0'
68
+ version: '4.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -123,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  requirements: []
126
- rubygems_version: 3.3.7
126
+ rubygems_version: 3.1.6
127
127
  signing_key:
128
128
  specification_version: 4
129
129
  summary: Validates Google Pay tokens