x402_kit/
facilitator.rs

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