payjp 0.0.7 → 0.0.8
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 +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:
|