webhukhs 0.5.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.
Files changed (105) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +14 -0
  3. data/.rubocop.yml +9 -0
  4. data/.standard.yml +3 -0
  5. data/Appraisals +13 -0
  6. data/CHANGELOG.md +54 -0
  7. data/LICENSE +21 -0
  8. data/README.md +71 -0
  9. data/Rakefile +26 -0
  10. data/config/routes.rb +3 -0
  11. data/example/.gitattributes +7 -0
  12. data/example/.gitignore +29 -0
  13. data/example/.ruby-version +1 -0
  14. data/example/Gemfile +32 -0
  15. data/example/Gemfile.lock +228 -0
  16. data/example/README.md +24 -0
  17. data/example/Rakefile +8 -0
  18. data/example/app/assets/images/.keep +0 -0
  19. data/example/app/assets/stylesheets/application.css +1 -0
  20. data/example/app/controllers/application_controller.rb +4 -0
  21. data/example/app/controllers/concerns/.keep +0 -0
  22. data/example/app/helpers/application_helper.rb +4 -0
  23. data/example/app/models/application_record.rb +5 -0
  24. data/example/app/models/concerns/.keep +0 -0
  25. data/example/app/views/layouts/application.html.erb +15 -0
  26. data/example/app/webhooks/webhook_test_handler.rb +13 -0
  27. data/example/bin/bundle +109 -0
  28. data/example/bin/rails +4 -0
  29. data/example/bin/rake +4 -0
  30. data/example/bin/setup +33 -0
  31. data/example/config/application.rb +39 -0
  32. data/example/config/boot.rb +5 -0
  33. data/example/config/credentials.yml.enc +1 -0
  34. data/example/config/database.yml +25 -0
  35. data/example/config/environment.rb +7 -0
  36. data/example/config/environments/development.rb +61 -0
  37. data/example/config/environments/production.rb +71 -0
  38. data/example/config/environments/test.rb +52 -0
  39. data/example/config/initializers/assets.rb +14 -0
  40. data/example/config/initializers/content_security_policy.rb +27 -0
  41. data/example/config/initializers/filter_parameter_logging.rb +10 -0
  42. data/example/config/initializers/generators.rb +7 -0
  43. data/example/config/initializers/inflections.rb +18 -0
  44. data/example/config/initializers/permissions_policy.rb +13 -0
  45. data/example/config/initializers/webhukhs.rb +9 -0
  46. data/example/config/locales/en.yml +33 -0
  47. data/example/config/puma.rb +45 -0
  48. data/example/config/routes.rb +5 -0
  49. data/example/config.ru +8 -0
  50. data/example/db/migrate/20240523125859_create_webhukhs_tables.rb +22 -0
  51. data/example/db/schema.rb +24 -0
  52. data/example/db/seeds.rb +9 -0
  53. data/example/lib/assets/.keep +0 -0
  54. data/example/lib/tasks/.keep +0 -0
  55. data/example/log/.keep +0 -0
  56. data/example/public/404.html +67 -0
  57. data/example/public/422.html +67 -0
  58. data/example/public/500.html +66 -0
  59. data/example/public/apple-touch-icon-precomposed.png +0 -0
  60. data/example/public/apple-touch-icon.png +0 -0
  61. data/example/public/favicon.ico +0 -0
  62. data/example/public/robots.txt +1 -0
  63. data/example/test/controllers/.keep +0 -0
  64. data/example/test/fixtures/files/.keep +0 -0
  65. data/example/test/helpers/.keep +0 -0
  66. data/example/test/integration/.keep +0 -0
  67. data/example/test/models/.keep +0 -0
  68. data/example/test/test_helper.rb +15 -0
  69. data/example/tmp/.keep +0 -0
  70. data/example/tmp/pids/.keep +0 -0
  71. data/example/vendor/.keep +0 -0
  72. data/gemfiles/rails_7.1.gemfile +10 -0
  73. data/gemfiles/rails_7.1.gemfile.lock +412 -0
  74. data/gemfiles/rails_8.0.gemfile +10 -0
  75. data/gemfiles/rails_8.0.gemfile.lock +405 -0
  76. data/gemfiles/rails_8.1.gemfile +10 -0
  77. data/gemfiles/rails_8.1.gemfile.lock +405 -0
  78. data/handler-examples/customer_io_handler.rb +36 -0
  79. data/handler-examples/revolut_business_v1_handler.rb +29 -0
  80. data/handler-examples/revolut_business_v2_handler.rb +42 -0
  81. data/handler-examples/starling_payments_handler.rb +25 -0
  82. data/lib/tasks/webhukhs_tasks.rake +4 -0
  83. data/lib/webhukhs/base_handler.rb +100 -0
  84. data/lib/webhukhs/controllers/receive_webhooks_controller.rb +55 -0
  85. data/lib/webhukhs/engine.rb +24 -0
  86. data/lib/webhukhs/install_generator.rb +31 -0
  87. data/lib/webhukhs/jobs/processing_job.rb +33 -0
  88. data/lib/webhukhs/models/received_webhook.rb +99 -0
  89. data/lib/webhukhs/templates/add_headers_to_webhukhs_webhooks.rb.erb +5 -0
  90. data/lib/webhukhs/templates/create_webhukhs_tables.rb.erb +23 -0
  91. data/lib/webhukhs/templates/webhukhs.rb +40 -0
  92. data/lib/webhukhs/version.rb +5 -0
  93. data/lib/webhukhs.rb +23 -0
  94. data/test/test-webhook-handlers/.DS_Store +0 -0
  95. data/test/test-webhook-handlers/extract_id_handler.rb +5 -0
  96. data/test/test-webhook-handlers/failing_with_concealed_errors.rb +7 -0
  97. data/test/test-webhook-handlers/failing_with_exposed_errors.rb +7 -0
  98. data/test/test-webhook-handlers/inactive_handler.rb +5 -0
  99. data/test/test-webhook-handlers/invalid_handler.rb +5 -0
  100. data/test/test-webhook-handlers/private_handler.rb +3 -0
  101. data/test/test-webhook-handlers/webhook_test_handler.rb +13 -0
  102. data/test/test_app.rb +66 -0
  103. data/test/test_helper.rb +50 -0
  104. data/test/webhukhs_test.rb +203 -0
  105. metadata +247 -0
@@ -0,0 +1,405 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ webhukhs (0.5.0)
5
+ rails (>= 7.0)
6
+ state_machine_enum
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ action_text-trix (2.1.16)
12
+ railties
13
+ actioncable (8.1.1)
14
+ actionpack (= 8.1.1)
15
+ activesupport (= 8.1.1)
16
+ nio4r (~> 2.0)
17
+ websocket-driver (>= 0.6.1)
18
+ zeitwerk (~> 2.6)
19
+ actionmailbox (8.1.1)
20
+ actionpack (= 8.1.1)
21
+ activejob (= 8.1.1)
22
+ activerecord (= 8.1.1)
23
+ activestorage (= 8.1.1)
24
+ activesupport (= 8.1.1)
25
+ mail (>= 2.8.0)
26
+ actionmailer (8.1.1)
27
+ actionpack (= 8.1.1)
28
+ actionview (= 8.1.1)
29
+ activejob (= 8.1.1)
30
+ activesupport (= 8.1.1)
31
+ mail (>= 2.8.0)
32
+ rails-dom-testing (~> 2.2)
33
+ actionpack (8.1.1)
34
+ actionview (= 8.1.1)
35
+ activesupport (= 8.1.1)
36
+ nokogiri (>= 1.8.5)
37
+ rack (>= 2.2.4)
38
+ rack-session (>= 1.0.1)
39
+ rack-test (>= 0.6.3)
40
+ rails-dom-testing (~> 2.2)
41
+ rails-html-sanitizer (~> 1.6)
42
+ useragent (~> 0.16)
43
+ actiontext (8.1.1)
44
+ action_text-trix (~> 2.1.15)
45
+ actionpack (= 8.1.1)
46
+ activerecord (= 8.1.1)
47
+ activestorage (= 8.1.1)
48
+ activesupport (= 8.1.1)
49
+ globalid (>= 0.6.0)
50
+ nokogiri (>= 1.8.5)
51
+ actionview (8.1.1)
52
+ activesupport (= 8.1.1)
53
+ builder (~> 3.1)
54
+ erubi (~> 1.11)
55
+ rails-dom-testing (~> 2.2)
56
+ rails-html-sanitizer (~> 1.6)
57
+ activejob (8.1.1)
58
+ activesupport (= 8.1.1)
59
+ globalid (>= 0.3.6)
60
+ activemodel (8.1.1)
61
+ activesupport (= 8.1.1)
62
+ activerecord (8.1.1)
63
+ activemodel (= 8.1.1)
64
+ activesupport (= 8.1.1)
65
+ timeout (>= 0.4.0)
66
+ activestorage (8.1.1)
67
+ actionpack (= 8.1.1)
68
+ activejob (= 8.1.1)
69
+ activerecord (= 8.1.1)
70
+ activesupport (= 8.1.1)
71
+ marcel (~> 1.0)
72
+ activesupport (8.1.1)
73
+ base64
74
+ bigdecimal
75
+ concurrent-ruby (~> 1.0, >= 1.3.1)
76
+ connection_pool (>= 2.2.5)
77
+ drb
78
+ i18n (>= 1.6, < 2)
79
+ json
80
+ logger (>= 1.4.2)
81
+ minitest (>= 5.1)
82
+ securerandom (>= 0.3)
83
+ tzinfo (~> 2.0, >= 2.0.5)
84
+ uri (>= 0.13.1)
85
+ appraisal (2.5.0)
86
+ bundler
87
+ rake
88
+ thor (>= 0.14.0)
89
+ ast (2.4.3)
90
+ base64 (0.3.0)
91
+ bigdecimal (4.0.1)
92
+ builder (3.3.0)
93
+ concurrent-ruby (1.3.6)
94
+ connection_pool (3.0.2)
95
+ crass (1.0.6)
96
+ date (3.5.1)
97
+ drb (2.2.3)
98
+ erb (6.0.1)
99
+ erubi (1.13.1)
100
+ globalid (1.3.0)
101
+ activesupport (>= 6.1)
102
+ i18n (1.14.8)
103
+ concurrent-ruby (~> 1.0)
104
+ io-console (0.8.2)
105
+ irb (1.16.0)
106
+ pp (>= 0.6.0)
107
+ rdoc (>= 4.0.0)
108
+ reline (>= 0.4.2)
109
+ json (2.18.0)
110
+ language_server-protocol (3.17.0.5)
111
+ lint_roller (1.1.0)
112
+ logger (1.7.0)
113
+ loofah (2.25.0)
114
+ crass (~> 1.0.2)
115
+ nokogiri (>= 1.12.0)
116
+ magic_frozen_string_literal (1.2.0)
117
+ mail (2.9.0)
118
+ logger
119
+ mini_mime (>= 0.1.1)
120
+ net-imap
121
+ net-pop
122
+ net-smtp
123
+ marcel (1.1.0)
124
+ mini_mime (1.1.5)
125
+ minitest (5.27.0)
126
+ net-imap (0.6.2)
127
+ date
128
+ net-protocol
129
+ net-pop (0.1.2)
130
+ net-protocol
131
+ net-protocol (0.2.2)
132
+ timeout
133
+ net-smtp (0.5.1)
134
+ net-protocol
135
+ nio4r (2.7.5)
136
+ nokogiri (1.19.0-aarch64-linux-gnu)
137
+ racc (~> 1.4)
138
+ nokogiri (1.19.0-aarch64-linux-musl)
139
+ racc (~> 1.4)
140
+ nokogiri (1.19.0-arm-linux-gnu)
141
+ racc (~> 1.4)
142
+ nokogiri (1.19.0-arm-linux-musl)
143
+ racc (~> 1.4)
144
+ nokogiri (1.19.0-arm64-darwin)
145
+ racc (~> 1.4)
146
+ nokogiri (1.19.0-x86_64-darwin)
147
+ racc (~> 1.4)
148
+ nokogiri (1.19.0-x86_64-linux-gnu)
149
+ racc (~> 1.4)
150
+ nokogiri (1.19.0-x86_64-linux-musl)
151
+ racc (~> 1.4)
152
+ parallel (1.27.0)
153
+ parser (3.3.10.0)
154
+ ast (~> 2.4.1)
155
+ racc
156
+ pp (0.6.3)
157
+ prettyprint
158
+ prettyprint (0.2.0)
159
+ prism (1.7.0)
160
+ psych (5.3.1)
161
+ date
162
+ stringio
163
+ racc (1.8.1)
164
+ rack (3.2.4)
165
+ rack-session (2.1.1)
166
+ base64 (>= 0.1.0)
167
+ rack (>= 3.0.0)
168
+ rack-test (2.2.0)
169
+ rack (>= 1.3)
170
+ rackup (2.3.1)
171
+ rack (>= 3)
172
+ rails (8.1.1)
173
+ actioncable (= 8.1.1)
174
+ actionmailbox (= 8.1.1)
175
+ actionmailer (= 8.1.1)
176
+ actionpack (= 8.1.1)
177
+ actiontext (= 8.1.1)
178
+ actionview (= 8.1.1)
179
+ activejob (= 8.1.1)
180
+ activemodel (= 8.1.1)
181
+ activerecord (= 8.1.1)
182
+ activestorage (= 8.1.1)
183
+ activesupport (= 8.1.1)
184
+ bundler (>= 1.15.0)
185
+ railties (= 8.1.1)
186
+ rails-dom-testing (2.3.0)
187
+ activesupport (>= 5.0.0)
188
+ minitest
189
+ nokogiri (>= 1.6)
190
+ rails-html-sanitizer (1.6.2)
191
+ loofah (~> 2.21)
192
+ nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
193
+ railties (8.1.1)
194
+ actionpack (= 8.1.1)
195
+ activesupport (= 8.1.1)
196
+ irb (~> 1.13)
197
+ rackup (>= 1.0.0)
198
+ rake (>= 12.2)
199
+ thor (~> 1.0, >= 1.2.2)
200
+ tsort (>= 0.2)
201
+ zeitwerk (~> 2.6)
202
+ rainbow (3.1.1)
203
+ rake (13.3.1)
204
+ rdoc (7.0.3)
205
+ erb
206
+ psych (>= 4.0.0)
207
+ tsort
208
+ regexp_parser (2.11.3)
209
+ reline (0.6.3)
210
+ io-console (~> 0.5)
211
+ rubocop (1.81.7)
212
+ json (~> 2.3)
213
+ language_server-protocol (~> 3.17.0.2)
214
+ lint_roller (~> 1.1.0)
215
+ parallel (~> 1.10)
216
+ parser (>= 3.3.0.2)
217
+ rainbow (>= 2.2.2, < 4.0)
218
+ regexp_parser (>= 2.9.3, < 3.0)
219
+ rubocop-ast (>= 1.47.1, < 2.0)
220
+ ruby-progressbar (~> 1.7)
221
+ unicode-display_width (>= 2.4.0, < 4.0)
222
+ rubocop-ast (1.49.0)
223
+ parser (>= 3.3.7.2)
224
+ prism (~> 1.7)
225
+ rubocop-performance (1.26.1)
226
+ lint_roller (~> 1.1)
227
+ rubocop (>= 1.75.0, < 2.0)
228
+ rubocop-ast (>= 1.47.1, < 2.0)
229
+ ruby-progressbar (1.13.0)
230
+ securerandom (0.4.1)
231
+ sprockets (4.2.2)
232
+ concurrent-ruby (~> 1.0)
233
+ logger
234
+ rack (>= 2.2.4, < 4)
235
+ sprockets-rails (3.5.2)
236
+ actionpack (>= 6.1)
237
+ activesupport (>= 6.1)
238
+ sprockets (>= 3.0.0)
239
+ sqlite3 (2.9.0-aarch64-linux-gnu)
240
+ sqlite3 (2.9.0-aarch64-linux-musl)
241
+ sqlite3 (2.9.0-arm-linux-gnu)
242
+ sqlite3 (2.9.0-arm-linux-musl)
243
+ sqlite3 (2.9.0-arm64-darwin)
244
+ sqlite3 (2.9.0-x86_64-darwin)
245
+ sqlite3 (2.9.0-x86_64-linux-gnu)
246
+ sqlite3 (2.9.0-x86_64-linux-musl)
247
+ standard (1.52.0)
248
+ language_server-protocol (~> 3.17.0.2)
249
+ lint_roller (~> 1.0)
250
+ rubocop (~> 1.81.7)
251
+ standard-custom (~> 1.0.0)
252
+ standard-performance (~> 1.8)
253
+ standard-custom (1.0.2)
254
+ lint_roller (~> 1.0)
255
+ rubocop (~> 1.50)
256
+ standard-performance (1.9.0)
257
+ lint_roller (~> 1.1)
258
+ rubocop-performance (~> 1.26.0)
259
+ state_machine_enum (0.1.4)
260
+ activesupport (>= 7.0)
261
+ stringio (3.2.0)
262
+ thor (1.4.0)
263
+ timeout (0.6.0)
264
+ tsort (0.2.0)
265
+ tzinfo (2.0.6)
266
+ concurrent-ruby (~> 1.0)
267
+ unicode-display_width (3.2.0)
268
+ unicode-emoji (~> 4.1)
269
+ unicode-emoji (4.2.0)
270
+ uri (1.1.1)
271
+ useragent (0.16.11)
272
+ websocket-driver (0.8.0)
273
+ base64
274
+ websocket-extensions (>= 0.1.0)
275
+ websocket-extensions (0.1.5)
276
+ zeitwerk (2.7.4)
277
+
278
+ PLATFORMS
279
+ aarch64-linux-gnu
280
+ aarch64-linux-musl
281
+ arm-linux-gnu
282
+ arm-linux-musl
283
+ arm64-darwin
284
+ x86_64-darwin
285
+ x86_64-linux-gnu
286
+ x86_64-linux-musl
287
+
288
+ DEPENDENCIES
289
+ appraisal
290
+ magic_frozen_string_literal
291
+ minitest (~> 5.0)
292
+ rails (~> 8.1)
293
+ rake (~> 13.0)
294
+ sprockets-rails
295
+ sqlite3
296
+ standard
297
+ webhukhs!
298
+
299
+ CHECKSUMS
300
+ action_text-trix (2.1.16) sha256=f645a2c21821b8449fd1d6770708f4031c91a2eedf9ef476e9be93c64e703a8a
301
+ actioncable (8.1.1) sha256=7262307e9693f09b299e281590110ce4b6ba7e4e4cee6da4b9d987eaf56f9139
302
+ actionmailbox (8.1.1) sha256=aa99703a9b2fa32c5a4a93bb21fef79e2935d8db4d1fd5ef0772847be5d43205
303
+ actionmailer (8.1.1) sha256=45755d7d4561363490ae82b17a5919bdef4dfe3bb400831819947c3a1d82afdf
304
+ actionpack (8.1.1) sha256=192e27c39a63c7d801ac7b6d50505f265e389516985eed9b2ee364896a6a06d7
305
+ actiontext (8.1.1) sha256=fd8d8da1e6bc0b04ff72fccfd127e78431238a99a82e736c7b52727c576a7640
306
+ actionview (8.1.1) sha256=ca480c8b099dea0862b0934f24182b84c2d29092e7dbf464fb3e6d4eb9b468dc
307
+ activejob (8.1.1) sha256=94f438a9f3b5a6b130fef53d8313f869dbd379309e7d639891bda36b12509383
308
+ activemodel (8.1.1) sha256=8b7e2496b9e333ced06248c16a43217b950192c98e0fe3aa117eee21501c6fbd
309
+ activerecord (8.1.1) sha256=e32c3a03e364fd803498eb4150c21bedc995aa83bc27122a94d480ab1dcb3d17
310
+ activestorage (8.1.1) sha256=bc01d8b4c55e309a0a2e218bfe502c382c9f232e28b1f4b0adc9d8719d2bf28d
311
+ activesupport (8.1.1) sha256=5e92534e8d0c8b8b5e6b16789c69dbea65c1d7b752269f71a39422e9546cea67
312
+ appraisal (2.5.0) sha256=36989221be127913b0dba8d114da2001e6b2dceea7bd4951200eaba764eed3ce
313
+ ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
314
+ base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
315
+ bigdecimal (4.0.1) sha256=8b07d3d065a9f921c80ceaea7c9d4ae596697295b584c296fe599dd0ad01c4a7
316
+ builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
317
+ concurrent-ruby (1.3.6) sha256=6b56837e1e7e5292f9864f34b69c5a2cbc75c0cf5338f1ce9903d10fa762d5ab
318
+ connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
319
+ crass (1.0.6) sha256=dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d
320
+ date (3.5.1) sha256=750d06384d7b9c15d562c76291407d89e368dda4d4fff957eb94962d325a0dc0
321
+ drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
322
+ erb (6.0.1) sha256=28ecdd99c5472aebd5674d6061e3c6b0a45c049578b071e5a52c2a7f13c197e5
323
+ erubi (1.13.1) sha256=a082103b0885dbc5ecf1172fede897f9ebdb745a4b97a5e8dc63953db1ee4ad9
324
+ globalid (1.3.0) sha256=05c639ad6eb4594522a0b07983022f04aa7254626ab69445a0e493aa3786ff11
325
+ i18n (1.14.8) sha256=285778639134865c5e0f6269e0b818256017e8cde89993fdfcbfb64d088824a5
326
+ io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
327
+ irb (1.16.0) sha256=2abe56c9ac947cdcb2f150572904ba798c1e93c890c256f8429981a7675b0806
328
+ json (2.18.0) sha256=b10506aee4183f5cf49e0efc48073d7b75843ce3782c68dbeb763351c08fd505
329
+ language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
330
+ lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
331
+ logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
332
+ loofah (2.25.0) sha256=df5ed7ac3bac6a4ec802df3877ee5cc86d027299f8952e6243b3dac446b060e6
333
+ magic_frozen_string_literal (1.2.0) sha256=ba2014401bdd571339960c122f7bafe98f2dee6bcc2fe2600548666f65042eb7
334
+ mail (2.9.0) sha256=6fa6673ecd71c60c2d996260f9ee3dd387d4673b8169b502134659ece6d34941
335
+ marcel (1.1.0) sha256=fdcfcfa33cc52e93c4308d40e4090a5d4ea279e160a7f6af988260fa970e0bee
336
+ mini_mime (1.1.5) sha256=8681b7e2e4215f2a159f9400b5816d85e9d8c6c6b491e96a12797e798f8bccef
337
+ minitest (5.27.0) sha256=2d3b17f8a36fe7801c1adcffdbc38233b938eb0b4966e97a6739055a45fa77d5
338
+ net-imap (0.6.2) sha256=08caacad486853c61676cca0c0c47df93db02abc4a8239a8b67eb0981428acc6
339
+ net-pop (0.1.2) sha256=848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3
340
+ net-protocol (0.2.2) sha256=aa73e0cba6a125369de9837b8d8ef82a61849360eba0521900e2c3713aa162a8
341
+ net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736
342
+ nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1
343
+ nokogiri (1.19.0-aarch64-linux-gnu) sha256=11a97ecc3c0e7e5edcf395720b10860ef493b768f6aa80c539573530bc933767
344
+ nokogiri (1.19.0-aarch64-linux-musl) sha256=eb70507f5e01bc23dad9b8dbec2b36ad0e61d227b42d292835020ff754fb7ba9
345
+ nokogiri (1.19.0-arm-linux-gnu) sha256=572a259026b2c8b7c161fdb6469fa2d0edd2b61cd599db4bbda93289abefbfe5
346
+ nokogiri (1.19.0-arm-linux-musl) sha256=23ed90922f1a38aed555d3de4d058e90850c731c5b756d191b3dc8055948e73c
347
+ nokogiri (1.19.0-arm64-darwin) sha256=0811dfd936d5f6dd3f6d32ef790568bf29b2b7bead9ba68866847b33c9cf5810
348
+ nokogiri (1.19.0-x86_64-darwin) sha256=1dad56220b603a8edb9750cd95798bffa2b8dd9dd9aa47f664009ee5b43e3067
349
+ nokogiri (1.19.0-x86_64-linux-gnu) sha256=f482b95c713d60031d48c44ce14562f8d2ce31e3a9e8dd0ccb131e9e5a68b58c
350
+ nokogiri (1.19.0-x86_64-linux-musl) sha256=1c4ca6b381622420073ce6043443af1d321e8ed93cc18b08e2666e5bd02ffae4
351
+ parallel (1.27.0) sha256=4ac151e1806b755fb4e2dc2332cbf0e54f2e24ba821ff2d3dcf86bf6dc4ae130
352
+ parser (3.3.10.0) sha256=ce3587fa5cc55a88c4ba5b2b37621b3329aadf5728f9eafa36bbd121462aabd6
353
+ pp (0.6.3) sha256=2951d514450b93ccfeb1df7d021cae0da16e0a7f95ee1e2273719669d0ab9df6
354
+ prettyprint (0.2.0) sha256=2bc9e15581a94742064a3cc8b0fb9d45aae3d03a1baa6ef80922627a0766f193
355
+ prism (1.7.0) sha256=10062f734bf7985c8424c44fac382ac04a58124ea3d220ec3ba9fe4f2da65103
356
+ psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974
357
+ racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
358
+ rack (3.2.4) sha256=5d74b6f75082a643f43c1e76b419c40f0e5527fcfee1e669ac1e6b73c0ccb6f6
359
+ rack-session (2.1.1) sha256=0b6dc07dea7e4b583f58a48e8b806d4c9f1c6c9214ebc202ec94562cbea2e4e9
360
+ rack-test (2.2.0) sha256=005a36692c306ac0b4a9350355ee080fd09ddef1148a5f8b2ac636c720f5c463
361
+ rackup (2.3.1) sha256=6c79c26753778e90983761d677a48937ee3192b3ffef6bc963c0950f94688868
362
+ rails (8.1.1) sha256=877509b7aef309239978685883097d2c03e21383a50a3f78882cf9b3b5c136f7
363
+ rails-dom-testing (2.3.0) sha256=8acc7953a7b911ca44588bf08737bc16719f431a1cc3091a292bca7317925c1d
364
+ rails-html-sanitizer (1.6.2) sha256=35fce2ca8242da8775c83b6ba9c1bcaad6751d9eb73c1abaa8403475ab89a560
365
+ railties (8.1.1) sha256=fb0c7038b147bea41bf6697fa443ff1c5c47d3bb1eedd9ecf1bceeb90efcb868
366
+ rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
367
+ rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
368
+ rdoc (7.0.3) sha256=dfe3d0981d19b7bba71d9dbaeb57c9f4e3a7a4103162148a559c4fc687ea81f9
369
+ regexp_parser (2.11.3) sha256=ca13f381a173b7a93450e53459075c9b76a10433caadcb2f1180f2c741fc55a4
370
+ reline (0.6.3) sha256=1198b04973565b36ec0f11542ab3f5cfeeec34823f4e54cebde90968092b1835
371
+ rubocop (1.81.7) sha256=6fb5cc298c731691e2a414fe0041a13eb1beed7bab23aec131da1bcc527af094
372
+ rubocop-ast (1.49.0) sha256=49c3676d3123a0923d333e20c6c2dbaaae2d2287b475273fddee0c61da9f71fd
373
+ rubocop-performance (1.26.1) sha256=cd19b936ff196df85829d264b522fd4f98b6c89ad271fa52744a8c11b8f71834
374
+ ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
375
+ securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
376
+ sprockets (4.2.2) sha256=761e5a49f1c288704763f73139763564c845a8f856d52fba013458f8af1b59b1
377
+ sprockets-rails (3.5.2) sha256=a9e88e6ce9f8c912d349aa5401509165ec42326baf9e942a85de4b76dbc4119e
378
+ sqlite3 (2.9.0-aarch64-linux-gnu) sha256=cfe1e0216f46d7483839719bf827129151e6c680317b99d7b8fc1597a3e13473
379
+ sqlite3 (2.9.0-aarch64-linux-musl) sha256=56a35cb2d70779afc2ac191baf2c2148242285ecfed72f9b021218c5c4917913
380
+ sqlite3 (2.9.0-arm-linux-gnu) sha256=a19a21504b0d7c8c825fbbf37b358ae316b6bd0d0134c619874060b2eef05435
381
+ sqlite3 (2.9.0-arm-linux-musl) sha256=fca5b26197c70e3363115d3faaea34d7b2ad9c7f5fa8d8312e31b64e7556ee07
382
+ sqlite3 (2.9.0-arm64-darwin) sha256=a917bd9b84285766ff3300b7d79cd583f5a067594c8c1263e6441618c04a6ed3
383
+ sqlite3 (2.9.0-x86_64-darwin) sha256=59fe51baa3cb33c36d27ce78b4ed9360cd33ccca09498c2ae63850c97c0a6026
384
+ sqlite3 (2.9.0-x86_64-linux-gnu) sha256=72fff9bd750070ba3af695511ba5f0e0a2d8a9206f84869640b3e99dfaf3d5a5
385
+ sqlite3 (2.9.0-x86_64-linux-musl) sha256=ef716ba7a66d7deb1ccc402ac3a6d7343da17fac862793b7f0be3d2917253c90
386
+ standard (1.52.0) sha256=ec050e63228e31fabe40da3ef96da7edda476f7acdf3e7c2ad47b6e153f6a076
387
+ standard-custom (1.0.2) sha256=424adc84179a074f1a2a309bb9cf7cd6bfdb2b6541f20c6bf9436c0ba22a652b
388
+ standard-performance (1.9.0) sha256=49483d31be448292951d80e5e67cdcb576c2502103c7b40aec6f1b6e9c88e3f2
389
+ state_machine_enum (0.1.4) sha256=959d72af8d00e31b995a447738716e76667aadfb3612c17f59f566c083b31aac
390
+ stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
391
+ thor (1.4.0) sha256=8763e822ccb0f1d7bee88cde131b19a65606657b847cc7b7b4b82e772bcd8a3d
392
+ timeout (0.6.0) sha256=6d722ad619f96ee383a0c557ec6eb8c4ecb08af3af62098a0be5057bf00de1af
393
+ tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f
394
+ tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
395
+ unicode-display_width (3.2.0) sha256=0cdd96b5681a5949cdbc2c55e7b420facae74c4aaf9a9815eee1087cb1853c42
396
+ unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
397
+ uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
398
+ useragent (0.16.11) sha256=700e6413ad4bb954bb63547fa098dddf7b0ebe75b40cc6f93b8d54255b173844
399
+ webhukhs (0.5.0)
400
+ websocket-driver (0.8.0) sha256=ed0dba4b943c22f17f9a734817e808bc84cdce6a7e22045f5315aa57676d4962
401
+ websocket-extensions (0.1.5) sha256=1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241
402
+ zeitwerk (2.7.4) sha256=2bef90f356bdafe9a6c2bd32bcd804f83a4f9b8bc27f3600fff051eb3edcec8b
403
+
404
+ BUNDLED WITH
405
+ 4.0.1
@@ -0,0 +1,36 @@
1
+ # This is an example handler for Customer.io reporting webhooks. You
2
+ # can find more documentation here https://customer.io/docs/api/webhooks/#operation/reportingWebhook
3
+ class Webhooks::CustomerIoHandler < Webhukhs::BaseHandler
4
+ def process(webhook)
5
+ json = JSON.parse(webhook.body, symbolize_names: true)
6
+ case json[:metric]
7
+ when "subscribed"
8
+ # ...
9
+ when "unsubscribed"
10
+ # ...
11
+ when "cio_subscription_preferences_changed"
12
+ # ...
13
+ end
14
+ end
15
+
16
+ def extract_event_id_from_request(action_dispatch_request)
17
+ action_dispatch_request.params.fetch(:event_id)
18
+ end
19
+
20
+ # Verify that request is actually comming from customer.io here
21
+ # @see https://customer.io/docs/api/webhooks/#section/Securely-Verifying-Requests
22
+ #
23
+ # - Should have "X-CIO-Signature", "X-CIO-Timestamp" headers.
24
+ # - Combine the version number, timestamp and body delimited by colons to form a string in the form v0:<timestamp>:<body>
25
+ # - Using HMAC-SHA256, hash the string using your webhook signing secret as the hash key.
26
+ # - Compare this value to the value of the X-CIO-Signature header sent with the request to confirm
27
+ def valid?(action_dispatch_request)
28
+ signing_key = Rails.application.secrets.customer_io_webhook_signing_key
29
+ xcio_signature = action_dispatch_request.headers["HTTP_X_CIO_SIGNATURE"]
30
+ xcio_timestamp = action_dispatch_request.headers["HTTP_X_CIO_TIMESTAMP"]
31
+ request_body = action_dispatch_request.body.read
32
+ string_to_sign = "v0:#{xcio_timestamp}:#{request_body}"
33
+ hmac = OpenSSL::HMAC.hexdigest("SHA256", signing_key, string_to_sign)
34
+ Rack::Utils.secure_compare(hmac, xcio_signature)
35
+ end
36
+ end
@@ -0,0 +1,29 @@
1
+ # This is for Revolut V1 API for webhooks - https://developer.revolut.com/docs/business/webhooks-v-1-deprecated
2
+ class RevolutBusinessV1Handler < Webhukhs::BaseHandler
3
+ def valid?(_)
4
+ # V1 of Revolut webhooks does not support signatures
5
+ true
6
+ end
7
+
8
+ def process(webhook)
9
+ parsed_payload = JSON.parse(webhook.body)
10
+ topic = parsed_payload.fetch("Topic")
11
+ case topic
12
+ when "tokens" # Account access revocation payload
13
+ # ...
14
+ when "draftpayments/transfers" # Draft payment transfer notification payload
15
+ # ...
16
+ else
17
+ # ...
18
+ end
19
+ end
20
+
21
+ def extract_event_id_from_request(action_dispatch_request)
22
+ # Since b-tree indices generally divide from the start of the string, place the highest
23
+ # entropy component at the start (the EventId)
24
+ key_components = %w[EventId Topic Version]
25
+ key_components.map do |key|
26
+ action_dispatch_request.params.fetch(key)
27
+ end.join("-")
28
+ end
29
+ end
@@ -0,0 +1,42 @@
1
+ # This is for Revolut V2 API for webhooks - https://developer.revolut.com/docs/business/webhooks-v-2
2
+ class RevolutBusinessV2Handler < Webhukhs::BaseHandler
3
+ def valid?(request)
4
+ # 1 - Validate the timestamp of the request. Prevent replay attacks.
5
+ # "To validate the event, make sure that the Revolut-Request-Timestamp date-time is within a 5-minute time tolerance of the current universal time (UTC)".
6
+ # Their examples list `timestamp = '1683650202360'` as a sample value, so their timestamp is in millis - not in seconds
7
+ timestamp_str_from_headers = request.headers["HTTP_REVOLUT_REQUEST_TIMESTAMP"]
8
+ delta_t_seconds = (timestamp_str_from_headers / 1000) - Time.now.to_i
9
+ return false unless delta_t_seconds.abs < (5 * 60)
10
+
11
+ # 2 - Validate the signature
12
+ # https://developer.revolut.com/docs/guides/manage-accounts/tutorials/work-with-webhooks/verify-the-payload-signature
13
+ string_to_sign = [
14
+ "v1",
15
+ timestamp_str_from_headers,
16
+ request.body.read
17
+ ].join(".")
18
+ computed_signature = "v1=" + OpenSSL::HMAC.hexdigest("SHA256", Rails.application.secrets.revolut_business_webhook_signing_key, string_to_sign)
19
+ # Note: "This means that in the period when multiple signing secrets remain valid, multiple signatures are sent."
20
+ # https://developer.revolut.com/docs/guides/manage-accounts/tutorials/work-with-webhooks/manage-webhooks#rotate-a-webhook-signing-secret
21
+ # https://developer.revolut.com/docs/guides/manage-accounts/tutorials/work-with-webhooks/about-webhooks#security
22
+ # An HTTP header may contain multiple values if it gets sent multiple times. But it does mean we need to test for multiple provided
23
+ # signatures in case of rotation.
24
+ provided_signatures = request.headers["HTTP_REVOLUT_SIGNATURE"].split(",")
25
+ # Use #select instead of `find` to compare all signatures even if only one matches - this to avoid timing leaks.
26
+ # Small effort but might be useful.
27
+ matches = provided_signatures.select do |provided_signature|
28
+ ActiveSupport::SecurityUtils.secure_compare(provided_signature, computed_signature)
29
+ end
30
+ matches.any?
31
+ end
32
+
33
+ def process(webhook)
34
+ Rails.logger.info { "Processing Revolut webhook #{webhook.body.inspect}" }
35
+ end
36
+
37
+ def extract_event_id_from_request(action_dispatch_request)
38
+ # The event ID is only available when you retrieve the failed webhooks, which is sad.
39
+ # We can divinate a synthetic ID though by taking a hash of the entire payload though.
40
+ Digest::SHA256.hexdigest(action_dispatch_request.body.read)
41
+ end
42
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This handler is an example for Starling Payments API,
4
+ # you can find the documentation here https://developer.starlingbank.com/payments/docs#account-and-address-structure-1
5
+ class StarlingPaymentsHandler < Webhukhs::BaseHandler
6
+ # This method will be used to process webhook by async worker.
7
+ def process(received_webhook)
8
+ Rails.logger.info { received_webhook.body }
9
+ end
10
+
11
+ # Starling supplies signatures in the form SHA512(secret + request_body)
12
+ def valid?(action_dispatch_request)
13
+ supplied_signature = action_dispatch_request.headers.fetch("X-Hook-Signature")
14
+ supplied_digest_bytes = Base64.strict_decode64(supplied_signature)
15
+ sha512 = Digest::SHA2.new(512)
16
+ signing_secret = Rails.credentials.starling_payments_webhook_signing_secret
17
+ computed_digest_bytes = sha512.digest(signing_secret.b + action_dispatch_request.body.b)
18
+ ActiveSupport::SecurityUtils.secure_compare(computed_digest_bytes, supplied_digest_bytes)
19
+ end
20
+
21
+ # Some Starling webhooks do not provide a notification UID, but for those which do we can deduplicate
22
+ def extract_event_id_from_request(action_dispatch_request)
23
+ action_dispatch_request.params.fetch("notificationUid", SecureRandom.uuid)
24
+ end
25
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :munster do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "jobs/processing_job"
4
+
5
+ module Webhukhs
6
+ class BaseHandler
7
+ # `handle` accepts the ActionDispatch HTTP request and saves the webhook for later processing. It then
8
+ # enqueues an ActiveJob which will perform the processing using `process`.
9
+ #
10
+ # @param action_dispatch_request[ActionDispatch::Request] the request from the controller
11
+ # @return [void]
12
+ def handle(action_dispatch_request)
13
+ handler_module_name = is_a?(Webhukhs::BaseHandler) ? self.class.name : to_s
14
+ handler_event_id = extract_event_id_from_request(action_dispatch_request)
15
+
16
+ webhook = Webhukhs::ReceivedWebhook.new(request: action_dispatch_request, handler_event_id: handler_event_id, handler_module_name: handler_module_name)
17
+ webhook.save!
18
+
19
+ enqueue(webhook)
20
+ rescue ActiveRecord::RecordNotUnique # Webhook deduplicated
21
+ Rails.logger.info { "#{inspect} Webhook #{handler_event_id} is a duplicate delivery and will not be stored." }
22
+ end
23
+
24
+ # Enqueues the processing job to process webhook asynchronously. The job class could be configured.
25
+ #
26
+ # @param webhook [Webhukhs::ReceivedWebhook]
27
+ # @return [void]
28
+ def enqueue(webhook)
29
+ # The configured job class can be a class name or a module, to support lazy loading
30
+ job_class_or_module_name = Webhukhs.configuration.processing_job_class
31
+ job_class = if job_class_or_module_name.respond_to?(:perform_later)
32
+ job_class_or_module_name
33
+ else
34
+ job_class_or_module_name.constantize
35
+ end
36
+
37
+ job_class.perform_later(webhook)
38
+ end
39
+
40
+ # This is the heart of your webhook processing. Override this method and define your processing inside of it.
41
+ # The `received_webhook` will provide access to the `ReceivedWebhook` model, which contains the received
42
+ # body of the webhook request, but also the full (as-full-as-possible) clone of the original ActionDispatch::Request
43
+ # that you can use.
44
+ #
45
+ # @param received_webhook[Webhukhs::ReceivedWebhook]
46
+ # @return [void]
47
+ def process(received_webhook)
48
+ end
49
+
50
+ # This method verifies that request is not malformed and actually comes from the webhook sender:
51
+ # signature validation, HTTP authentication, IP whitelisting and the like. There is a difference depending
52
+ # on whether you validate sync (in the receiving controller) or async (in the processing job):
53
+ # Validation is async - it takes place in the background job that gets enqueued to process the webhook.
54
+ # The `action_dispatch_request` will be reconstructed from the `ReceivedWebhook` data. Background validation
55
+ # is used because the most common misconfiguration that may occur is usually forgetting or misidentifying the
56
+ # signature for signed webhooks. If such a misconfiguration has taken place, the background validation
57
+ # (instead of rejecting the webhook at input) permits you to still process the webhook once the secrets
58
+ # have been configured correctly.
59
+ #
60
+ # If this method returns `false`, the webhook will be marked as `failed_validation` in the database. If this
61
+ # method returns `true`, the `process` method of the handler is going to be called.
62
+ #
63
+ # @see Webhukhs::ReceivedWebhook#request
64
+ # @param action_dispatch_request[ActionDispatch::Request] the reconstructed request from the controller
65
+ # @return [Boolean]
66
+ def valid?(action_dispatch_request)
67
+ true
68
+ end
69
+
70
+ # Default implementation just generates a random UUID, but if the webhook sender sends us
71
+ # an event ID we use it for deduplication. A duplicate webhook is not going to be
72
+ # stored in the database if it is already present there.
73
+ #
74
+ # @return [String]
75
+ def extract_event_id_from_request(action_dispatch_request)
76
+ SecureRandom.uuid
77
+ end
78
+
79
+ # Webhook senders have varying retry behaviors, and often you want to "pretend"
80
+ # everything is fine even though there is an error so that they keep sending you
81
+ # data and do not disable your endpoint forcibly. We allow this to be configured
82
+ # on a per-handler basis - a better webhooks sender will be able to make out
83
+ # some sense of the errors.
84
+ #
85
+ # @return [Boolean]
86
+ def expose_errors_to_sender?
87
+ true
88
+ end
89
+
90
+ # Tells the controller whether this handler is active or not. This can be used
91
+ # to deactivate a particular handler via feature flags for example, or use other
92
+ # logic to determine whether the handler may be used to create new received webhooks
93
+ # in the system. This is primarily needed for load shedding.
94
+ #
95
+ # @return [Boolean]
96
+ def active?
97
+ true
98
+ end
99
+ end
100
+ end