Skip to main content

r402_http/server/
gate.rs

1//! Three Gate entries. Verify (steps 1–5) lives in [`super::verify`].
2
3use std::convert::Infallible;
4use std::future::Future;
5#[cfg(feature = "siwx")]
6use std::pin::Pin;
7use std::sync::Arc;
8
9use axum_core::extract::Request;
10use axum_core::response::Response;
11use r402_protocol::error::FacilitatorError;
12use r402_protocol::payment::{PriceTag, ResourceInfo, SettleResponse, SettlementOverrides};
13use r402_server::{
14    AfterHandler, BackgroundSettlementTracker, CancellationGuard, CompletedSettlement,
15    PaymentFlowName, ResourceServer, ScheduledSettlement, SequentialFinish, SettlePhase,
16    SettlementMode, finish, run, schedule,
17};
18use tower::Service;
19
20use super::fail::GateError;
21use super::hooks::DynGateHooks;
22use super::overrides::strip_response_settlement_overrides;
23use super::receipt::{
24    attach_failure_path_settlement, attach_payment_response, skip_handler_response,
25};
26use super::verify::{
27    HANDLER_FAILED, VerifiedPayment, cancellation_guard, handler_failed_reason, map_settle,
28    payment_flow_of, phases_of, settle_before_handler_if_needed,
29};
30
31/// HTTP payment gate.
32pub struct Gate {
33    pub(crate) server: ResourceServer,
34    pub(crate) accepts: Arc<[PriceTag]>,
35    pub(crate) resource: ResourceInfo,
36    pub(crate) payment_required: Option<r402_protocol::payment::PaymentRequired>,
37    pub(crate) hooks: Option<Arc<dyn DynGateHooks>>,
38    pub(crate) settlement_mode: SettlementMode,
39    pub(crate) settlement_tracker: Option<BackgroundSettlementTracker>,
40    #[cfg(feature = "siwx")]
41    pub(crate) siwx: Option<Arc<super::SiwxGate>>,
42}
43
44impl std::fmt::Debug for Gate {
45    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46        f.debug_struct("Gate")
47            .field("accepts", &self.accepts.len())
48            .field("resource", &self.resource)
49            .field("settlement_mode", &self.settlement_mode)
50            .finish_non_exhaustive()
51    }
52}
53
54enum Prepared {
55    Skipped(Response),
56    Ready {
57        verified: Box<VerifiedPayment>,
58        before_handler: Option<Box<CompletedSettlement>>,
59    },
60}
61
62#[derive(Debug)]
63enum ConcurrentHandlerError {
64    Failed(Box<Response>),
65    SettlementAborted(String),
66}
67
68impl Gate {
69    pub(crate) fn from_parts(
70        server: ResourceServer,
71        accepts: Vec<PriceTag>,
72        resource: ResourceInfo,
73        settlement_mode: SettlementMode,
74        settlement_tracker: Option<BackgroundSettlementTracker>,
75        hooks: Option<Arc<dyn DynGateHooks>>,
76        #[cfg(feature = "siwx")] siwx: Option<Arc<super::SiwxGate>>,
77    ) -> Self {
78        Self {
79            server,
80            accepts: accepts.into(),
81            resource,
82            payment_required: None,
83            hooks,
84            settlement_mode,
85            settlement_tracker,
86            #[cfg(feature = "siwx")]
87            siwx,
88        }
89    }
90
91    /// Inserts a fresh SIWX challenge into the built 402 body.
92    #[cfg(feature = "siwx")]
93    pub(crate) fn attach_siwx_challenge(
94        &mut self,
95        siwx: &super::SiwxGate,
96        path: &str,
97    ) -> Result<(), GateError> {
98        let Some(payment_required) = self.payment_required.as_mut() else {
99            return Ok(());
100        };
101        let entry = siwx
102            .challenge_entry(path)
103            .map_err(|err| GateError::PaymentRequiredBuild(err.to_string()))?;
104        payment_required.extensions.insert(super::SIWX_KEY, entry);
105        Ok(())
106    }
107
108    fn record_siwx_success(&self, path: &str, settlement: &SettleResponse) {
109        #[cfg(feature = "siwx")]
110        if let Some(siwx) = self.siwx.as_ref() {
111            record_paid_address(siwx, path, settlement);
112        }
113        #[cfg(not(feature = "siwx"))]
114        {
115            let _ = (path, settlement);
116        }
117    }
118
119    /// Resource server (facilitator + schemes).
120    #[must_use]
121    pub const fn resource_server(&self) -> &ResourceServer {
122        &self.server
123    }
124
125    /// Accepted price tags.
126    #[must_use]
127    pub fn accepts(&self) -> &[PriceTag] {
128        &self.accepts
129    }
130
131    /// Resource metadata on 402 bodies.
132    #[must_use]
133    pub const fn resource(&self) -> &ResourceInfo {
134        &self.resource
135    }
136
137    /// Built 402 body, if [`Self::build_payment_required`] ran.
138    #[must_use]
139    pub const fn payment_required(&self) -> Option<&r402_protocol::payment::PaymentRequired> {
140        self.payment_required.as_ref()
141    }
142
143    /// Requires a registered scheme for every accept. Escrow needs `settle_on_cancel`.
144    ///
145    /// # Errors
146    ///
147    /// [`GateError::MissingScheme`], [`GateError::MissingSettleOnCancel`], or flow errors.
148    pub async fn require_schemes(&self) -> Result<(), GateError> {
149        for tag in self.accepts.iter() {
150            let requirements = &tag.requirements;
151            if self
152                .server
153                .registered_scheme(requirements.scheme.as_str(), &requirements.network)
154                .is_none()
155            {
156                return Err(GateError::MissingScheme {
157                    scheme: requirements.scheme.clone(),
158                    network: requirements.network.clone(),
159                });
160            }
161            let flow = payment_flow_of(&self.server, requirements)?;
162            if flow == PaymentFlowName::Escrow
163                && !self.server.has_settle_on_cancel(requirements).await
164            {
165                return Err(GateError::MissingSettleOnCancel {
166                    scheme: requirements.scheme.clone(),
167                });
168            }
169        }
170        Ok(())
171    }
172
173    /// Concurrent/Background are illegal when any resolved accept is upfront or escrow.
174    ///
175    /// # Errors
176    ///
177    /// [`GateError::IncompatibleSettlementMode`] or flow-resolution errors.
178    pub fn assert_mode_compatible(&self, mode: SettlementMode) -> Result<(), GateError> {
179        if mode == SettlementMode::Sequential {
180            return Ok(());
181        }
182        for tag in self.accepts.iter() {
183            let flow = payment_flow_of(&self.server, &tag.requirements)?;
184            let phases = flow.phases();
185            if let Err(err) = schedule(phases, mode) {
186                return Err(GateError::IncompatibleSettlementMode {
187                    mode: err.mode,
188                    flow: err.flow,
189                });
190            }
191        }
192        Ok(())
193    }
194
195    /// Sequential: verify → settle-before → handler → finish(after-handler).
196    ///
197    /// Handler 4xx/5xx never enter [`finish`]. Transport maps to 502 at the layer.
198    ///
199    /// # Errors
200    ///
201    /// [`GateError`] for verify/settle/header failures.
202    pub async fn handle_request<S>(&self, inner: S, mut req: Request) -> Result<Response, GateError>
203    where
204        S: Service<Request, Response = Response, Error = Infallible>,
205        S::Future: Send,
206    {
207        let path = req.uri().path().to_owned();
208        match self.prepare(&mut req).await? {
209            Prepared::Skipped(response) => Ok(response),
210            Prepared::Ready {
211                verified,
212                before_handler,
213            } => {
214                sequential_after_verify(
215                    self,
216                    &path,
217                    inner,
218                    req,
219                    *verified,
220                    before_handler.map(|b| *b),
221                )
222                .await
223            }
224        }
225    }
226
227    /// Concurrent: steps 1–5, then wrap 4xx/overrides as Err, then [`run`] `JoinSettle`.
228    ///
229    /// # Errors
230    ///
231    /// [`GateError`] for verify/settle/header failures. Overrides → 402
232    /// [`GateError::SettlementAborted`].
233    pub async fn handle_request_concurrent<S>(
234        &self,
235        inner: S,
236        mut req: Request,
237    ) -> Result<Response, GateError>
238    where
239        S: Service<Request, Response = Response, Error = Infallible> + Send,
240        S::Future: Send,
241    {
242        let path = req.uri().path().to_owned();
243        match self.prepare(&mut req).await? {
244            Prepared::Skipped(response) => Ok(response),
245            Prepared::Ready {
246                verified,
247                before_handler,
248            } => {
249                concurrent_after_verify(
250                    self,
251                    &path,
252                    inner,
253                    req,
254                    *verified,
255                    before_handler.map(|b| *b),
256                )
257                .await
258            }
259        }
260    }
261
262    /// Background: steps 1–5, then [`run`] `SpawnSettle`, strip overrides, no receipt.
263    ///
264    /// # Errors
265    ///
266    /// [`GateError`] for verify/header failures. Settlement errors are not returned.
267    pub async fn handle_request_background<S>(
268        &self,
269        inner: S,
270        mut req: Request,
271    ) -> Result<Response, GateError>
272    where
273        S: Service<Request, Response = Response, Error = Infallible> + Send,
274        S::Future: Send,
275    {
276        let path = req.uri().path().to_owned();
277        match self.prepare(&mut req).await? {
278            Prepared::Skipped(response) => Ok(response),
279            Prepared::Ready {
280                verified,
281                before_handler,
282            } => {
283                background_after_verify(
284                    self,
285                    &path,
286                    inner,
287                    req,
288                    *verified,
289                    before_handler.map(|b| *b),
290                )
291                .await
292            }
293        }
294    }
295
296    async fn prepare(&self, req: &mut Request) -> Result<Prepared, GateError> {
297        let verified = self.verify_only(req.headers()).await?;
298        if let Some(h) = self.hooks.as_ref() {
299            h.on_payment_verified(req).await;
300        }
301        if let Some(directive) = verified.skip_handler.clone() {
302            let receipt = sequential_after_handler(&verified, None, None).await?;
303            self.record_siwx_success(req.uri().path(), &receipt);
304            return Ok(Prepared::Skipped(skip_handler_response(
305                &directive, &receipt,
306            )?));
307        }
308        let (flow, phases) = phases_of(&verified)?;
309        let before_handler = settle_before_handler_if_needed(&verified, flow, phases).await?;
310        Ok(Prepared::Ready {
311            verified: Box::new(verified),
312            before_handler: before_handler.map(Box::new),
313        })
314    }
315}
316
317async fn sequential_after_verify<S>(
318    gate: &Gate,
319    path: &str,
320    inner: S,
321    req: Request,
322    verified: VerifiedPayment,
323    before_handler: Option<CompletedSettlement>,
324) -> Result<Response, GateError>
325where
326    S: Service<Request, Response = Response, Error = Infallible>,
327    S::Future: Send,
328{
329    let cancel = cancellation_guard(&verified, before_handler.as_ref());
330    let response = match call_inner(inner, req).await {
331        Ok(r) => r,
332        Err(never) => match never {},
333    };
334
335    if response.status().is_client_error() || response.status().is_server_error() {
336        if let Some(completed) = before_handler.as_ref() {
337            gate.record_siwx_success(path, &completed.result);
338        }
339        return failed_handler_response(
340            response,
341            &cancel,
342            before_handler.as_ref(),
343            Some(&verified.payload),
344        )
345        .await;
346    }
347
348    let mut response = response;
349    let override_amount = super::overrides::resolve_response_settlement_amount(
350        &mut response,
351        verified.requirements(),
352    )
353    .map_err(|e| GateError::SettlementAborted(e.to_string()))?;
354    let overrides = override_amount.as_deref().map(SettlementOverrides::amount);
355
356    let settlement =
357        sequential_after_handler(&verified, overrides.as_ref(), before_handler.as_ref()).await?;
358    gate.record_siwx_success(path, &settlement);
359    attach_payment_response(&mut response, &settlement)?;
360    Ok(response)
361}
362
363async fn concurrent_after_verify<S>(
364    gate: &Gate,
365    path: &str,
366    inner: S,
367    req: Request,
368    verified: VerifiedPayment,
369    before_handler: Option<CompletedSettlement>,
370) -> Result<Response, GateError>
371where
372    S: Service<Request, Response = Response, Error = Infallible> + Send,
373    S::Future: Send,
374{
375    let (_, phases) = phases_of(&verified)?;
376    let scheduled = schedule(phases, SettlementMode::Concurrent).map_err(|err| {
377        GateError::IncompatibleSettlementMode {
378            mode: err.mode,
379            flow: err.flow,
380        }
381    })?;
382    if scheduled.after_handler() != AfterHandler::JoinSettle {
383        return Err(GateError::PaymentRequiredBuild(
384            "concurrent handle_request cannot run sequential finish".into(),
385        ));
386    }
387
388    let cancel = cancellation_guard(&verified, before_handler.as_ref());
389    let settle = after_handler_settle(verified.clone());
390    let outcome = run(scheduled, concurrent_handler(inner, req), settle).await;
391    map_concurrent_outcome(
392        gate,
393        path,
394        outcome,
395        cancel,
396        before_handler.as_ref(),
397        Some(&verified.payload),
398    )
399    .await
400}
401
402async fn background_after_verify<S>(
403    gate: &Gate,
404    path: &str,
405    inner: S,
406    req: Request,
407    verified: VerifiedPayment,
408    before_handler: Option<CompletedSettlement>,
409) -> Result<Response, GateError>
410where
411    S: Service<Request, Response = Response, Error = Infallible> + Send,
412    S::Future: Send,
413{
414    let (_, phases) = phases_of(&verified)?;
415    let scheduled = schedule(phases, SettlementMode::Background).map_err(|err| {
416        GateError::IncompatibleSettlementMode {
417            mode: err.mode,
418            flow: err.flow,
419        }
420    })?;
421    let scheduled = match gate.settlement_tracker.clone() {
422        Some(tracker) => scheduled.with_tracker(tracker),
423        None => scheduled,
424    };
425    if scheduled.after_handler() != AfterHandler::SpawnSettle {
426        return Err(GateError::PaymentRequiredBuild(
427            "background handle_request cannot run sequential finish".into(),
428        ));
429    }
430
431    let cancel = cancellation_guard(&verified, before_handler.as_ref());
432    let settle = wrap_background_siwx(gate, path, after_handler_settle(verified.clone()));
433    let outcome = run(scheduled, background_handler(inner, req), settle).await;
434    map_background_outcome(outcome, cancel).await
435}
436
437/// Spawned settle success is otherwise invisible to `PaidAddressStore`.
438#[cfg(feature = "siwx")]
439fn wrap_background_siwx(
440    gate: &Gate,
441    path: &str,
442    settle: impl Future<Output = Result<SettleResponse, FacilitatorError>> + Send + 'static,
443) -> Pin<Box<dyn Future<Output = Result<SettleResponse, FacilitatorError>> + Send>> {
444    let siwx = gate.siwx.clone();
445    let path = path.to_owned();
446    Box::pin(async move {
447        let result = settle.await;
448        if let (Some(siwx), Ok(settlement)) = (siwx.as_ref(), result.as_ref()) {
449            record_paid_address(siwx, &path, settlement);
450        }
451        result
452    })
453}
454
455#[cfg(not(feature = "siwx"))]
456fn wrap_background_siwx(
457    _gate: &Gate,
458    _path: &str,
459    settle: impl Future<Output = Result<SettleResponse, FacilitatorError>> + Send + 'static,
460) -> impl Future<Output = Result<SettleResponse, FacilitatorError>> + Send + 'static {
461    settle
462}
463
464#[cfg(feature = "siwx")]
465fn record_paid_address(siwx: &super::SiwxGate, path: &str, settlement: &SettleResponse) {
466    let SettleResponse::Success { payer, .. } = settlement else {
467        return;
468    };
469    let payer = payer.as_deref().unwrap_or("");
470    if payer.is_empty() {
471        return;
472    }
473    siwx.record_success(path, payer);
474}
475
476async fn after_handler_settle(
477    verified: VerifiedPayment,
478) -> Result<SettleResponse, FacilitatorError> {
479    verified.settle_phase(SettlePhase::AfterHandler, None).await
480}
481
482async fn concurrent_handler<S>(inner: S, req: Request) -> Result<Response, ConcurrentHandlerError>
483where
484    S: Service<Request, Response = Response, Error = Infallible> + Send,
485    S::Future: Send,
486{
487    let mut response = match call_inner(inner, req).await {
488        Ok(r) => r,
489        Err(never) => match never {},
490    };
491    if response.status().is_client_error() || response.status().is_server_error() {
492        return Err(ConcurrentHandlerError::Failed(Box::new(response)));
493    }
494    // Partial settlement cannot apply: settle already runs at the signed max.
495    if strip_response_settlement_overrides(&mut response) {
496        return Err(ConcurrentHandlerError::SettlementAborted(
497            "Settlement-Overrides / UptoActualAmount require SettlementMode::Sequential".into(),
498        ));
499    }
500    Ok(response)
501}
502
503async fn background_handler<S>(inner: S, req: Request) -> Result<Response, Box<Response>>
504where
505    S: Service<Request, Response = Response, Error = Infallible> + Send,
506    S::Future: Send,
507{
508    let response = match call_inner(inner, req).await {
509        Ok(r) => r,
510        Err(never) => match never {},
511    };
512    if response.status().is_client_error() || response.status().is_server_error() {
513        Err(Box::new(response))
514    } else {
515        Ok(response)
516    }
517}
518
519async fn map_concurrent_outcome(
520    gate: &Gate,
521    path: &str,
522    outcome: ScheduledSettlement<Response, ConcurrentHandlerError>,
523    cancel: CancellationGuard,
524    before_handler: Option<&CompletedSettlement>,
525    payload: Option<&r402_server::WirePaymentPayload>,
526) -> Result<Response, GateError> {
527    match outcome {
528        ScheduledSettlement::HandlerOkSettleOk { mut value, receipt } => {
529            let settlement = map_settle(Ok(*receipt))?;
530            gate.record_siwx_success(path, &settlement);
531            attach_payment_response(&mut value, &settlement)?;
532            Ok(value)
533        }
534        ScheduledSettlement::HandlerErrDetach { error } => match error {
535            ConcurrentHandlerError::SettlementAborted(detail) => {
536                Err(GateError::SettlementAborted(detail))
537            }
538            ConcurrentHandlerError::Failed(response) => {
539                failed_handler_response(*response, &cancel, before_handler, payload).await
540            }
541        },
542        ScheduledSettlement::SettleErr { error, .. } => {
543            Err(GateError::from_settle_facilitator(error))
544        }
545        ScheduledSettlement::Spawned { .. } => Err(GateError::PaymentRequiredBuild(
546            "concurrent run returned Spawned".into(),
547        )),
548    }
549}
550
551async fn map_background_outcome(
552    outcome: ScheduledSettlement<Response, Box<Response>>,
553    cancel: CancellationGuard,
554) -> Result<Response, GateError> {
555    match outcome {
556        ScheduledSettlement::HandlerErrDetach { mut error } => {
557            let status = error.status().as_u16();
558            let _ = cancel
559                .cancel(handler_failed_reason(), Some(HANDLER_FAILED), Some(status))
560                .await;
561            let _ = strip_response_settlement_overrides(&mut error);
562            Ok(*error)
563        }
564        ScheduledSettlement::Spawned { mut value }
565        | ScheduledSettlement::HandlerOkSettleOk { mut value, .. }
566        | ScheduledSettlement::SettleErr { mut value, .. } => {
567            let _ = strip_response_settlement_overrides(&mut value);
568            Ok(value)
569        }
570    }
571}
572
573async fn failed_handler_response(
574    response: Response,
575    cancel: &CancellationGuard,
576    before_handler: Option<&CompletedSettlement>,
577    payload: Option<&r402_server::WirePaymentPayload>,
578) -> Result<Response, GateError> {
579    let cancel_settlement = cancel
580        .cancel(
581            handler_failed_reason(),
582            Some(HANDLER_FAILED),
583            Some(response.status().as_u16()),
584        )
585        .await;
586    let mut response = response;
587    attach_failure_path_settlement(
588        &mut response,
589        cancel_settlement.as_ref(),
590        before_handler,
591        payload,
592    )?;
593    Ok(response)
594}
595
596async fn sequential_after_handler(
597    verified: &VerifiedPayment,
598    overrides: Option<&SettlementOverrides>,
599    before_handler: Option<&CompletedSettlement>,
600) -> Result<SettleResponse, GateError> {
601    let (_, phases) = phases_of(verified)?;
602    let scheduled = schedule(phases, SettlementMode::Sequential).map_err(|err| {
603        GateError::IncompatibleSettlementMode {
604            mode: err.mode,
605            flow: err.flow,
606        }
607    })?;
608
609    match scheduled.after_handler() {
610        AfterHandler::WaitThenSettle => {
611            let fut = verified.settle_phase(SettlePhase::AfterHandler, overrides);
612            match finish(scheduled, Some(fut)).await {
613                Ok(SequentialFinish::Settled(receipt)) => map_settle(Ok(receipt)),
614                Ok(SequentialFinish::Echo) => Ok(echo_or_empty(before_handler, verified)),
615                Err(err) => Err(GateError::from_settle_facilitator(err)),
616            }
617        }
618        AfterHandler::EchoReceipt => match finish(
619            scheduled,
620            None::<std::future::Ready<Result<SettleResponse, FacilitatorError>>>,
621        )
622        .await
623        {
624            Ok(SequentialFinish::Echo) => Ok(echo_or_empty(before_handler, verified)),
625            Ok(SequentialFinish::Settled(receipt)) => map_settle(Ok(receipt)),
626            Err(err) => Err(GateError::from_settle_facilitator(err)),
627        },
628        AfterHandler::JoinSettle | AfterHandler::SpawnSettle => {
629            Err(GateError::PaymentRequiredBuild(
630                "sequential handle_request cannot run concurrent/background finish".into(),
631            ))
632        }
633    }
634}
635
636fn echo_or_empty(
637    before_handler: Option<&CompletedSettlement>,
638    verified: &VerifiedPayment,
639) -> SettleResponse {
640    if let Some(completed) = before_handler {
641        return completed.result.clone();
642    }
643    SettleResponse::Success {
644        payer: None,
645        transaction: compact_str::CompactString::default(),
646        network: verified.requirements().network.to_string().into(),
647        amount: None,
648        extensions: r402_protocol::payment::Extensions::new(),
649        extension_responses: r402_protocol::payment::Extensions::new(),
650        extra: None,
651    }
652}
653
654async fn call_inner<S>(mut inner: S, req: Request) -> Result<Response, Infallible>
655where
656    S: Service<Request, Response = Response, Error = Infallible>,
657    S::Future: Send,
658{
659    inner.call(req).await
660}