mpp-rb 0.1.8 → 0.1.10

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
2
  SHA256:
3
- metadata.gz: e5d5c20207eb5f09b4f077cea8bdd1fb1f2acc15d12c5d00928cc7e8eddcf627
4
- data.tar.gz: 1f080945c2a322985b47d184ccaf00b543931bcb6f4623c070175d03efab5cd8
3
+ metadata.gz: 4a9ab53da9713524d119320b1843e58948b433e213e17250cd641e90f4286083
4
+ data.tar.gz: 9ed02c682b99438d702cf231e97e96f0b275e84954785ef9eb2f0750b4a1a7c5
5
5
  SHA512:
6
- metadata.gz: 7044c78ebef4814fce755614c194a6723c39df930071762aab94e19e944bb241f33bfd554fdc18ed0ef5f7b3f9cdc9d219923ab4a9aac6278e94896069b2634c
7
- data.tar.gz: d71d6869c20d1a569a0425cde39dd39938688735d0360db47fe82f80ac454f3d4ec015624f942c1bf1760c271e9297ca848300ac63d5d0366496b57a2475efc6
6
+ metadata.gz: 12603bd4ec974e488a632ce3ec6d91304e4996bf218cc47638a652c2a096b73aa2124c12242effde8d711f9106e2c4138ee68ca8542c13d9341bcadb2d42b24a
7
+ data.tar.gz: 74a80bdc6320841a891e305ae39838156292a6656ce6d73f48b92283dfd5e639f91e415e631d2cdf4cd4ffb91c6c7871c905e30685380f4a6981f074ee11977b
@@ -136,7 +136,7 @@ module Mpp
136
136
  end
137
137
 
138
138
  def x402_credential?(credential)
139
- credential.payload.is_a?(Hash) && credential.payload["_x402"] == true
139
+ credential.is_a?(X402Credential)
140
140
  end
141
141
 
142
142
  def addresses_equal?(left, right)
@@ -99,9 +99,9 @@ module Mpp
99
99
  http_method: http_method
100
100
  )
101
101
  echo = challenge.to_echo
102
- Mpp::Credential.new(
102
+ X402Credential.new(
103
103
  challenge: echo,
104
- payload: authorization.merge("_x402" => true),
104
+ payload: authorization,
105
105
  source: source_for(authorization["from"])
106
106
  )
107
107
  end
@@ -0,0 +1,12 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module Mpp
5
+ module Methods
6
+ module Evm
7
+ # Credential bound server-side from an x402 PAYMENT-SIGNATURE header.
8
+ class X402Credential < Mpp::Credential
9
+ end
10
+ end
11
+ end
12
+ end
@@ -8,6 +8,7 @@ module Mpp
8
8
  autoload :Authorization, "mpp/methods/evm/authorization"
9
9
  autoload :ChargeIntent, "mpp/methods/evm/charge_intent"
10
10
  autoload :EvmMethod, "mpp/methods/evm/evm_method"
11
+ autoload :X402Credential, "mpp/methods/evm/x402_credential"
11
12
 
12
13
  # Factory for a server-side EVM charge method with inline x402 exact support.
13
14
  def self.charge(currency:, recipient:, x402:, authorization: nil, chain_id: nil, decimals: nil,
@@ -221,6 +221,7 @@ module Mpp
221
221
 
222
222
  receipt_data = fetch_transaction_receipt(rpc_url, reserved_tx_hash)
223
223
  verify_transaction_receipt!(receipt_data, request, credential: credential)
224
+ claim_settlement!(reserved_tx_hash)
224
225
  @store.put(store_key, TRANSACTION_VERIFIED)
225
226
  return Mpp::Receipt.success(reserved_tx_hash)
226
227
  end
@@ -241,6 +242,7 @@ module Mpp
241
242
  if transaction_submission_may_have_succeeded?(e)
242
243
  receipt_data = fetch_transaction_receipt(rpc_url, reserved_tx_hash)
243
244
  verify_transaction_receipt!(receipt_data, request, credential: credential)
245
+ claim_settlement!(reserved_tx_hash)
244
246
  @store.put(store_key, TRANSACTION_VERIFIED)
245
247
  return Mpp::Receipt.success(reserved_tx_hash)
246
248
  end
@@ -260,16 +262,26 @@ module Mpp
260
262
  # claim until verification finishes so a concurrent retry cannot
261
263
  # rebroadcast the same payload.
262
264
  raw_store_key = store_key
265
+ raw_tx_hash = reserved_tx_hash
263
266
  store_key, reserved_tx_hash = rebase_reservation_to_chain_hash(store_key, reserved_tx_hash, tx_hash)
264
267
 
265
268
  receipt_data = fetch_transaction_receipt(rpc_url, reserved_tx_hash)
266
269
  verify_transaction_receipt!(receipt_data, request, credential: credential)
270
+ claim_settlement!(raw_tx_hash, reserved_tx_hash)
267
271
  @store.put(store_key, TRANSACTION_VERIFIED)
268
272
  @store.put(raw_store_key, TRANSACTION_VERIFIED) if raw_store_key != store_key
269
273
 
270
274
  Mpp::Receipt.success(reserved_tx_hash)
271
275
  end
272
276
 
277
+ def claim_settlement!(*tx_hashes)
278
+ tx_hashes.map(&:downcase).uniq.each do |tx_hash|
279
+ unless @store.put_if_absent("mpp:charge:settled:#{tx_hash}", true)
280
+ raise Mpp::VerificationError, "Transaction hash already used"
281
+ end
282
+ end
283
+ end
284
+
273
285
  def transaction_submission_may_have_succeeded?(error)
274
286
  message = "#{error.class}: #{error.message}".downcase
275
287
 
@@ -33,8 +33,13 @@ module Mpp
33
33
 
34
34
  sig { params(env: T.untyped).returns(T::Array[T.untyped]) }
35
35
  def call(env)
36
+ original_input = env["rack.input"]
37
+ body_capture = capture_request_body(env)
36
38
  charge_opts = @pricing.call(env)
37
- return @app.call(env) unless charge_opts
39
+ unless charge_opts
40
+ env["rack.input"] = original_input if body_capture && env["rack.input"].equal?(body_capture)
41
+ return @app.call(env)
42
+ end
38
43
 
39
44
  authorization = env["HTTP_AUTHORIZATION"]
40
45
  payment_authorization = env["HTTP_PAYMENT_AUTHORIZATION"]
@@ -42,7 +47,6 @@ module Mpp
42
47
  accept_payment = env["HTTP_ACCEPT_PAYMENT"]
43
48
  http_method = env["REQUEST_METHOD"]
44
49
  url = request_url(env)
45
- body_capture = capture_request_body(env)
46
50
 
47
51
  request_body = body_capture&.materialize
48
52
  env["rack.input"] = StringIO.new(request_body || "") if body_capture
@@ -278,8 +282,11 @@ module Mpp
278
282
 
279
283
  sig { returns(T.untyped) }
280
284
  def rewind
281
- @input.rewind if @input.respond_to?(:rewind)
285
+ return unless @input.respond_to?(:rewind)
286
+
287
+ result = @input.rewind
282
288
  @buffer = +"".b
289
+ result
283
290
  end
284
291
 
285
292
  sig { returns(T.untyped) }
data/lib/mpp/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module Mpp
5
- VERSION = "0.1.8"
5
+ VERSION = "0.1.10"
6
6
  end
@@ -150,7 +150,7 @@ module Mpp
150
150
  route_bound = !client_nonce.nil?
151
151
  route_requires_binding = !challenge.digest.nil? || !challenge.opaque.nil?
152
152
 
153
- if route_requires_binding && !route_bound && route_binding == :required
153
+ if !route_bound && route_binding == :required
154
154
  Kernel.raise Mpp::VerificationFailedError.new(reason: "x402 payment payload does not bind required route metadata")
155
155
  end
156
156
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mpp-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.1.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stripe
@@ -86,6 +86,7 @@ files:
86
86
  - lib/mpp/methods/evm/authorization.rb
87
87
  - lib/mpp/methods/evm/charge_intent.rb
88
88
  - lib/mpp/methods/evm/evm_method.rb
89
+ - lib/mpp/methods/evm/x402_credential.rb
89
90
  - lib/mpp/methods/stripe.rb
90
91
  - lib/mpp/methods/stripe/analytics_metadata.rb
91
92
  - lib/mpp/methods/stripe/charge_intent.rb