defra_ruby_mocks 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/README.md +45 -5
- data/Rakefile +0 -2
- data/app/controllers/defra_ruby_mocks/company_controller.rb +1 -1
- data/app/controllers/defra_ruby_mocks/worldpay_controller.rb +18 -5
- data/app/services/defra_ruby_mocks/base_service.rb +8 -2
- data/app/services/defra_ruby_mocks/worldpay_resource_service.rb +55 -0
- data/app/services/defra_ruby_mocks/worldpay_response_service.rb +71 -52
- data/app/views/defra_ruby_mocks/worldpay/stuck.html.erb +37 -0
- data/lib/defra_ruby_mocks/engine.rb +1 -1
- data/lib/defra_ruby_mocks/missing_resource_error.rb +9 -0
- data/lib/defra_ruby_mocks/version.rb +1 -1
- data/spec/requests/company_spec.rb +4 -4
- data/spec/requests/worldpay_spec.rb +64 -20
- data/spec/services/worldpay_resource_service_spec.rb +112 -0
- data/spec/services/worldpay_response_service_spec.rb +185 -58
- data/spec/spec_helper.rb +3 -0
- metadata +44 -19
- data/lib/defra_ruby_mocks/missing_registration_error.rb +0 -9
- data/spec/dummy/log/development.log +0 -0
- data/spec/dummy/log/test.log +0 -479
- data/spec/examples.txt +0 -56
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a765c4c2a161ee30df850c9d40cfbfad3ed9cb5a521e41083b6e27794678b340
|
4
|
+
data.tar.gz: bbef0343174295fe77676a0f8f3d50faca67fc016994c4474bfd8de9ff66dc8f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5d4b1e1890c8568d644542407c4a4395e2920bb1a8ad0a545568d064447b70a22c2f9190756e9048a70adef917bca55e055806e9e827beeeecd85d5cfa472aa2
|
7
|
+
data.tar.gz: 71598c40f92872c6be03a66d747629a892e3a1a60bea98cd753b046e8be495d0074579cb36fc4101afff9cbbd85fe41ab52b76d8508d99e63e3a0eacc06b292d
|
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# Defra Ruby Mocks
|
2
2
|
|
3
|
-
[](https://travis-ci.com/DEFRA/defra-ruby-mocks)
|
4
|
+
[](https://sonarcloud.io/dashboard?id=DEFRA_defra-ruby-mocks)
|
5
|
+
[](https://sonarcloud.io/dashboard?id=DEFRA_defra-ruby-mocks)
|
6
|
+
[](https://hakiri.io/github/DEFRA/defra-ruby-mocks/main)
|
7
7
|
[](http://www.nationalarchives.gov.uk/doc/open-government-licence/version/3)
|
8
8
|
|
9
9
|
A Rails Engine used by the [Ruby services team](https://github.com/DEFRA/ruby-services-team) in their digital services.
|
@@ -21,7 +21,7 @@ Things to note
|
|
21
21
|
|
22
22
|
Make sure you already have:
|
23
23
|
|
24
|
-
- Ruby 2.
|
24
|
+
- Ruby 2.7.1
|
25
25
|
- [Bundler](http://bundler.io/) – for installing Ruby gems
|
26
26
|
|
27
27
|
## Mounting the engine
|
@@ -122,6 +122,46 @@ This Worldpay mock replicates those 2 interactions with the following urls
|
|
122
122
|
- `../worldpay/payments-service`
|
123
123
|
- `../worldpay/dispatcher`
|
124
124
|
|
125
|
+
##### Cancelled payments
|
126
|
+
|
127
|
+
The engine has the ability to mock a user cancelling a payment when on the Worldpay site. To have the mock return a cancelled payment response just ensure the registration's company name includes the word `cancel` (case doesn't matter).
|
128
|
+
|
129
|
+
If it does the engine will redirect back to the cancelled url instead of the success url provided, plus set the payment status to `CANCELLED`.
|
130
|
+
|
131
|
+
This allows us to test how the application handles Worldpay responding with a cancelled payment response.
|
132
|
+
|
133
|
+
##### Errored payments
|
134
|
+
|
135
|
+
The engine has the ability to Worldpay erroring during a payment. To have the mock return an errored payment response just ensure the registration's company name includes the word `error` (case doesn't matter).
|
136
|
+
|
137
|
+
If it does the engine will redirect back to the error url instead of the success url provided, plus set the payment status to `ERROR`.
|
138
|
+
|
139
|
+
This allows us to test how the application handles Worldpay responding with an errored payment response.
|
140
|
+
|
141
|
+
##### Pending payments
|
142
|
+
|
143
|
+
The engine has the ability to also mock Worldpay marking a payment as pending. To have the mock return a payment pending response just ensure the registration's company name includes the word `pending` (case doesn't matter).
|
144
|
+
|
145
|
+
If it does the engine will redirect back to the pending url instead of the success url provided, plus set the payment status to `SENT_FOR_AUTHORISATION`.
|
146
|
+
|
147
|
+
This allows us to test how the application handles Worldpay responding with a payment pending response.
|
148
|
+
|
149
|
+
##### Refused payments
|
150
|
+
|
151
|
+
The engine has the ability to also mock Worldpay refusing a payment. To have the mock refuse payment just ensure the registration's company name includes the word `reject` (case doesn't matter).
|
152
|
+
|
153
|
+
If it does the engine will redirect back to the failure url instead of the success url provided, plus set the payment status to `REFUSED`.
|
154
|
+
|
155
|
+
This allows us to test how the application handles both successful and unsucessful Worldpay payments.
|
156
|
+
|
157
|
+
##### Stuck payments
|
158
|
+
|
159
|
+
The engine has the ability to also mock Worldpay not redirecting back to the service. This is the equivalent of a registration getting 'stuck at Worldpay'. To have the mock not respond just ensure the registration's company name includes the word `stuck` (case doesn't matter).
|
160
|
+
|
161
|
+
If it does the engine will not redirect back to the service, but instead render a 'You're stuck!' page.
|
162
|
+
|
163
|
+
This allows us to test how the application handles Worldpay not returning after we redirect a user to them.
|
164
|
+
|
125
165
|
#### Refunds
|
126
166
|
|
127
167
|
Requesting a refund from Worldpay is a single step process.
|
data/Rakefile
CHANGED
@@ -25,7 +25,6 @@ Bundler::GemHelper.install_tasks
|
|
25
25
|
# This is wrapped to prevent an error when rake is called in environments where
|
26
26
|
# rspec may not be available, e.g. production. As such we don't need to handle
|
27
27
|
# the error.
|
28
|
-
# rubocop:disable Lint/HandleExceptions
|
29
28
|
begin
|
30
29
|
require "rspec/core/rake_task"
|
31
30
|
|
@@ -35,4 +34,3 @@ begin
|
|
35
34
|
rescue LoadError
|
36
35
|
# no rspec available
|
37
36
|
end
|
38
|
-
# rubocop:enable Lint/HandleExceptions
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module DefraRubyMocks
|
4
|
-
class WorldpayController < ApplicationController
|
4
|
+
class WorldpayController < ::DefraRubyMocks::ApplicationController
|
5
5
|
|
6
6
|
before_action :set_default_response_format
|
7
7
|
|
@@ -10,14 +10,27 @@ module DefraRubyMocks
|
|
10
10
|
|
11
11
|
render_payment_response if @values[:request_type] == :payment
|
12
12
|
render_refund_response if @values[:request_type] == :refund
|
13
|
-
rescue StandardError
|
13
|
+
rescue StandardError => e
|
14
|
+
Rails.logger.error("MOCKS: Worldpay payments service error: #{e.message}")
|
14
15
|
head 500
|
15
16
|
end
|
16
17
|
|
17
18
|
def dispatcher
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
@response = WorldpayResponseService.run(
|
20
|
+
success_url: params[:successURL],
|
21
|
+
failure_url: params[:failureURL],
|
22
|
+
pending_url: params[:pendingURL],
|
23
|
+
cancel_url: params[:cancelURL],
|
24
|
+
error_url: params[:errorURL]
|
25
|
+
)
|
26
|
+
|
27
|
+
if @response.status == :STUCK
|
28
|
+
render formats: :html, action: "stuck", layout: false
|
29
|
+
else
|
30
|
+
redirect_to @response.url
|
31
|
+
end
|
32
|
+
rescue StandardError => e
|
33
|
+
Rails.logger.error("MOCKS: Worldpay dispatcher error: #{e.message}")
|
21
34
|
head 500
|
22
35
|
end
|
23
36
|
|
@@ -2,8 +2,14 @@
|
|
2
2
|
|
3
3
|
module DefraRubyMocks
|
4
4
|
class BaseService
|
5
|
-
def self.run(
|
6
|
-
|
5
|
+
def self.run(options = nil)
|
6
|
+
if options && !options.is_a?(Hash)
|
7
|
+
new.run(options)
|
8
|
+
elsif options
|
9
|
+
new.run(**options)
|
10
|
+
else
|
11
|
+
new.run
|
12
|
+
end
|
7
13
|
end
|
8
14
|
end
|
9
15
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DefraRubyMocks
|
4
|
+
class WorldpayResourceService < BaseService
|
5
|
+
|
6
|
+
def run(reference:)
|
7
|
+
@reference = reference
|
8
|
+
|
9
|
+
raise(MissingResourceError, @reference) if resource.nil?
|
10
|
+
|
11
|
+
WorldpayResource.new(resource, order, company_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
WorldpayResource = Struct.new(:resource, :order, :company_name)
|
17
|
+
|
18
|
+
def resource
|
19
|
+
@_resource ||= locate_transient_registration || locate_completed_registration
|
20
|
+
end
|
21
|
+
|
22
|
+
def locate_transient_registration
|
23
|
+
"WasteCarriersEngine::TransientRegistration"
|
24
|
+
.constantize
|
25
|
+
.where(token: @reference)
|
26
|
+
.first
|
27
|
+
end
|
28
|
+
|
29
|
+
def locate_completed_registration
|
30
|
+
"WasteCarriersEngine::Registration"
|
31
|
+
.constantize
|
32
|
+
.where(reg_uuid: @reference)
|
33
|
+
.first
|
34
|
+
end
|
35
|
+
|
36
|
+
def locate_original_registration(reg_identifier)
|
37
|
+
"WasteCarriersEngine::Registration"
|
38
|
+
.constantize
|
39
|
+
.where(reg_identifier: reg_identifier)
|
40
|
+
.first
|
41
|
+
end
|
42
|
+
|
43
|
+
def order
|
44
|
+
@_order ||= resource.finance_details&.orders&.order_by(dateCreated: :desc)&.first
|
45
|
+
end
|
46
|
+
|
47
|
+
def company_name
|
48
|
+
if resource.class.to_s == "WasteCarriersEngine::OrderCopyCardsRegistration"
|
49
|
+
locate_original_registration(resource.reg_identifier).company_name.downcase
|
50
|
+
else
|
51
|
+
resource.company_name.downcase
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -3,16 +3,40 @@
|
|
3
3
|
module DefraRubyMocks
|
4
4
|
class WorldpayResponseService < BaseService
|
5
5
|
|
6
|
-
def run(success_url)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
def run(success_url:, failure_url:, pending_url:, cancel_url:, error_url:)
|
7
|
+
urls = {
|
8
|
+
success: success_url,
|
9
|
+
failure: failure_url,
|
10
|
+
pending: pending_url,
|
11
|
+
cancel: cancel_url,
|
12
|
+
error: error_url
|
13
|
+
}
|
14
|
+
|
15
|
+
parse_reference(urls[:success])
|
16
|
+
@resource = WorldpayResourceService.run(reference: @reference)
|
17
|
+
|
18
|
+
generate_response(urls)
|
12
19
|
end
|
13
20
|
|
14
21
|
private
|
15
22
|
|
23
|
+
WorldpayResponse = Struct.new(:supplied_url, :separator, :order_key, :mac, :value, :status, :reference) do
|
24
|
+
def url
|
25
|
+
[supplied_url, separator, params].join
|
26
|
+
end
|
27
|
+
|
28
|
+
def params
|
29
|
+
[
|
30
|
+
"orderKey=#{order_key}",
|
31
|
+
"paymentStatus=#{status}",
|
32
|
+
"paymentAmount=#{value}",
|
33
|
+
"paymentCurrency=GBP",
|
34
|
+
"mac=#{mac}",
|
35
|
+
"source=WP"
|
36
|
+
].join("&")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
16
40
|
def parse_reference(url)
|
17
41
|
path = URI.parse(url).path
|
18
42
|
parts = path.split("/")
|
@@ -28,71 +52,66 @@ module DefraRubyMocks
|
|
28
52
|
end
|
29
53
|
end
|
30
54
|
|
31
|
-
def locate_registration
|
32
|
-
@registration = locate_transient_registration || locate_completed_registration
|
33
|
-
|
34
|
-
raise(MissingRegistrationError, @reference) if @registration.nil?
|
35
|
-
end
|
36
|
-
|
37
|
-
def locate_transient_registration
|
38
|
-
"WasteCarriersEngine::TransientRegistration"
|
39
|
-
.constantize
|
40
|
-
.where(token: @reference)
|
41
|
-
.first
|
42
|
-
end
|
43
|
-
|
44
|
-
def locate_completed_registration
|
45
|
-
"WasteCarriersEngine::Registration"
|
46
|
-
.constantize
|
47
|
-
.where(reg_uuid: @reference)
|
48
|
-
.first
|
49
|
-
end
|
50
|
-
|
51
|
-
def last_order
|
52
|
-
@registration.finance_details&.orders&.order_by(dateCreated: :desc)&.first
|
53
|
-
end
|
54
|
-
|
55
55
|
def order_key
|
56
56
|
[
|
57
57
|
DefraRubyMocks.configuration.worldpay_admin_code,
|
58
58
|
DefraRubyMocks.configuration.worldpay_merchant_code,
|
59
|
-
@order.order_code
|
59
|
+
@resource.order.order_code
|
60
60
|
].join("^")
|
61
61
|
end
|
62
62
|
|
63
63
|
def order_value
|
64
|
-
@order.total_amount.to_s
|
64
|
+
@resource.order.total_amount.to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
def payment_status
|
68
|
+
return :REFUSED if @resource.company_name.include?("reject")
|
69
|
+
return :STUCK if @resource.company_name.include?("stuck")
|
70
|
+
return :SENT_FOR_AUTHORISATION if @resource.company_name.include?("pending")
|
71
|
+
return :CANCELLED if @resource.company_name.include?("cancel")
|
72
|
+
return :ERROR if @resource.company_name.include?("error")
|
73
|
+
|
74
|
+
:AUTHORISED
|
75
|
+
end
|
76
|
+
|
77
|
+
def url(payment_status, urls)
|
78
|
+
return urls[:failure] if %i[REFUSED STUCK].include?(payment_status)
|
79
|
+
return urls[:pending] if payment_status == :SENT_FOR_AUTHORISATION
|
80
|
+
return urls[:cancel] if payment_status == :CANCELLED
|
81
|
+
return urls[:error] if payment_status == :ERROR
|
82
|
+
|
83
|
+
urls[:success]
|
65
84
|
end
|
66
85
|
|
67
|
-
|
86
|
+
# Generate a mac that matches what Worldpay would generate
|
87
|
+
#
|
88
|
+
# For whatever reason, if the payment is cancelled by the user Worldpay does
|
89
|
+
# not include the payment status in the mac it generates. Plus the order of
|
90
|
+
# things in the array is important.
|
91
|
+
def generate_mac(status)
|
68
92
|
data = [
|
69
93
|
order_key,
|
70
94
|
order_value,
|
71
|
-
"GBP"
|
72
|
-
"AUTHORISED",
|
73
|
-
DefraRubyMocks.configuration.worldpay_mac_secret
|
95
|
+
"GBP"
|
74
96
|
]
|
97
|
+
data << status unless status == :CANCELLED
|
98
|
+
data << DefraRubyMocks.configuration.worldpay_mac_secret
|
75
99
|
|
76
100
|
Digest::MD5.hexdigest(data.join).to_s
|
77
101
|
end
|
78
102
|
|
79
|
-
def
|
80
|
-
|
81
|
-
"orderKey=#{order_key}",
|
82
|
-
"paymentStatus=AUTHORISED",
|
83
|
-
"paymentAmount=#{order_value}",
|
84
|
-
"paymentCurrency=GBP",
|
85
|
-
"mac=#{generate_mac}",
|
86
|
-
"source=WP"
|
87
|
-
].join("&")
|
88
|
-
end
|
103
|
+
def generate_response(urls)
|
104
|
+
status = payment_status
|
89
105
|
|
90
|
-
|
91
|
-
|
92
|
-
"
|
93
|
-
|
94
|
-
|
95
|
-
|
106
|
+
WorldpayResponse.new(
|
107
|
+
url(status, urls),
|
108
|
+
@url_format == :new ? "?" : "&",
|
109
|
+
order_key,
|
110
|
+
generate_mac(status),
|
111
|
+
order_value,
|
112
|
+
status,
|
113
|
+
@reference
|
114
|
+
)
|
96
115
|
end
|
97
116
|
end
|
98
117
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
|
3
|
+
<html lang="en">
|
4
|
+
<head>
|
5
|
+
<meta charset="utf-8">
|
6
|
+
|
7
|
+
<title>You is stuck</title>
|
8
|
+
<meta name="description" content="Thw Worldpay stuck page">
|
9
|
+
<meta name="author" content="Defra">
|
10
|
+
</head>
|
11
|
+
|
12
|
+
<body>
|
13
|
+
<main>
|
14
|
+
<div id="message">
|
15
|
+
<h1>Stuck!</h1>
|
16
|
+
<p>Looks like your registration has gotten stuck whilst paying for it using Worldpay.</p>
|
17
|
+
<p>We hope that's what you expected to happen.</p>
|
18
|
+
<p>Regards, the Ruby Services Team</p>
|
19
|
+
</div>
|
20
|
+
<% if @response %>
|
21
|
+
<div id="debug">
|
22
|
+
<h2>Debug</h2>
|
23
|
+
<ul>
|
24
|
+
<li><strong>Supplied URL</strong> <code><%= @response.supplied_url %></code></li>
|
25
|
+
<li><strong>Separator</strong> <code><%= @response.separator %></code></li>
|
26
|
+
<li><strong>Order key</strong> <code><%= @response.order_key %></code></li>
|
27
|
+
<li><strong>Mac</strong> <code><%= @response.mac %></code></li>
|
28
|
+
<li><strong>Value</strong> <code><%= @response.value %></code></li>
|
29
|
+
<li><strong>Status</strong> <code><%= @response.status %></code></li>
|
30
|
+
<li><strong>Reference</strong> <code><%= @response.reference %></code></li>
|
31
|
+
<li><strong>Url</strong> <code><%= @response.url %></code></li>
|
32
|
+
</ul>
|
33
|
+
</div>
|
34
|
+
<% end %>
|
35
|
+
</main>
|
36
|
+
</body>
|
37
|
+
</html>
|
@@ -18,7 +18,7 @@ module DefraRubyMocks
|
|
18
18
|
get "#{path}/#{company_number}"
|
19
19
|
content = JSON.parse(response.body)
|
20
20
|
|
21
|
-
expect(response.
|
21
|
+
expect(response.media_type).to eq("application/json")
|
22
22
|
expect(response.code).to eq("404")
|
23
23
|
expect(content).to include("errors")
|
24
24
|
end
|
@@ -31,7 +31,7 @@ module DefraRubyMocks
|
|
31
31
|
get "#{path}/#{company_number}"
|
32
32
|
company_status = JSON.parse(response.body)["company_status"]
|
33
33
|
|
34
|
-
expect(response.
|
34
|
+
expect(response.media_type).to eq("application/json")
|
35
35
|
expect(response.code).to eq("200")
|
36
36
|
expect(company_status).not_to eq("active")
|
37
37
|
end
|
@@ -45,7 +45,7 @@ module DefraRubyMocks
|
|
45
45
|
get "#{path}/#{company_number}"
|
46
46
|
company_status = JSON.parse(response.body)["company_status"]
|
47
47
|
|
48
|
-
expect(response.
|
48
|
+
expect(response.media_type).to eq("application/json")
|
49
49
|
expect(response.code).to eq("200")
|
50
50
|
expect(company_status).to eq("active")
|
51
51
|
end
|
@@ -58,7 +58,7 @@ module DefraRubyMocks
|
|
58
58
|
get "#{path}/#{company_number}"
|
59
59
|
content = JSON.parse(response.body)
|
60
60
|
|
61
|
-
expect(response.
|
61
|
+
expect(response.media_type).to eq("application/json")
|
62
62
|
expect(response.code).to eq("404")
|
63
63
|
expect(content).to include("errors")
|
64
64
|
end
|