adyen-ruby-api-library 4.0.0 → 4.0.1
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 +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
|