payjp 0.0.7 → 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/test.yml +20 -0
- data/lib/payjp/version.rb +1 -1
- data/lib/payjp.rb +28 -2
- data/payjp.gemspec +4 -1
- data/test/payjp/api_resource_test.rb +135 -0
- data/test/test_data.rb +9 -0
- metadata +9 -9
- data/.travis.yml +0 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 21b1328487d0db17d3b7e78afe2b1a9f00df6506c2da1b896c216eec238b4936
|
4
|
+
data.tar.gz: 6416cc480d88f3fcf135ed0005ee37a2d2d8d70e4d10c7ed4fa68f9a408dbe78
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9d907d975eb58780bd31183c3e6fd68fa99c21fa34bcbe545be98b8d9f18453d822c1cb98e7f6020235dbcf70aeedaf3e2b5022132b97c62671be847d5326923
|
7
|
+
data.tar.gz: 0be34aabaceb678126e61f71d8a0832140972f554789e135e043693996cf288875aa196383fe3f97b87073e448df265ed028ffc32c3bf26ac1a3ce10226a7be7
|
@@ -0,0 +1,20 @@
|
|
1
|
+
name: Ruby Build Test
|
2
|
+
|
3
|
+
on: push
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build-test:
|
7
|
+
|
8
|
+
runs-on: ubuntu-latest
|
9
|
+
strategy:
|
10
|
+
matrix:
|
11
|
+
ruby-version: [2.0.0, 2.1, 2.2, 2.3.0, jruby-9.2.17.0]
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
- name: Set up Ruby ${{ matrix.ruby-version }}
|
15
|
+
uses: ruby/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
ruby-version: ${{ matrix.ruby-version }}
|
18
|
+
bundler-cache: true
|
19
|
+
- name: Run tests
|
20
|
+
run: bundle exec rake
|
data/lib/payjp/version.rb
CHANGED
data/lib/payjp.rb
CHANGED
@@ -50,23 +50,39 @@ module Payjp
|
|
50
50
|
@ssl_ca_file = nil
|
51
51
|
@ssl_ca_path = nil
|
52
52
|
@ssl_cert_store = nil
|
53
|
+
@max_retry = 0
|
54
|
+
@retry_initial_delay = 2
|
55
|
+
@retry_max_delay = 32
|
53
56
|
|
54
57
|
class << self
|
55
58
|
attr_accessor :api_key, :api_base, :api_version, :connect_base, :uploads_base,
|
56
|
-
:open_timeout, :read_timeout, :ssl_ca_file, :ssl_ca_path, :ssl_cert_store
|
59
|
+
:open_timeout, :read_timeout, :ssl_ca_file, :ssl_ca_path, :ssl_cert_store, :max_retry, :retry_initial_delay, :retry_max_delay
|
57
60
|
end
|
58
61
|
|
59
62
|
def self.api_url(url = '', api_base_url = nil)
|
60
63
|
(api_base_url || @api_base) + url
|
61
64
|
end
|
62
65
|
|
63
|
-
def self.
|
66
|
+
def self.get_retry_delay(retry_count, retry_initial_delay, retry_max_delay)
|
67
|
+
# Get retry delay seconds.
|
68
|
+
# Based on "Exponential backoff with equal jitter" algorithm.
|
69
|
+
# https://aws.amazon.com/jp/blogs/architecture/exponential-backoff-and-jitter/
|
70
|
+
|
71
|
+
wait = [retry_max_delay, retry_initial_delay * 2 ** retry_count].min
|
72
|
+
random = Random.new()
|
73
|
+
(wait / 2) + (random.rand(wait / 2.0))
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.request(method, url, api_key, params = {}, headers = {}, api_base_url = nil, open_timeout = nil, read_timeout = nil, ssl_ca_file = nil, ssl_ca_path = nil, ssl_cert_store = nil, max_retry = nil, retry_initial_delay= nil, retry_max_delay = nil)
|
64
77
|
api_base_url ||= @api_base
|
65
78
|
open_timeout ||= @open_timeout
|
66
79
|
read_timeout ||= @read_timeout
|
67
80
|
ssl_ca_file ||= @ssl_ca_file
|
68
81
|
ssl_ca_path ||= @ssl_ca_path
|
69
82
|
ssl_cert_store ||= @ssl_cert_store
|
83
|
+
max_retry ||= @max_retry
|
84
|
+
retry_initial_delay ||= @retry_initial_delay
|
85
|
+
retry_max_delay ||= @retry_max_delay
|
70
86
|
|
71
87
|
unless api_key ||= @api_key
|
72
88
|
raise AuthenticationError.new('No API key provided. ' \
|
@@ -107,6 +123,8 @@ module Payjp
|
|
107
123
|
:ssl_ca_file => ssl_ca_file, :ssl_ca_path => ssl_ca_path,
|
108
124
|
:ssl_cert_store => ssl_cert_store)
|
109
125
|
|
126
|
+
retry_count = 1
|
127
|
+
|
110
128
|
begin
|
111
129
|
# $stderr.puts request_opts
|
112
130
|
|
@@ -122,6 +140,12 @@ module Payjp
|
|
122
140
|
raise
|
123
141
|
end
|
124
142
|
rescue RestClient::ExceptionWithResponse => e
|
143
|
+
if e.http_code == 429 and retry_count <= max_retry then
|
144
|
+
sleep get_retry_delay(retry_count, retry_initial_delay, retry_max_delay)
|
145
|
+
retry_count += 1
|
146
|
+
retry
|
147
|
+
end
|
148
|
+
|
125
149
|
if rcode = e.http_code and rbody = e.http_body
|
126
150
|
handle_api_error(rcode, rbody)
|
127
151
|
else
|
@@ -239,6 +263,8 @@ module Payjp
|
|
239
263
|
raise authentication_error error, rcode, rbody, error_obj
|
240
264
|
when 402
|
241
265
|
raise card_error error, rcode, rbody, error_obj
|
266
|
+
when 429
|
267
|
+
raise api_error error, rcode, rbody, error_obj
|
242
268
|
else
|
243
269
|
raise api_error error, rcode, rbody, error_obj
|
244
270
|
end
|
data/payjp.gemspec
CHANGED
@@ -23,5 +23,8 @@ Gem::Specification.new do |s|
|
|
23
23
|
s.files = `git ls-files`.split("\n")
|
24
24
|
s.test_files = `git ls-files -- test/*`.split("\n")
|
25
25
|
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
26
|
-
s.require_paths = ['lib']
|
26
|
+
s.require_paths = ['lib']
|
27
|
+
s.metadata = {
|
28
|
+
"source_code_uri" => "https://github.com/payjp/payjp-ruby",
|
29
|
+
}
|
27
30
|
end
|
@@ -411,6 +411,25 @@ module Payjp
|
|
411
411
|
|
412
412
|
assert_equal true, rescued
|
413
413
|
end
|
414
|
+
|
415
|
+
should "429s should raise a APIError of over capacity error code" do
|
416
|
+
response = test_response(test_over_capacity_error, 429)
|
417
|
+
@mock.expects(:get).once.raises(RestClient::ExceptionWithResponse.new(response, 429))
|
418
|
+
|
419
|
+
rescued = false
|
420
|
+
begin
|
421
|
+
Payjp::Customer.new("test_customer").refresh
|
422
|
+
assert false # shouldn't get here either
|
423
|
+
rescue Payjp::APIError => e # we don't use assert_raises because we want to examine e
|
424
|
+
rescued = true
|
425
|
+
assert e.is_a? Payjp::APIError
|
426
|
+
assert_equal(429, e.http_status)
|
427
|
+
assert_equal(true, !!e.http_body)
|
428
|
+
assert_equal("over_capacity", e.json_body[:error][:code])
|
429
|
+
end
|
430
|
+
|
431
|
+
assert_equal true, rescued
|
432
|
+
end
|
414
433
|
end
|
415
434
|
|
416
435
|
should 'save nothing if nothing changes' do
|
@@ -442,4 +461,120 @@ module Payjp
|
|
442
461
|
end
|
443
462
|
end
|
444
463
|
end
|
464
|
+
|
465
|
+
class APIRequestorRetryTest < Test::Unit::TestCase
|
466
|
+
|
467
|
+
setup do
|
468
|
+
Payjp.max_retry = 0
|
469
|
+
Payjp.retry_initial_delay = 0.1
|
470
|
+
end
|
471
|
+
|
472
|
+
def mock_exceptions(error_codes, call_count)
|
473
|
+
exception_mock = nil
|
474
|
+
error_codes.with_index(1) { |code, id|
|
475
|
+
if id == 1
|
476
|
+
exception_mock = @mock.expects(:get).raises(RestClient::ExceptionWithResponse.new(test_response(test_api_error, code)))
|
477
|
+
else
|
478
|
+
exception_mock = exception_mock.then.raises(RestClient::ExceptionWithResponse.new(test_response(test_api_error, code)))
|
479
|
+
end
|
480
|
+
}
|
481
|
+
exception_mock.at_least(call_count)
|
482
|
+
end
|
483
|
+
|
484
|
+
context "checking retry" do
|
485
|
+
should "over capacity retry disabled" do
|
486
|
+
error_codes = [429, 599].to_enum
|
487
|
+
# returns 599 at 2nd try but max_retry 0 then retry disabled
|
488
|
+
mock_exceptions(error_codes, 1)
|
489
|
+
rescued = false
|
490
|
+
|
491
|
+
begin
|
492
|
+
Payjp::Charge.retrieve("test_charge")
|
493
|
+
rescue Payjp::APIError => e # we don't use assert_raises because we want to examine e
|
494
|
+
assert e.is_a? Payjp::APIError
|
495
|
+
assert_equal(429, e.http_status)
|
496
|
+
rescued = true
|
497
|
+
end
|
498
|
+
assert_equal true, rescued
|
499
|
+
end
|
500
|
+
|
501
|
+
should "no retry" do
|
502
|
+
Payjp.max_retry = 2
|
503
|
+
Payjp.retry_initial_delay = 0.1
|
504
|
+
error_codes = [599, 429, 429, 429].to_enum
|
505
|
+
# returns 599 at first try
|
506
|
+
mock_exceptions(error_codes, 1)
|
507
|
+
rescued = false
|
508
|
+
|
509
|
+
begin
|
510
|
+
Payjp::Charge.retrieve("test_charge")
|
511
|
+
rescue Payjp::APIError => e # we don't use assert_raises because we want to examine e
|
512
|
+
assert e.is_a? Payjp::APIError
|
513
|
+
assert_equal(599, e.http_status)
|
514
|
+
assert_equal(true, !!e.http_body)
|
515
|
+
rescued = true
|
516
|
+
end
|
517
|
+
assert_equal true, rescued
|
518
|
+
end
|
519
|
+
|
520
|
+
should "over capacity full retry" do
|
521
|
+
Payjp.max_retry = 2
|
522
|
+
Payjp.retry_initial_delay = 0.1
|
523
|
+
error_codes = [429, 429, 429, 429, 599].to_enum
|
524
|
+
|
525
|
+
# first try + 2 retries + unexpected 599
|
526
|
+
mock_exceptions(error_codes, 3)
|
527
|
+
rescued = false
|
528
|
+
|
529
|
+
begin
|
530
|
+
Payjp::Charge.retrieve("test_charge")
|
531
|
+
rescue Payjp::APIError => e # we don't use assert_raises because we want to examine e
|
532
|
+
assert e.is_a? Payjp::APIError
|
533
|
+
assert_equal(429, e.http_status)
|
534
|
+
rescued = true
|
535
|
+
end
|
536
|
+
|
537
|
+
assert_equal true, rescued
|
538
|
+
end
|
539
|
+
|
540
|
+
should "over capacity halfway of retries" do
|
541
|
+
Payjp.max_retry = 5
|
542
|
+
Payjp.retry_initial_delay = 0.1
|
543
|
+
|
544
|
+
error_codes = [429, 599, 429, 429, 429].to_enum
|
545
|
+
rescued = false
|
546
|
+
|
547
|
+
# returns not 429 status at 2nd try
|
548
|
+
mock_exceptions(error_codes, 2)
|
549
|
+
begin
|
550
|
+
Payjp::Charge.retrieve("test_charge")
|
551
|
+
rescue Payjp::APIError => e # we don't use assert_raises because we want to examine e
|
552
|
+
assert e.is_a? Payjp::APIError
|
553
|
+
assert_equal(599, e.http_status)
|
554
|
+
rescued = true
|
555
|
+
end
|
556
|
+
|
557
|
+
assert_equal true, rescued
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
context "retry interval" do
|
562
|
+
should "retry initial delay" do
|
563
|
+
retry_initial_delay = 2
|
564
|
+
retry_max_delay = 32
|
565
|
+
|
566
|
+
assert_equal(true, Payjp.get_retry_delay(0, retry_initial_delay, retry_max_delay).between?(1, 2))
|
567
|
+
assert_equal(true, Payjp.get_retry_delay(1, retry_initial_delay, retry_max_delay).between?(2, 4))
|
568
|
+
assert_equal(true, Payjp.get_retry_delay(2, retry_initial_delay, retry_max_delay).between?(4, 8))
|
569
|
+
# cap
|
570
|
+
assert_equal(true, Payjp.get_retry_delay(4, retry_initial_delay, retry_max_delay).between?(16, 32))
|
571
|
+
assert_equal(true, Payjp.get_retry_delay(10, retry_initial_delay, retry_max_delay).between?(16, 32))
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
teardown do
|
576
|
+
Payjp.max_retry = 0
|
577
|
+
Payjp.retry_initial_delay = 0.1
|
578
|
+
end
|
579
|
+
end
|
445
580
|
end
|
data/test/test_data.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: payjp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- PAY.JP
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-10-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-client
|
@@ -107,10 +107,10 @@ executables: []
|
|
107
107
|
extensions: []
|
108
108
|
extra_rdoc_files: []
|
109
109
|
files:
|
110
|
+
- ".github/workflows/test.yml"
|
110
111
|
- ".gitignore"
|
111
112
|
- ".rubocop.yml"
|
112
113
|
- ".rubocop_todo.yml"
|
113
|
-
- ".travis.yml"
|
114
114
|
- CONTRIBUTORS
|
115
115
|
- Gemfile
|
116
116
|
- History.txt
|
@@ -163,8 +163,9 @@ files:
|
|
163
163
|
homepage: https://pay.jp
|
164
164
|
licenses:
|
165
165
|
- MIT
|
166
|
-
metadata:
|
167
|
-
|
166
|
+
metadata:
|
167
|
+
source_code_uri: https://github.com/payjp/payjp-ruby
|
168
|
+
post_install_message:
|
168
169
|
rdoc_options: []
|
169
170
|
require_paths:
|
170
171
|
- lib
|
@@ -179,9 +180,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
179
180
|
- !ruby/object:Gem::Version
|
180
181
|
version: '0'
|
181
182
|
requirements: []
|
182
|
-
|
183
|
-
|
184
|
-
signing_key:
|
183
|
+
rubygems_version: 3.1.2
|
184
|
+
signing_key:
|
185
185
|
specification_version: 4
|
186
186
|
summary: Ruby bindings for the Payjp API
|
187
187
|
test_files:
|