defra_ruby_govpay 1.0.2 → 1.1.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 +4 -4
- data/CHANGELOG.md +20 -0
- data/Gemfile.lock +12 -11
- data/README.md +8 -7
- data/lib/defra_ruby_govpay/services/webhook_base_service.rb +16 -16
- data/lib/defra_ruby_govpay/services/webhook_payment_service.rb +1 -17
- data/lib/defra_ruby_govpay/services/webhook_refund_service.rb +7 -26
- data/lib/defra_ruby_govpay/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a727a626f74c5efcd304567763038db11f30b9f81afa6e18189e0cd04a96f0b4
|
4
|
+
data.tar.gz: 78382f4bd08bef079d068db1938fbb7d9e21137239750746051a6ea47edaa750
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8956999c3c6cdf5d8861c3502155ea7342175d21b94d0409c443f81afac8d61cdafbd80d5ce68a70cc1361e1fe1f385fca45181879091ef3aaeabfd95e36d412
|
7
|
+
data.tar.gz: ce0434005dc833dcab5b5b2537753ebe538f83683b2ba5cf054baac2450e15cd8cf6f2535fd84dc1b4ce7f5a26f43b1ed634fad296cefc6fe2953aa4d657e3ab
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,25 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## [Unreleased](https://github.com/DEFRA/defra-ruby-govpay/tree/HEAD)
|
4
|
+
|
5
|
+
[Full Changelog](https://github.com/DEFRA/defra-ruby-govpay/compare/v1.0.2...HEAD)
|
6
|
+
|
7
|
+
**Fixed bugs:**
|
8
|
+
|
9
|
+
- Fix/ruby 3931 refund callbacks [\#45](https://github.com/DEFRA/defra-ruby-govpay/pull/45) ([PaulDoyle-EA](https://github.com/PaulDoyle-EA))
|
10
|
+
|
11
|
+
**Merged pull requests:**
|
12
|
+
|
13
|
+
- Bump rubocop-rspec from 3.6.0 to 3.7.0 [\#46](https://github.com/DEFRA/defra-ruby-govpay/pull/46) ([dependabot[bot]](https://github.com/apps/dependabot))
|
14
|
+
|
15
|
+
## [v1.0.2](https://github.com/DEFRA/defra-ruby-govpay/tree/v1.0.2) (2025-08-06)
|
16
|
+
|
17
|
+
[Full Changelog](https://github.com/DEFRA/defra-ruby-govpay/compare/v1.0.1...v1.0.2)
|
18
|
+
|
19
|
+
**Fixed bugs:**
|
20
|
+
|
21
|
+
- Fix/exclude gem file [\#42](https://github.com/DEFRA/defra-ruby-govpay/pull/42) ([PaulDoyle-EA](https://github.com/PaulDoyle-EA))
|
22
|
+
|
3
23
|
## [v1.0.1](https://github.com/DEFRA/defra-ruby-govpay/tree/v1.0.1) (2025-08-05)
|
4
24
|
|
5
25
|
[Full Changelog](https://github.com/DEFRA/defra-ruby-govpay/compare/v1.0.0...v1.0.1)
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
defra_ruby_govpay (1.0
|
4
|
+
defra_ruby_govpay (1.1.0)
|
5
5
|
rest-client (~> 2.1)
|
6
6
|
|
7
7
|
GEM
|
@@ -42,7 +42,7 @@ GEM
|
|
42
42
|
async (>= 1.25)
|
43
43
|
base64 (0.1.1)
|
44
44
|
bigdecimal (3.2.2)
|
45
|
-
byebug (
|
45
|
+
byebug (12.0.0)
|
46
46
|
concurrent-ruby (1.2.2)
|
47
47
|
connection_pool (2.4.1)
|
48
48
|
console (1.23.2)
|
@@ -114,14 +114,14 @@ GEM
|
|
114
114
|
public_suffix (6.0.2)
|
115
115
|
racc (1.8.1)
|
116
116
|
rainbow (3.1.1)
|
117
|
-
rake (13.
|
118
|
-
regexp_parser (2.11.
|
117
|
+
rake (13.3.0)
|
118
|
+
regexp_parser (2.11.2)
|
119
119
|
rest-client (2.1.0)
|
120
120
|
http-accept (>= 1.7.0, < 2.0)
|
121
121
|
http-cookie (>= 1.0.2, < 2.0)
|
122
122
|
mime-types (>= 1.16, < 4.0)
|
123
123
|
netrc (~> 0.8)
|
124
|
-
rexml (3.4.
|
124
|
+
rexml (3.4.2)
|
125
125
|
rspec (3.13.1)
|
126
126
|
rspec-core (~> 3.13.0)
|
127
127
|
rspec-expectations (~> 3.13.0)
|
@@ -134,8 +134,8 @@ GEM
|
|
134
134
|
rspec-mocks (3.13.5)
|
135
135
|
diff-lcs (>= 1.2.0, < 2.0)
|
136
136
|
rspec-support (~> 3.13.0)
|
137
|
-
rspec-support (3.13.
|
138
|
-
rubocop (1.
|
137
|
+
rspec-support (3.13.5)
|
138
|
+
rubocop (1.80.1)
|
139
139
|
json (~> 2.3)
|
140
140
|
language_server-protocol (~> 3.17.0.2)
|
141
141
|
lint_roller (~> 1.1.0)
|
@@ -152,9 +152,10 @@ GEM
|
|
152
152
|
rubocop-factory_bot (2.27.1)
|
153
153
|
lint_roller (~> 1.1)
|
154
154
|
rubocop (~> 1.72, >= 1.72.1)
|
155
|
-
rubocop-rake (0.
|
156
|
-
|
157
|
-
|
155
|
+
rubocop-rake (0.7.1)
|
156
|
+
lint_roller (~> 1.1)
|
157
|
+
rubocop (>= 1.72.1)
|
158
|
+
rubocop-rspec (3.7.0)
|
158
159
|
lint_roller (~> 1.1)
|
159
160
|
rubocop (~> 1.72, >= 1.72.1)
|
160
161
|
ruby-progressbar (1.13.0)
|
@@ -169,7 +170,7 @@ GEM
|
|
169
170
|
unf (0.1.4)
|
170
171
|
unf_ext
|
171
172
|
unf_ext (0.0.8.2)
|
172
|
-
unicode-display_width (3.1.
|
173
|
+
unicode-display_width (3.1.5)
|
173
174
|
unicode-emoji (~> 4.0, >= 4.0.4)
|
174
175
|
unicode-emoji (4.0.4)
|
175
176
|
webmock (3.25.1)
|
data/README.md
CHANGED
@@ -98,12 +98,13 @@ result = DefraRubyGovpay::WebhookRefundService.run(webhook_body)
|
|
98
98
|
|
99
99
|
### Validating Webhook Signatures
|
100
100
|
|
101
|
-
|
101
|
+
The `WebhookSignatureService` service returns two HMAC hexdigests for the webhook body, one created using the front-office signature and one using the back-office signature. This is necessary because both MOTO and non-MOTO payment webhooks are processed by the front-office, as the back-office is inaccessible to GOV.UK Pay.
|
102
|
+
|
103
|
+
To validate the signature of a webhook, use the `WebhookBodyValidatorService` class:
|
102
104
|
|
103
105
|
```ruby
|
104
|
-
valid = DefraRubyGovpay::
|
105
|
-
|
106
|
-
ENV['GOVPAY_WEBHOOK_SIGNING_SECRET'],
|
106
|
+
valid = DefraRubyGovpay::WebhookBodyValidatorService.call(
|
107
|
+
body: webhook_body,
|
107
108
|
request.headers['Pay-Signature']
|
108
109
|
)
|
109
110
|
|
@@ -116,10 +117,10 @@ end
|
|
116
117
|
|
117
118
|
### Payment vs Refund Webhooks
|
118
119
|
|
119
|
-
The gem can handle both payment and refund webhooks:
|
120
|
+
The gem can handle both payment and refund webhooks. Both have a top-level `resource_id` which refers to the relevant payment. There is no distinct identifier for a refund. Note that `resource_type` is "PAYMENT" for both payment and refund webhoks. The `event_type` indicates whether the webhook is for a payment or a refund:
|
120
121
|
|
121
|
-
- **Payment
|
122
|
-
- **Refund
|
122
|
+
- **Payment webhook**: `card_payment_succeeded` (for example)
|
123
|
+
- **Refund webhook**: `card_payment_refunded`
|
123
124
|
|
124
125
|
The appropriate service class will be used based on the webhook type:
|
125
126
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/core_ext/object/blank"
|
4
|
+
require "active_support/core_ext/hash/keys"
|
4
5
|
|
5
6
|
module DefraRubyGovpay
|
6
7
|
class WebhookBaseService
|
@@ -15,12 +16,8 @@ module DefraRubyGovpay
|
|
15
16
|
new.run(webhook_body, previous_status: previous_status)
|
16
17
|
end
|
17
18
|
|
18
|
-
def initialize
|
19
|
-
# No initialization needed
|
20
|
-
end
|
21
|
-
|
22
19
|
def run(webhook_body, previous_status: nil)
|
23
|
-
@webhook_body = webhook_body
|
20
|
+
@webhook_body = webhook_body.deep_symbolize_keys
|
24
21
|
@previous_status = previous_status
|
25
22
|
|
26
23
|
validate_webhook_body
|
@@ -35,7 +32,6 @@ module DefraRubyGovpay
|
|
35
32
|
)
|
36
33
|
end
|
37
34
|
|
38
|
-
# Extract and return data from webhook
|
39
35
|
extract_data_from_webhook
|
40
36
|
end
|
41
37
|
|
@@ -51,30 +47,34 @@ module DefraRubyGovpay
|
|
51
47
|
|
52
48
|
def extract_data_from_webhook
|
53
49
|
{
|
54
|
-
id:
|
50
|
+
id: webhook_payment_id,
|
55
51
|
status: webhook_payment_or_refund_status,
|
56
52
|
webhook_body: webhook_body
|
57
53
|
}
|
58
54
|
end
|
59
55
|
|
60
|
-
|
61
|
-
|
62
|
-
raise NotImplementedError
|
56
|
+
def webhook_payment_id
|
57
|
+
@webhook_payment_id ||= webhook_body[:resource_id]
|
63
58
|
end
|
64
59
|
|
65
|
-
def
|
66
|
-
|
60
|
+
def log_webhook_context
|
61
|
+
" for payment #{webhook_payment_id}"
|
67
62
|
end
|
68
63
|
|
69
|
-
def
|
70
|
-
|
64
|
+
def webhook_payment_or_refund_status
|
65
|
+
@webhook_payment_or_refund_status ||= webhook_body.dig(:resource, :state, :status)
|
71
66
|
end
|
72
67
|
|
73
|
-
def
|
68
|
+
def webhook_resource_type
|
69
|
+
@webhook_resource_type ||= webhook_body[:resource_type]&.downcase
|
70
|
+
end
|
71
|
+
|
72
|
+
# The following methods must be implemented in subclasses
|
73
|
+
def payment_or_refund_str
|
74
74
|
raise NotImplementedError
|
75
75
|
end
|
76
76
|
|
77
|
-
def
|
77
|
+
def validate_webhook_body
|
78
78
|
raise NotImplementedError
|
79
79
|
end
|
80
80
|
end
|
@@ -24,23 +24,7 @@ module DefraRubyGovpay
|
|
24
24
|
|
25
25
|
return unless webhook_payment_or_refund_status.blank?
|
26
26
|
|
27
|
-
raise ArgumentError, "Webhook body missing payment status: #{webhook_body}"
|
28
|
-
end
|
29
|
-
|
30
|
-
def webhook_resource_type
|
31
|
-
@webhook_resource_type ||= webhook_body["resource_type"]&.downcase
|
32
|
-
end
|
33
|
-
|
34
|
-
def webhook_payment_or_refund_id
|
35
|
-
@webhook_payment_or_refund_id ||= webhook_body.dig("resource", "payment_id")
|
36
|
-
end
|
37
|
-
|
38
|
-
def webhook_payment_or_refund_status
|
39
|
-
@webhook_payment_or_refund_status ||= webhook_body.dig("resource", "state", "status")
|
40
|
-
end
|
41
|
-
|
42
|
-
def log_webhook_context
|
43
|
-
"for payment #{webhook_payment_or_refund_id}"
|
27
|
+
raise ArgumentError, "Webhook body missing payment status: #{WebhookSanitizerService.call(webhook_body)}"
|
44
28
|
end
|
45
29
|
end
|
46
30
|
end
|
@@ -4,7 +4,7 @@ module DefraRubyGovpay
|
|
4
4
|
class WebhookRefundService < WebhookBaseService
|
5
5
|
|
6
6
|
VALID_STATUS_TRANSITIONS = {
|
7
|
-
"submitted" => %w[success],
|
7
|
+
"submitted" => %w[success error],
|
8
8
|
"success" => %w[],
|
9
9
|
"error" => %w[]
|
10
10
|
}.freeze
|
@@ -16,35 +16,16 @@ module DefraRubyGovpay
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def validate_webhook_body
|
19
|
-
return if
|
19
|
+
return if webhook_body[:event_type] == "card_payment_refunded" &&
|
20
|
+
webhook_payment_id.present? &&
|
21
|
+
webhook_refund_status.present?
|
20
22
|
|
21
|
-
raise ArgumentError, "Invalid refund webhook: #{webhook_body}"
|
23
|
+
raise ArgumentError, "Invalid refund webhook: #{WebhookSanitizerService.call(webhook_body)}"
|
22
24
|
end
|
23
25
|
|
24
|
-
def
|
25
|
-
@
|
26
|
+
def webhook_refund_status
|
27
|
+
@webhook_refund_status ||= webhook_body.dig(:resource, :state, :status)
|
26
28
|
end
|
27
29
|
|
28
|
-
def webhook_payment_or_refund_id
|
29
|
-
@webhook_payment_or_refund_id ||= webhook_body["refund_id"]
|
30
|
-
end
|
31
|
-
|
32
|
-
def webhook_payment_or_refund_status
|
33
|
-
@webhook_payment_or_refund_status ||= webhook_body["status"]
|
34
|
-
end
|
35
|
-
|
36
|
-
def extract_data_from_webhook
|
37
|
-
data = super
|
38
|
-
|
39
|
-
data.merge!(
|
40
|
-
payment_id: webhook_payment_id
|
41
|
-
)
|
42
|
-
|
43
|
-
data
|
44
|
-
end
|
45
|
-
|
46
|
-
def log_webhook_context
|
47
|
-
"for refund #{webhook_payment_or_refund_id}"
|
48
|
-
end
|
49
30
|
end
|
50
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: defra_ruby_govpay
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jerome Pratt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-09-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-client
|