adyen-ruby-api-library 6.1.0 → 6.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/.github/CODEOWNERS +1 -1
  3. data/.github/workflows/codeql.yml +41 -0
  4. data/.github/workflows/ruby.yml +1 -1
  5. data/.github/workflows/rubygems_release.yml +19 -0
  6. data/lib/adyen/client.rb +20 -7
  7. data/lib/adyen/errors.rb +2 -2
  8. data/lib/adyen/services/checkout.rb +80 -6
  9. data/lib/adyen/services/service.rb +5 -0
  10. data/lib/adyen/utils/hmac_validator.rb +2 -3
  11. data/lib/adyen/version.rb +2 -2
  12. data/spec/checkout_spec.rb +242 -0
  13. data/spec/client_spec.rb +27 -0
  14. data/spec/errors_spec.rb +1 -1
  15. data/spec/mocks/requests/Checkout/amount_updates.json +22 -0
  16. data/spec/mocks/requests/Checkout/capture.json +34 -0
  17. data/spec/mocks/requests/Checkout/generic_cancel.json +5 -0
  18. data/spec/mocks/requests/Checkout/modifications_request.json +0 -0
  19. data/spec/mocks/requests/Checkout/psp_cancel.json +4 -0
  20. data/spec/mocks/requests/Checkout/refund.json +34 -0
  21. data/spec/mocks/responses/Checkout/amount_updates.json +24 -0
  22. data/spec/mocks/responses/Checkout/capture.json +37 -0
  23. data/spec/mocks/responses/Checkout/generic_cancel.json +7 -0
  24. data/spec/mocks/responses/Checkout/modifications.json +0 -0
  25. data/spec/mocks/responses/Checkout/psp_cancel.json +7 -0
  26. data/spec/mocks/responses/Checkout/refund.json +37 -0
  27. data/spec/mocks/responses/Checkout/stored_payment_methods.json +1 -0
  28. data/spec/mocks/responses/Webhooks/backslash_notification.json +41 -0
  29. data/spec/mocks/responses/Webhooks/colon_notification.json +41 -0
  30. data/spec/mocks/responses/Webhooks/forwardslash_notification.json +41 -0
  31. data/spec/mocks/responses/Webhooks/mixed_notification.json +41 -0
  32. data/spec/utils/hmac_validator_spec.rb +20 -6
  33. metadata +21 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4b2156b9db20964cb7f43be56c19a462ccc091e42fdcecd887c9da04dc880116
4
- data.tar.gz: 47cb2a027baa58061a6eb46e5c703ff7bc18b05002e5a7c0c5b9d9e9d68616fd
3
+ metadata.gz: 45627b45fc5df26d004c1a86d7640412d615383ccac35201f24f0e3ebed99394
4
+ data.tar.gz: 72a067ba4b95419ed87e526a371db2377d5b1af7056f9ac035a6e11327715da0
5
5
  SHA512:
6
- metadata.gz: a1e4c4a2fd0b91b3e375a66a59910548a9e272beb8940e2ecf6432593e2c14494006d7d6387020245fea48af0f24b50b7803d135db654294e9079c842f818390
7
- data.tar.gz: de54e9a6c6da6a49b922d26cb6197651f59f145fef43412bfdf50863097f287d8139dd1647e6efd585d1c08d8dbffaca845f16e0e46ec49334e323f73ade74d1
6
+ metadata.gz: a893d921b3236805bb89b6ca5fc1c553888d010c84c1927689f6bc47e1a78b23302c081edbc979d596ac70c0b6f23cad61b9a83cc11c6f8b22e1d73520951bdb
7
+ data.tar.gz: 3cc14ff3d1550ffb11f6ec08c37ae7858b5e39e1b14023e6323e3a132441b59ee383e39ca6db6dcb83eff24e4b9ae937bff416bc9684e1ba6cb8ba7b20e37405
data/.github/CODEOWNERS CHANGED
@@ -1 +1 @@
1
- * @crrood @wboereboom @AlexandrosMor @michaelpaul
1
+ * @Adyen/api-libraries-reviewers
@@ -0,0 +1,41 @@
1
+ name: "CodeQL"
2
+
3
+ on:
4
+ push:
5
+ branches: [ "develop", "main" ]
6
+ pull_request:
7
+ branches: [ "develop" ]
8
+ schedule:
9
+ - cron: "40 12 * * 0"
10
+
11
+ jobs:
12
+ analyze:
13
+ name: Analyze
14
+ runs-on: ubuntu-latest
15
+ permissions:
16
+ actions: read
17
+ contents: read
18
+ security-events: write
19
+
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ language: [ javascript ]
24
+
25
+ steps:
26
+ - name: Checkout
27
+ uses: actions/checkout@v3
28
+
29
+ - name: Initialize CodeQL
30
+ uses: github/codeql-action/init@v2
31
+ with:
32
+ languages: ${{ matrix.language }}
33
+ queries: +security-and-quality
34
+
35
+ - name: Autobuild
36
+ uses: github/codeql-action/autobuild@v2
37
+
38
+ - name: Perform CodeQL Analysis
39
+ uses: github/codeql-action/analyze@v2
40
+ with:
41
+ category: "/language:${{ matrix.language }}"
@@ -12,7 +12,7 @@ jobs:
12
12
  ruby: [2.5, 2.6, 2.7, '3.0', head]
13
13
  runs-on: ${{ matrix.os }}
14
14
  steps:
15
- - uses: actions/checkout@v2
15
+ - uses: actions/checkout@v3
16
16
  - uses: ruby/setup-ruby@v1
17
17
  with:
18
18
  ruby-version: ${{ matrix.ruby }}
@@ -0,0 +1,19 @@
1
+ name: Publish Gem
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ build:
9
+ runs-on: ubuntu-latest
10
+
11
+ steps:
12
+ - uses: actions/checkout@v3
13
+
14
+ - name: Release Gem on RubyGems
15
+ if: contains(github.ref, 'refs/tags/v')
16
+ uses: cadwallion/publish-rubygems-action@master
17
+ env:
18
+ GITHUB_TOKEN: ${{secrets.TOKEN_RUBYGEMS_RELEASES_WITH_EXPIRATION}}
19
+ RUBYGEMS_API_KEY: ${{secrets.RUBYGEMS_API_KEY}}
data/lib/adyen/client.rb CHANGED
@@ -6,9 +6,9 @@ require_relative "./result"
6
6
  module Adyen
7
7
  class Client
8
8
  attr_accessor :ws_user, :ws_password, :api_key, :client, :adapter, :live_url_prefix
9
- attr_reader :env
9
+ attr_reader :env, :connection_options
10
10
 
11
- def initialize(ws_user: nil, ws_password: nil, api_key: nil, env: :live, adapter: nil, mock_port: 3001, live_url_prefix: nil, mock_service_url_base: nil)
11
+ def initialize(ws_user: nil, ws_password: nil, api_key: nil, env: :live, adapter: nil, mock_port: 3001, live_url_prefix: nil, mock_service_url_base: nil, connection_options: nil)
12
12
  @ws_user = ws_user
13
13
  @ws_password = ws_password
14
14
  @api_key = api_key
@@ -16,6 +16,7 @@ module Adyen
16
16
  @adapter = adapter || Faraday.default_adapter
17
17
  @mock_service_url_base = mock_service_url_base || "http://localhost:#{mock_port}"
18
18
  @live_url_prefix = live_url_prefix
19
+ @connection_options = connection_options || Faraday::ConnectionOptions.new
19
20
  end
20
21
 
21
22
  # make sure that env can only be :live, :test, or :mock
@@ -96,7 +97,7 @@ module Adyen
96
97
  end
97
98
 
98
99
  # initialize Faraday connection object
99
- conn = Faraday.new(url: url) do |faraday|
100
+ conn = Faraday.new(url, @connection_options) do |faraday|
100
101
  faraday.adapter @adapter
101
102
  faraday.headers["Content-Type"] = "application/json"
102
103
  faraday.headers["User-Agent"] = Adyen::NAME + "/" + Adyen::VERSION
@@ -143,6 +144,13 @@ module Adyen
143
144
  raise connection_error, "Connection to #{url} failed"
144
145
  end
145
146
  end
147
+ if action.fetch(:method) == "delete"
148
+ begin
149
+ response = conn.delete
150
+ rescue Faraday::ConnectionFailed => connection_error
151
+ raise connection_error, "Connection to #{url} failed"
152
+ end
153
+ end
146
154
  if action.fetch(:method) == "patch"
147
155
  begin
148
156
  response = conn.patch do |req|
@@ -168,11 +176,16 @@ module Adyen
168
176
  when 401
169
177
  raise Adyen::AuthenticationError.new("Invalid API authentication; https://docs.adyen.com/user-management/how-to-get-the-api-key", request_data)
170
178
  when 403
171
- raise Adyen::PermissionError.new("Missing user permissions; https://docs.adyen.com/user-management/user-roles", request_data)
179
+ raise Adyen::PermissionError.new("Missing user permissions; https://docs.adyen.com/user-management/user-roles", request_data, response.body)
172
180
  end
173
-
174
- formatted_response = AdyenResult.new(response.body, response.headers, response.status)
175
-
181
+
182
+ # delete has no response.body (unless it throws an error)
183
+ if response.body == nil
184
+ formatted_response = AdyenResult.new("{}", response.headers, response.status)
185
+ else
186
+ formatted_response = AdyenResult.new(response.body, response.headers, response.status)
187
+ end
188
+
176
189
  formatted_response
177
190
  end
178
191
 
data/lib/adyen/errors.rb CHANGED
@@ -70,8 +70,8 @@ module Adyen
70
70
  end
71
71
 
72
72
  class PermissionError < AdyenError
73
- def initialize(msg, request)
74
- super(request, nil, msg, 403)
73
+ def initialize(msg, request, response)
74
+ super(request, response, msg, 403)
75
75
  end
76
76
  end
77
77
 
@@ -2,7 +2,7 @@ require_relative "service"
2
2
 
3
3
  module Adyen
4
4
  class Checkout < Service
5
- DEFAULT_VERSION = 68
5
+ DEFAULT_VERSION = 70
6
6
 
7
7
  def initialize(client, version = DEFAULT_VERSION)
8
8
  service = "Checkout"
@@ -13,7 +13,7 @@ module Adyen
13
13
  ]
14
14
 
15
15
  with_application_info = [
16
- :payment_session,
16
+ :payment_session
17
17
  ]
18
18
 
19
19
  super(client, version, service, method_names, with_application_info)
@@ -42,7 +42,7 @@ module Adyen
42
42
  else
43
43
  action = "paymentLinks"
44
44
  args[1] ||= {} # optional headers arg
45
- @client.call_adyen_api(@service, action, args[0], args[1], @version, true)
45
+ @client.call_adyen_api(@service, action, args[0], args[1], @version)
46
46
  end
47
47
  end
48
48
 
@@ -71,6 +71,14 @@ module Adyen
71
71
  def apple_pay
72
72
  @apple_pay ||= Adyen::CheckoutApplePay.new(@client, @version)
73
73
  end
74
+
75
+ def modifications
76
+ @modifications ||= Adyen::Modifications.new(@client, @version)
77
+ end
78
+
79
+ def stored_payment_methods
80
+ @stored_payment_methods ||= Adyen::StoredPaymentMethods.new(@client, @version)
81
+ end
74
82
  end
75
83
 
76
84
  class CheckoutDetail < Service
@@ -89,6 +97,16 @@ module Adyen
89
97
  action = "payments/result"
90
98
  @client.call_adyen_api(@service, action, request, headers, @version)
91
99
  end
100
+
101
+ def donations(request, headers = {})
102
+ action = "donations"
103
+ @client.call_adyen_api(@service, action, request, headers, @version)
104
+ end
105
+
106
+ def card_details(request, headers = {})
107
+ action = "cardDetails"
108
+ @client.call_adyen_api(@service, action, request, headers, @version)
109
+ end
92
110
  end
93
111
 
94
112
  class CheckoutLink < Service
@@ -100,12 +118,12 @@ module Adyen
100
118
 
101
119
  def get(linkId, headers = {})
102
120
  action = { method: 'get', url: "paymentLinks/" + linkId }
103
- @client.call_adyen_api(@service, action, {}, headers, @version, true)
121
+ @client.call_adyen_api(@service, action, {}, headers, @version)
104
122
  end
105
123
 
106
124
  def update(linkId, request, headers = {})
107
125
  action = { method: 'patch', url: "paymentLinks/" + linkId }
108
- @client.call_adyen_api(@service, action, request, headers, @version, false)
126
+ @client.call_adyen_api(@service, action, request, headers, @version)
109
127
  end
110
128
  end
111
129
 
@@ -147,4 +165,60 @@ module Adyen
147
165
  @client.call_adyen_api(@service, action, request, headers, @version)
148
166
  end
149
167
  end
150
- end
168
+
169
+ class Modifications < Service
170
+ def initialize(client, version = DEFAULT_VERSION)
171
+ @service = "Checkout"
172
+ @client = client
173
+ @version = version
174
+ end
175
+
176
+ def capture(linkId, request, headers = {})
177
+ action = "payments/" + linkId + "/captures"
178
+ @client.call_adyen_api(@service, action, request, headers, @version)
179
+ end
180
+
181
+ def cancel(linkId, request, headers = {})
182
+ action = "payments/" + linkId + "/cancels"
183
+ @client.call_adyen_api(@service, action, request, headers, @version)
184
+ end
185
+
186
+ def genericCancel(request, headers = {})
187
+ action = "cancels"
188
+ @client.call_adyen_api(@service, action, request, headers, @version)
189
+ end
190
+
191
+ def refund(linkId, request, headers = {})
192
+ action = "payments/" + linkId + "/refunds"
193
+ @client.call_adyen_api(@service, action, request, headers, @version)
194
+ end
195
+
196
+ def reversal(linkId, request, headers = {})
197
+ action = "payments/" + linkId + "/reversals"
198
+ @client.call_adyen_api(@service, action, request, headers, @version)
199
+ end
200
+
201
+ def amountUpdate(linkId, request, headers = {})
202
+ action = "payments/" + linkId + "/amountUpdates"
203
+ @client.call_adyen_api(@service, action, request, headers, @version)
204
+ end
205
+ end
206
+
207
+ class StoredPaymentMethods < Service
208
+ def initialize(client, version = DEFAULT_VERSION)
209
+ @service = "Checkout"
210
+ @client = client
211
+ @version = version
212
+ end
213
+
214
+ def get(query_array={}, headers = {})
215
+ action = { method: 'get', url: "storedPaymentMethods" + create_query_string(query_array)}
216
+ @client.call_adyen_api(@service, action, {}, headers, @version)
217
+ end
218
+
219
+ def delete(recurringId, query_array={}, headers = {})
220
+ action = { method: 'delete', url: "storedPaymentMethods/%s" % recurringId + create_query_string(query_array)}
221
+ @client.call_adyen_api(@service, action, {}, headers, @version)
222
+ end
223
+ end
224
+ end
@@ -24,5 +24,10 @@ module Adyen
24
24
  end
25
25
  end
26
26
  end
27
+
28
+ # create query parameter from an array
29
+ def create_query_string(arr)
30
+ "?" + URI.encode_www_form(arr)
31
+ end
27
32
  end
28
33
  end
@@ -22,16 +22,15 @@ module Adyen
22
22
  end
23
23
 
24
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(':', '\\:') }
25
+ data = NOTIFICATION_VALIDATION_KEYS.map { |key| fetch(notification_request_item, key).to_s }
27
26
  .join(DATA_SEPARATOR)
27
+ return data
28
28
  end
29
29
 
30
30
  private
31
31
 
32
32
  def fetch(hash, keys)
33
33
  value = hash
34
-
35
34
  keys.to_s.split('.').each do |key|
36
35
  value = if key.to_i.to_s == key
37
36
  value[key.to_i]
data/lib/adyen/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Adyen
2
2
  NAME = "adyen-ruby-api-library"
3
- VERSION = "6.1.0".freeze
4
- end
3
+ VERSION = "6.3.0".freeze
4
+ end
@@ -405,6 +405,248 @@ RSpec.describe Adyen::Checkout, service: "checkout" do
405
405
  to be_a_kind_of Hash
406
406
  end
407
407
 
408
+ it "makes a capture call" do
409
+ request_body = JSON.parse(json_from_file("mocks/requests/Checkout/capture.json"))
410
+
411
+ response_body = json_from_file("mocks/responses/Checkout/capture.json")
412
+
413
+ url = @shared_values[:client].service_url(@shared_values[:service], "payments/12345/captures", @shared_values[:client].checkout.version)
414
+ WebMock.stub_request(:post, url).
415
+ with(
416
+ body: request_body,
417
+ headers: {
418
+ "x-api-key" => @shared_values[:client].api_key
419
+ }
420
+ )
421
+ .to_return(body: response_body, status: 201)
422
+
423
+ result = @shared_values[:client].checkout.modifications.capture("12345", request_body)
424
+ response_hash = result.response
425
+
426
+ expect(result.status).
427
+ to eq(201)
428
+ expect(response_hash).
429
+ to eq(JSON.parse(response_body))
430
+ expect(response_hash).
431
+ to be_a Adyen::HashWithAccessors
432
+ expect(response_hash).
433
+ to be_a_kind_of Hash
434
+ expect(response_hash.reference).
435
+ to eq("123456789")
436
+ expect(response_hash.pspReference).
437
+ to eq("12345")
438
+ end
439
+
440
+ it "makes a psp specific cancel call" do
441
+ request_body = JSON.parse(json_from_file("mocks/requests/Checkout/psp_cancel.json"))
442
+
443
+ response_body = json_from_file("mocks/responses/Checkout/psp_cancel.json")
444
+
445
+ url = @shared_values[:client].service_url(@shared_values[:service], "payments/12345/cancels", @shared_values[:client].checkout.version)
446
+ WebMock.stub_request(:post, url).
447
+ with(
448
+ body: request_body,
449
+ headers: {
450
+ "x-api-key" => @shared_values[:client].api_key
451
+ }
452
+ )
453
+ .to_return(body: response_body, status: 201)
454
+
455
+ result = @shared_values[:client].checkout.modifications.cancel("12345", request_body)
456
+ response_hash = result.response
457
+
458
+ expect(result.status).
459
+ to eq(201)
460
+ expect(response_hash).
461
+ to eq(JSON.parse(response_body))
462
+ expect(response_hash).
463
+ to be_a Adyen::HashWithAccessors
464
+ expect(response_hash).
465
+ to be_a_kind_of Hash
466
+ expect(response_hash.reference).
467
+ to eq("123456789")
468
+ expect(response_hash.pspReference).
469
+ to eq("12345")
470
+ end
471
+
472
+ it "makes a psp specific refunds call" do
473
+ request_body = JSON.parse(json_from_file("mocks/requests/Checkout/refund.json"))
474
+
475
+ response_body = json_from_file("mocks/responses/Checkout/refund.json")
476
+
477
+ url = @shared_values[:client].service_url(@shared_values[:service], "payments/12345/refunds", @shared_values[:client].checkout.version)
478
+ WebMock.stub_request(:post, url).
479
+ with(
480
+ body: request_body,
481
+ headers: {
482
+ "x-api-key" => @shared_values[:client].api_key
483
+ }
484
+ )
485
+ .to_return(body: response_body, status: 201)
486
+
487
+ result = @shared_values[:client].checkout.modifications.refund("12345", request_body)
488
+ response_hash = result.response
489
+
490
+ expect(result.status).
491
+ to eq(201)
492
+ expect(response_hash).
493
+ to eq(JSON.parse(response_body))
494
+ expect(response_hash).
495
+ to be_a Adyen::HashWithAccessors
496
+ expect(response_hash).
497
+ to be_a_kind_of Hash
498
+ expect(response_hash.reference).
499
+ to eq("123456789")
500
+ expect(response_hash.pspReference).
501
+ to eq("12345")
502
+ end
503
+
504
+ it "makes a psp specific reversals call" do
505
+ request_body = JSON.parse(json_from_file("mocks/requests/Checkout/psp_cancel.json"))
506
+
507
+ response_body = json_from_file("mocks/responses/Checkout/psp_cancel.json")
508
+
509
+ url = @shared_values[:client].service_url(@shared_values[:service], "payments/12345/reversals", @shared_values[:client].checkout.version)
510
+ WebMock.stub_request(:post, url).
511
+ with(
512
+ body: request_body,
513
+ headers: {
514
+ "x-api-key" => @shared_values[:client].api_key
515
+ }
516
+ )
517
+ .to_return(body: response_body, status: 201)
518
+
519
+ result = @shared_values[:client].checkout.modifications.reversal("12345", request_body)
520
+ response_hash = result.response
521
+
522
+ expect(result.status).
523
+ to eq(201)
524
+ expect(response_hash).
525
+ to eq(JSON.parse(response_body))
526
+ expect(response_hash).
527
+ to be_a Adyen::HashWithAccessors
528
+ expect(response_hash).
529
+ to be_a_kind_of Hash
530
+ expect(response_hash.reference).
531
+ to eq("123456789")
532
+ expect(response_hash.pspReference).
533
+ to eq("12345")
534
+ end
535
+
536
+ it "makes a psp specific amountUpdates call" do
537
+ request_body = JSON.parse(json_from_file("mocks/requests/Checkout/amount_updates.json"))
538
+
539
+ response_body = json_from_file("mocks/responses/Checkout/amount_updates.json")
540
+
541
+ url = @shared_values[:client].service_url(@shared_values[:service], "payments/12345/amountUpdates", @shared_values[:client].checkout.version)
542
+ WebMock.stub_request(:post, url).
543
+ with(
544
+ body: request_body,
545
+ headers: {
546
+ "x-api-key" => @shared_values[:client].api_key
547
+ }
548
+ )
549
+ .to_return(body: response_body, status: 201)
550
+
551
+ result = @shared_values[:client].checkout.modifications.amountUpdate("12345", request_body)
552
+ response_hash = result.response
553
+
554
+ expect(result.status).
555
+ to eq(201)
556
+ expect(response_hash).
557
+ to eq(JSON.parse(response_body))
558
+ expect(response_hash).
559
+ to be_a Adyen::HashWithAccessors
560
+ expect(response_hash).
561
+ to be_a_kind_of Hash
562
+ expect(response_hash.reference).
563
+ to eq("123456789")
564
+ expect(response_hash.pspReference).
565
+ to eq("12345")
566
+ end
567
+
568
+ it "makes a generic cancel call" do
569
+ request_body = JSON.parse(json_from_file("mocks/requests/Checkout/generic_cancel.json"))
570
+
571
+ response_body = json_from_file("mocks/responses/Checkout/generic_cancel.json")
572
+
573
+ url = @shared_values[:client].service_url(@shared_values[:service], "cancels", @shared_values[:client].checkout.version)
574
+ WebMock.stub_request(:post, url).
575
+ with(
576
+ body: request_body,
577
+ headers: {
578
+ "x-api-key" => @shared_values[:client].api_key
579
+ }
580
+ )
581
+ .to_return(body: response_body, status: 201)
582
+
583
+ result = @shared_values[:client].checkout.modifications.genericCancel(request_body)
584
+ response_hash = result.response
585
+
586
+ expect(result.status).
587
+ to eq(201)
588
+ expect(response_hash).
589
+ to eq(JSON.parse(response_body))
590
+ expect(response_hash).
591
+ to be_a Adyen::HashWithAccessors
592
+ expect(response_hash).
593
+ to be_a_kind_of Hash
594
+ expect(response_hash.reference).
595
+ to eq("123456789")
596
+ expect(response_hash.pspReference).
597
+ to eq("12345")
598
+ end
599
+
600
+ it "makes a get storedPaymentMethods call" do
601
+ response_body = json_from_file("mocks/responses/Checkout/stored_payment_methods.json")
602
+
603
+ url = @shared_values[:client].service_url(@shared_values[:service], "storedPaymentMethods?merchantAccount=TestMerchantAccount&shopperReference=test-1234", @shared_values[:client].checkout.version)
604
+ WebMock.stub_request(:get, url).
605
+ with(
606
+ headers: {
607
+ "x-api-key" => @shared_values[:client].api_key
608
+ }
609
+ ).
610
+ to_return(
611
+ body: response_body
612
+ )
613
+
614
+ result = @shared_values[:client].checkout.stored_payment_methods.get({"merchantAccount" => "TestMerchantAccount", "shopperReference" => "test-1234"})
615
+ response_hash = result.response
616
+
617
+ expect(result.status).
618
+ to eq(200)
619
+ expect(response_hash).
620
+ to eq(JSON.parse(response_body))
621
+ expect(response_hash).
622
+ to be_a Adyen::HashWithAccessors
623
+ expect(response_hash).
624
+ to be_a_kind_of Hash
625
+ expect(response_hash["shopperReference"]).
626
+ to eq("test-1234")
627
+ end
628
+
629
+ it "makes a delete storedPaymentMethods call" do
630
+ response_body = json_from_file("mocks/responses/Checkout/stored_payment_methods.json")
631
+
632
+ url = @shared_values[:client].service_url(@shared_values[:service], "storedPaymentMethods/RL8FW7WZM6KXWD82?merchantAccount=TestMerchantAccount&shopperReference=test-1234", @shared_values[:client].checkout.version)
633
+ WebMock.stub_request(:delete, url).
634
+ with(
635
+ headers: {
636
+ "x-api-key" => @shared_values[:client].api_key
637
+ }
638
+ ).
639
+ to_return(
640
+ body: response_body
641
+ )
642
+
643
+ result = @shared_values[:client].checkout.stored_payment_methods.delete("RL8FW7WZM6KXWD82", {"merchantAccount" => "TestMerchantAccount", "shopperReference" => "test-1234"})
644
+ response_hash = result.response
645
+
646
+ expect(result.status).
647
+ to eq(200)
648
+ end
649
+
408
650
  # create client for automated tests
409
651
  client = create_client(:api_key)
410
652
 
data/spec/client_spec.rb CHANGED
@@ -83,4 +83,31 @@ RSpec.describe Adyen do
83
83
  expect(client.service_url_base("Payment")).
84
84
  to eq("https://abcdef1234567890-TestCompany-pal-live.adyenpayments.com/pal/servlet")
85
85
  end
86
+
87
+ it "generates a new set of ConnectionOptions when none are provided" do
88
+ expect(Faraday::ConnectionOptions).to receive(:new).and_call_original
89
+ client = Adyen::Client.new(env: :test)
90
+ end
91
+
92
+ it "uses the ConnectionOptions provided" do
93
+ connection_options = Faraday::ConnectionOptions.new
94
+ expect(Faraday::ConnectionOptions).not_to receive(:new)
95
+ client = Adyen::Client.new(env: :test, connection_options: connection_options)
96
+ end
97
+
98
+ it "initiates a Faraday connection with the provided options" do
99
+ connection_options = Faraday::ConnectionOptions.new
100
+ expect(Faraday::ConnectionOptions).not_to receive(:new)
101
+ client = Adyen::Client.new(api_key: "api_key", env: :mock, connection_options: connection_options)
102
+
103
+ mock_faraday_connection = double(Faraday::Connection)
104
+ url = client.service_url(@shared_values[:service], "payments/details", client.checkout.version)
105
+ request_body = JSON.parse(json_from_file("mocks/requests/Checkout/payment-details.json"))
106
+ mock_response = Faraday::Response.new(status: 200)
107
+
108
+ expect(Adyen::AdyenResult).to receive(:new)
109
+ expect(Faraday).to receive(:new).with("http://localhost:3001/v70/payments/details", connection_options).and_return(mock_faraday_connection)
110
+ expect(mock_faraday_connection).to receive(:post).and_return(mock_response)
111
+ client.checkout.payments.details(request_body)
112
+ end
86
113
  end
data/spec/errors_spec.rb CHANGED
@@ -33,7 +33,7 @@ RSpec.describe Adyen::AdyenError do
33
33
  expect(Adyen::AdyenError.new(@shared_values[:request], nil, nil, 'code').to_s).to eq("Adyen::AdyenError code:code, request:#{@shared_values[:request]}")
34
34
  end
35
35
  it 'uses the proper error class name' do
36
- expect(Adyen::PermissionError.new('message', @shared_values[:request]).to_s).to eq("Adyen::PermissionError code:403, msg:message, request:#{@shared_values[:request]}")
36
+ expect(Adyen::PermissionError.new('message', @shared_values[:request], 'response').to_s).to eq("Adyen::PermissionError code:403, msg:message, request:#{@shared_values[:request]}, response:response")
37
37
  end
38
38
  end
39
39
  describe '#masking' do
@@ -0,0 +1,22 @@
1
+ {
2
+ "amount": {
3
+ "currency": "str",
4
+ "value": 0
5
+ },
6
+ "merchantAccount": "TestMerchant",
7
+ "reason": "delayedCharge",
8
+ "reference": "123456789",
9
+ "splits": [
10
+ {
11
+ "account": "string",
12
+ "amount": {
13
+ "currency": "str",
14
+ "value": 0
15
+ },
16
+ "description": "string",
17
+ "reference": "string",
18
+ "type": "BalanceAccount"
19
+ }
20
+ ]
21
+ }
22
+
@@ -0,0 +1,34 @@
1
+ {
2
+ "amount": {
3
+ "currency": "str",
4
+ "value": 0
5
+ },
6
+ "lineItems": [
7
+ {
8
+ "amountExcludingTax": 0,
9
+ "amountIncludingTax": 0,
10
+ "description": "string",
11
+ "id": "string",
12
+ "imageUrl": "string",
13
+ "itemCategory": "string",
14
+ "productUrl": "string",
15
+ "quantity": 0,
16
+ "taxAmount": 0,
17
+ "taxPercentage": 0
18
+ }
19
+ ],
20
+ "merchantAccount": "string",
21
+ "reference": "string",
22
+ "splits": [
23
+ {
24
+ "account": "string",
25
+ "amount": {
26
+ "currency": "str",
27
+ "value": 0
28
+ },
29
+ "description": "string",
30
+ "reference": "string",
31
+ "type": "BalanceAccount"
32
+ }
33
+ ]
34
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "merchantAccount": "TestMerchant",
3
+ "paymentReference": "12345",
4
+ "reference": "123456789"
5
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "merchantAccount": "TestMerchant",
3
+ "reference": "123456789"
4
+ }
@@ -0,0 +1,34 @@
1
+ {
2
+ "amount": {
3
+ "currency": "str",
4
+ "value": 0
5
+ },
6
+ "lineItems": [
7
+ {
8
+ "amountExcludingTax": 0,
9
+ "amountIncludingTax": 0,
10
+ "description": "string",
11
+ "id": "string",
12
+ "imageUrl": "string",
13
+ "itemCategory": "string",
14
+ "productUrl": "string",
15
+ "quantity": 0,
16
+ "taxAmount": 0,
17
+ "taxPercentage": 0
18
+ }
19
+ ],
20
+ "merchantAccount": "TestMerchant",
21
+ "reference": "123456789",
22
+ "splits": [
23
+ {
24
+ "account": "string",
25
+ "amount": {
26
+ "currency": "str",
27
+ "value": 0
28
+ },
29
+ "description": "string",
30
+ "reference": "string",
31
+ "type": "BalanceAccount"
32
+ }
33
+ ]
34
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "amount": {
3
+ "currency": "str",
4
+ "value": 0
5
+ },
6
+ "merchantAccount": "TestMerchant",
7
+ "paymentPspReference": "123456789",
8
+ "pspReference": "12345",
9
+ "reason": "delayedCharge",
10
+ "reference": "123456789",
11
+ "splits": [
12
+ {
13
+ "account": "string",
14
+ "amount": {
15
+ "currency": "str",
16
+ "value": 0
17
+ },
18
+ "description": "string",
19
+ "reference": "string",
20
+ "type": "BalanceAccount"
21
+ }
22
+ ],
23
+ "status": "received"
24
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "amount": {
3
+ "currency": "str",
4
+ "value": 0
5
+ },
6
+ "lineItems": [
7
+ {
8
+ "amountExcludingTax": 0,
9
+ "amountIncludingTax": 0,
10
+ "description": "string",
11
+ "id": "string",
12
+ "imageUrl": "string",
13
+ "itemCategory": "string",
14
+ "productUrl": "string",
15
+ "quantity": 0,
16
+ "taxAmount": 0,
17
+ "taxPercentage": 0
18
+ }
19
+ ],
20
+ "merchantAccount": "string",
21
+ "paymentPspReference": "string",
22
+ "pspReference": "12345",
23
+ "reference": "123456789",
24
+ "splits": [
25
+ {
26
+ "account": "string",
27
+ "amount": {
28
+ "currency": "str",
29
+ "value": 0
30
+ },
31
+ "description": "string",
32
+ "reference": "string",
33
+ "type": "BalanceAccount"
34
+ }
35
+ ],
36
+ "status": "received"
37
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "merchantAccount": "string",
3
+ "paymentReference": "123456789",
4
+ "pspReference": "12345",
5
+ "reference": "123456789",
6
+ "status": "received"
7
+ }
File without changes
@@ -0,0 +1,7 @@
1
+ {
2
+ "merchantAccount": "TestMerchant",
3
+ "paymentPspReference": "string",
4
+ "pspReference": "12345",
5
+ "reference": "123456789",
6
+ "status": "received"
7
+ }
@@ -0,0 +1,37 @@
1
+ {
2
+ "amount": {
3
+ "currency": "str",
4
+ "value": 0
5
+ },
6
+ "lineItems": [
7
+ {
8
+ "amountExcludingTax": 0,
9
+ "amountIncludingTax": 0,
10
+ "description": "string",
11
+ "id": "string",
12
+ "imageUrl": "string",
13
+ "itemCategory": "string",
14
+ "productUrl": "string",
15
+ "quantity": 0,
16
+ "taxAmount": 0,
17
+ "taxPercentage": 0
18
+ }
19
+ ],
20
+ "merchantAccount": "TestMerchant",
21
+ "paymentPspReference": "123456789",
22
+ "pspReference": "12345",
23
+ "reference": "123456789",
24
+ "splits": [
25
+ {
26
+ "account": "string",
27
+ "amount": {
28
+ "currency": "str",
29
+ "value": 0
30
+ },
31
+ "description": "string",
32
+ "reference": "string",
33
+ "type": "BalanceAccount"
34
+ }
35
+ ],
36
+ "status": "received"
37
+ }
@@ -0,0 +1 @@
1
+ {"merchantAccount":"TestMerchantAccount", "shopperReference":"test-1234"}
@@ -0,0 +1,41 @@
1
+ {
2
+ "additionalData": {
3
+ "acquirerCode": "TestPmmAcquirer",
4
+ "acquirerReference": "DZMKWLXW6N6",
5
+ "authCode": "076181",
6
+ "avsResult": "5 No AVS data provided",
7
+ "avsResultRaw": "5",
8
+ "cardSummary": "1111",
9
+ "checkout.cardAddedBrand": "visa",
10
+ "cvcResult": "1 Matches",
11
+ "cvcResultRaw": "M",
12
+ "expiryDate": "03/2030",
13
+ "hmacSignature": "nIgT81gaB5oJpn2jPXupDq68iRo2wUlBsuYjtYfwKqo=",
14
+ "paymentMethod": "visa",
15
+ "refusalReasonRaw": "AUTHORISED",
16
+ "retry.attempt1.acquirer": "TestPmmAcquirer",
17
+ "retry.attempt1.acquirerAccount": "TestPmmAcquirerAccount",
18
+ "retry.attempt1.avsResultRaw": "5",
19
+ "retry.attempt1.rawResponse": "AUTHORISED",
20
+ "retry.attempt1.responseCode": "Approved",
21
+ "retry.attempt1.scaExemptionRequested": "lowValue",
22
+ "scaExemptionRequested": "lowValue"
23
+ },
24
+ "amount": {
25
+ "currency": "EUR",
26
+ "value": 1000
27
+ },
28
+ "eventCode": "AUTHORISATION",
29
+ "eventDate": "2023-01-09T16:27:29+01:00",
30
+ "merchantAccountCode": "AntoniStroinski",
31
+ "merchantReference": "\\\\slashes are fun",
32
+ "operations": [
33
+ "CANCEL",
34
+ "CAPTURE",
35
+ "REFUND"
36
+ ],
37
+ "paymentMethod": "visa",
38
+ "pspReference": "T7FD4VM4D3RZNN82",
39
+ "reason": "076181:1111:03/2030",
40
+ "success": "true"
41
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "additionalData": {
3
+ "acquirerCode": "TestPmmAcquirer",
4
+ "acquirerReference": "8NQH5BNF58M",
5
+ "authCode": "039404",
6
+ "avsResult": "5 No AVS data provided",
7
+ "avsResultRaw": "5",
8
+ "cardSummary": "1111",
9
+ "checkout.cardAddedBrand": "visa",
10
+ "cvcResult": "1 Matches",
11
+ "cvcResultRaw": "M",
12
+ "expiryDate": "03/2030",
13
+ "hmacSignature": "2EQYm7YJpKO4EtHSPu55SQTyWf8dkW5u2nD1tJFpViA=",
14
+ "paymentMethod": "visa",
15
+ "refusalReasonRaw": "AUTHORISED",
16
+ "retry.attempt1.acquirer": "TestPmmAcquirer",
17
+ "retry.attempt1.acquirerAccount": "TestPmmAcquirerAccount",
18
+ "retry.attempt1.avsResultRaw": "5",
19
+ "retry.attempt1.rawResponse": "AUTHORISED",
20
+ "retry.attempt1.responseCode": "Approved",
21
+ "retry.attempt1.scaExemptionRequested": "lowValue",
22
+ "scaExemptionRequested": "lowValue"
23
+ },
24
+ "amount": {
25
+ "currency": "EUR",
26
+ "value": 1000
27
+ },
28
+ "eventCode": "AUTHORISATION",
29
+ "eventDate": "2023-01-10T13:40:54+01:00",
30
+ "merchantAccountCode": "AntoniStroinski",
31
+ "merchantReference": ":slashes are fun",
32
+ "operations": [
33
+ "CANCEL",
34
+ "CAPTURE",
35
+ "REFUND"
36
+ ],
37
+ "paymentMethod": "visa",
38
+ "pspReference": "M8NB66SBZSGLNK82",
39
+ "reason": "039404:1111:03/2030",
40
+ "success": "true"
41
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "amount": {
3
+ "value": 1000,
4
+ "currency": "EUR"
5
+ },
6
+ "reason": "087330:1111:03/2030",
7
+ "success": "true",
8
+ "eventCode": "AUTHORISATION",
9
+ "eventDate": "2023-01-10T13:37:30+01:00",
10
+ "operations": [
11
+ "CANCEL",
12
+ "CAPTURE",
13
+ "REFUND"
14
+ ],
15
+ "pspReference": "X3GWNS6KJ8NKGK82",
16
+ "paymentMethod": "visa",
17
+ "additionalData": {
18
+ "authCode": "087330",
19
+ "avsResult": "5 No AVS data provided",
20
+ "cvcResult": "1 Matches",
21
+ "expiryDate": "03/2030",
22
+ "cardSummary": "1111",
23
+ "acquirerCode": "TestPmmAcquirer",
24
+ "avsResultRaw": "5",
25
+ "cvcResultRaw": "M",
26
+ "hmacSignature": "9Z0xdpG9Xi3zcmXv14t/BvMBut77O/Xq9D4CQXSDUi4=",
27
+ "paymentMethod": "visa",
28
+ "refusalReasonRaw": "AUTHORISED",
29
+ "acquirerReference": "HHCCC326PH6",
30
+ "scaExemptionRequested": "lowValue",
31
+ "checkout.cardAddedBrand": "visa",
32
+ "retry.attempt1.acquirer": "TestPmmAcquirer",
33
+ "retry.attempt1.rawResponse": "AUTHORISED",
34
+ "retry.attempt1.avsResultRaw": "5",
35
+ "retry.attempt1.responseCode": "Approved",
36
+ "retry.attempt1.acquirerAccount": "TestPmmAcquirerAccount",
37
+ "retry.attempt1.scaExemptionRequested": "lowValue"
38
+ },
39
+ "merchantReference": "//slashes are fun",
40
+ "merchantAccountCode": "AntoniStroinski"
41
+ }
@@ -0,0 +1,41 @@
1
+ {
2
+ "additionalData": {
3
+ "acquirerCode": "TestPmmAcquirer",
4
+ "acquirerReference": "J8DXDJ2PV6P",
5
+ "authCode": "052095",
6
+ "avsResult": "5 No AVS data provided",
7
+ "avsResultRaw": "5",
8
+ "cardSummary": "1111",
9
+ "checkout.cardAddedBrand": "visa",
10
+ "cvcResult": "1 Matches",
11
+ "cvcResultRaw": "M",
12
+ "expiryDate": "03/2030",
13
+ "hmacSignature": "CZErGCNQaSsxbaQfZaJlakqo7KPP+mIa8a+wx3yNs9A=",
14
+ "paymentMethod": "visa",
15
+ "refusalReasonRaw": "AUTHORISED",
16
+ "retry.attempt1.acquirer": "TestPmmAcquirer",
17
+ "retry.attempt1.acquirerAccount": "TestPmmAcquirerAccount",
18
+ "retry.attempt1.avsResultRaw": "5",
19
+ "retry.attempt1.rawResponse": "AUTHORISED",
20
+ "retry.attempt1.responseCode": "Approved",
21
+ "retry.attempt1.scaExemptionRequested": "lowValue",
22
+ "scaExemptionRequested": "lowValue"
23
+ },
24
+ "amount": {
25
+ "currency": "EUR",
26
+ "value": 1000
27
+ },
28
+ "eventCode": "AUTHORISATION",
29
+ "eventDate": "2023-01-10T13:42:29+01:00",
30
+ "merchantAccountCode": "AntoniStroinski",
31
+ "merchantReference": "\\:/\\/slashes are fun",
32
+ "operations": [
33
+ "CANCEL",
34
+ "CAPTURE",
35
+ "REFUND"
36
+ ],
37
+ "paymentMethod": "visa",
38
+ "pspReference": "ZVWN7D3WSMK2WN82",
39
+ "reason": "052095:1111:03/2030",
40
+ "success": "true"
41
+ }
@@ -28,12 +28,6 @@ RSpec.describe Adyen::Utils::HmacValidator do
28
28
  expect(data_to_sign).to eq '7914073381342284::TestMerchant:TestPayment-1407325143704:1130:EUR:AUTHORISATION:true'
29
29
  end
30
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
31
  it 'should encrypt properly' do
38
32
  encrypted = validator.calculate_notification_hmac(notification_request_item, key)
39
33
  expect(encrypted).to eq expected_sign
@@ -48,5 +42,25 @@ RSpec.describe Adyen::Utils::HmacValidator do
48
42
 
49
43
  expect(validator.valid_notification_hmac?(notification_request_item, key)).to be false
50
44
  end
45
+
46
+ it 'should validate backslashes correctly' do
47
+ webhook = JSON.parse(json_from_file("mocks/responses/Webhooks/backslash_notification.json"))
48
+ expect(validator.valid_notification_hmac?(webhook, '74F490DD33F7327BAECC88B2947C011FC02D014A473AAA33A8EC93E4DC069174')).to be true
49
+ end
50
+
51
+ it 'should validate colons correctly' do
52
+ webhook = JSON.parse(json_from_file("mocks/responses/Webhooks/colon_notification.json"))
53
+ expect(validator.valid_notification_hmac?(webhook, '74F490DD33F7327BAECC88B2947C011FC02D014A473AAA33A8EC93E4DC069174')).to be true
54
+ end
55
+
56
+ it 'should validate forward slashes correctly' do
57
+ webhook = JSON.parse(json_from_file("mocks/responses/Webhooks/forwardslash_notification.json"))
58
+ expect(validator.valid_notification_hmac?(webhook, '74F490DD33F7327BAECC88B2947C011FC02D014A473AAA33A8EC93E4DC069174')).to be true
59
+ end
60
+
61
+ it 'should validate mix of slashes and colon correctly' do
62
+ webhook = JSON.parse(json_from_file("mocks/responses/Webhooks/mixed_notification.json"))
63
+ expect(validator.valid_notification_hmac?(webhook, '74F490DD33F7327BAECC88B2947C011FC02D014A473AAA33A8EC93E4DC069174')).to be true
64
+ end
51
65
  end
52
66
  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: 6.1.0
4
+ version: 6.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adyen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-12 00:00:00.000000000 Z
11
+ date: 2023-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: faraday
@@ -81,7 +81,9 @@ files:
81
81
  - ".github/ISSUE_TEMPLATE/feature_request.md"
82
82
  - ".github/PULL_REQUEST_TEMPLATE.md"
83
83
  - ".github/dependabot.yml"
84
+ - ".github/workflows/codeql.yml"
84
85
  - ".github/workflows/ruby.yml"
86
+ - ".github/workflows/rubygems_release.yml"
85
87
  - ".gitignore"
86
88
  - CODE_OF_CONDUCT.md
87
89
  - CONTRIBUTING.md
@@ -153,7 +155,11 @@ files:
153
155
  - spec/mocks/requests/Account/upload_document.json
154
156
  - spec/mocks/requests/BinLookup/get_3ds_availability.json
155
157
  - spec/mocks/requests/BinLookup/get_cost_estimate.json
158
+ - spec/mocks/requests/Checkout/amount_updates.json
156
159
  - spec/mocks/requests/Checkout/apple_pay_sessions.json
160
+ - spec/mocks/requests/Checkout/capture.json
161
+ - spec/mocks/requests/Checkout/generic_cancel.json
162
+ - spec/mocks/requests/Checkout/modifications_request.json
157
163
  - spec/mocks/requests/Checkout/orders.json
158
164
  - spec/mocks/requests/Checkout/orders_cancel.json
159
165
  - spec/mocks/requests/Checkout/origin_keys.json
@@ -164,6 +170,8 @@ files:
164
170
  - spec/mocks/requests/Checkout/payment_methods_balance.json
165
171
  - spec/mocks/requests/Checkout/payment_session.json
166
172
  - spec/mocks/requests/Checkout/payments.json
173
+ - spec/mocks/requests/Checkout/psp_cancel.json
174
+ - spec/mocks/requests/Checkout/refund.json
167
175
  - spec/mocks/requests/Checkout/sessions.json
168
176
  - spec/mocks/requests/Checkout/verify.json
169
177
  - spec/mocks/requests/DataProtectionService/request_subject_erasure.json
@@ -230,8 +238,12 @@ files:
230
238
  - spec/mocks/responses/Account/upload_document.json
231
239
  - spec/mocks/responses/BinLookup/get_3ds_availability.json
232
240
  - spec/mocks/responses/BinLookup/get_cost_estimate.json
241
+ - spec/mocks/responses/Checkout/amount_updates.json
233
242
  - spec/mocks/responses/Checkout/apple_pay_sessions.json
243
+ - spec/mocks/responses/Checkout/capture.json
244
+ - spec/mocks/responses/Checkout/generic_cancel.json
234
245
  - spec/mocks/responses/Checkout/get-payment-link.json
246
+ - spec/mocks/responses/Checkout/modifications.json
235
247
  - spec/mocks/responses/Checkout/orders.json
236
248
  - spec/mocks/responses/Checkout/orders_cancel.json
237
249
  - spec/mocks/responses/Checkout/origin_keys.json
@@ -242,7 +254,10 @@ files:
242
254
  - spec/mocks/responses/Checkout/payment_methods_balance.json
243
255
  - spec/mocks/responses/Checkout/payment_session.json
244
256
  - spec/mocks/responses/Checkout/payments.json
257
+ - spec/mocks/responses/Checkout/psp_cancel.json
258
+ - spec/mocks/responses/Checkout/refund.json
245
259
  - spec/mocks/responses/Checkout/sessions-success.json
260
+ - spec/mocks/responses/Checkout/stored_payment_methods.json
246
261
  - spec/mocks/responses/Checkout/update-payment-link.json
247
262
  - spec/mocks/responses/Checkout/verify.json
248
263
  - spec/mocks/responses/DataProtectionService/request_subject_erasure.json
@@ -290,6 +305,10 @@ files:
290
305
  - spec/mocks/responses/Terminal/assign_terminals.json
291
306
  - spec/mocks/responses/Terminal/find_terminal.json
292
307
  - spec/mocks/responses/Terminal/get_terminals_under_account.json
308
+ - spec/mocks/responses/Webhooks/backslash_notification.json
309
+ - spec/mocks/responses/Webhooks/colon_notification.json
310
+ - spec/mocks/responses/Webhooks/forwardslash_notification.json
311
+ - spec/mocks/responses/Webhooks/mixed_notification.json
293
312
  - spec/notification_spec.rb
294
313
  - spec/payments_spec.rb
295
314
  - spec/payouts_spec.rb