r402_core/resource_server/
hooks.rs1use std::fmt::{self, Debug, Formatter};
26use std::future::Future;
27use std::pin::Pin;
28
29use crate::error::FacilitatorError;
30use crate::facilitator::BoxFuture;
31use crate::facilitator::FailureRecovery;
32use crate::wire::{PaymentPayload, PaymentRequirements, SettleResponse, VerifyResponse};
33
34pub type WirePaymentPayload = PaymentPayload<PaymentRequirements, serde_json::Value>;
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
42#[non_exhaustive]
43pub enum CancelReason {
44 HandlerThrew,
46 HandlerFailed,
48 AfterVerifyAborted,
50}
51
52impl CancelReason {
53 #[must_use]
55 pub const fn as_str(self) -> &'static str {
56 match self {
57 Self::HandlerThrew => "handler_threw",
58 Self::HandlerFailed => "handler_failed",
59 Self::AfterVerifyAborted => "after_verify_aborted",
60 }
61 }
62}
63
64impl fmt::Display for CancelReason {
65 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
66 f.write_str(self.as_str())
67 }
68}
69
70#[derive(Debug)]
72#[non_exhaustive]
73pub enum BeforeOpDecision<T> {
74 Continue,
76 Abort {
78 reason: String,
80 message: String,
82 },
83 Skip {
85 result: T,
87 },
88}
89
90#[derive(Debug, Clone, Default)]
95#[non_exhaustive]
96pub struct SkipHandlerDirective {
97 pub content_type: Option<String>,
99 pub body: Option<serde_json::Value>,
101}
102
103impl SkipHandlerDirective {
104 #[must_use]
106 pub const fn empty() -> Self {
107 Self {
108 content_type: None,
109 body: None,
110 }
111 }
112
113 #[must_use]
115 pub fn with_content_type(mut self, content_type: impl Into<String>) -> Self {
116 self.content_type = Some(content_type.into());
117 self
118 }
119
120 #[must_use]
122 pub fn with_body(mut self, body: serde_json::Value) -> Self {
123 self.body = Some(body);
124 self
125 }
126}
127
128#[derive(Debug, Clone, Default)]
130#[non_exhaustive]
131pub enum AfterVerifyDecision {
132 #[default]
134 Continue,
135 Abort {
137 reason: String,
139 message: String,
141 },
142 SkipHandler {
144 response: SkipHandlerDirective,
146 },
147}
148
149#[derive(Clone)]
151#[non_exhaustive]
152pub struct PaymentHookContext {
153 pub payload: WirePaymentPayload,
155 pub requirements: PaymentRequirements,
157}
158
159impl Debug for PaymentHookContext {
160 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
161 f.debug_struct("PaymentHookContext")
162 .field("requirements", &self.requirements)
163 .finish_non_exhaustive()
164 }
165}
166
167#[derive(Clone)]
169#[non_exhaustive]
170pub struct VerifyResultContext {
171 pub payment: PaymentHookContext,
173 pub result: VerifyResponse,
176}
177
178impl Debug for VerifyResultContext {
179 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
180 f.debug_struct("VerifyResultContext")
181 .field("payment", &self.payment)
182 .field("result_valid", &self.result.is_valid())
183 .finish_non_exhaustive()
184 }
185}
186
187#[derive(Clone)]
189#[non_exhaustive]
190pub struct SettleResultContext {
191 pub payment: PaymentHookContext,
193 pub result: SettleResponse,
195}
196
197impl Debug for SettleResultContext {
198 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
199 f.debug_struct("SettleResultContext")
200 .field("payment", &self.payment)
201 .field("result_success", &self.result.is_success())
202 .finish_non_exhaustive()
203 }
204}
205
206#[derive(Clone)]
208#[non_exhaustive]
209pub struct VerifiedPaymentCanceledContext {
210 pub payment: PaymentHookContext,
212 pub reason: CancelReason,
214 pub error: Option<String>,
216 pub response_status: Option<u16>,
218}
219
220impl Debug for VerifiedPaymentCanceledContext {
221 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
222 f.debug_struct("VerifiedPaymentCanceledContext")
223 .field("reason", &self.reason)
224 .field("response_status", &self.response_status)
225 .finish_non_exhaustive()
226 }
227}
228
229pub trait ResourceServerHooks: Send + Sync {
233 fn before_verify<'a>(
235 &'a self,
236 _ctx: &'a PaymentHookContext,
237 ) -> impl Future<Output = BeforeOpDecision<VerifyResponse>> + Send + 'a {
238 async { BeforeOpDecision::Continue }
239 }
240
241 fn after_verify<'a>(
243 &'a self,
244 _ctx: &'a VerifyResultContext,
245 ) -> impl Future<Output = AfterVerifyDecision> + Send + 'a {
246 async { AfterVerifyDecision::Continue }
247 }
248
249 fn on_verify_failure<'a>(
251 &'a self,
252 _ctx: &'a PaymentHookContext,
253 _error: &'a FacilitatorError,
254 ) -> impl Future<Output = FailureRecovery<VerifyResponse>> + Send + 'a {
255 async { FailureRecovery::Propagate }
256 }
257
258 fn before_settle<'a>(
260 &'a self,
261 _ctx: &'a PaymentHookContext,
262 ) -> impl Future<Output = BeforeOpDecision<SettleResponse>> + Send + 'a {
263 async { BeforeOpDecision::Continue }
264 }
265
266 fn after_settle<'a>(
268 &'a self,
269 _ctx: &'a SettleResultContext,
270 ) -> impl Future<Output = ()> + Send + 'a {
271 async {}
272 }
273
274 fn on_settle_failure<'a>(
276 &'a self,
277 _ctx: &'a PaymentHookContext,
278 _error: &'a FacilitatorError,
279 ) -> impl Future<Output = FailureRecovery<SettleResponse>> + Send + 'a {
280 async { FailureRecovery::Propagate }
281 }
282
283 fn on_verified_payment_canceled<'a>(
285 &'a self,
286 _ctx: &'a VerifiedPaymentCanceledContext,
287 ) -> impl Future<Output = ()> + Send + 'a {
288 async {}
289 }
290}
291
292pub trait DynResourceServerHooks: Send + Sync {
294 fn before_verify<'a>(
296 &'a self,
297 ctx: &'a PaymentHookContext,
298 ) -> Pin<Box<dyn Future<Output = BeforeOpDecision<VerifyResponse>> + Send + 'a>>;
299
300 fn after_verify<'a>(
302 &'a self,
303 ctx: &'a VerifyResultContext,
304 ) -> Pin<Box<dyn Future<Output = AfterVerifyDecision> + Send + 'a>>;
305
306 fn on_verify_failure<'a>(
308 &'a self,
309 ctx: &'a PaymentHookContext,
310 error: &'a FacilitatorError,
311 ) -> BoxFuture<'a, FailureRecovery<VerifyResponse>>;
312
313 fn before_settle<'a>(
315 &'a self,
316 ctx: &'a PaymentHookContext,
317 ) -> Pin<Box<dyn Future<Output = BeforeOpDecision<SettleResponse>> + Send + 'a>>;
318
319 fn after_settle<'a>(&'a self, ctx: &'a SettleResultContext) -> BoxFuture<'a, ()>;
321
322 fn on_settle_failure<'a>(
324 &'a self,
325 ctx: &'a PaymentHookContext,
326 error: &'a FacilitatorError,
327 ) -> BoxFuture<'a, FailureRecovery<SettleResponse>>;
328
329 fn on_verified_payment_canceled<'a>(
331 &'a self,
332 ctx: &'a VerifiedPaymentCanceledContext,
333 ) -> BoxFuture<'a, ()>;
334}
335
336impl<T: ResourceServerHooks + ?Sized> DynResourceServerHooks for T {
337 fn before_verify<'a>(
338 &'a self,
339 ctx: &'a PaymentHookContext,
340 ) -> Pin<Box<dyn Future<Output = BeforeOpDecision<VerifyResponse>> + Send + 'a>> {
341 Box::pin(<Self as ResourceServerHooks>::before_verify(self, ctx))
342 }
343
344 fn after_verify<'a>(
345 &'a self,
346 ctx: &'a VerifyResultContext,
347 ) -> Pin<Box<dyn Future<Output = AfterVerifyDecision> + Send + 'a>> {
348 Box::pin(<Self as ResourceServerHooks>::after_verify(self, ctx))
349 }
350
351 fn on_verify_failure<'a>(
352 &'a self,
353 ctx: &'a PaymentHookContext,
354 error: &'a FacilitatorError,
355 ) -> BoxFuture<'a, FailureRecovery<VerifyResponse>> {
356 Box::pin(<Self as ResourceServerHooks>::on_verify_failure(
357 self, ctx, error,
358 ))
359 }
360
361 fn before_settle<'a>(
362 &'a self,
363 ctx: &'a PaymentHookContext,
364 ) -> Pin<Box<dyn Future<Output = BeforeOpDecision<SettleResponse>> + Send + 'a>> {
365 Box::pin(<Self as ResourceServerHooks>::before_settle(self, ctx))
366 }
367
368 fn after_settle<'a>(&'a self, ctx: &'a SettleResultContext) -> BoxFuture<'a, ()> {
369 Box::pin(<Self as ResourceServerHooks>::after_settle(self, ctx))
370 }
371
372 fn on_settle_failure<'a>(
373 &'a self,
374 ctx: &'a PaymentHookContext,
375 error: &'a FacilitatorError,
376 ) -> BoxFuture<'a, FailureRecovery<SettleResponse>> {
377 Box::pin(<Self as ResourceServerHooks>::on_settle_failure(
378 self, ctx, error,
379 ))
380 }
381
382 fn on_verified_payment_canceled<'a>(
383 &'a self,
384 ctx: &'a VerifiedPaymentCanceledContext,
385 ) -> BoxFuture<'a, ()> {
386 Box::pin(<Self as ResourceServerHooks>::on_verified_payment_canceled(
387 self, ctx,
388 ))
389 }
390}