x402_core/
facilitator.rs

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