payu_pl 0.2.0 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a3153797e232dc9495c8f175f37558a8f360688b9f53e311cf925652a55db620
4
- data.tar.gz: ca4dcc6531dec57bbc908c39cd5dd801d8fb9e5e33de22eca1df876e40abdd8e
3
+ metadata.gz: 0e5d4017013c53056c374299d981c16553d6de4372f9446bd6dbb2aebbc05929
4
+ data.tar.gz: 22ecc4cfcf8b66c153b21956b5fa4754b70ec51589e51e8bc6703d33e2789216
5
5
  SHA512:
6
- metadata.gz: 144220b42c84c7745c1f41755afe6c118bf9b0d2b06567abdea30ab21403341c7991525f914e70da3f971781ae4944d66b22cb6826603c4fcf96f18a2c88f1c8
7
- data.tar.gz: c7a8325b86ca1102977cbfbe776b6013c31c4a78ddf7f16d7401933aeebf881d8aa47ac427a47a3f9ab28ed7ff539e5f5f4cc56c0dce074a7a3af2cdbc712d64
6
+ metadata.gz: 321e0e7efebc1c9dfabf68f8a41428634f342e42e089da13198c604753d75fe45c6284bf816e648dc7ca2031a09889e7ec838ffda67bb7838c690bea1d07c49c
7
+ data.tar.gz: 60e13ebceb420e9a8759b741391744163e28efa1ed30211914c73509f0501d835ab2a150027496af9624fa2559037ff0ff60d9026e1d33eb938bbabd2c926a1f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,50 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2026-01-09
4
+
5
+ ### Added
6
+
7
+ - **Webhook Validation**: Complete webhook signature verification and payload parsing
8
+ - `PayuPl::Webhooks::Validator` - Validates PayU webhook signatures and parses JSON payloads
9
+ - `PayuPl::Webhooks::Result` - Success/failure result object for webhook validation
10
+ - Support for all PayU signature algorithms: MD5, SHA1, SHA256, SHA384, SHA512
11
+ - Automatic detection and verification of both MD5 concatenation variants (body+key and key+body)
12
+ - Framework-agnostic design (works with Rails, Sinatra, Hanami, plain Rack)
13
+ - Optional logging for debugging webhook processing
14
+ - Constant-time signature comparison for security
15
+
16
+ - **Configuration**: `PayuPl::Configuration#second_key` for webhook signature verification
17
+ - Three configuration methods: initializer, ENV variable, or direct parameter
18
+ - Automatic fallback from config → ENV → direct parameter
19
+
20
+ - **Documentation**:
21
+ - Comprehensive webhook integration guide (`examples/WEBHOOK_GUIDE.md`)
22
+ - Rails controller example (`examples/webhooks_controller.rb`)
23
+ - Sinatra example (`examples/sinatra_webhook_example.rb`)
24
+ - Rack example (`examples/rack_webhook_example.ru`)
25
+ - Updated README with webhook section and best practices
26
+ - PayU IP addresses for webhook whitelisting (production + sandbox)
27
+ - PayU notification retry mechanism details (20 attempts over 72 hours)
28
+ - Official webhook header documentation
29
+ - Payment status lifecycle documentation
30
+
31
+ - **Testing**: 25 comprehensive webhook tests
32
+ - Signature verification tests for all algorithms
33
+ - MD5 variant testing (both concatenation orders)
34
+ - Configuration priority testing
35
+ - Error handling and edge cases
36
+ - Logging behavior tests
37
+
38
+ ### Fixed
39
+
40
+ - Amount display in logs now correctly converts from minor units to major currency units
41
+ - Example: `2900` (minor units) now displays as `29.00 PLN` (major units)
42
+ - Updated validator logs and example controllers
43
+
44
+ ### Dependencies
45
+
46
+ - Added `rack ~> 3.0` to development dependencies for webhook testing
47
+
3
48
  ## [0.2.0] - 2025-12-31
4
49
 
5
50
  - Add Shop Data endpoint: `GET /api/v2_1/shops/{shopId}` (`Client#retrieve_shop_data`)
@@ -0,0 +1,144 @@
1
+ # Documentation Updates Based on PayU Official Documentation
2
+
3
+ ## Changes Made
4
+
5
+ Based on the official PayU webhook documentation, the following enhancements have been made to ensure complete alignment with PayU's specifications.
6
+
7
+ ### 1. Payment Status Clarifications
8
+
9
+ Updated status descriptions to match official definitions:
10
+
11
+ - **PENDING** - Payment is currently being processed
12
+ - **WAITING_FOR_CONFIRMATION** - PayU is waiting for merchant to capture payment (when auto-receive is disabled)
13
+ - **COMPLETED** - Payment accepted, funds will be paid out shortly
14
+ - **CANCELED** - Payment cancelled, buyer was not charged
15
+
16
+ ### 2. Retry Mechanism Documentation
17
+
18
+ Added complete details about PayU's notification retry mechanism:
19
+ - PayU expects **200 HTTP status code**
20
+ - Up to **20 retry attempts** over 72 hours
21
+ - Specific retry intervals documented (1min, 2min, 5min, 10min, 30min, 1hr, etc.)
22
+ - Emphasis on returning 200 even if business logic fails
23
+
24
+ ### 3. IP Address Whitelisting
25
+
26
+ Added official PayU IP addresses for webhook filtering:
27
+
28
+ **Production:**
29
+ - 185.68.12.10, 185.68.12.11, 185.68.12.12
30
+ - 185.68.12.26, 185.68.12.27, 185.68.12.28
31
+
32
+ **Sandbox:**
33
+ - 185.68.14.10, 185.68.14.11, 185.68.14.12
34
+ - 185.68.14.26, 185.68.14.27, 185.68.14.28
35
+
36
+ ### 4. Webhook Headers Documentation
37
+
38
+ Documented all official PayU webhook headers:
39
+ - `OpenPayu-Signature` (also sent as `X-OpenPayU-Signature`)
40
+ - `Content-Type: application/json;charset=UTF-8`
41
+ - `Authorization` - Basic auth credentials
42
+ - `PayU-Processing-Time` - Processing duration in milliseconds
43
+
44
+ Added example of signature header format:
45
+ ```
46
+ OpenPayu-Signature: sender=checkout;signature=...;algorithm=MD5;content=DOCUMENT
47
+ ```
48
+
49
+ ### 5. Payload Structure Details
50
+
51
+ Enhanced payload documentation with official field descriptions:
52
+
53
+ - **localReceiptDateTime** - Only present for COMPLETED status
54
+ - **properties[PAYMENT_ID]** - Transaction identifier (Trans ID in management panel)
55
+ - **payMethod.type** - Payment method indicators:
56
+ - `PBL` - Online/standard transfer
57
+ - `CARD_TOKEN` - Card payment
58
+ - `INSTALLMENTS` - PayU Installments
59
+
60
+ ### 6. Best Practices Updates
61
+
62
+ Enhanced best practices section:
63
+
64
+ - **Duplicate Handling** - Added context about retry mechanism causing duplicates
65
+ - **Status Codes** - Clarified critical importance of returning 200 OK
66
+ - **Async Processing** - Emphasized responding quickly to avoid retries
67
+ - Added code examples showing correct vs incorrect patterns
68
+
69
+ ### 7. Controller Example Enhancements
70
+
71
+ Updated example controller with:
72
+ - PayU-Processing-Time header logging for monitoring
73
+ - Clarified WAITING_FOR_CONFIRMATION status handling
74
+ - Better error handling examples
75
+ - More detailed status case handling
76
+
77
+ ## Implementation Notes
78
+
79
+ ### What Works Perfectly
80
+
81
+ ✅ **Signature Verification** - Handles all PayU algorithms (MD5, SHA1, SHA256, SHA384, SHA512)
82
+ ✅ **MD5 Variants** - Automatically checks both `MD5(body+key)` and `MD5(key+body)`
83
+ ✅ **Header Detection** - Works with both `OpenPayu-Signature` and `X-OpenPayU-Signature`
84
+ ✅ **Framework Agnostic** - Compatible with Rails, Sinatra, Hanami, plain Rack
85
+ ✅ **Logging Support** - Optional detailed logging for debugging
86
+ ✅ **Error Handling** - Clear error messages for all failure scenarios
87
+
88
+ ### Developer Experience Improvements
89
+
90
+ 1. **Flexible Configuration** - Three ways to set second_key (config, ENV, direct)
91
+ 2. **Simple API** - Clean success/failure result pattern
92
+ 3. **Comprehensive Examples** - Rails, Sinatra, Rack examples provided
93
+ 4. **Detailed Guide** - Step-by-step integration guide with troubleshooting
94
+ 5. **Full Test Coverage** - 25 webhook-specific tests, all passing
95
+
96
+ ## Files Updated
97
+
98
+ ### Documentation
99
+ - `examples/WEBHOOK_GUIDE.md` - Added IP addresses, retry mechanism, headers info
100
+ - `README.md` - Added best practices summary and IP addresses
101
+ - `examples/webhooks_controller.rb` - Enhanced with monitoring and better comments
102
+
103
+ ### No Code Changes Required
104
+
105
+ The webhook validator implementation already correctly handles:
106
+ - All PayU signature algorithms
107
+ - Both MD5 concatenation variants
108
+ - Proper header parsing (signature format with semicolons)
109
+ - Request body preservation for multiple reads
110
+ - Constant-time signature comparison for security
111
+
112
+ ## Verification
113
+
114
+ All tests pass (70 total, 25 webhook-specific):
115
+ ```
116
+ PayuPl::Webhooks::Validator
117
+ ✓ #initialize (4 examples)
118
+ ✓ #verify_signature! (7 examples)
119
+ ✓ #parse_payload (2 examples)
120
+ ✓ #validate_and_parse (4 examples)
121
+ ✓ logging (2 examples)
122
+
123
+ PayuPl::Webhooks::Result
124
+ ✓ All result object behaviors (6 examples)
125
+ ```
126
+
127
+ ## Next Steps for Users
128
+
129
+ When integrating webhooks, users should:
130
+
131
+ 1. Get second_key from PayU merchant panel (Settings → POS Configuration)
132
+ 2. Configure second_key in their application
133
+ 3. Set up webhook endpoint (see examples)
134
+ 4. Configure notifyUrl in PayU to point to their endpoint
135
+ 5. Optional: Whitelist PayU IPs if using IP filtering
136
+ 6. Test with PayU sandbox before production
137
+ 7. Monitor PayU-Processing-Time header for performance insights
138
+
139
+ ## References
140
+
141
+ All documentation updates are based on official PayU documentation:
142
+ - PayU API Reference: https://developers.payu.com/europe/api/
143
+ - Webhook Documentation: https://developers.payu.com/europe/docs/webhooks/
144
+ - Payment Lifecycle: https://developers.payu.com/europe/docs/payment-lifecycle/
data/README.md CHANGED
@@ -112,6 +112,155 @@ client.retrieve_refund(order_id, "5000000142")
112
112
  client.retrieve_transactions(order_id)
113
113
  ```
114
114
 
115
+ ### Webhook validation
116
+
117
+ PayU sends webhook notifications when order status changes. This gem provides a validator to verify webhook signatures and parse payloads.
118
+
119
+ #### Basic usage in Rails
120
+
121
+ ```ruby
122
+ # config/routes.rb
123
+ post '/webhooks/payu', to: 'webhooks/payu#create'
124
+
125
+ # app/controllers/webhooks/payu_controller.rb
126
+ class Webhooks::PayuController < ApplicationController
127
+ skip_before_action :verify_authenticity_token
128
+
129
+ def create
130
+ result = PayuPl::Webhooks::Validator.new(request).validate_and_parse
131
+
132
+ if result.failure?
133
+ Rails.logger.error("PayU webhook validation failed: #{result.error}")
134
+ return head :bad_request
135
+ end
136
+
137
+ payload = result.data
138
+ order_id = payload.dig('order', 'orderId')
139
+ status = payload.dig('order', 'status')
140
+
141
+ # Process the webhook...
142
+ # (check for duplicates, enqueue background job, etc.)
143
+
144
+ head :ok
145
+ end
146
+ end
147
+ ```
148
+
149
+ #### Configuration
150
+
151
+ Set your PayU second key (MD5 key) in one of three ways:
152
+
153
+ ```ruby
154
+ # 1. Via initializer (recommended)
155
+ # config/initializers/payu.rb
156
+ PayuPl.configure do |config|
157
+ config.second_key = ENV.fetch('PAYU_SECOND_KEY')
158
+ end
159
+
160
+ # 2. Via ENV variable (automatic fallback)
161
+ ENV['PAYU_SECOND_KEY'] = 'your_second_key'
162
+
163
+ # 3. Pass directly to validator
164
+ validator = PayuPl::Webhooks::Validator.new(request, second_key: 'custom_key')
165
+ ```
166
+
167
+ #### With custom logger
168
+
169
+ ```ruby
170
+ logger = Logger.new(STDOUT)
171
+ validator = PayuPl::Webhooks::Validator.new(request, logger: logger)
172
+ result = validator.validate_and_parse
173
+ ```
174
+
175
+ #### Validation only (without parsing)
176
+
177
+ ```ruby
178
+ validator = PayuPl::Webhooks::Validator.new(request)
179
+
180
+ begin
181
+ validator.verify_signature!
182
+ # Signature is valid
183
+ rescue => e
184
+ # Signature validation failed
185
+ Rails.logger.error("Invalid signature: #{e.message}")
186
+ end
187
+ ```
188
+
189
+ #### Result object
190
+
191
+ The validator returns a `PayuPl::Webhooks::Result` object:
192
+
193
+ ```ruby
194
+ result = validator.validate_and_parse
195
+
196
+ if result.success?
197
+ payload = result.data
198
+ # Access webhook data
199
+ order_id = payload.dig('order', 'orderId')
200
+ status = payload.dig('order', 'status')
201
+
202
+ # Convert amount from minor units (2900 = 29.00 PLN)
203
+ total_amount = payload.dig('order', 'totalAmount').to_i / 100.0
204
+ currency = payload.dig('order', 'currencyCode')
205
+ else
206
+ error_message = result.error
207
+ # Handle validation error
208
+ end
209
+ ```
210
+
211
+ #### Signature algorithms
212
+
213
+ PayU supports multiple signature algorithms (MD5, SHA1, SHA256, SHA384, SHA512). The validator automatically detects the algorithm from the webhook header and verifies accordingly.
214
+
215
+ For MD5, PayU may use either `MD5(body + key)` or `MD5(key + body)`. The validator checks both variants automatically.
216
+
217
+ #### Important: Webhook Best Practices
218
+
219
+ 1. **Always return 200 OK** - After signature validation, return 200 even if processing fails
220
+ 2. **Handle duplicates** - PayU retries up to 20 times if you don't return 200
221
+ 3. **Process asynchronously** - Store webhook and process in background job
222
+ 4. **IP Whitelisting** (optional) - Allow PayU IPs:
223
+ - Production: `185.68.12.10-12`, `185.68.12.26-28`
224
+ - Sandbox: `185.68.14.10-12`, `185.68.14.26-28`
225
+
226
+ See `examples/WEBHOOK_GUIDE.md` for comprehensive integration guide.
227
+
228
+ #### Non-Rails frameworks
229
+
230
+ The validator works with any Rack-compatible request object:
231
+
232
+ ```ruby
233
+ # Sinatra
234
+ post '/webhooks/payu' do
235
+ result = PayuPl::Webhooks::Validator.new(request).validate_and_parse
236
+
237
+ if result.success?
238
+ # Process webhook
239
+ status 200
240
+ else
241
+ status 400
242
+ end
243
+ end
244
+
245
+ # Hanami
246
+ module Web::Controllers::Webhooks
247
+ class Payu
248
+ include Web::Action
249
+
250
+ def call(params)
251
+ result = PayuPl::Webhooks::Validator.new(request).validate_and_parse
252
+
253
+ if result.success?
254
+ # Process webhook
255
+ self.status = 200
256
+ else
257
+ self.status = 400
258
+ end
259
+ end
260
+ end
261
+ end
262
+ ```
263
+
115
264
  ### Retrieve shop data
116
265
 
117
266
  ```ruby
@@ -0,0 +1,153 @@
1
+ # PayU Webhook Integration - Summary
2
+
3
+ ## What Was Added
4
+
5
+ This integration adds webhook validation capabilities to the `payu_pl` gem, allowing you to securely process PayU webhook notifications in any Ruby application.
6
+
7
+ ## Files Created
8
+
9
+ ### Core Library
10
+ - `lib/payu_pl/webhooks/result.rb` - Result object for success/failure responses
11
+ - `lib/payu_pl/webhooks/validator.rb` - Main webhook validator class
12
+ - `lib/payu_pl/configuration.rb` - Updated to support `second_key` configuration
13
+
14
+ ### Tests
15
+ - `spec/webhooks/result_spec.rb` - Tests for Result class (6 examples)
16
+ - `spec/webhooks/validator_spec.rb` - Tests for Validator class (19 examples)
17
+ - All tests passing (70 total examples)
18
+
19
+ ### Examples & Documentation
20
+ - `examples/WEBHOOK_GUIDE.md` - Comprehensive integration guide
21
+ - `examples/webhooks_controller.rb` - Rails controller example
22
+ - `examples/sinatra_webhook_example.rb` - Sinatra example
23
+ - `examples/rack_webhook_example.ru` - Plain Rack example
24
+ - `README.md` - Updated with webhook section
25
+
26
+ ## Key Features
27
+
28
+ ### 1. Flexible Configuration
29
+ ```ruby
30
+ # Option 1: Via configuration
31
+ PayuPl.configure do |config|
32
+ config.second_key = ENV.fetch('PAYU_SECOND_KEY')
33
+ end
34
+
35
+ # Option 2: Via ENV (automatic)
36
+ ENV['PAYU_SECOND_KEY'] = 'your_key'
37
+
38
+ # Option 3: Pass directly
39
+ validator = PayuPl::Webhooks::Validator.new(request, second_key: 'key')
40
+ ```
41
+
42
+ ### 2. Framework Agnostic
43
+ Works with any Rack-compatible framework:
44
+ - Rails
45
+ - Sinatra
46
+ - Hanami
47
+ - Roda
48
+ - Plain Rack
49
+
50
+ ### 3. Comprehensive Signature Validation
51
+ Supports all PayU signature algorithms:
52
+ - MD5 (with both key+body and body+key variants)
53
+ - SHA1, SHA256, SHA384, SHA512
54
+
55
+ ### 4. Optional Logging
56
+ ```ruby
57
+ validator = PayuPl::Webhooks::Validator.new(request, logger: Rails.logger)
58
+ ```
59
+
60
+ Logs:
61
+ - Signature verification steps
62
+ - Algorithm details
63
+ - Payload parsing
64
+ - Errors and backtraces
65
+
66
+ ### 5. Simple API
67
+ ```ruby
68
+ result = validator.validate_and_parse
69
+
70
+ if result.success?
71
+ payload = result.data
72
+ # Process webhook...
73
+ else
74
+ error = result.error
75
+ # Handle error...
76
+ end
77
+ ```
78
+
79
+ ## Usage in Your Controller
80
+
81
+ ```ruby
82
+ class Webhooks::PayuController < ApplicationController
83
+ skip_before_action :verify_authenticity_token
84
+
85
+ def create
86
+ result = PayuPl::Webhooks::Validator.new(request).validate_and_parse
87
+
88
+ if result.failure?
89
+ return head :bad_request
90
+ end
91
+
92
+ payload = result.data
93
+ order_id = payload.dig('order', 'orderId')
94
+ status = payload.dig('order', 'status')
95
+
96
+ # Process webhook...
97
+
98
+ head :ok
99
+ end
100
+ end
101
+ ```
102
+
103
+ ## Security Features
104
+
105
+ 1. **Constant-time signature comparison** - Prevents timing attacks
106
+ 2. **Multi-algorithm support** - Automatic algorithm detection
107
+ 3. **Configurable secret key** - Flexible configuration options
108
+ 4. **Request body preservation** - Can read body multiple times
109
+ 5. **Comprehensive error handling** - Clear error messages
110
+
111
+ ## Testing
112
+
113
+ All webhook functionality is fully tested:
114
+ - ✅ Configuration options (ENV, config, direct)
115
+ - ✅ Signature verification (all algorithms)
116
+ - ✅ MD5 variants (body+key and key+body)
117
+ - ✅ Payload parsing
118
+ - ✅ Error handling
119
+ - ✅ Logging behavior
120
+ - ✅ Result object behavior
121
+
122
+ ## Dependencies
123
+
124
+ Added to Gemfile for testing:
125
+ - `rack ~> 3.0` - For Rack::Request in tests
126
+
127
+ No additional runtime dependencies required.
128
+
129
+ ## Next Steps
130
+
131
+ 1. ✅ Webhook validation implemented
132
+ 2. ✅ Comprehensive tests written
133
+ 3. ✅ Documentation created
134
+ 4. ✅ Examples provided
135
+ 5. 🔄 Ready for use!
136
+
137
+ ## Integration Checklist
138
+
139
+ For users wanting to integrate webhooks:
140
+
141
+ - [ ] Get PayU second_key from merchant panel
142
+ - [ ] Configure second_key (ENV or initializer)
143
+ - [ ] Create webhook controller/endpoint
144
+ - [ ] Add route for webhook
145
+ - [ ] Test with PayU sandbox
146
+ - [ ] Deploy and configure notifyUrl in PayU
147
+ - [ ] Monitor webhook logs
148
+
149
+ ## Links
150
+
151
+ - See `examples/WEBHOOK_GUIDE.md` for detailed integration guide
152
+ - See `examples/webhooks_controller.rb` for Rails example
153
+ - See `README.md` for API documentation