defra_ruby_mocks 2.3.3 → 2.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +32 -75
- data/app/controllers/defra_ruby_mocks/govpay_controller.rb +23 -0
- data/app/services/defra_ruby_mocks/govpay_refund_details_service.rb +43 -0
- data/app/services/defra_ruby_mocks/govpay_request_refund_service.rb +27 -0
- data/config/routes.rb +10 -10
- data/lib/defra_ruby_mocks/configuration.rb +1 -1
- data/lib/defra_ruby_mocks/engine.rb +0 -1
- data/lib/defra_ruby_mocks/version.rb +1 -1
- data/spec/dummy/log/test.log +1256 -2285
- data/spec/examples.txt +68 -130
- data/spec/requests/govpay_spec.rb +44 -1
- data/spec/services/govpay_refund_details_service_spec.rb +58 -0
- data/spec/services/govpay_request_refund_service_spec.rb +31 -0
- metadata +8 -48
- data/app/controllers/defra_ruby_mocks/worldpay_controller.rb +0 -57
- data/app/services/defra_ruby_mocks/worldpay_payment_service.rb +0 -47
- data/app/services/defra_ruby_mocks/worldpay_refund_service.rb +0 -37
- data/app/services/defra_ruby_mocks/worldpay_request_handler_service.rb +0 -40
- data/app/services/defra_ruby_mocks/worldpay_resource_service.rb +0 -55
- data/app/services/defra_ruby_mocks/worldpay_response_service.rb +0 -119
- data/app/views/defra_ruby_mocks/worldpay/payment_request.xml.erb +0 -4
- data/app/views/defra_ruby_mocks/worldpay/refund_request.xml.erb +0 -4
- data/app/views/defra_ruby_mocks/worldpay/stuck.html.erb +0 -37
- data/lib/defra_ruby_mocks/unrecognised_worldpay_request_error.rb +0 -5
- data/spec/fixtures/files/worldpay/payment_request_invalid.xml +0 -6
- data/spec/fixtures/files/worldpay/payment_request_valid.xml +0 -30
- data/spec/fixtures/files/worldpay/refund_request_invalid.xml +0 -6
- data/spec/fixtures/files/worldpay/refund_request_valid.xml +0 -11
- data/spec/fixtures/files/worldpay/unrecognised_request.xml +0 -6
- data/spec/requests/worldpay_spec.rb +0 -163
- data/spec/services/worldpay_payment_service_spec.rb +0 -95
- data/spec/services/worldpay_refund_service_spec.rb +0 -68
- data/spec/services/worldpay_request_handler_service_spec.rb +0 -79
- data/spec/services/worldpay_resource_service_spec.rb +0 -120
- data/spec/services/worldpay_response_service_spec.rb +0 -280
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a13df047d9e598de81cd0095bef9249f4a38024966f316f16d1c502210501fd6
|
4
|
+
data.tar.gz: 9aae938178998072c2d609d5bc0d67bbf00d0d7005c4d6ba5e6a48c1b0d1713d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4fe9e16492507fb0675b3428fc94ea86ddeb45712987bf72610f011df375f73db480df6a868d3b16503b5c9530792401d3249a4111bdeb061346a22a45654873
|
7
|
+
data.tar.gz: e0c5166bd090c4ea242a35ec1a35ecedb3b60b0a282a0a326a08825b1acc06b707942540dab1af24159dc13d27d6362664bd647c0cc83ee649df1bf09d353634
|
data/README.md
CHANGED
@@ -132,117 +132,74 @@ The list of possible statuses was taken from
|
|
132
132
|
- [Companies House API](https://developer.companieshouse.gov.uk/api/docs/company/company_number/companyProfile-resource.html)
|
133
133
|
- [Companies House API enumerations](https://github.com/companieshouse/api-enumerations/blob/master/constants.yml)
|
134
134
|
|
135
|
-
###
|
135
|
+
### Govpay
|
136
136
|
|
137
|
-
When mounted into an app you can simulate interacting with the
|
137
|
+
When mounted into an app you can simulate interacting with the Govpay hosted pages service. The following endpoints are supported:
|
138
138
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
This Worldpay mock replicates those 2 interactions with the following urls
|
149
|
-
|
150
|
-
- `../worldpay/payments-service`
|
151
|
-
- `../worldpay/dispatcher`
|
152
|
-
|
153
|
-
##### Cancelled payments
|
154
|
-
|
155
|
-
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).
|
156
|
-
|
157
|
-
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`.
|
158
|
-
|
159
|
-
This allows us to test how the application handles Worldpay responding with a cancelled payment response.
|
160
|
-
|
161
|
-
##### Errored payments
|
162
|
-
|
163
|
-
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).
|
164
|
-
|
165
|
-
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`.
|
166
|
-
|
167
|
-
This allows us to test how the application handles Worldpay responding with an errored payment response.
|
168
|
-
|
169
|
-
##### Pending payments
|
170
|
-
|
171
|
-
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).
|
139
|
+
- `POST /govpay/v1/payments`
|
140
|
+
- Create a payment. In its current form it always returns a fixed (success) response regardless of the input parameters.
|
141
|
+
- `GET /govpay/v1/payments/:payment_id`
|
142
|
+
- Get details of an existing payment. The response includes the payment_id from the input parameters, a random amount, and the current time as created_at value.
|
143
|
+
- `POST /govpay/v1/payments/:payment_id/refunds`
|
144
|
+
- Request a refund. The response includes the amount from the input parameters and a status of "submitted", meaning that the refund is pending. It also writes the current time to a temporary file to support refund details checking - see *Govpay refund status* below.
|
145
|
+
- `GET /govpay/v1/payments/:payment_id/refunds/:refund_id`
|
146
|
+
- Get the details of an existing refund. This currently returns a fixed response, with the exception of the `status` value. See *Govpay refund status* below.
|
172
147
|
|
173
|
-
|
148
|
+
#### Govpay refund status
|
149
|
+
The Govpay service behaves differently in production and in test (sandbox) modes.
|
150
|
+
- In **production**, a refund is initially assigned a status of `submitted` and this is the `status` value that will be received in the response to a successful create refund request. When the payment provider processes the refund, the status within the Govpayservice will be updated to `success`.
|
151
|
+
- In **sandbox** mode, a refund will be assigned a status of `success` as soon as it is created.
|
174
152
|
|
175
|
-
This
|
153
|
+
This causes issues for testing, as it is not possible to test behaviour around `submitted` (i.e. pending) refunds. To mitigate this, the mock API for getting refund details behaves as follows:
|
154
|
+
- Default: Return success
|
155
|
+
- If the environment variable `GOVPAY_REFUND_SUBMITTED_SUCCESS_LAG` is set (integer value):
|
156
|
+
- If fewer than that number of seconds has elapsed since the most recent refund request, return submitted
|
157
|
+
- If greater than that number of seconds has elapsed since the most recent refund request, return success
|
176
158
|
|
177
|
-
|
159
|
+
So setting the `GOVPAY_REFUND_SUBMITTED_SUCCESS_LAG` variable should allow a developer or tester to simulate production behaviour. The iniital response will be `submitted`; checking the refund status before the specified lag has passed will also return `submitted`; and checking the refund status after the lag has passed will return `success`.
|
178
160
|
|
179
|
-
|
180
|
-
|
181
|
-
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`.
|
182
|
-
|
183
|
-
This allows us to test how the application handles both successful and unsucessful Worldpay payments.
|
184
|
-
|
185
|
-
##### Stuck payments
|
186
|
-
|
187
|
-
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).
|
188
|
-
|
189
|
-
If it does the engine will not redirect back to the service, but instead render a 'You're stuck!' page.
|
190
|
-
|
191
|
-
This allows us to test how the application handles Worldpay not returning after we redirect a user to them.
|
192
|
-
|
193
|
-
#### Refunds
|
161
|
+
#### Payments
|
194
162
|
|
195
|
-
|
163
|
+
Making a payment with Govpay requires three steps:
|
196
164
|
|
197
|
-
1. The app sends
|
165
|
+
1. The app sends a JSON request to Govpay asking it to prepare for a new payment, and providing a unique identifier and a callback URL.
|
166
|
+
2. Govpay subsequently invokes the callback URL, passing a Govpay URL to which the user should be redirected.
|
167
|
+
2. The app redirects the user to the Govpay URL with params that tell Govpay where to redirect the user to when the payment is complete.
|
198
168
|
|
199
|
-
|
169
|
+
This Govpay mock replicates those 2 interactions with the following url
|
170
|
+
- `../govpay/v1/payments`
|
200
171
|
|
201
172
|
#### Configuration
|
202
173
|
|
203
|
-
In order to use the
|
174
|
+
In order to use the govpay mock you'll need to provide additional configuration details
|
204
175
|
|
205
176
|
```ruby
|
206
177
|
# config/initializers/defra_ruby_mocks.rb
|
207
178
|
require "defra_ruby_mocks"
|
208
179
|
|
209
180
|
DefraRubyMocks.configure do |config|
|
210
|
-
config.
|
211
|
-
config.delay = 1000
|
212
|
-
|
213
|
-
config.worldpay_admin_code = "admincode1"
|
214
|
-
config.worldpay_mac_secret = "macsecret1"
|
215
|
-
config.worldpay_merchant_code = "merchantcode1"
|
216
|
-
config.worldpay_domain = "http://localhost:3000/mocks"
|
181
|
+
config.govpay_domain = File.join(ENV["WCRS_GOVPAY_DOMAIN"] || "http://localhost:3002", "/fo/mocks/govpay/v1")
|
217
182
|
end
|
218
183
|
```
|
219
184
|
|
220
|
-
|
221
|
-
|
222
|
-
The domain is used when generating the URL we tell the app to redirect users to. As this is just an engine and not a standalone service, we need to tell it what domain it is running from. For example, if the engine is mounted into the app like this
|
185
|
+
The domain is used when generating the URL we tell the app to redirect users to. As this is just an engine and not a standalone service, we need to tell it what domain it is running from.
|
223
186
|
|
224
187
|
```ruby
|
225
188
|
mount DefraRubyMocks::Engine => "/mocks"
|
226
189
|
```
|
227
190
|
|
228
|
-
And the app is running at `http://localhost:3000`, this engine can then use that information to tell the app to redirect users to `http://localhost:3000/mocks/worldpay/dispatcher` as part of the `payments-service` response.
|
229
|
-
|
230
191
|
#### Only for Waste Carriers
|
231
192
|
|
232
|
-
At this time there is only one digital service built using Ruby on Rails that uses
|
193
|
+
At this time there is only one digital service built using Ruby on Rails that uses Govpay; the [Waste Carriers Registration service](https://github.com/DEFRA/ruby-services-team/tree/master/services/wcr). So the Govpay mock has been written with the assumption it will only be mounted into one of the Waste Carriers apps.
|
233
194
|
|
234
195
|
A critical aspect of this is the expectation that the following classes will be loaded and available when the engine is mounted and the app is running
|
235
196
|
|
236
197
|
- `WasteCarriersEngine::TransientRegistration`
|
237
198
|
- `WasteCarriersEngine::Registration`
|
238
199
|
|
239
|
-
We need these classes so we can use them to query the database for the registration the payment is being made against. We only get the registration reference in the request made to `/worldpay/dispatcher`, not the order code. As the response needs to include the order code we need access to these ActiveRecord models to locate the last order added.
|
240
|
-
|
241
|
-
In the live Worldpay service this information (along with the amount to be paid) is saved after the initial request to `/payments-service`. The mock however isn't persisting anything to reduce complexity. So instead it needs to be able to query the database for the information it needs via ActiveRecord.
|
242
|
-
|
243
200
|
#### Payment pages are not mocked
|
244
201
|
|
245
|
-
The actual
|
202
|
+
The actual Govpay service presents payment pages that display a form where users are able to enter their credit card details and confirm the payment is correct. This mock does **not** replicate the UI of Govpay, only the API. Bear this in mind when building any automated acceptance tests.
|
246
203
|
|
247
204
|
## Installation
|
248
205
|
|
@@ -27,6 +27,23 @@ module DefraRubyMocks
|
|
27
27
|
head 422
|
28
28
|
end
|
29
29
|
|
30
|
+
def create_refund
|
31
|
+
valid_create_refund_params
|
32
|
+
render json: GovpayRequestRefundService.new.run(payment_id: params[:payment_id],
|
33
|
+
amount: params[:amount],
|
34
|
+
refund_amount_available: params[:refund_amount_available])
|
35
|
+
rescue StandardError => e
|
36
|
+
Rails.logger.error("MOCKS: Govpay refund error: #{e.message}")
|
37
|
+
head 500
|
38
|
+
end
|
39
|
+
|
40
|
+
def refund_details
|
41
|
+
render json: GovpayRefundDetailsService.new.run(payment_id: params[:payment_id], refund_id: params[:refund_id])
|
42
|
+
rescue StandardError => e
|
43
|
+
Rails.logger.error("MOCKS: Govpay refund error: #{e.message}")
|
44
|
+
head 500
|
45
|
+
end
|
46
|
+
|
30
47
|
def valid_create_params
|
31
48
|
params.require(%i[amount description return_url])
|
32
49
|
end
|
@@ -36,5 +53,11 @@ module DefraRubyMocks
|
|
36
53
|
|
37
54
|
raise ArgumentError, "Invalid Govpay payment ID #{params[:payment_id]}"
|
38
55
|
end
|
56
|
+
|
57
|
+
def valid_create_refund_params
|
58
|
+
valid_payment_id
|
59
|
+
raise ArgumentError, "Invalid refund amount" unless params[:amount].present?
|
60
|
+
raise ArgumentError, "Invalid refund amount available" unless params[:refund_amount_available].present?
|
61
|
+
end
|
39
62
|
end
|
40
63
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DefraRubyMocks
|
4
|
+
class GovpayRefundDetailsService < BaseService
|
5
|
+
|
6
|
+
def run(payment_id:, refund_id:) # rubocop:disable Lint/UnusedMethodArgument
|
7
|
+
{
|
8
|
+
"amount": 2000,
|
9
|
+
"created_date": "2019-09-19T16:53:03.213Z",
|
10
|
+
"refund_id": refund_id,
|
11
|
+
"status": status,
|
12
|
+
"settlement_summary": {
|
13
|
+
"settled_date": "2019-09-21"
|
14
|
+
}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# "submitted" for up to GOVPAY_REFUND_SUBMITTED_SUCCESS_LAG seconds after the last refund request, then "success"
|
21
|
+
def status
|
22
|
+
timestamp = File.read("#{Dir.tmpdir}/govpay_request_refund_service_last_run_time")
|
23
|
+
last_refund_time = timestamp ? Time.parse(timestamp) : 1.day.ago
|
24
|
+
submitted_success_lag = ENV.fetch("GOVPAY_REFUND_SUBMITTED_SUCCESS_LAG", 0).to_i
|
25
|
+
cutoff_time = (last_refund_time + submitted_success_lag.seconds).to_time
|
26
|
+
return "success" if submitted_success_lag.zero?
|
27
|
+
|
28
|
+
Time.zone.now < cutoff_time ? "submitted" : "success"
|
29
|
+
rescue Errno::ENOENT
|
30
|
+
write_timestamp_file("govpay_request_refund_service_last_run_time")
|
31
|
+
|
32
|
+
"success"
|
33
|
+
end
|
34
|
+
|
35
|
+
def write_timestamp_file(filename)
|
36
|
+
filepath = "#{Dir.tmpdir}/#{filename}"
|
37
|
+
|
38
|
+
# FileUtils.touch seems unreliable in VM so need to write/read the actual time
|
39
|
+
File.write(filepath, Time.zone.now)
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DefraRubyMocks
|
4
|
+
class GovpayRequestRefundService < BaseService
|
5
|
+
|
6
|
+
def run(payment_id:, amount:, refund_amount_available:) # rubocop:disable Lint/UnusedMethodArgument
|
7
|
+
write_timestamp
|
8
|
+
|
9
|
+
# This currently supports only "submitted" status:
|
10
|
+
{
|
11
|
+
"amount": amount,
|
12
|
+
"created_date": "2019-09-19T16:53:03.213Z",
|
13
|
+
"refund_id": SecureRandom.hex(22),
|
14
|
+
"status": "submitted"
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
# let the refund details service know how long since the refund was requested
|
21
|
+
def write_timestamp
|
22
|
+
filepath = "#{Dir.tmpdir}/govpay_request_refund_service_last_run_time"
|
23
|
+
# FileUtils.touch seems unreliable in VM so need to write/read the actual time
|
24
|
+
File.write(filepath, Time.zone.now)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/config/routes.rb
CHANGED
@@ -11,16 +11,6 @@ DefraRubyMocks::Engine.routes.draw do
|
|
11
11
|
as: "company_officers",
|
12
12
|
constraints: ->(_request) { DefraRubyMocks.configuration.enabled? }
|
13
13
|
|
14
|
-
post "/worldpay/payments-service",
|
15
|
-
to: "worldpay#payments_service",
|
16
|
-
as: "worldpay_payments_service",
|
17
|
-
constraints: ->(_request) { DefraRubyMocks.configuration.enabled? }
|
18
|
-
|
19
|
-
get "/worldpay/dispatcher",
|
20
|
-
to: "worldpay#dispatcher",
|
21
|
-
as: "worldpay_dispatcher",
|
22
|
-
constraints: ->(_request) { DefraRubyMocks.configuration.enabled? }
|
23
|
-
|
24
14
|
post "/govpay/v1/payments",
|
25
15
|
to: "govpay#create_payment",
|
26
16
|
as: "govpay_create_payment",
|
@@ -30,4 +20,14 @@ DefraRubyMocks::Engine.routes.draw do
|
|
30
20
|
to: "govpay#payment_details",
|
31
21
|
as: "govpay_payment_details",
|
32
22
|
constraints: ->(_request) { DefraRubyMocks.configuration.enabled? }
|
23
|
+
|
24
|
+
post "/govpay/v1/payments/:payment_id/refunds",
|
25
|
+
to: "govpay#create_refund",
|
26
|
+
as: "govpay_create_refund",
|
27
|
+
constraints: ->(_request) { DefraRubyMocks.configuration.enabled? }
|
28
|
+
|
29
|
+
get "/govpay/v1/payments/:payment_id/refunds/:refund_id",
|
30
|
+
to: "govpay#refund_details",
|
31
|
+
as: "govpay_refund_details",
|
32
|
+
constraints: ->(_request) { DefraRubyMocks.configuration.enabled? }
|
33
33
|
end
|