1use std::fmt::{self, Debug, Formatter};
4use std::future::Future;
5
6use compact_str::CompactString;
7use r402_facilitator::{BoxFuture, FailureRecovery};
8use r402_protocol::error::FacilitatorError;
9use r402_protocol::payment::{
10 Extensions, PaymentPayload, PaymentRequirements, SettleResponse, VerifyResponse,
11};
12
13use crate::payment_flow::SettlePhase;
14
15pub type WirePaymentPayload = PaymentPayload<PaymentRequirements, serde_json::Value>;
17
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23#[non_exhaustive]
24pub enum CancelReason {
25 HandlerThrew,
27 HandlerFailed,
29 AfterVerifyAborted,
31}
32
33impl CancelReason {
34 #[must_use]
36 pub const fn as_str(self) -> &'static str {
37 match self {
38 Self::HandlerThrew => "handler_threw",
39 Self::HandlerFailed => "handler_failed",
40 Self::AfterVerifyAborted => "after_verify_aborted",
41 }
42 }
43}
44
45impl fmt::Display for CancelReason {
46 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
47 f.write_str(self.as_str())
48 }
49}
50
51#[derive(Debug)]
53#[non_exhaustive]
54pub enum BeforeOpDecision<T> {
55 Continue,
57 Abort {
59 reason: String,
61 message: String,
63 },
64 Skip {
66 result: T,
68 },
69}
70
71#[derive(Debug, Clone, Default)]
76#[non_exhaustive]
77pub struct SkipHandlerDirective {
78 pub content_type: Option<String>,
80 pub body: Option<serde_json::Value>,
82}
83
84impl SkipHandlerDirective {
85 #[must_use]
87 pub const fn empty() -> Self {
88 Self {
89 content_type: None,
90 body: None,
91 }
92 }
93
94 #[must_use]
96 pub fn with_content_type(mut self, content_type: impl Into<String>) -> Self {
97 self.content_type = Some(content_type.into());
98 self
99 }
100
101 #[must_use]
103 pub fn with_body(mut self, body: serde_json::Value) -> Self {
104 self.body = Some(body);
105 self
106 }
107}
108
109#[derive(Debug, Clone, Default)]
111#[non_exhaustive]
112pub enum AfterVerifyDecision {
113 #[default]
115 Continue,
116 Abort {
118 reason: String,
120 message: String,
122 },
123 SkipHandler {
125 response: SkipHandlerDirective,
127 },
128}
129
130#[derive(Clone)]
132#[non_exhaustive]
133pub struct PaymentHookContext {
134 pub payload: WirePaymentPayload,
136 pub requirements: PaymentRequirements,
138}
139
140impl Debug for PaymentHookContext {
141 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
142 f.debug_struct("PaymentHookContext")
143 .field("requirements", &self.requirements)
144 .finish_non_exhaustive()
145 }
146}
147
148impl PaymentHookContext {
149 #[must_use]
151 pub const fn new(payload: WirePaymentPayload, requirements: PaymentRequirements) -> Self {
152 Self {
153 payload,
154 requirements,
155 }
156 }
157}
158
159#[derive(Clone)]
161#[non_exhaustive]
162pub struct VerifyResultContext {
163 pub payment: PaymentHookContext,
165 pub result: VerifyResponse,
168}
169
170impl Debug for VerifyResultContext {
171 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
172 f.debug_struct("VerifyResultContext")
173 .field("payment", &self.payment)
174 .field("result_valid", &self.result.is_valid())
175 .finish_non_exhaustive()
176 }
177}
178
179#[derive(Clone)]
181#[non_exhaustive]
182pub struct SettleContext {
183 pub payment: PaymentHookContext,
185 pub declared_extensions: Extensions,
187 pub phase: SettlePhase,
189 pub resource_url: Option<CompactString>,
191}
192
193impl Debug for SettleContext {
194 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
195 f.debug_struct("SettleContext")
196 .field("phase", &self.phase)
197 .field("payment", &self.payment)
198 .finish_non_exhaustive()
199 }
200}
201
202impl SettleContext {
203 #[must_use]
205 pub fn new(payment: PaymentHookContext, phase: SettlePhase) -> Self {
206 Self {
207 payment,
208 declared_extensions: Extensions::new(),
209 phase,
210 resource_url: None,
211 }
212 }
213}
214
215#[derive(Clone)]
217#[non_exhaustive]
218pub struct SettleResultContext {
219 pub settle: SettleContext,
221 pub result: SettleResponse,
223}
224
225impl Debug for SettleResultContext {
226 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
227 f.debug_struct("SettleResultContext")
228 .field("settle", &self.settle)
229 .field("result_success", &self.result.is_success())
230 .finish_non_exhaustive()
231 }
232}
233
234#[derive(Clone)]
236#[non_exhaustive]
237pub struct VerifiedPaymentCanceledContext {
238 pub payment: PaymentHookContext,
240 pub reason: CancelReason,
242 pub error: Option<String>,
244 pub response_status: Option<u16>,
246 pub settled_phases: Vec<SettlePhase>,
248}
249
250impl Debug for VerifiedPaymentCanceledContext {
251 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
252 f.debug_struct("VerifiedPaymentCanceledContext")
253 .field("reason", &self.reason)
254 .field("response_status", &self.response_status)
255 .field("settled_phases", &self.settled_phases)
256 .finish_non_exhaustive()
257 }
258}
259
260impl VerifiedPaymentCanceledContext {
261 #[must_use]
263 pub const fn new(
264 payment: PaymentHookContext,
265 reason: CancelReason,
266 settled_phases: Vec<SettlePhase>,
267 ) -> Self {
268 Self {
269 payment,
270 reason,
271 error: None,
272 response_status: None,
273 settled_phases,
274 }
275 }
276}
277
278pub trait ResourceServerHooks: Send + Sync {
282 fn before_verify<'a>(
284 &'a self,
285 _ctx: &'a PaymentHookContext,
286 ) -> impl Future<Output = BeforeOpDecision<VerifyResponse>> + Send + 'a {
287 async { BeforeOpDecision::Continue }
288 }
289
290 fn after_verify<'a>(
292 &'a self,
293 _ctx: &'a VerifyResultContext,
294 ) -> impl Future<Output = AfterVerifyDecision> + Send + 'a {
295 async { AfterVerifyDecision::Continue }
296 }
297
298 fn on_verify_failure<'a>(
300 &'a self,
301 _ctx: &'a PaymentHookContext,
302 _error: &'a FacilitatorError,
303 ) -> impl Future<Output = FailureRecovery<VerifyResponse>> + Send + 'a {
304 async { FailureRecovery::Propagate }
305 }
306
307 fn before_settle<'a>(
309 &'a self,
310 _ctx: &'a SettleContext,
311 ) -> impl Future<Output = BeforeOpDecision<SettleResponse>> + Send + 'a {
312 async { BeforeOpDecision::Continue }
313 }
314
315 fn after_settle<'a>(
317 &'a self,
318 _ctx: &'a SettleResultContext,
319 ) -> impl Future<Output = ()> + Send + 'a {
320 async {}
321 }
322
323 fn on_settle_failure<'a>(
325 &'a self,
326 _ctx: &'a SettleContext,
327 _error: &'a FacilitatorError,
328 ) -> impl Future<Output = FailureRecovery<SettleResponse>> + Send + 'a {
329 async { FailureRecovery::Propagate }
330 }
331
332 fn on_verified_payment_canceled<'a>(
334 &'a self,
335 _ctx: &'a VerifiedPaymentCanceledContext,
336 ) -> impl Future<Output = ()> + Send + 'a {
337 async {}
338 }
339}
340
341pub trait DynResourceServerHooks: Send + Sync {
343 fn before_verify<'a>(
345 &'a self,
346 ctx: &'a PaymentHookContext,
347 ) -> BoxFuture<'a, BeforeOpDecision<VerifyResponse>>;
348
349 fn after_verify<'a>(
351 &'a self,
352 ctx: &'a VerifyResultContext,
353 ) -> BoxFuture<'a, AfterVerifyDecision>;
354
355 fn on_verify_failure<'a>(
357 &'a self,
358 ctx: &'a PaymentHookContext,
359 error: &'a FacilitatorError,
360 ) -> BoxFuture<'a, FailureRecovery<VerifyResponse>>;
361
362 fn before_settle<'a>(
364 &'a self,
365 ctx: &'a SettleContext,
366 ) -> BoxFuture<'a, BeforeOpDecision<SettleResponse>>;
367
368 fn after_settle<'a>(&'a self, ctx: &'a SettleResultContext) -> BoxFuture<'a, ()>;
370
371 fn on_settle_failure<'a>(
373 &'a self,
374 ctx: &'a SettleContext,
375 error: &'a FacilitatorError,
376 ) -> BoxFuture<'a, FailureRecovery<SettleResponse>>;
377
378 fn on_verified_payment_canceled<'a>(
380 &'a self,
381 ctx: &'a VerifiedPaymentCanceledContext,
382 ) -> BoxFuture<'a, ()>;
383}
384
385impl<T: ResourceServerHooks + ?Sized> DynResourceServerHooks for T {
386 fn before_verify<'a>(
387 &'a self,
388 ctx: &'a PaymentHookContext,
389 ) -> BoxFuture<'a, BeforeOpDecision<VerifyResponse>> {
390 Box::pin(<Self as ResourceServerHooks>::before_verify(self, ctx))
391 }
392
393 fn after_verify<'a>(
394 &'a self,
395 ctx: &'a VerifyResultContext,
396 ) -> BoxFuture<'a, AfterVerifyDecision> {
397 Box::pin(<Self as ResourceServerHooks>::after_verify(self, ctx))
398 }
399
400 fn on_verify_failure<'a>(
401 &'a self,
402 ctx: &'a PaymentHookContext,
403 error: &'a FacilitatorError,
404 ) -> BoxFuture<'a, FailureRecovery<VerifyResponse>> {
405 Box::pin(<Self as ResourceServerHooks>::on_verify_failure(
406 self, ctx, error,
407 ))
408 }
409
410 fn before_settle<'a>(
411 &'a self,
412 ctx: &'a SettleContext,
413 ) -> BoxFuture<'a, BeforeOpDecision<SettleResponse>> {
414 Box::pin(<Self as ResourceServerHooks>::before_settle(self, ctx))
415 }
416
417 fn after_settle<'a>(&'a self, ctx: &'a SettleResultContext) -> BoxFuture<'a, ()> {
418 Box::pin(<Self as ResourceServerHooks>::after_settle(self, ctx))
419 }
420
421 fn on_settle_failure<'a>(
422 &'a self,
423 ctx: &'a SettleContext,
424 error: &'a FacilitatorError,
425 ) -> BoxFuture<'a, FailureRecovery<SettleResponse>> {
426 Box::pin(<Self as ResourceServerHooks>::on_settle_failure(
427 self, ctx, error,
428 ))
429 }
430
431 fn on_verified_payment_canceled<'a>(
432 &'a self,
433 ctx: &'a VerifiedPaymentCanceledContext,
434 ) -> BoxFuture<'a, ()> {
435 Box::pin(<Self as ResourceServerHooks>::on_verified_payment_canceled(
436 self, ctx,
437 ))
438 }
439}