rust_x402/middleware/
service.rs1use super::payment::PaymentMiddleware;
4use tower::ServiceBuilder;
5use tower_http::trace::TraceLayer;
6
7pub 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#[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#[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 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 match crate::types::PaymentPayload::from_base64(&payment_b64) {
95 Ok(payment_payload) => {
96 let requirements =
98 match middleware.config.create_payment_requirements(&uri_path) {
99 Ok(req) => req,
100 Err(e) => {
101 return Err(
103 Box::new(e) as Box<dyn std::error::Error + Send + Sync>
104 );
105 }
106 };
107
108 match middleware
110 .verify_with_requirements(&payment_payload, &requirements)
111 .await
112 {
113 Ok(true) => {
114 let response = future.await?;
116
117 if let Ok(settlement) = middleware
119 .settle_with_requirements(&payment_payload, &requirements)
120 .await
121 {
122 let _ = settlement; }
127
128 Ok(response)
129 }
130 Ok(false) => {
131 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 Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
140 }
141 }
142 }
143 Err(e) => {
144 Err(Box::new(e) as Box<dyn std::error::Error + Send + Sync>)
146 }
147 }
148 }
149 None => {
150 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}