From 488b3412480e1e3470c38b728f494f6d230d2e2d Mon Sep 17 00:00:00 2001 From: Michael Moffett Date: Mon, 21 Sep 2026 10:08:20 -0600 Subject: [PATCH] fix(ruby,lua): default mpp.expires_in to 120 s per the interface table Change the PayKit-layer `mpp.expires_in` default from 300 to 120 seconds in Ruby and Lua (including the Kong plugin's env fallback), and drop the drift notes from the interface doc. `docs/paykit-interface.md:107` makes 120 s canonical and says "align down"; TypeScript, PHP, Python and Go already ship 120, Ruby and Lua do not. --- docs/paykit-interface.md | 3 +-- lua/pay_kit/internal/config.lua | 13 +++++-------- lua/pay_kit/protocols/mpp/init.lua | 10 ++++------ lua/plugins/kong/plugins/pay-kit/init.lua | 2 +- lua/tests/pay_kit/config_spec.lua | 2 +- lua/tests/pay_kit/kong_plugin_runtime_spec.lua | 2 +- ruby/lib/pay_kit/config.rb | 2 +- ruby/test/pay_kit/config_test.rb | 2 +- 8 files changed, 15 insertions(+), 21 deletions(-) diff --git a/docs/paykit-interface.md b/docs/paykit-interface.md index a7d433c31..25a82b177 100644 --- a/docs/paykit-interface.md +++ b/docs/paykit-interface.md @@ -104,7 +104,7 @@ Canonical defaults (today they drift): | `operator.signer` | `Signer.demo` (refused on mainnet at boot) | | `operator.fee_payer` | `true` | | `mpp.realm` | `"App"` | -| `mpp.expires_in` | **120 seconds** (Ruby currently says 300 — align down) | +| `mpp.expires_in` | **120 seconds** | | `x402.scheme` | `"exact"` | | `preflight` | `true` | @@ -265,7 +265,6 @@ What each SDK changes to meet this spec. Everything not listed is already conformant. **Ruby** -- `mpp.expires_in` default 300 → 120. - Add `accepts?(protocol)` on `Gate`; keep `x402_accepted?` / `mpp_accepted?` as deprecated aliases for one release. - Add `payer` and ensure `scheme` on `Payment`. diff --git a/lua/pay_kit/internal/config.lua b/lua/pay_kit/internal/config.lua index cc1ad633d..a4bf896dc 100644 --- a/lua/pay_kit/internal/config.lua +++ b/lua/pay_kit/internal/config.lua @@ -26,7 +26,7 @@ Surface (issue #140): mpp = { realm = "MyApp", challenge_binding_secret = "...", - expires_in = 300, + expires_in = 120, }, }) @@ -200,15 +200,12 @@ function M.configure(opts) local mpp = opts.mpp or {} local mpp_realm = mpp.realm or 'App' local mpp_secret = mpp.challenge_binding_secret - -- expires_in defaults to a short 300s TTL so issued challenges are not - -- valid indefinitely (parity with Python/Rust/Ruby short-TTL defaults - -- and the PHP/Lua expiry-wiring fix). `expires_in = false` is the - -- explicit development opt-out: challenges are then issued with no - -- expiry. Any non-positive number is rejected so `0` is not silently - -- treated as "never expires". + -- `expires_in = false` is the explicit development opt-out: challenges are + -- then issued with no expiry. Any non-positive number is rejected so `0` is + -- not silently treated as "never expires". local mpp_expires_in = mpp.expires_in if mpp_expires_in == nil then - mpp_expires_in = 300 + mpp_expires_in = 120 end if mpp_secret ~= nil and type(mpp_secret) ~= 'string' then return nil, 'pay_kit: mpp.challenge_binding_secret must be a string or nil' diff --git a/lua/pay_kit/protocols/mpp/init.lua b/lua/pay_kit/protocols/mpp/init.lua index c479a7281..b1d158aba 100644 --- a/lua/pay_kit/protocols/mpp/init.lua +++ b/lua/pay_kit/protocols/mpp/init.lua @@ -240,12 +240,10 @@ function Adapter:challenge_headers(gate, _req) local splits = splits_for(gate) if splits then options.splits = splits end -- Wire the configured challenge TTL into issuance so signed challenges - -- are not valid indefinitely. `config.mpp.expires_in` is seconds-from-now - -- (default 300); `false` is the explicit development opt-out that leaves - -- the challenge without an expiry. Mirrors PHP/Ruby/Python which seed a - -- short TTL at challenge construction rather than relying on every caller - -- to pass one. `verify_credential_with_expected` enforces the expiry via - -- `challenge_value:is_expired`. + -- are not valid indefinitely. `config.mpp.expires_in` is seconds-from-now; + -- `false` is the explicit development opt-out that leaves the challenge + -- without an expiry. `verify_credential_with_expected` enforces the expiry + -- via `challenge_value:is_expired`. local expires_in = config.mpp.expires_in if type(expires_in) == 'number' and expires_in > 0 then options.expires = expires_mod.format_rfc3339(os.time() + expires_in) diff --git a/lua/plugins/kong/plugins/pay-kit/init.lua b/lua/plugins/kong/plugins/pay-kit/init.lua index eebee8f23..ee24e1c51 100644 --- a/lua/plugins/kong/plugins/pay-kit/init.lua +++ b/lua/plugins/kong/plugins/pay-kit/init.lua @@ -64,7 +64,7 @@ function M.setup() mpp = { realm = os.getenv('PAY_KIT_MPP_REALM') or 'PayKit (Kong)', challenge_binding_secret = os.getenv('PAY_KIT_MPP_CHALLENGE_BINDING_SECRET'), - expires_in = env_int('PAY_KIT_MPP_EXPIRES_IN', 300), + expires_in = env_int('PAY_KIT_MPP_EXPIRES_IN', 120), }, } local ok, err = pay_kit.configure(opts) diff --git a/lua/tests/pay_kit/config_spec.lua b/lua/tests/pay_kit/config_spec.lua index 81981a201..12974c2f8 100644 --- a/lua/tests/pay_kit/config_spec.lua +++ b/lua/tests/pay_kit/config_spec.lua @@ -146,7 +146,7 @@ end) helper.test('configure() mpp.expires_in default + override', function() reset() assert(pay_kit.configure()) - helper.assert_equal(pay_kit.config().mpp.expires_in, 300) + helper.assert_equal(pay_kit.config().mpp.expires_in, 120) reset() assert(pay_kit.configure({mpp = {expires_in = 60}})) diff --git a/lua/tests/pay_kit/kong_plugin_runtime_spec.lua b/lua/tests/pay_kit/kong_plugin_runtime_spec.lua index 26ef3efb7..57ceb7567 100644 --- a/lua/tests/pay_kit/kong_plugin_runtime_spec.lua +++ b/lua/tests/pay_kit/kong_plugin_runtime_spec.lua @@ -85,7 +85,7 @@ helper.test('Kong bootstrap honours empty / blank env defaults', function() restore_env() local cfg = pay_kit.config() helper.assert_equal(cfg.network, 'solana_devnet') - helper.assert_equal(cfg.mpp.expires_in, 300) -- env_int default + helper.assert_equal(cfg.mpp.expires_in, 120) -- env_int default helper.assert_true(#cfg.accept >= 1) end) diff --git a/ruby/lib/pay_kit/config.rb b/ruby/lib/pay_kit/config.rb index 54531af96..4e48c65fd 100644 --- a/ruby/lib/pay_kit/config.rb +++ b/ruby/lib/pay_kit/config.rb @@ -194,7 +194,7 @@ class MppConfig def initialize @realm = "App" @challenge_binding_secret = nil - @expires_in = 300 + @expires_in = 120 end # Server-side HMAC secret used for stateless challenge binding diff --git a/ruby/test/pay_kit/config_test.rb b/ruby/test/pay_kit/config_test.rb index 21ea530eb..12d726c49 100644 --- a/ruby/test/pay_kit/config_test.rb +++ b/ruby/test/pay_kit/config_test.rb @@ -184,7 +184,7 @@ def test_challenge_binding_secret_setter_and_reader def test_mpp_expires_in_default_and_override PayKit.configure { |_c| } - assert_equal 300, PayKit.config.mpp.expires_in + assert_equal 120, PayKit.config.mpp.expires_in PayKit.reset! PayKit.configure { |c| c.mpp.expires_in = 600 }