Skip to content

Commit

Permalink
Fix specs
Browse files Browse the repository at this point in the history
  • Loading branch information
brorbw committed Jan 31, 2024
1 parent a1ca68d commit eda2cf4
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 20 deletions.
21 changes: 18 additions & 3 deletions lib/aliquot/payment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,16 @@ def process

begin
@message = JSON.parse(decrypt(aes_key, @signed_message[:encryptedMessage]))
@message.merge!('threedsCryptogram' => @message.delete('3dsCryptogram')) if @message['3dsCryptogram']
@message

@message["paymentMethodDetails"].transform_keys!('3dsCryptogram' => 'threedsCryptogram' ) if
@message['paymentMethodDetails']['3dsCryptogram']
rescue JSON::JSONError => e
raise InputError, "encryptedMessage JSON is invalid, #{e.message}"
rescue => e
raise DecryptionError, "decryption failed, #{e.message}"
end


@message = validate_message

raise TokenExpiredError, 'token is expired' if expired?
Expand Down Expand Up @@ -223,7 +225,20 @@ def validate_message
validator.validate

# Output is hashed with symbolized keys.
validator.output
message_hash = validator.output

payment_method_details_message = message_hash[:paymentMethodDetails]
message_details_validator =
if message_hash[:paymentMethod] == 'TOKENIZED_CARD' ||
message_hash[:paymentMethodDetails]['authMethod'] == 'CRYPTOGRAM_3DS'
Aliquot::Validator::PaymentMethodDetailsValidator.new(payment_method_details_message, protocol_version, true)
else
Aliquot::Validator::PaymentMethodDetailsValidator.new(payment_method_details_message, protocol_version, false)
end
message_details_validator.validate
message_hash[:paymentMethodDetails] = message_details_validator.output

message_hash
end

##
Expand Down
46 changes: 36 additions & 10 deletions lib/aliquot/validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -169,15 +169,15 @@ class CommonPaymentMethodDetailsContract < Dry::Validation::Contract
rule(:expirationYear).validate(:year?)
end

class ECv1_PaymentMethodDetailsContract < Dry::Validation::Contract
class ECv1_PaymentMethodDetailsContract < CommonPaymentMethodDetailsContract
json(CommonPaymentMethodDetailsContract.schema) do
required(:pan).filled(:str?)
end

rule(:pan).validate(:integer_string?, :pan?)
end

class ECv1_TokenizedPaymentMethodDetailsContract < Dry::Validation::Contract
class ECv1_TokenizedPaymentMethodDetailsContract < CommonPaymentMethodDetailsContract
json(CommonPaymentMethodDetailsContract.schema) do
required(:dpan).filled(:str?)
required(:threedsCryptogram).filled(:str?)
Expand All @@ -189,7 +189,7 @@ class ECv1_TokenizedPaymentMethodDetailsContract < Dry::Validation::Contract
rule(:eciIndicator).validate(:eci?)
end

class ECv2_PaymentMethodDetailsContract < Dry::Validation::Contract
class ECv2_PaymentMethodDetailsContract < CommonPaymentMethodDetailsContract
json(CommonPaymentMethodDetailsContract.schema) do
required(:pan).filled(:str?)
required(:authMethod).filled(:str?, included_in?: %w[PAN_ONLY])
Expand All @@ -198,7 +198,7 @@ class ECv2_PaymentMethodDetailsContract < Dry::Validation::Contract
rule(:pan).validate(:integer_string?, :pan?)
end

class ECv2_TokenizedPaymentMethodDetailsContract < Dry::Validation::Contract
class ECv2_TokenizedPaymentMethodDetailsContract < CommonPaymentMethodDetailsContract
json(CommonPaymentMethodDetailsContract.schema) do
required(:pan).filled(:str?)
required(:cryptogram).filled(:str?)
Expand All @@ -210,8 +210,6 @@ class ECv2_TokenizedPaymentMethodDetailsContract < Dry::Validation::Contract
rule(:eciIndicator).validate(:eci?)
end

# PaymentMethodDetailsSchema = PaymentMethodDetailsContract.new

# DRY-Validation schema for encryptedMessage component Google Pay token
class EncryptedMessageContract < Dry::Validation::Contract
json do
Expand All @@ -224,20 +222,23 @@ class EncryptedMessageContract < Dry::Validation::Contract
rule(:messageExpiration).validate(:integer_string?)

rule(:paymentMethodDetails).validate do
contract =
if values[:protocolVersion] == 'ECv1'
if values[:paymentMethod] == 'TOKENIZED_CARD'
return ECv1_TokenizedPaymentMethodDetailsContract.new
ECv1_TokenizedPaymentMethodDetailsContract.new
else
return ECv1_PaymentMethodDetailsContract.new
ECv1_PaymentMethodDetailsContract.new
end
else
if values[:authMethod] == 'CRYPTOGRAM_3DS'
return ECv2_TokenizedPaymentMethodDetailsContract.new
ECv2_TokenizedPaymentMethodDetailsContract.new
else
return ECv2_PaymentMethodDetailsContract.new
ECv2_PaymentMethodDetailsContract.new
end
end
contract.call(values[:paymentMethodDetails])
end

rule(:paymentMethod) do
if values[:paymentMethodDetails] && values[:paymentMethodDetails].is_a?(Hash)
if '3DS'.eql?(values[:paymentMethodDetails] && values[:paymentMethodDetails]['authMethod']) # Tokenized ECv1
Expand Down Expand Up @@ -317,6 +318,31 @@ def initialize(input)
end
end

# Class for validating the encryptedMessage component of a Google Pay token
class PaymentMethodDetailsValidator
include InstanceMethods

class Error < ::Aliquot::Error; end

def initialize(input, version, tokenized)
@input = input
@schema =
if version == 'ECv1'
if tokenized
Aliquot::Validator::ECv1_TokenizedPaymentMethodDetailsContract.new
else
Aliquot::Validator::ECv1_PaymentMethodDetailsContract.new
end
else
if tokenized
Aliquot::Validator::ECv2_TokenizedPaymentMethodDetailsContract.new
else
Aliquot::Validator::ECv2_PaymentMethodDetailsContract.new
end
end
end
end

class SignedKeyValidator
include InstanceMethods

Expand Down
17 changes: 10 additions & 7 deletions spec/lib/aliquot/payment_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@
end

describe Aliquot::Payment do
let(:generator) { AliquotPay.new(protocol_version: :ECv1, type: :browser) }
subject do
-> do Aliquot::Payment.new(generator.token.to_json,
generator.shared_secret,
Expand All @@ -111,22 +110,23 @@
context 'ECv1' do
context 'non-tokenized' do
let(:generator) { AliquotPay.new(protocol_version: :ECv1, type: :browser) }
let(:token) { generator.token }

include_examples 'all protocol versions'

it 'decrypts with PAN_ONLY' do
expect { subject.call }.to_not raise_error
expect(subject.call[:paymentMethodDetails]).to_not include('authMethod' => 'PAN_ONLY')
expect(subject.call[:paymentMethodDetails]).to_not include(:authMethod => 'PAN_ONLY')
end
end

context 'tokenized' do
let(:generator) { AliquotPay.new(protocol_version: :ECv1, type: :app) }
let(:token) { generator.token }

include_examples 'all protocol versions'

it 'decrypts with 3DS' do
expect { subject.call }.to_not raise_error
expect(subject.call[:paymentMethodDetails]).to include('authMethod' => '3DS')
expect(subject.call[:paymentMethodDetails]).to include(:authMethod => '3DS')
end
end
end
Expand All @@ -138,20 +138,23 @@

include_examples 'all protocol versions'
include_examples 'only ECv2'

it 'decrypts' do
expect { subject.call }.to_not raise_error
expect(subject.call[:paymentMethodDetails]).to include('authMethod' => 'PAN_ONLY')
expect(subject.call[:paymentMethodDetails]).to include(:authMethod => 'PAN_ONLY')
end
end

context 'tokenized' do
let(:generator) { AliquotPay.new(protocol_version: :ECv2, type: :app) }
let(:token) { generator.token }

include_examples 'all protocol versions'
include_examples 'only ECv2'

it 'decrypts' do
expect { subject.call }.to_not raise_error
expect(subject.call[:paymentMethodDetails]).to include('authMethod' => 'CRYPTOGRAM_3DS')
expect(subject.call[:paymentMethodDetails]).to include(:authMethod => 'CRYPTOGRAM_3DS')
end
end
end
Expand Down

0 comments on commit eda2cf4

Please sign in to comment.