mpp-rb 0.1.5 → 0.1.6
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 +4 -4
- data/README.md +65 -0
- data/lib/mpp/challenge.rb +31 -8
- data/lib/mpp/challenge_echo.rb +14 -3
- data/lib/mpp/challenge_id.rb +30 -6
- data/lib/mpp/client/transport.rb +1 -1
- data/lib/mpp/methods/evm/evm_method.rb +18 -2
- data/lib/mpp/methods/evm.rb +5 -2
- data/lib/mpp/methods/stripe/charge_intent.rb +6 -7
- data/lib/mpp/methods/stripe/crypto_payment_recorder.rb +55 -0
- data/lib/mpp/methods/stripe/defaults.rb +1 -0
- data/lib/mpp/methods/stripe/machine_payments.rb +130 -0
- data/lib/mpp/methods/stripe/stripe_method.rb +22 -3
- data/lib/mpp/methods/stripe.rb +23 -0
- data/lib/mpp/methods/tempo/client_method.rb +22 -4
- data/lib/mpp/methods/tempo/intents.rb +53 -41
- data/lib/mpp/parsing.rb +20 -2
- data/lib/mpp/server/compose.rb +22 -10
- data/lib/mpp/server/decorator.rb +3 -1
- data/lib/mpp/server/method.rb +15 -0
- data/lib/mpp/server/middleware.rb +16 -2
- data/lib/mpp/server/mpp_handler.rb +66 -12
- data/lib/mpp/server/verify.rb +24 -14
- data/lib/mpp/version.rb +1 -1
- data/lib/mpp.rb +13 -3
- metadata +17 -1
|
@@ -10,7 +10,7 @@ module Mpp
|
|
|
10
10
|
class MppHandler
|
|
11
11
|
extend T::Sig
|
|
12
12
|
|
|
13
|
-
REQUEST_OPTION_KEYS = [:body, :url, :payment_signature, :accept_payment, :http_method].freeze
|
|
13
|
+
REQUEST_OPTION_KEYS = [:body, :url, :payment_signature, :accept_payment, :http_method, :payment_authorization].freeze
|
|
14
14
|
|
|
15
15
|
sig { returns(T.untyped) }
|
|
16
16
|
attr_reader :method
|
|
@@ -27,6 +27,12 @@ module Mpp
|
|
|
27
27
|
sig { returns(T::Hash[String, T.untyped]) }
|
|
28
28
|
attr_reader :defaults
|
|
29
29
|
|
|
30
|
+
sig { returns(T::Boolean) }
|
|
31
|
+
attr_reader :requires_auth
|
|
32
|
+
|
|
33
|
+
sig { returns(T.nilable(String)) }
|
|
34
|
+
attr_reader :credential_header
|
|
35
|
+
|
|
30
36
|
sig do
|
|
31
37
|
params(
|
|
32
38
|
realm: String,
|
|
@@ -34,16 +40,21 @@ module Mpp
|
|
|
34
40
|
method: T.untyped,
|
|
35
41
|
methods: T.nilable(T::Array[T.untyped]),
|
|
36
42
|
defaults: T.nilable(T::Hash[String, T.untyped]),
|
|
37
|
-
events: T.nilable(Mpp::Events::Dispatcher)
|
|
43
|
+
events: T.nilable(Mpp::Events::Dispatcher),
|
|
44
|
+
requires_auth: T::Boolean
|
|
38
45
|
).void
|
|
39
46
|
end
|
|
40
|
-
def initialize(realm:, secret_key:, method: nil, methods: nil, defaults: nil, events: nil)
|
|
47
|
+
def initialize(realm:, secret_key:, method: nil, methods: nil, defaults: nil, events: nil, requires_auth: false)
|
|
41
48
|
@methods = T.let(normalize_methods(method, methods), T::Array[T.untyped])
|
|
42
49
|
@method = T.let(@methods.first, T.untyped)
|
|
43
50
|
@realm = T.let(realm, String)
|
|
44
51
|
@secret_key = T.let(secret_key, String)
|
|
45
52
|
@defaults = T.let(defaults || {}, T::Hash[String, T.untyped])
|
|
46
53
|
@events = T.let(events || Mpp::Events.server_dispatcher, Mpp::Events::Dispatcher)
|
|
54
|
+
@method_hook_events = T.let(Mpp::Events.server_dispatcher, Mpp::Events::Dispatcher)
|
|
55
|
+
@requires_auth = T.let(requires_auth, T::Boolean)
|
|
56
|
+
@credential_header = T.let(requires_auth ? Mpp::PAYMENT_AUTHORIZATION_HEADER : nil, T.nilable(String))
|
|
57
|
+
register_method_payment_success
|
|
47
58
|
end
|
|
48
59
|
|
|
49
60
|
# Create with auto-detected realm and secret_key.
|
|
@@ -53,16 +64,18 @@ module Mpp
|
|
|
53
64
|
methods: T.nilable(T::Array[T.untyped]),
|
|
54
65
|
realm: T.untyped,
|
|
55
66
|
secret_key: T.untyped,
|
|
56
|
-
events: T.nilable(Mpp::Events::Dispatcher)
|
|
67
|
+
events: T.nilable(Mpp::Events::Dispatcher),
|
|
68
|
+
requires_auth: T::Boolean
|
|
57
69
|
).returns(T.attached_class)
|
|
58
70
|
end
|
|
59
|
-
def self.create(method: nil, methods: nil, realm: nil, secret_key: nil, events: nil)
|
|
71
|
+
def self.create(method: nil, methods: nil, realm: nil, secret_key: nil, events: nil, requires_auth: false)
|
|
60
72
|
new(
|
|
61
73
|
method: method,
|
|
62
74
|
methods: methods,
|
|
63
75
|
realm: realm || Defaults.detect_realm,
|
|
64
76
|
secret_key: secret_key || Defaults.detect_secret_key,
|
|
65
|
-
events: events
|
|
77
|
+
events: events,
|
|
78
|
+
requires_auth: requires_auth
|
|
66
79
|
)
|
|
67
80
|
end
|
|
68
81
|
|
|
@@ -86,6 +99,14 @@ module Mpp
|
|
|
86
99
|
on(Mpp::Events::PAYMENT_SUCCESS, handler, &block)
|
|
87
100
|
end
|
|
88
101
|
|
|
102
|
+
# Select the HTTP header value that carries the Payment credential.
|
|
103
|
+
# When requires_auth is true, Payment credentials ride in
|
|
104
|
+
# Payment-Authorization so Authorization can hold application auth.
|
|
105
|
+
sig { params(authorization: T.nilable(String), payment_authorization: T.nilable(String)).returns(T.nilable(String)) }
|
|
106
|
+
def payment_credential_value(authorization, payment_authorization = nil)
|
|
107
|
+
@requires_auth ? payment_authorization : authorization
|
|
108
|
+
end
|
|
109
|
+
|
|
89
110
|
# Combine method offers into a single callable that presents every method.
|
|
90
111
|
sig { params(entries: T.untyped).returns(ComposedHandler) }
|
|
91
112
|
def compose(*entries)
|
|
@@ -119,11 +140,12 @@ module Mpp
|
|
|
119
140
|
|
|
120
141
|
request_opts = kwargs.slice(*REQUEST_OPTION_KEYS)
|
|
121
142
|
offer_opts = kwargs.except(*REQUEST_OPTION_KEYS)
|
|
143
|
+
payment = payment_credential_value(authorization, request_opts[:payment_authorization])
|
|
122
144
|
|
|
123
145
|
if charge_methods.length == 1
|
|
124
146
|
return charge_one(
|
|
125
147
|
charge_methods.first,
|
|
126
|
-
|
|
148
|
+
payment,
|
|
127
149
|
amount,
|
|
128
150
|
**request_opts,
|
|
129
151
|
**offer_opts
|
|
@@ -133,6 +155,7 @@ module Mpp
|
|
|
133
155
|
entries = charge_methods.map { |candidate| [candidate, offer_opts.merge(amount: amount)] }
|
|
134
156
|
ComposedHandler.from_entries(self, entries).call(
|
|
135
157
|
authorization: authorization,
|
|
158
|
+
payment_authorization: request_opts[:payment_authorization],
|
|
136
159
|
payment_signature: request_opts[:payment_signature],
|
|
137
160
|
body: request_opts[:body],
|
|
138
161
|
url: request_opts[:url],
|
|
@@ -142,8 +165,10 @@ module Mpp
|
|
|
142
165
|
end
|
|
143
166
|
|
|
144
167
|
# Verify or challenge a single method. Used by compose and charge.
|
|
145
|
-
|
|
146
|
-
|
|
168
|
+
# The optional request guard can omit an offer after its canonical request
|
|
169
|
+
# is built, ensuring the guard and challenge use the exact same request.
|
|
170
|
+
sig { params(method: T.untyped, authorization: T.nilable(String), amount: String, kwargs: T.untyped, request_guard: T.nilable(T.proc.params(request: T::Hash[String, T.untyped]).returns(T::Boolean))).returns(T.untyped) }
|
|
171
|
+
def charge_one(method, authorization, amount, **kwargs, &request_guard)
|
|
147
172
|
intent = method.intents["charge"]
|
|
148
173
|
raise ArgumentError, "Method #{method.name} does not support charge intent" unless intent
|
|
149
174
|
|
|
@@ -152,6 +177,7 @@ module Mpp
|
|
|
152
177
|
payment_signature = kwargs[:payment_signature]
|
|
153
178
|
offer_opts = kwargs.except(*REQUEST_OPTION_KEYS)
|
|
154
179
|
request = build_charge_request(method, amount, **offer_opts)
|
|
180
|
+
return nil if request_guard && !request_guard.call(request)
|
|
155
181
|
|
|
156
182
|
if payment_signature && method.respond_to?(:bind_x402_credential)
|
|
157
183
|
return verify_x402(
|
|
@@ -177,7 +203,9 @@ module Mpp
|
|
|
177
203
|
description: offer_opts[:description],
|
|
178
204
|
expires: offer_opts[:expires],
|
|
179
205
|
events: @events,
|
|
180
|
-
|
|
206
|
+
method_hook_events: @method_hook_events,
|
|
207
|
+
body: body,
|
|
208
|
+
header: @credential_header
|
|
181
209
|
)
|
|
182
210
|
end
|
|
183
211
|
|
|
@@ -304,7 +332,8 @@ module Mpp
|
|
|
304
332
|
description,
|
|
305
333
|
nil,
|
|
306
334
|
expires,
|
|
307
|
-
body
|
|
335
|
+
body,
|
|
336
|
+
@credential_header
|
|
308
337
|
)
|
|
309
338
|
method_context = {name: method.name, intent: intent.name}
|
|
310
339
|
|
|
@@ -346,9 +375,10 @@ module Mpp
|
|
|
346
375
|
raise
|
|
347
376
|
end
|
|
348
377
|
|
|
349
|
-
if @events.has_handlers?(Mpp::Events::PAYMENT_SUCCESS)
|
|
378
|
+
if @events.has_handlers?(Mpp::Events::PAYMENT_SUCCESS) || @method_hook_events.has_handlers?(Mpp::Events::PAYMENT_SUCCESS)
|
|
350
379
|
Verify.emit_payment_success(
|
|
351
380
|
dispatcher: @events,
|
|
381
|
+
method_hook_dispatcher: @method_hook_events,
|
|
352
382
|
challenge: challenge,
|
|
353
383
|
credential: credential,
|
|
354
384
|
method: method_context,
|
|
@@ -358,6 +388,30 @@ module Mpp
|
|
|
358
388
|
end
|
|
359
389
|
[credential, receipt]
|
|
360
390
|
end
|
|
391
|
+
|
|
392
|
+
private
|
|
393
|
+
|
|
394
|
+
sig { void }
|
|
395
|
+
def register_method_payment_success
|
|
396
|
+
@methods.each do |payment_method|
|
|
397
|
+
next unless payment_method.respond_to?(:on_payment_success)
|
|
398
|
+
|
|
399
|
+
hook = payment_method.on_payment_success
|
|
400
|
+
next if hook.nil?
|
|
401
|
+
raise ArgumentError, "on_payment_success must be callable" unless hook.respond_to?(:call)
|
|
402
|
+
|
|
403
|
+
method_name = payment_method.name
|
|
404
|
+
intent_names = payment_method.intents.each_value.map { |intent| intent.name }
|
|
405
|
+
@method_hook_events.on(Mpp::Events::PAYMENT_SUCCESS) do |payload|
|
|
406
|
+
event_method = payload[:method]
|
|
407
|
+
next unless event_method.is_a?(Hash)
|
|
408
|
+
next unless event_method[:name] == method_name
|
|
409
|
+
next unless intent_names.include?(event_method[:intent])
|
|
410
|
+
|
|
411
|
+
hook.call(payload)
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
end
|
|
361
415
|
end
|
|
362
416
|
end
|
|
363
417
|
end
|
data/lib/mpp/server/verify.rb
CHANGED
|
@@ -15,17 +15,17 @@ module Mpp
|
|
|
15
15
|
# Verify a payment credential or generate a new challenge.
|
|
16
16
|
#
|
|
17
17
|
# Returns Challenge (payment required) or [Credential, Receipt] (verified).
|
|
18
|
-
sig { params(authorization: T.nilable(String), intent: T.untyped, request: T::Hash[String, T.untyped], realm: String, secret_key: String, method: T.nilable(String), description: T.nilable(String), meta: T.nilable(T::Hash[String, T.untyped]), expires: T.nilable(String), events: T.nilable(Mpp::Events::Dispatcher), body: T.untyped).returns(T.untyped) }
|
|
18
|
+
sig { params(authorization: T.nilable(String), intent: T.untyped, request: T::Hash[String, T.untyped], realm: String, secret_key: String, method: T.nilable(String), description: T.nilable(String), meta: T.nilable(T::Hash[String, T.untyped]), expires: T.nilable(String), events: T.nilable(Mpp::Events::Dispatcher), method_hook_events: T.nilable(Mpp::Events::Dispatcher), body: T.untyped, header: T.nilable(String)).returns(T.untyped) }
|
|
19
19
|
def verify_or_challenge(authorization:, intent:, request:, realm:, secret_key:,
|
|
20
|
-
method: nil, description: nil, meta: nil, expires: nil, events: nil, body: nil)
|
|
20
|
+
method: nil, description: nil, meta: nil, expires: nil, events: nil, method_hook_events: nil, body: nil, header: nil)
|
|
21
21
|
method_name = method || "tempo"
|
|
22
22
|
request = Mpp::Units.transform_units(request)
|
|
23
23
|
dispatcher = events
|
|
24
|
-
events_enabled = dispatcher&.has_handlers?
|
|
24
|
+
events_enabled = dispatcher&.has_handlers? || method_hook_events&.has_handlers?
|
|
25
25
|
method_context = events_enabled ? {name: method_name, intent: intent.name} : nil
|
|
26
26
|
|
|
27
27
|
new_challenge = Kernel.lambda { |credential = nil, error = nil, submitted_challenge = nil|
|
|
28
|
-
challenge = create_challenge(method_name, intent.name, request, realm, secret_key, description, meta, expires, body)
|
|
28
|
+
challenge = create_challenge(method_name, intent.name, request, realm, secret_key, description, meta, expires, body, header)
|
|
29
29
|
if error && dispatcher&.has_handlers?(Mpp::Events::PAYMENT_FAILED)
|
|
30
30
|
emit_payment_failed(
|
|
31
31
|
dispatcher: dispatcher,
|
|
@@ -79,7 +79,8 @@ module Mpp
|
|
|
79
79
|
request: echo_request,
|
|
80
80
|
expires: echo.expires,
|
|
81
81
|
digest: echo.digest,
|
|
82
|
-
opaque: echo_opaque
|
|
82
|
+
opaque: echo_opaque,
|
|
83
|
+
header: echo.header
|
|
83
84
|
)
|
|
84
85
|
unless Mpp.secure_compare(echo.id, expected_id)
|
|
85
86
|
return new_challenge.call(
|
|
@@ -172,9 +173,10 @@ module Mpp
|
|
|
172
173
|
end
|
|
173
174
|
Kernel.raise
|
|
174
175
|
end
|
|
175
|
-
if dispatcher&.has_handlers?(Mpp::Events::PAYMENT_SUCCESS)
|
|
176
|
+
if dispatcher&.has_handlers?(Mpp::Events::PAYMENT_SUCCESS) || method_hook_events&.has_handlers?(Mpp::Events::PAYMENT_SUCCESS)
|
|
176
177
|
emit_payment_success(
|
|
177
178
|
dispatcher: dispatcher,
|
|
179
|
+
method_hook_dispatcher: method_hook_events,
|
|
178
180
|
challenge: echo,
|
|
179
181
|
credential: credential,
|
|
180
182
|
method: T.must(method_context),
|
|
@@ -185,9 +187,9 @@ module Mpp
|
|
|
185
187
|
[credential, receipt]
|
|
186
188
|
end
|
|
187
189
|
|
|
188
|
-
sig { params(method: String, intent_name: String, request: T::Hash[String, T.untyped], realm: String, secret_key: String, description: T.nilable(String), meta: T.nilable(T::Hash[String, T.untyped]), expires: T.nilable(String), body: T.untyped).returns(Mpp::Challenge) }
|
|
190
|
+
sig { params(method: String, intent_name: String, request: T::Hash[String, T.untyped], realm: String, secret_key: String, description: T.nilable(String), meta: T.nilable(T::Hash[String, T.untyped]), expires: T.nilable(String), body: T.untyped, header: T.nilable(String)).returns(Mpp::Challenge) }
|
|
189
191
|
def create_challenge(method, intent_name, request, realm, secret_key,
|
|
190
|
-
description = nil, meta = nil, expires = nil, body = nil)
|
|
192
|
+
description = nil, meta = nil, expires = nil, body = nil, header = nil)
|
|
191
193
|
expires = nil if expires && !expires.is_a?(String)
|
|
192
194
|
|
|
193
195
|
if expires.nil?
|
|
@@ -205,7 +207,8 @@ module Mpp
|
|
|
205
207
|
expires: expires,
|
|
206
208
|
digest: digest,
|
|
207
209
|
description: description,
|
|
208
|
-
meta: meta
|
|
210
|
+
meta: meta,
|
|
211
|
+
header: header
|
|
209
212
|
)
|
|
210
213
|
end
|
|
211
214
|
|
|
@@ -258,17 +261,24 @@ module Mpp
|
|
|
258
261
|
dispatcher.emit(Mpp::Events::PAYMENT_FAILED, payload)
|
|
259
262
|
end
|
|
260
263
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
+
# Method hooks are owned by a handler, while `dispatcher` may be shared
|
|
265
|
+
# by many handlers. Emit the same payload to each without exposing that
|
|
266
|
+
# handler ownership in the public event payload.
|
|
267
|
+
sig { params(dispatcher: T.nilable(Mpp::Events::Dispatcher), challenge: T.untyped, credential: T.untyped, method: T::Hash[Symbol, T.untyped], receipt: T.untyped, request: T::Hash[String, T.untyped], method_hook_dispatcher: T.nilable(Mpp::Events::Dispatcher)).void }
|
|
268
|
+
def emit_payment_success(dispatcher:, challenge:, credential:, method:, receipt:, request:, method_hook_dispatcher: nil)
|
|
269
|
+
public_handlers = dispatcher&.has_handlers?(Mpp::Events::PAYMENT_SUCCESS)
|
|
270
|
+
hook_handlers = method_hook_dispatcher&.has_handlers?(Mpp::Events::PAYMENT_SUCCESS)
|
|
271
|
+
return unless public_handlers || hook_handlers
|
|
264
272
|
|
|
265
|
-
|
|
273
|
+
payload = {
|
|
266
274
|
challenge: challenge,
|
|
267
275
|
credential: credential,
|
|
268
276
|
method: method,
|
|
269
277
|
receipt: receipt,
|
|
270
278
|
request: request
|
|
271
|
-
}
|
|
279
|
+
}
|
|
280
|
+
dispatcher.emit(Mpp::Events::PAYMENT_SUCCESS, payload) if public_handlers
|
|
281
|
+
method_hook_dispatcher.emit(Mpp::Events::PAYMENT_SUCCESS, payload) if hook_handlers && method_hook_dispatcher != dispatcher
|
|
272
282
|
end
|
|
273
283
|
end
|
|
274
284
|
end
|
data/lib/mpp/version.rb
CHANGED
data/lib/mpp.rb
CHANGED
|
@@ -11,6 +11,9 @@ require_relative "mpp/secure_compare"
|
|
|
11
11
|
module Mpp
|
|
12
12
|
extend T::Sig
|
|
13
13
|
|
|
14
|
+
AUTHORIZATION_HEADER = T.let("Authorization", String)
|
|
15
|
+
PAYMENT_AUTHORIZATION_HEADER = T.let("Payment-Authorization", String)
|
|
16
|
+
|
|
14
17
|
autoload :Challenge, "mpp/challenge"
|
|
15
18
|
autoload :ChallengeEcho, "mpp/challenge_echo"
|
|
16
19
|
autoload :Credential, "mpp/credential"
|
|
@@ -43,9 +46,16 @@ module Mpp
|
|
|
43
46
|
autoload :MCP, "mpp/extensions/mcp"
|
|
44
47
|
end
|
|
45
48
|
|
|
46
|
-
sig { params(method: T.untyped, methods: T.nilable(T::Array[T.untyped]), realm: T.untyped, secret_key: T.untyped, events: T.nilable(Mpp::Events::Dispatcher)).returns(T.untyped) }
|
|
47
|
-
def self.create(method: nil, methods: nil, realm: nil, secret_key: nil, events: nil)
|
|
48
|
-
Server::MppHandler.create(
|
|
49
|
+
sig { params(method: T.untyped, methods: T.nilable(T::Array[T.untyped]), realm: T.untyped, secret_key: T.untyped, events: T.nilable(Mpp::Events::Dispatcher), requires_auth: T::Boolean).returns(T.untyped) }
|
|
50
|
+
def self.create(method: nil, methods: nil, realm: nil, secret_key: nil, events: nil, requires_auth: false)
|
|
51
|
+
Server::MppHandler.create(
|
|
52
|
+
method: method,
|
|
53
|
+
methods: methods,
|
|
54
|
+
realm: realm,
|
|
55
|
+
secret_key: secret_key,
|
|
56
|
+
events: events,
|
|
57
|
+
requires_auth: requires_auth
|
|
58
|
+
)
|
|
49
59
|
end
|
|
50
60
|
|
|
51
61
|
# Error hierarchy
|
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.
|
|
4
|
+
version: 0.1.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Stripe
|
|
@@ -37,6 +37,20 @@ dependencies:
|
|
|
37
37
|
- - "~>"
|
|
38
38
|
- !ruby/object:Gem::Version
|
|
39
39
|
version: '1.3'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: sorbet-runtime
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - ">="
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - ">="
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
40
54
|
description: Ruby SDK for the Machine Payments Protocol (MPP) — an HTTP 402 Payment
|
|
41
55
|
Authentication scheme.
|
|
42
56
|
executables: []
|
|
@@ -75,7 +89,9 @@ files:
|
|
|
75
89
|
- lib/mpp/methods/stripe.rb
|
|
76
90
|
- lib/mpp/methods/stripe/charge_intent.rb
|
|
77
91
|
- lib/mpp/methods/stripe/client_method.rb
|
|
92
|
+
- lib/mpp/methods/stripe/crypto_payment_recorder.rb
|
|
78
93
|
- lib/mpp/methods/stripe/defaults.rb
|
|
94
|
+
- lib/mpp/methods/stripe/machine_payments.rb
|
|
79
95
|
- lib/mpp/methods/stripe/stripe_method.rb
|
|
80
96
|
- lib/mpp/methods/tempo.rb
|
|
81
97
|
- lib/mpp/methods/tempo/account.rb
|