adyen-ruby-api-library 4.0.0 → 4.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/adyen-ruby-api-library.rb +2 -0
- data/lib/adyen/client.rb +0 -1
- data/lib/adyen/hash_with_accessors.rb +38 -0
- data/lib/adyen/result.rb +2 -2
- data/lib/adyen/services/service.rb +10 -1
- data/lib/adyen/utils/hmac_validator.rb +48 -0
- data/lib/adyen/version.rb +2 -2
- data/spec/checkout_spec.rb +12 -4
- data/spec/checkout_utility_spec.rb +4 -2
- data/spec/hash_with_accessors_spec.rb +127 -0
- data/spec/service_spec.rb +42 -0
- data/spec/spec_helper.rb +7 -4
- data/spec/utils/hmac_validator_spec.rb +52 -0
- metadata +7 -3
- data/lib/adyen/util.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50ac4158ef06b200711ce1b69d7b6c12cad0df4b
|
4
|
+
data.tar.gz: 338fbfd9fed83cd6a3569dd1f706fdf439e259c1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c71dfcff2e34dd0244abdd56da08a0ab430b608de4fc095a472711a69269c416ffcee0b6021ed0db56bff3b8a5d1fc25e1fe13bac4f0c844367a61498ad6de1b
|
7
|
+
data.tar.gz: 9ea5ee2a69f6de0523ba4fff25975202583bdf3d1218a8cda8d0abe978b57e30158cccd4bb94921c928531ce40442df067089cdb74a066567a4f2cd1f526bcf5
|
@@ -8,6 +8,8 @@ require_relative "adyen/services/payouts"
|
|
8
8
|
require_relative "adyen/services/recurring"
|
9
9
|
require_relative "adyen/services/marketpay"
|
10
10
|
require_relative "adyen/services/service"
|
11
|
+
require_relative "adyen/hash_with_accessors"
|
12
|
+
require_relative "adyen/utils/hmac_validator"
|
11
13
|
|
12
14
|
# add snake case to camel case converter to String
|
13
15
|
# to convert rubinic method names to Adyen API methods
|
data/lib/adyen/client.rb
CHANGED
@@ -0,0 +1,38 @@
|
|
1
|
+
# This utility method inherits from Hash, but allows keys to be read
|
2
|
+
# and updated with dot notation. Usage is entirely optional (i.e., hash values
|
3
|
+
# can still be accessed via symbol and string keys).
|
4
|
+
#
|
5
|
+
# Based on: https://gist.github.com/winfred/2185384#file-ruby-dot-hash-access-rb
|
6
|
+
module Adyen
|
7
|
+
class HashWithAccessors < Hash
|
8
|
+
def method_missing(method, *args)
|
9
|
+
string_key = method.to_s.sub(/=\z/, '')
|
10
|
+
sym_key = string_key.to_sym
|
11
|
+
|
12
|
+
key = if has_key?(string_key)
|
13
|
+
string_key
|
14
|
+
elsif has_key?(sym_key)
|
15
|
+
sym_key
|
16
|
+
end
|
17
|
+
|
18
|
+
return super unless key
|
19
|
+
|
20
|
+
assignment = sym_key != method
|
21
|
+
|
22
|
+
if assignment
|
23
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 1)" unless args.size == 1
|
24
|
+
|
25
|
+
self[key] = args.first
|
26
|
+
else
|
27
|
+
raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0)" unless args.size == 0
|
28
|
+
|
29
|
+
self[key]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def respond_to_missing?(method, include_private = false)
|
34
|
+
string_key = method.to_s.sub(/=\z/, '')
|
35
|
+
has_key?(string_key) || has_key?(string_key.to_sym) || super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/adyen/result.rb
CHANGED
@@ -5,11 +5,11 @@ module Adyen
|
|
5
5
|
attr_reader :response, :header, :status
|
6
6
|
|
7
7
|
def initialize(response, header, status)
|
8
|
-
@response = JSON.parse(response)
|
8
|
+
@response = JSON.parse(response, object_class: HashWithAccessors)
|
9
9
|
|
10
10
|
# `header` in Faraday response is not a JSON string, but rather a
|
11
11
|
# Faraday `Headers` object. Convert first before parsing
|
12
|
-
@header = JSON.parse(header.to_json)
|
12
|
+
@header = JSON.parse(header.to_json, object_class: HashWithAccessors)
|
13
13
|
@status = status
|
14
14
|
end
|
15
15
|
end
|
@@ -2,6 +2,15 @@ module Adyen
|
|
2
2
|
class Service
|
3
3
|
attr_accessor :service, :version
|
4
4
|
|
5
|
+
# add snake case to camel case converter to String
|
6
|
+
# to convert rubinic method names to Adyen API methods
|
7
|
+
#
|
8
|
+
# i.e. snake_case -> snakeCase
|
9
|
+
# note that the first letter is not capitalized as normal
|
10
|
+
def self.action_for_method_name(method_name)
|
11
|
+
method_name.to_s.gsub(/_./) { |x| x[1].upcase }
|
12
|
+
end
|
13
|
+
|
5
14
|
def initialize(client, version, service, method_names)
|
6
15
|
@client = client
|
7
16
|
@version = version
|
@@ -10,7 +19,7 @@ module Adyen
|
|
10
19
|
# dynamically create API methods
|
11
20
|
method_names.each do |method_name|
|
12
21
|
define_singleton_method method_name do |request, headers = {}|
|
13
|
-
action =
|
22
|
+
action = self.class.action_for_method_name(method_name)
|
14
23
|
@client.call_adyen_api(@service, action, request, headers, @version)
|
15
24
|
end
|
16
25
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Adyen
|
2
|
+
module Utils
|
3
|
+
class HmacValidator
|
4
|
+
HMAC_ALGORITHM = 'sha256'.freeze
|
5
|
+
DATA_SEPARATOR = ':'.freeze
|
6
|
+
NOTIFICATION_VALIDATION_KEYS = %w[
|
7
|
+
pspReference originalReference merchantAccountCode merchantReference
|
8
|
+
amount.value amount.currency eventCode success
|
9
|
+
].freeze
|
10
|
+
|
11
|
+
def valid_notification_hmac?(notification_request_item, hmac_key)
|
12
|
+
expected_sign = calculate_notification_hmac(notification_request_item, hmac_key)
|
13
|
+
merchant_sign = fetch(notification_request_item, 'additionalData.hmacSignature')
|
14
|
+
|
15
|
+
expected_sign == merchant_sign
|
16
|
+
end
|
17
|
+
|
18
|
+
def calculate_notification_hmac(notification_request_item, hmac_key)
|
19
|
+
data = data_to_sign(notification_request_item)
|
20
|
+
|
21
|
+
Base64.strict_encode64(OpenSSL::HMAC.digest(HMAC_ALGORITHM, [hmac_key].pack('H*'), data))
|
22
|
+
end
|
23
|
+
|
24
|
+
def data_to_sign(notification_request_item)
|
25
|
+
NOTIFICATION_VALIDATION_KEYS.map { |key| fetch(notification_request_item, key).to_s }
|
26
|
+
.map { |value| value.gsub('\\', '\\\\').gsub(':', '\\:') }
|
27
|
+
.join(DATA_SEPARATOR)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def fetch(hash, keys)
|
33
|
+
value = hash
|
34
|
+
|
35
|
+
keys.to_s.split('.').each do |key|
|
36
|
+
value = if key.to_i.to_s == key
|
37
|
+
value[key.to_i]
|
38
|
+
else
|
39
|
+
value[key].nil? ? value[key.to_sym] : value[key]
|
40
|
+
end
|
41
|
+
break if value.nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
value
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/adyen/version.rb
CHANGED
data/spec/checkout_spec.rb
CHANGED
@@ -57,10 +57,14 @@ RSpec.describe Adyen::Checkout, service: "checkout" do
|
|
57
57
|
to eq(200)
|
58
58
|
expect(response_hash).
|
59
59
|
to eq(JSON.parse(response_body))
|
60
|
-
expect(response_hash
|
61
|
-
to
|
60
|
+
expect(response_hash).
|
61
|
+
to be_a Adyen::HashWithAccessors
|
62
|
+
expect(response_hash).
|
63
|
+
to be_a_kind_of Hash
|
62
64
|
expect(response_hash["resultCode"]).
|
63
65
|
to eq("RedirectShopper")
|
66
|
+
expect(response_hash.resultCode).
|
67
|
+
to eq("RedirectShopper")
|
64
68
|
end
|
65
69
|
|
66
70
|
# must be created manually due to payments/result format
|
@@ -89,10 +93,14 @@ RSpec.describe Adyen::Checkout, service: "checkout" do
|
|
89
93
|
to eq(200)
|
90
94
|
expect(response_hash).
|
91
95
|
to eq(JSON.parse(response_body))
|
92
|
-
expect(response_hash
|
93
|
-
to
|
96
|
+
expect(response_hash).
|
97
|
+
to be_a Adyen::HashWithAccessors
|
98
|
+
expect(response_hash).
|
99
|
+
to be_a_kind_of Hash
|
94
100
|
expect(response_hash["resultCode"]).
|
95
101
|
to eq("Authorised")
|
102
|
+
expect(response_hash.resultCode).
|
103
|
+
to eq("Authorised")
|
96
104
|
end
|
97
105
|
|
98
106
|
# create client for automated tests
|
@@ -19,8 +19,10 @@ RSpec.describe Adyen::CheckoutUtility, service: "checkout utility" do
|
|
19
19
|
# must be created manually because every field in the response is an array
|
20
20
|
it "makes an origin_keys call" do
|
21
21
|
parsed_body = create_test(@shared_values[:client], @shared_values[:service], "origin_keys", @shared_values[:client].checkout_utility)
|
22
|
-
expect(parsed_body["originKeys"]
|
23
|
-
to
|
22
|
+
expect(parsed_body["originKeys"]).
|
23
|
+
to be_a Adyen::HashWithAccessors
|
24
|
+
expect(parsed_body["originKeys"]).
|
25
|
+
to be_a_kind_of Hash
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Adyen::HashWithAccessors do
|
4
|
+
shared_examples :hash_with_accessors do
|
5
|
+
subject do
|
6
|
+
h = described_class.new
|
7
|
+
h[key] = 1
|
8
|
+
h
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'returns values of a hashes' do
|
12
|
+
expect(subject.arbitrary_accessor).to be 1
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'can assign existing values' do
|
16
|
+
subject.arbitrary_accessor = 2
|
17
|
+
expect(subject.arbitrary_accessor).to be 2
|
18
|
+
expect(subject[key]).to be 2
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'complains if there are arguments for the accessor' do
|
22
|
+
expect { subject.arbitrary_accessor(1) }.to raise_error(ArgumentError, 'wrong number of arguments (given 1, expected 0)')
|
23
|
+
expect { subject.arbitrary_accessor(1, 2) }.to raise_error(ArgumentError, 'wrong number of arguments (given 2, expected 0)')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'complains if there are arguments for the accessor =' do
|
27
|
+
# using send because i'm not sure how to do this wrong with normal ruby setter calling.
|
28
|
+
# just here for completeness
|
29
|
+
expect { subject.send(:arbitrary_accessor=) }.to raise_error(ArgumentError, 'wrong number of arguments (given 0, expected 1)')
|
30
|
+
expect { subject.send(:arbitrary_accessor=, 1, 2) }.to raise_error(ArgumentError, 'wrong number of arguments (given 2, expected 1)')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'responds to the accessor' do
|
34
|
+
expect(subject).to respond_to(:arbitrary_accessor)
|
35
|
+
expect(subject).to respond_to(:arbitrary_accessor=)
|
36
|
+
expect(subject).to_not respond_to(:another_accessor)
|
37
|
+
expect(subject).to_not respond_to(:another_accessor=)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "raises when the key doesn't exist" do
|
41
|
+
expect { subject.another_accessor }.to raise_error(NoMethodError)
|
42
|
+
expect { subject.another_accessor = 1 }.to raise_error(NoMethodError)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with a string key' do
|
47
|
+
let(:key) { 'arbitrary_accessor' }
|
48
|
+
|
49
|
+
it_behaves_like :hash_with_accessors
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'with a symbol key' do
|
53
|
+
let(:key) { :arbitrary_accessor }
|
54
|
+
|
55
|
+
it_behaves_like :hash_with_accessors
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'with a conflicting key' do
|
59
|
+
subject do
|
60
|
+
h = described_class.new
|
61
|
+
h['keys'] = 'not the keys'
|
62
|
+
h
|
63
|
+
end
|
64
|
+
|
65
|
+
it "does original thing if there'd be a conflict" do
|
66
|
+
expect(subject.keys).to eq ['keys'] # the default behaviour
|
67
|
+
expect(subject['keys']).to eq 'not the keys'
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'still does the writer thing even if the reader is defined' do
|
71
|
+
subject.keys = 'written keys'
|
72
|
+
expect(subject['keys']).to eq 'written keys'
|
73
|
+
expect(subject.keys).to eq ['keys']
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'with some other method missing defined' do
|
78
|
+
# this test setup is kind of janky,
|
79
|
+
# but we want to confirm super is set up correctly
|
80
|
+
# We could do a lot more house-keeping if we weren't sure Hash doesn't
|
81
|
+
# define its own method_missing and respond_to_missing? by default,
|
82
|
+
# and there was any particular reason to clean up properly and remove our
|
83
|
+
# called_super method from Hash.
|
84
|
+
|
85
|
+
before(:all) do
|
86
|
+
class Hash
|
87
|
+
def called_super(*args)
|
88
|
+
end
|
89
|
+
|
90
|
+
def method_missing(*args)
|
91
|
+
called_super(:method_missing, *args)
|
92
|
+
super
|
93
|
+
end
|
94
|
+
|
95
|
+
def respond_to_missing?(*args)
|
96
|
+
called_super(:respond_to_missing?, *args)
|
97
|
+
super
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
subject do
|
103
|
+
h = described_class.new
|
104
|
+
h[:my_accessor] = 1
|
105
|
+
h
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'can fall back to another respond_to_missing?' do
|
109
|
+
expect(subject).to_not receive(:called_super).with(:respond_to_missing?, :my_accessor, false)
|
110
|
+
expect(subject).to respond_to(:my_accessor)
|
111
|
+
expect(subject).to receive(:called_super).with(:respond_to_missing?, :literally_anything, false)
|
112
|
+
expect(subject.respond_to?(:literally_anything)).to be false
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'can fall back to another method_missing' do
|
116
|
+
expect(subject).to_not receive(:called_super).with(:method_missing, :my_accessor)
|
117
|
+
expect(subject.my_accessor).to be 1
|
118
|
+
expect(subject).to receive(:called_super).with(:method_missing, :something_else)
|
119
|
+
expect { subject.something_else }.to raise_error(NoMethodError)
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
|
124
|
+
it "doesn't modify all hashes" do
|
125
|
+
expect { {a: 1}.a }.to raise_error(NoMethodError)
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Adyen::Service do
|
4
|
+
describe '.action_for_method_name' do
|
5
|
+
it 'handles all methods that exist currently' do
|
6
|
+
expect(described_class.action_for_method_name(:adjust_authorisation)).to eq 'adjustAuthorisation'
|
7
|
+
expect(described_class.action_for_method_name(:authorise)).to eq 'authorise'
|
8
|
+
expect(described_class.action_for_method_name(:authorise3d)).to eq 'authorise3d'
|
9
|
+
expect(described_class.action_for_method_name(:authorise3ds2)).to eq 'authorise3ds2'
|
10
|
+
expect(described_class.action_for_method_name(:cancel)).to eq 'cancel'
|
11
|
+
expect(described_class.action_for_method_name(:cancel_or_refund)).to eq 'cancelOrRefund'
|
12
|
+
expect(described_class.action_for_method_name(:capture)).to eq 'capture'
|
13
|
+
expect(described_class.action_for_method_name(:close_account)).to eq 'closeAccount'
|
14
|
+
expect(described_class.action_for_method_name(:close_account_holder)).to eq 'closeAccountHolder'
|
15
|
+
expect(described_class.action_for_method_name(:confirm_third_party)).to eq 'confirmThirdParty'
|
16
|
+
expect(described_class.action_for_method_name(:create_account)).to eq 'createAccount'
|
17
|
+
expect(described_class.action_for_method_name(:create_account_holder)).to eq 'createAccountHolder'
|
18
|
+
expect(described_class.action_for_method_name(:decline_third_party)).to eq 'declineThirdParty'
|
19
|
+
expect(described_class.action_for_method_name(:delete_bank_accounts)).to eq 'deleteBankAccounts'
|
20
|
+
expect(described_class.action_for_method_name(:delete_shareholders)).to eq 'deleteShareholders'
|
21
|
+
expect(described_class.action_for_method_name(:disable)).to eq 'disable'
|
22
|
+
expect(described_class.action_for_method_name(:get_account_holder)).to eq 'getAccountHolder'
|
23
|
+
expect(described_class.action_for_method_name(:get_tier_configuration)).to eq 'getTierConfiguration'
|
24
|
+
expect(described_class.action_for_method_name(:get_uploaded_documents)).to eq 'getUploadedDocuments'
|
25
|
+
expect(described_class.action_for_method_name(:list_recurring_details)).to eq 'listRecurringDetails'
|
26
|
+
expect(described_class.action_for_method_name(:origin_keys)).to eq 'originKeys'
|
27
|
+
expect(described_class.action_for_method_name(:payment_methods)).to eq 'paymentMethods'
|
28
|
+
expect(described_class.action_for_method_name(:payment_session)).to eq 'paymentSession'
|
29
|
+
expect(described_class.action_for_method_name(:refund)).to eq 'refund'
|
30
|
+
expect(described_class.action_for_method_name(:store_detail)).to eq 'storeDetail'
|
31
|
+
expect(described_class.action_for_method_name(:store_detail_and_submit_third_party)).to eq 'storeDetailAndSubmitThirdParty'
|
32
|
+
expect(described_class.action_for_method_name(:store_token)).to eq 'storeToken'
|
33
|
+
expect(described_class.action_for_method_name(:submit_third_party)).to eq 'submitThirdParty'
|
34
|
+
expect(described_class.action_for_method_name(:suspend_account_holder)).to eq 'suspendAccountHolder'
|
35
|
+
expect(described_class.action_for_method_name(:un_suspend_account_holder)).to eq 'unSuspendAccountHolder'
|
36
|
+
expect(described_class.action_for_method_name(:update_account)).to eq 'updateAccount'
|
37
|
+
expect(described_class.action_for_method_name(:update_account_holder)).to eq 'updateAccountHolder'
|
38
|
+
expect(described_class.action_for_method_name(:update_account_holder_state)).to eq 'updateAccountHolderState'
|
39
|
+
expect(described_class.action_for_method_name(:upload_document)).to eq 'uploadDocument'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -39,7 +39,8 @@ def create_test(client, service, method_name, parent_object)
|
|
39
39
|
end
|
40
40
|
|
41
41
|
# stub request
|
42
|
-
|
42
|
+
action = Adyen::Service.action_for_method_name(method_name)
|
43
|
+
url = client.service_url(service, action, parent_object.version)
|
43
44
|
WebMock.stub_request(:post, url).
|
44
45
|
with(
|
45
46
|
body: request_body,
|
@@ -50,7 +51,7 @@ def create_test(client, service, method_name, parent_object)
|
|
50
51
|
)
|
51
52
|
result = parent_object.public_send(method_name, request_body)
|
52
53
|
|
53
|
-
# result.response is already a Ruby
|
54
|
+
# result.response is already a Ruby object (Adyen::HashWithAccessors) (rather than an unparsed JSON string)
|
54
55
|
response_hash = result.response
|
55
56
|
|
56
57
|
# boilerplate error checks
|
@@ -58,8 +59,10 @@ def create_test(client, service, method_name, parent_object)
|
|
58
59
|
to eq(200)
|
59
60
|
expect(response_hash).
|
60
61
|
to eq(JSON.parse(response_body))
|
61
|
-
expect(response_hash
|
62
|
-
to
|
62
|
+
expect(response_hash).
|
63
|
+
to be_a Adyen::HashWithAccessors
|
64
|
+
expect(response_hash).
|
65
|
+
to be_a_kind_of Hash
|
63
66
|
|
64
67
|
response_hash
|
65
68
|
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Adyen::Utils::HmacValidator do
|
4
|
+
let(:validator) { described_class.new }
|
5
|
+
let(:key) { '44782DEF547AAA06C910C43932B1EB0C71FC68D9D0C057550C48EC2ACF6BA056' }
|
6
|
+
let(:expected_sign) { 'coqCmt/IZ4E3CzPvMY8zTjQVL5hYJUiBRg8UU+iCWo0=' }
|
7
|
+
let(:notification_request_item) do
|
8
|
+
{
|
9
|
+
additionalData: {
|
10
|
+
hmacSignature: expected_sign
|
11
|
+
},
|
12
|
+
amount: {
|
13
|
+
value: 1130,
|
14
|
+
currency: 'EUR'
|
15
|
+
},
|
16
|
+
pspReference: '7914073381342284',
|
17
|
+
eventCode: 'AUTHORISATION',
|
18
|
+
merchantAccountCode: 'TestMerchant',
|
19
|
+
merchantReference: 'TestPayment-1407325143704',
|
20
|
+
paymentMethod: 'visa',
|
21
|
+
success: 'true'
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'HMAC Validator' do
|
26
|
+
it 'should get correct data' do
|
27
|
+
data_to_sign = validator.data_to_sign(notification_request_item)
|
28
|
+
expect(data_to_sign).to eq '7914073381342284::TestMerchant:TestPayment-1407325143704:1130:EUR:AUTHORISATION:true'
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should get correct data with escaped characters' do
|
32
|
+
notification_request_item['merchantAccountCode'] = 'Test:\\Merchant'
|
33
|
+
data_to_sign = validator.data_to_sign(notification_request_item)
|
34
|
+
expect(data_to_sign).to eq '7914073381342284::Test\\:\\Merchant:TestPayment-1407325143704:1130:EUR:AUTHORISATION:true'
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should encrypt properly' do
|
38
|
+
encrypted = validator.calculate_notification_hmac(notification_request_item, key)
|
39
|
+
expect(encrypted).to eq expected_sign
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should have a valid hmac' do
|
43
|
+
expect(validator.valid_notification_hmac?(notification_request_item, key)).to be true
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should have an invalid hmac' do
|
47
|
+
notification_request_item['additionalData'] = { 'hmacSignature' => 'invalidHMACsign' }
|
48
|
+
|
49
|
+
expect(validator.valid_notification_hmac?(notification_request_item, key)).to be false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: adyen-ruby-api-library
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.
|
4
|
+
version: 4.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adyen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -103,6 +103,7 @@ files:
|
|
103
103
|
- lib/adyen-ruby-api-library.rb
|
104
104
|
- lib/adyen/client.rb
|
105
105
|
- lib/adyen/errors.rb
|
106
|
+
- lib/adyen/hash_with_accessors.rb
|
106
107
|
- lib/adyen/result.rb
|
107
108
|
- lib/adyen/services/checkout.rb
|
108
109
|
- lib/adyen/services/checkout_utility.rb
|
@@ -111,7 +112,7 @@ files:
|
|
111
112
|
- lib/adyen/services/payouts.rb
|
112
113
|
- lib/adyen/services/recurring.rb
|
113
114
|
- lib/adyen/services/service.rb
|
114
|
-
- lib/adyen/
|
115
|
+
- lib/adyen/utils/hmac_validator.rb
|
115
116
|
- lib/adyen/version.rb
|
116
117
|
- spec/account_spec.rb
|
117
118
|
- spec/checkout_spec.rb
|
@@ -119,6 +120,7 @@ files:
|
|
119
120
|
- spec/client_spec.rb
|
120
121
|
- spec/errors_spec.rb
|
121
122
|
- spec/fund_spec.rb
|
123
|
+
- spec/hash_with_accessors_spec.rb
|
122
124
|
- spec/mocks/requests/Account/close_account.json
|
123
125
|
- spec/mocks/requests/Account/close_account_holder.json
|
124
126
|
- spec/mocks/requests/Account/create_account.json
|
@@ -221,7 +223,9 @@ files:
|
|
221
223
|
- spec/payments_spec.rb
|
222
224
|
- spec/payouts_spec.rb
|
223
225
|
- spec/recurring_spec.rb
|
226
|
+
- spec/service_spec.rb
|
224
227
|
- spec/spec_helper.rb
|
228
|
+
- spec/utils/hmac_validator_spec.rb
|
225
229
|
homepage: https://www.adyen.com
|
226
230
|
licenses:
|
227
231
|
- MIT
|
data/lib/adyen/util.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
# This utility method monkey-patches Ruby's Hash class to allow keys to be read
|
2
|
-
# and updated with dot notation. Usage is entirely optional (i.e., hash values
|
3
|
-
# can still be accessed via symbol and string keys).
|
4
|
-
#
|
5
|
-
# Credit: https://gist.github.com/winfred/2185384#file-ruby-dot-hash-access-rb
|
6
|
-
|
7
|
-
class Hash
|
8
|
-
class NoKeyOrMethodError < NoMethodError; end
|
9
|
-
def method_missing(method,*args)
|
10
|
-
m = method.to_s
|
11
|
-
string_key = m.gsub(/=$/,'')
|
12
|
-
sym_key = string_key.to_sym
|
13
|
-
if self.has_key? string_key
|
14
|
-
m.match(/=$/) ? self.send("[#{string_key}]=", *args) : self[string_key]
|
15
|
-
elsif self.has_key? sym_key
|
16
|
-
m.match(/=$/) ? self.send("[#{sym_key}]=", *args) : self[sym_key]
|
17
|
-
else
|
18
|
-
raise NoKeyOrMethodError
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|