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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 27457effbc47ae96c13305248fc780f9e2f1ca00
4
- data.tar.gz: 4a06532b2b0eaf162e2db5b3795584f92d98d3ef
2
+ SHA256:
3
+ metadata.gz: 21b1328487d0db17d3b7e78afe2b1a9f00df6506c2da1b896c216eec238b4936
4
+ data.tar.gz: 6416cc480d88f3fcf135ed0005ee37a2d2d8d70e4d10c7ed4fa68f9a408dbe78
5
5
  SHA512:
6
- metadata.gz: b65d4c3f8df6e10e198dc8a289210adbd06098d8e8220497d2d1f40d5315c6bb775ab7ecb73d94b87b6374bbb6b6116fdeae486ddab71512c95311b64e3556bd
7
- data.tar.gz: ee4ee024077834fd5d9840a251784dd568ea4e0c33e2893c663d100b91cf8c6f89951e49d604c57641819ba82304ece75bb62619524a10ec6165fc04187a4612
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
@@ -1,3 +1,3 @@
1
1
  module Payjp
2
- VERSION = '0.0.7'
2
+ VERSION = '0.0.8'
3
3
  end
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.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)
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
@@ -287,5 +287,14 @@ module Payjp
287
287
  }
288
288
  }
289
289
  end
290
+
291
+ def test_over_capacity_error
292
+ {
293
+ :error => {
294
+ :code => "over_capacity",
295
+ :type => "api_error"
296
+ }
297
+ }
298
+ end
290
299
  end
291
300
  end
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.7
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: 2019-07-22 00:00:00.000000000 Z
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
- post_install_message:
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
- rubyforge_project:
183
- rubygems_version: 2.5.2.3
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:
data/.travis.yml DELETED
@@ -1,10 +0,0 @@
1
- language: ruby
2
-
3
- rvm:
4
- - 2.0.0
5
- - 2.1
6
- - 2.2
7
- - 2.3.0
8
- - jruby-9.0.5.0
9
-
10
- sudo: false