x402_kit/
facilitator.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{
4    transport::{PaymentPayload, PaymentRequirements, SettlementResponse},
5    types::{AnyJson, Base64EncodedHeader, Record, X402V2},
6};
7
8#[derive(Debug, Clone)]
9pub struct PaymentRequest {
10    pub payment_signature: Base64EncodedHeader,
11    pub payment_payload: PaymentPayload,
12    pub payment_requirements: PaymentRequirements,
13}
14
15#[derive(Debug, Clone)]
16pub enum VerifyResult {
17    Valid(VerifyValid),
18    Invalid(VerifyInvalid),
19}
20
21impl VerifyResult {
22    pub fn is_valid(&self) -> bool {
23        matches!(self, VerifyResult::Valid(_))
24    }
25
26    pub fn valid(valid: VerifyValid) -> Self {
27        VerifyResult::Valid(valid)
28    }
29
30    pub fn invalid(invalid: VerifyInvalid) -> Self {
31        VerifyResult::Invalid(invalid)
32    }
33
34    pub fn as_valid(&self) -> Option<&VerifyValid> {
35        match self {
36            VerifyResult::Valid(v) => Some(v),
37            _ => None,
38        }
39    }
40
41    pub fn as_invalid(&self) -> Option<&VerifyInvalid> {
42        match self {
43            VerifyResult::Invalid(v) => Some(v),
44            _ => None,
45        }
46    }
47}
48
49#[derive(Debug, Clone)]
50pub struct VerifyValid {
51    pub payer: String,
52}
53
54#[derive(Debug, Clone)]
55pub struct VerifyInvalid {
56    pub invalid_reason: String,
57    pub payer: Option<String>,
58}
59
60#[derive(Debug, Clone)]
61pub enum SettleResult {
62    Success(SettleSuccess),
63    Failed(SettleFailed),
64}
65
66impl SettleResult {
67    pub fn is_success(&self) -> bool {
68        matches!(self, SettleResult::Success(_))
69    }
70
71    pub fn success(success: SettleSuccess) -> Self {
72        SettleResult::Success(success)
73    }
74
75    pub fn failed(failed: SettleFailed) -> Self {
76        SettleResult::Failed(failed)
77    }
78
79    pub fn as_success(&self) -> Option<&SettleSuccess> {
80        match self {
81            SettleResult::Success(v) => Some(v),
82            _ => None,
83        }
84    }
85
86    pub fn as_failed(&self) -> Option<&SettleFailed> {
87        match self {
88            SettleResult::Failed(v) => Some(v),
89            _ => None,
90        }
91    }
92}
93
94#[derive(Debug, Clone)]
95pub struct SettleSuccess {
96    pub payer: String,
97    pub transaction: String,
98    pub network: String,
99}
100
101#[derive(Debug, Clone)]
102pub struct SettleFailed {
103    pub error_reason: String,
104    pub payer: Option<String>,
105}
106
107#[derive(Debug, Clone, Serialize, Deserialize)]
108#[serde(rename_all = "camelCase")]
109pub struct SupportedKinds {
110    pub x402_version: X402V2,
111    pub scheme: String,
112    pub network: String,
113    pub extra: Option<AnyJson>,
114}
115
116#[derive(Debug, Clone, Serialize, Deserialize)]
117#[serde(rename_all = "camelCase")]
118pub struct SupportedResponse {
119    pub kinds: Vec<SupportedKinds>,
120
121    // TODO: implement stronger typings for extensions
122    /// Array of extension identifiers the facilitator has implemented
123    pub extensions: Vec<AnyJson>,
124    /// Map of CAIP-2 patterns (e.g., eip155:*) to public signer addresses
125    pub signers: Record<Vec<String>>,
126}
127
128impl From<SettleSuccess> for SettlementResponse {
129    fn from(success: SettleSuccess) -> Self {
130        SettlementResponse {
131            success: true,
132            transaction: success.transaction,
133            network: success.network,
134            payer: success.payer,
135        }
136    }
137}
138
139/// X402 facilitator interface.
140pub trait Facilitator {
141    type Error: std::error::Error;
142
143    fn supported(&self) -> impl Future<Output = Result<SupportedResponse, Self::Error>>;
144
145    fn verify(
146        &self,
147        request: PaymentRequest,
148    ) -> impl Future<Output = Result<VerifyResult, Self::Error>>;
149
150    fn settle(
151        &self,
152        request: PaymentRequest,
153    ) -> impl Future<Output = Result<SettleResult, Self::Error>>;
154}