defra_ruby_mocks 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Build Status](https://travis-ci.com/DEFRA/defra-ruby-mocks.svg?branch=
|
4
|
-
[![Maintainability](https://
|
5
|
-
[![
|
6
|
-
[![security](https://hakiri.io/github/DEFRA/defra-ruby-mocks/
|
3
|
+
[![Build Status](https://travis-ci.com/DEFRA/defra-ruby-mocks.svg?branch=main)](https://travis-ci.com/DEFRA/defra-ruby-mocks)
|
4
|
+
[![Maintainability Rating](https://sonarcloud.io/api/project_badges/measure?project=DEFRA_defra-ruby-mocks&metric=sqale_rating)](https://sonarcloud.io/dashboard?id=DEFRA_defra-ruby-mocks)
|
5
|
+
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=DEFRA_defra-ruby-mocks&metric=coverage)](https://sonarcloud.io/dashboard?id=DEFRA_defra-ruby-mocks)
|
6
|
+
[![security](https://hakiri.io/github/DEFRA/defra-ruby-mocks/main.svg)](https://hakiri.io/github/DEFRA/defra-ruby-mocks/main)
|
7
7
|
[![Licence](https://img.shields.io/badge/Licence-OGLv3-blue.svg)](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
|