wajub 1.1.1 → 1.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 +4 -4
- data/CHANGELOG.md +51 -0
- data/README.md +4 -4
- data/lib/wajub/http/base_client.rb +8 -0
- data/lib/wajub/http/retry_policy.rb +14 -0
- data/lib/wajub/resources.rb +65 -5
- data/lib/wajub/version.rb +1 -1
- data/lib/wajub/webhooks.rb +7 -2
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3a5524dcd4b29eab3e66cc9c5fb1890324ee8fa4b7b090f1117d4feb4492dee7
|
|
4
|
+
data.tar.gz: a863c218296f0ed42c0b89198b612519ad3ec85589829b189aaad40564f3ef98
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 92e45ed29b1f73f55eb0d43bd4bf2933ae744a622605f028fc017efc7af842f2462bd9e0d3aafa8c84bc54f6ce03da1d8dc1c15c5c205e6052effe1a9ed9603b
|
|
7
|
+
data.tar.gz: 4decf56f623ce500d86109d640608620c654c862f3bfdf948f2587e7c6aa7bb4dcb1ddf2dddbf80a9fbc49300a4ab0a383fb8866d800894dc76edbc60ccd1d60
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to the `wajub` gem are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
6
|
+
`wajub` uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## [1.3.0] — 2026-10-12
|
|
11
|
+
|
|
12
|
+
1.2.1 was never published to RubyGems: its changes ship in this release.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- The tax and Shield methods the other server SDKs already had, with the
|
|
16
|
+
same paths and the same results as the Python SDK:
|
|
17
|
+
- `tax.reports`, `tax.list_codes`, `tax.retrieve_code`, `tax.jurisdictions`,
|
|
18
|
+
`tax.thresholds` and `tax.threshold_alerts`;
|
|
19
|
+
- `tax.list_registrations` (a page), `tax.create_registration`,
|
|
20
|
+
`tax.retrieve_registration`, `tax.update_registration` and
|
|
21
|
+
`tax.delete_registration`;
|
|
22
|
+
- `shield.list_blocklist`, `shield.add_to_blocklist` and
|
|
23
|
+
`shield.remove_from_blocklist`.
|
|
24
|
+
|
|
25
|
+
The README listed them, and calling one raised `NoMethodError`.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- `customers.delete_tax_id` called `DELETE /customers/%2Fcustomers%2F…` and
|
|
29
|
+
answered `404`: the nested path went through the resource's own `delete`,
|
|
30
|
+
which takes an id and encodes it as one path segment. It now calls
|
|
31
|
+
`DELETE /customers/{id}/tax_ids/{tax_id}`.
|
|
32
|
+
- `webhooks.construct_event` with a missing `X-Wajub-Signature` or
|
|
33
|
+
`X-Wajub-Timestamp` header (`nil`) raised `NoMethodError`, which a handler
|
|
34
|
+
rescuing `WebhookSignatureVerificationError` let through as a 500. It now
|
|
35
|
+
raises `WebhookSignatureVerificationError`, as does a timestamp that is not a
|
|
36
|
+
number.
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
- **The `409` for an `Idempotency-Key` still being processed is retried.** A
|
|
40
|
+
call sent again after a timeout can reach the API while the first one is still
|
|
41
|
+
running; the API answers `409` with `errors.idempotency_key`. The client now
|
|
42
|
+
waits 2 then 4 seconds and asks again with the same key, within its usual 2
|
|
43
|
+
retries, and returns the first request's result. Every other `409` is still
|
|
44
|
+
raised at once.
|
|
45
|
+
|
|
46
|
+
## [1.2.0] — 2026-10-01
|
|
47
|
+
|
|
48
|
+
Tagged, never published to RubyGems. Its one change ships with 1.3.0.
|
|
49
|
+
|
|
50
|
+
### Removed
|
|
51
|
+
- `payments.initialize_payment`, an alias of `payments.create`. Call `create`.
|
data/README.md
CHANGED
|
@@ -30,7 +30,7 @@ Use **[Wajub.js](https://docs.wajub.com/libraries/components/js)** for embedded
|
|
|
30
30
|
Add to your Gemfile:
|
|
31
31
|
|
|
32
32
|
```ruby
|
|
33
|
-
gem 'wajub', '~> 1.
|
|
33
|
+
gem 'wajub', '~> 1.3'
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Or install directly:
|
|
@@ -99,7 +99,7 @@ class WebhooksController < ApplicationController
|
|
|
99
99
|
request.headers['X-Wajub-Timestamp']
|
|
100
100
|
)
|
|
101
101
|
|
|
102
|
-
case event['
|
|
102
|
+
case event['event']
|
|
103
103
|
when 'payment.succeeded'
|
|
104
104
|
# fulfill order
|
|
105
105
|
end
|
|
@@ -125,7 +125,7 @@ Test mode is selected by your API key prefix (`sk_test.…`), not by the API URL
|
|
|
125
125
|
| Property | Methods |
|
|
126
126
|
|----------|---------|
|
|
127
127
|
| `client.global` | `ping`, `channels`, `countries`, `currencies` |
|
|
128
|
-
| `client.payments` | `create`, `
|
|
128
|
+
| `client.payments` | `create`, `retrieve`, `list`, `cancel`, `process`, `process_split`, `list_refunds` |
|
|
129
129
|
| `client.customers` | `create`, `retrieve`, `update`, `delete`, `list`, `block`, `unblock`, `activate`, `deactivate`, `list_tax_ids`, `create_tax_id`, `delete_tax_id` |
|
|
130
130
|
| `client.refunds` | `create`, `retrieve`, `list` |
|
|
131
131
|
| `client.transfers` | `create`, `retrieve`, `list` |
|
|
@@ -162,7 +162,7 @@ rescue Wajub::WebhookSignatureVerificationError
|
|
|
162
162
|
halt 400
|
|
163
163
|
end
|
|
164
164
|
|
|
165
|
-
case event['
|
|
165
|
+
case event['event']
|
|
166
166
|
when 'payment.succeeded'
|
|
167
167
|
# fulfill order
|
|
168
168
|
end
|
|
@@ -89,6 +89,14 @@ module Wajub
|
|
|
89
89
|
next
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
+
# The same key still being processed: the first request's answer is
|
|
93
|
+
# coming. Asked again with the same key, the API returns it.
|
|
94
|
+
if idempotency_key && RetryPolicy.idempotency_key_in_progress?(status, data) && attempt < RetryPolicy.max_retries
|
|
95
|
+
attempt += 1
|
|
96
|
+
sleep RetryPolicy.idempotency_conflict_delay_seconds(attempt)
|
|
97
|
+
next
|
|
98
|
+
end
|
|
99
|
+
|
|
92
100
|
retry_after = response.dig(:headers, 'retry-after')&.to_i
|
|
93
101
|
raise Errors.from_response(status, data, retry_after: retry_after)
|
|
94
102
|
end
|
|
@@ -15,6 +15,20 @@ module Wajub
|
|
|
15
15
|
MAX_RETRIES
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
# The 409 the API answers while a request with the same Idempotency-Key is
|
|
19
|
+
# still running (errors.idempotency_key). A call sent again after a timeout
|
|
20
|
+
# can arrive before the first has finished: its answer is on the way, so the
|
|
21
|
+
# call is worth repeating with the same key. Every other 409 is a refusal.
|
|
22
|
+
def idempotency_key_in_progress?(status, body)
|
|
23
|
+
status == 409 && body.is_a?(Hash) && body['errors'].is_a?(Hash) && body['errors'].key?('idempotency_key')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# The pause before asking again about a key still being processed:
|
|
27
|
+
# 2, 4, then 8 seconds.
|
|
28
|
+
def idempotency_conflict_delay_seconds(attempt)
|
|
29
|
+
2**[attempt, 1].max
|
|
30
|
+
end
|
|
31
|
+
|
|
18
32
|
def delay_seconds(attempt, response: nil)
|
|
19
33
|
header = response&.[]('retry-after')&.strip
|
|
20
34
|
if header && !header.empty?
|
data/lib/wajub/resources.rb
CHANGED
|
@@ -41,10 +41,6 @@ module Wajub
|
|
|
41
41
|
to_payment_object(post('/payments', params, options))
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
def initialize_payment(params, options = nil)
|
|
45
|
-
create(params, options)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
44
|
def retrieve(id)
|
|
49
45
|
res = get("/payments/#{URI.encode_www_form_component(id)}")
|
|
50
46
|
to_payment_object('transaction' => HttpUtils.pick_resource(res, 'transaction'))
|
|
@@ -159,7 +155,9 @@ module Wajub
|
|
|
159
155
|
end
|
|
160
156
|
|
|
161
157
|
def delete_tax_id(customer_id, tax_id, options = nil)
|
|
162
|
-
delete
|
|
158
|
+
# Not `delete`: on a CrudResource that one takes an id and encodes it as a path segment.
|
|
159
|
+
path = "/customers/#{URI.encode_www_form_component(customer_id)}/tax_ids/#{URI.encode_www_form_component(tax_id)}"
|
|
160
|
+
request('DELETE', path, options: options)
|
|
163
161
|
end
|
|
164
162
|
end
|
|
165
163
|
|
|
@@ -254,6 +252,55 @@ module Wajub
|
|
|
254
252
|
def calculate(params, options = nil)
|
|
255
253
|
HttpUtils.pick_resource(post('/tax/calculate', params, options), 'tax', 'calculation')
|
|
256
254
|
end
|
|
255
|
+
|
|
256
|
+
def reports(params = nil)
|
|
257
|
+
list = HttpUtils.pick_list(get('/tax/reports', params), 'reports', 'tax_reports')
|
|
258
|
+
[list[:data], list[:meta]]
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
def list_codes(params = nil)
|
|
262
|
+
list = HttpUtils.pick_list(get('/tax/codes', params), 'tax_codes', 'codes')
|
|
263
|
+
[list[:data], list[:meta]]
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def retrieve_code(code)
|
|
267
|
+
HttpUtils.pick_resource(get("/tax/codes/#{URI.encode_www_form_component(code)}"), 'tax_code', 'code')
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
def list_registrations(params = nil)
|
|
271
|
+
PagedResult.create(self, '/tax/registrations', 'registrations', params)
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def create_registration(params, options = nil)
|
|
275
|
+
HttpUtils.pick_resource(post('/tax/registrations', params, options), 'registration', 'tax_registration')
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
def retrieve_registration(id)
|
|
279
|
+
HttpUtils.pick_resource(get("/tax/registrations/#{URI.encode_www_form_component(id)}"), 'registration', 'tax_registration')
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def update_registration(id, params, options = nil)
|
|
283
|
+
HttpUtils.pick_resource(put("/tax/registrations/#{URI.encode_www_form_component(id)}", params, options), 'registration', 'tax_registration')
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def delete_registration(id, options = nil)
|
|
287
|
+
request('DELETE', "/tax/registrations/#{URI.encode_www_form_component(id)}", options: options)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def jurisdictions(params = nil)
|
|
291
|
+
list = HttpUtils.pick_list(get('/tax/jurisdictions', params), 'jurisdictions', 'tax_jurisdictions')
|
|
292
|
+
[list[:data], list[:meta]]
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
def thresholds(params = nil)
|
|
296
|
+
list = HttpUtils.pick_list(get('/tax/thresholds', params), 'thresholds', 'tax_thresholds')
|
|
297
|
+
[list[:data], list[:meta]]
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def threshold_alerts(params = nil)
|
|
301
|
+
list = HttpUtils.pick_list(get('/tax/thresholds/alerts', params), 'alerts', 'threshold_alerts')
|
|
302
|
+
[list[:data], list[:meta]]
|
|
303
|
+
end
|
|
257
304
|
end
|
|
258
305
|
|
|
259
306
|
class ShieldResource < BaseClient
|
|
@@ -268,6 +315,19 @@ module Wajub
|
|
|
268
315
|
def stats(params = nil)
|
|
269
316
|
HttpUtils.pick_resource(get('/shield/stats', params), 'shield', 'stats')
|
|
270
317
|
end
|
|
318
|
+
|
|
319
|
+
def list_blocklist(params = nil)
|
|
320
|
+
list = HttpUtils.pick_list(get('/shield/blocklist', params), 'blocklist', 'entries')
|
|
321
|
+
[list[:data], list[:meta]]
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def add_to_blocklist(params, options = nil)
|
|
325
|
+
HttpUtils.pick_resource(post('/shield/blocklist', params, options), 'blocklist', 'entry')
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def remove_from_blocklist(id, options = nil)
|
|
329
|
+
request('DELETE', "/shield/blocklist/#{URI.encode_www_form_component(id)}", options: options)
|
|
330
|
+
end
|
|
271
331
|
end
|
|
272
332
|
|
|
273
333
|
class ListenResource < BaseClient
|
data/lib/wajub/version.rb
CHANGED
data/lib/wajub/webhooks.rb
CHANGED
|
@@ -15,8 +15,13 @@ module Wajub
|
|
|
15
15
|
@secret = secret.to_s
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
# A missing signature or timestamp header (nil, as request.headers returns
|
|
19
|
+
# it) raises WebhookSignatureVerificationError, like a wrong one.
|
|
18
20
|
def construct_event(payload, signature, timestamp, tolerance = DEFAULT_TOLERANCE)
|
|
19
21
|
raise ArgumentError, 'Wajub: webhook_secret is required for construct_event' if @secret.empty?
|
|
22
|
+
if signature.to_s.empty? || timestamp.to_s.empty?
|
|
23
|
+
raise WebhookSignatureVerificationError, 'Missing X-Wajub-Signature or X-Wajub-Timestamp header.'
|
|
24
|
+
end
|
|
20
25
|
raise WebhookSignatureVerificationError, 'Invalid webhook signature format. Expected v1={hash}.' unless signature.start_with?('v1=')
|
|
21
26
|
raise ArgumentError, 'Wajub: payload must be the raw request body as a String, not a parsed object' unless payload.is_a?(String)
|
|
22
27
|
|
|
@@ -26,8 +31,8 @@ module Wajub
|
|
|
26
31
|
|
|
27
32
|
raise WebhookSignatureVerificationError, 'Webhook signature verification failed.' unless Crypto.timing_safe_equal(expected, received)
|
|
28
33
|
|
|
29
|
-
ts = Float(timestamp)
|
|
30
|
-
raise WebhookSignatureVerificationError, 'Invalid webhook timestamp.' unless ts
|
|
34
|
+
ts = Float(timestamp, exception: false)
|
|
35
|
+
raise WebhookSignatureVerificationError, 'Invalid webhook timestamp.' unless ts&.finite?
|
|
31
36
|
|
|
32
37
|
drift = (Time.now.to_i - ts).abs
|
|
33
38
|
if tolerance.positive? && drift > tolerance
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: wajub
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Wajub
|
|
@@ -16,6 +16,7 @@ executables: []
|
|
|
16
16
|
extensions: []
|
|
17
17
|
extra_rdoc_files: []
|
|
18
18
|
files:
|
|
19
|
+
- CHANGELOG.md
|
|
19
20
|
- LICENSE
|
|
20
21
|
- README.md
|
|
21
22
|
- lib/wajub.rb
|
|
@@ -37,6 +38,7 @@ licenses:
|
|
|
37
38
|
metadata:
|
|
38
39
|
homepage_uri: https://docs.wajub.com/libraries/sdks/ruby
|
|
39
40
|
source_code_uri: https://github.com/wajubhq/wajub-ruby
|
|
41
|
+
changelog_uri: https://github.com/wajubhq/wajub-ruby/blob/main/CHANGELOG.md
|
|
40
42
|
rdoc_options: []
|
|
41
43
|
require_paths:
|
|
42
44
|
- lib
|
|
@@ -51,7 +53,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
51
53
|
- !ruby/object:Gem::Version
|
|
52
54
|
version: '0'
|
|
53
55
|
requirements: []
|
|
54
|
-
rubygems_version: 4.0.
|
|
56
|
+
rubygems_version: 4.0.21
|
|
55
57
|
specification_version: 4
|
|
56
58
|
summary: Official Wajub Ruby SDK
|
|
57
59
|
test_files: []
|