Skip to main content

r402_http/client/
hooks.rs

1//! Lifecycle hooks for the x402 client payment creation pipeline.
2//!
3//! Hooks allow applications to intercept and customize the payment
4//! creation lifecycle. This mirrors the Go SDK's `client_hooks.go` design,
5//! using the same `HookDecision` / `FailureRecovery` enums as the
6//! server-side hooks for a consistent API.
7//!
8//! ## Hook Lifecycle
9//!
10//! 1. **`before_payment_creation`** — Run before payment creation; can abort it.
11//! 2. **Payment signing executes**
12//! 3. **`after_payment_creation`** (on success) — Observes the result.
13//! 4. **`on_payment_creation_failure`** (on error) — Can recover with substitute headers.
14//!
15//! ## Usage
16//!
17//! Implement [`ClientHooks`] with only the hooks you need — all methods
18//! have default no-op implementations.
19
20use http::HeaderMap;
21use r402::facilitator::BoxFuture;
22use r402::hooks::{FailureRecovery, HookDecision};
23use r402::proto;
24
25/// Context passed to client payment creation lifecycle hooks.
26#[derive(Debug, Clone)]
27pub struct PaymentCreationContext {
28    /// The parsed payment requirements from the 402 response.
29    pub payment_required: proto::PaymentRequired,
30}
31
32/// Lifecycle hooks for client-side payment creation.
33///
34/// All methods have default no-op implementations. Override only the hooks you
35/// need. This trait is dyn-compatible for use in heterogeneous hook lists.
36///
37/// The hook lifecycle mirrors [`r402::hooks::FacilitatorHooks`]:
38///
39/// 1. **`before_payment_creation`** — Can abort with [`HookDecision::Abort`].
40/// 2. **Payment signing executes**
41/// 3. **`after_payment_creation`** (on success) — Observes the signed headers.
42/// 4. **`on_payment_creation_failure`** (on error) — Can recover with [`FailureRecovery::Recovered`].
43pub trait ClientHooks: Send + Sync {
44    /// Called before payment creation.
45    ///
46    /// If any hook returns [`HookDecision::Abort`], payment creation is skipped
47    /// and the original 402 response is returned to the caller.
48    fn before_payment_creation<'a>(
49        &'a self,
50        _ctx: &'a PaymentCreationContext,
51    ) -> BoxFuture<'a, HookDecision> {
52        Box::pin(async { HookDecision::Continue })
53    }
54
55    /// Called after successful payment creation.
56    ///
57    /// Receives the signed payment headers. Cannot affect the outcome.
58    fn after_payment_creation<'a>(
59        &'a self,
60        _ctx: &'a PaymentCreationContext,
61        _headers: &'a HeaderMap,
62    ) -> BoxFuture<'a, ()> {
63        Box::pin(async {})
64    }
65
66    /// Called when payment creation fails.
67    ///
68    /// If a hook returns [`FailureRecovery::Recovered`], the provided headers
69    /// replace the error.
70    fn on_payment_creation_failure<'a>(
71        &'a self,
72        _ctx: &'a PaymentCreationContext,
73        _error: &'a str,
74    ) -> BoxFuture<'a, FailureRecovery<HeaderMap>> {
75        Box::pin(async { FailureRecovery::Propagate })
76    }
77}