rust_x402/middleware/
service.rs

1//! Tower service layer for payment middleware
2
3use super::payment::PaymentMiddleware;
4use tower::ServiceBuilder;
5use tower_http::trace::TraceLayer;
6
7/// Create a service builder with x402 payment middleware
8pub fn create_payment_service(
9    middleware: PaymentMiddleware,
10) -> impl tower::Layer<tower::ServiceBuilder<tower::layer::util::Identity>> + Clone {
11    ServiceBuilder::new()
12        .layer(TraceLayer::new_for_http())
13        .layer(tower::layer::util::Stack::new(
14            tower::layer::util::Identity::new(),
15            PaymentServiceLayer::new(middleware),
16        ))
17}
18
19/// Tower service layer for x402 payment middleware
20#[derive(Clone)]
21pub struct PaymentServiceLayer {
22    middleware: PaymentMiddleware,
23}
24
25impl PaymentServiceLayer {
26    pub fn new(middleware: PaymentMiddleware) -> Self {
27        Self { middleware }
28    }
29}
30
31impl<S> tower::Layer<S> for PaymentServiceLayer {
32    type Service = PaymentService<S>;
33
34    fn layer(&self, inner: S) -> Self::Service {
35        PaymentService {
36            inner,
37            middleware: self.middleware.clone(),
38        }
39    }
40}
41
42/// Tower service for x402 payment middleware
43#[derive(Clone)]
44pub struct PaymentService<S> {
45    inner: S,
46    middleware: PaymentMiddleware,
47}
48
49impl<S, ReqBody, ResBody> tower::Service<http::Request<ReqBody>> for PaymentService<S>
50where
51    S: tower::Service<
52            http::Request<ReqBody>,
53            Response = http::Response<ResBody>,
54            Error = Box<dyn std::error::Error + Send + Sync>,
55        > + Send
56        + 'static,
57    S::Future: Send + 'static,
58    ReqBody: Send + 'static,
59    ResBody: Send + 'static,
60{
61    type Response = S::Response;
62    type Error = S::Error;
63    type Future = std::pin::Pin<
64        Box<
65            dyn std::future::Future<Output = std::result::Result<Self::Response, Self::Error>>
66                + Send,
67        >,
68    >;
69
70    fn poll_ready(
71        &mut self,
72        cx: &mut std::task::Context<'_>,
73    ) -> std::task::Poll<std::result::Result<(), Self::Error>> {
74        self.inner.poll_ready(cx)
75    }
76
77    fn call(&mut self, req: http::Request<ReqBody>) -> Self::Future {
78        let middleware = self.middleware.clone();
79
80        // Extract payment header before moving the request
81        let payment_header = req
82            .headers()
83            .get("X-PAYMENT")
84            .and_then(|h| h.to_str().ok())
85            .map(|s| s.to_string());
86        let uri_path = req.uri().path().to_string();
87
88        let future = self.inner.call(req);
89
90        Box::pin(async move {
91            match payment_header {
92                Some(payment_b64) => {
93                    // Parse payment payload
94                    match crate::types::PaymentPayload::from_base64(&payment_b64) {
95                        Ok(payment_payload) => {
96                            // Create payment requirements
97                            let requirements =
98                                match middleware.config.create_payment_requirements(&uri_path) {
99                                    Ok(req) => req,
100                                    Err(e) => {
101                                        // Return 500 error if we can't create requirements
102                                        return Err(
103                                            Box::new(e) as Box<dyn std::error::Error + Send + Sync>
104                                        );
105                                    }
106                                };
107
108                            // Verify payment
109                            match middleware
110                                .verify_with_requirements(&payment_payload, &requirements)
111                                .await
112                            {
113                                Ok(true) => {
114                                    // Payment is valid, proceed with request
115                                    let response = future.await?;
116
117                                    // Settle payment after successful response
118                                    if let Ok(settlement) = middleware
119                                        .settle_with_requirements(&payment_payload, &requirements)
120                                        .await
121                                    {
122                                        // Note: In a real implementation, we would need to modify the response
123                                        // to add the X-PAYMENT-RESPONSE header, but this requires
124                                        // more complex response handling in Tower
125                                        let _ = settlement; // Acknowledge settlement
126                                    }
127
128                                    Ok(response)
129                                }
130                                Ok(false) => {
131                                    // Payment verification failed
132                                    Err(Box::new(crate::X402Error::payment_verification_failed(
133                                        "Payment verification failed",
134                                    ))
135                                        as Box<dyn std::error::Error + Send + Sync>)
136                                }
137                                Err(e) => {
138                                    // Error during verification
139                                    Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
140                                }
141                            }
142                        }
143                        Err(e) => {
144                            // Invalid payment payload
145                            Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
146                        }
147                    }
148                }
149                None => {
150                    // No payment header provided
151                    Err(Box::new(crate::X402Error::payment_verification_failed(
152                        "X-PAYMENT header is required",
153                    ))
154                        as Box<dyn std::error::Error + Send + Sync>)
155                }
156            }
157        })
158    }
159}