x402_kit/
transport.rs

1use std::fmt::Display;
2
3use base64::{Engine, prelude::BASE64_STANDARD};
4use serde::{Deserialize, Serialize};
5use url::Url;
6
7use crate::types::{AmountValue, AnyJson, OutputSchema, X402Version};
8
9#[derive(Debug, Clone, Serialize, Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub struct PaymentRequirements {
12    /// Scheme name, defined in "schemes" protocol
13    pub scheme: String,
14    /// Network name, defined in "schemes" protocol
15    pub network: String,
16    /// Maximum amount required for the payment in smallest units
17    pub max_amount_required: AmountValue,
18    /// Resource URL to fetch payment details
19    pub resource: Url,
20    /// Description of the resource
21    pub description: String,
22    /// MIME type of the payment payload
23    pub mime_type: String,
24    /// Destination address or account to pay to
25    pub pay_to: String,
26    /// Maximum timeout in seconds for the payment to be completed
27    pub max_timeout_seconds: u64,
28    /// Asset address or identifier
29    pub asset: String,
30    /// Schema of the input / output payload
31    #[serde(skip_serializing_if = "Option::is_none")]
32    pub output_schema: Option<OutputSchema>,
33    /// Extra fields for extensibility
34    #[serde(skip_serializing_if = "Option::is_none")]
35    pub extra: Option<AnyJson>,
36}
37
38#[derive(Debug, Clone, Serialize, Deserialize)]
39#[serde(rename_all = "camelCase")]
40pub struct PaymentPayload {
41    pub x402_version: X402Version,
42    pub scheme: String,
43    pub network: String,
44    pub payload: AnyJson,
45}
46
47#[derive(Debug, Clone, Serialize, Deserialize)]
48#[serde(rename_all = "camelCase")]
49pub struct PaymentRequirementsResponse {
50    pub x402_version: X402Version,
51    pub error: String,
52    pub accepts: Vec<PaymentRequirements>,
53}
54
55#[derive(Debug, Clone)]
56pub struct FacilitatorPaymentRequest {
57    pub payload: FacilitatorPaymentRequestPayload,
58    pub x_payment_header: Base64EncodedHeader,
59}
60
61#[derive(Debug, Clone)]
62pub struct FacilitatorPaymentRequestPayload {
63    pub payment_payload: PaymentPayload,
64    pub payment_requirements: PaymentRequirements,
65}
66
67#[derive(Debug, Clone)]
68pub enum FacilitatorVerifyResponse {
69    Valid(FacilitatorVerifyValid),
70    Invalid(FacilitatorVerifyInvalid),
71}
72
73impl FacilitatorVerifyResponse {
74    pub fn is_valid(&self) -> bool {
75        matches!(self, FacilitatorVerifyResponse::Valid(_))
76    }
77
78    pub fn valid(valid: FacilitatorVerifyValid) -> Self {
79        FacilitatorVerifyResponse::Valid(valid)
80    }
81
82    pub fn invalid(invalid: FacilitatorVerifyInvalid) -> Self {
83        FacilitatorVerifyResponse::Invalid(invalid)
84    }
85}
86
87#[derive(Debug, Clone, Serialize, Deserialize)]
88pub struct FacilitatorVerifyValid {
89    pub payer: String,
90}
91
92#[derive(Debug, Clone, Serialize, Deserialize)]
93pub struct FacilitatorVerifyInvalid {
94    pub invalid_reason: String,
95    pub payer: Option<String>,
96}
97
98#[derive(Debug, Clone)]
99pub enum FacilitatorSettleResponse {
100    Success(FacilitatorSettleSuccess),
101    Failed(FacilitatorSettleFailed),
102}
103
104impl FacilitatorSettleResponse {
105    pub fn is_success(&self) -> bool {
106        matches!(self, FacilitatorSettleResponse::Success(_))
107    }
108
109    pub fn success(success: FacilitatorSettleSuccess) -> Self {
110        FacilitatorSettleResponse::Success(success)
111    }
112
113    pub fn failed(failed: FacilitatorSettleFailed) -> Self {
114        FacilitatorSettleResponse::Failed(failed)
115    }
116}
117
118#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct FacilitatorSettleSuccess {
120    pub payer: String,
121    pub transaction: String,
122    pub network: String,
123}
124
125#[derive(Debug, Clone, Serialize, Deserialize)]
126pub struct FacilitatorSettleFailed {
127    pub error_reason: String,
128    pub payer: Option<String>,
129}
130
131#[derive(Debug, Clone, Serialize, Deserialize)]
132#[serde(rename_all = "camelCase")]
133pub struct FacilitatorSupportedKinds {
134    pub x402_version: X402Version,
135    pub scheme: String,
136    pub network: String,
137    pub extra: Option<AnyJson>,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize)]
141#[serde(rename_all = "camelCase")]
142pub struct FacilitatorSupportedResponse {
143    pub kinds: Vec<FacilitatorSupportedKinds>,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
147#[serde(rename_all = "camelCase")]
148pub struct PaymentResponse {
149    pub success: bool,
150    pub transaction: String,
151    pub network: String,
152    pub payer: String,
153}
154
155impl From<FacilitatorSettleSuccess> for PaymentResponse {
156    fn from(success: FacilitatorSettleSuccess) -> Self {
157        PaymentResponse {
158            success: true,
159            transaction: success.transaction,
160            network: success.network,
161            payer: success.payer,
162        }
163    }
164}
165
166#[derive(Debug, Clone, PartialEq, Eq)]
167pub struct Base64EncodedHeader(pub String);
168
169impl Serialize for Base64EncodedHeader {
170    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
171    where
172        S: serde::Serializer,
173    {
174        serializer.serialize_str(&self.0)
175    }
176}
177
178impl<'de> Deserialize<'de> for Base64EncodedHeader {
179    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
180    where
181        D: serde::Deserializer<'de>,
182    {
183        let s = String::deserialize(deserializer)?;
184        Ok(Base64EncodedHeader(s))
185    }
186}
187
188impl Display for Base64EncodedHeader {
189    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190        write!(f, "{}", self.0)
191    }
192}
193
194impl TryFrom<PaymentPayload> for Base64EncodedHeader {
195    type Error = serde_json::Error;
196
197    fn try_from(value: PaymentPayload) -> Result<Self, Self::Error> {
198        let json = serde_json::to_string(&value)?;
199        let encoded = BASE64_STANDARD.encode(json);
200        Ok(Base64EncodedHeader(encoded))
201    }
202}
203
204impl TryFrom<Base64EncodedHeader> for PaymentPayload {
205    type Error = crate::errors::Error;
206
207    fn try_from(value: Base64EncodedHeader) -> Result<Self, Self::Error> {
208        let decoded_bytes = BASE64_STANDARD.decode(&value.0)?;
209        let json_str = String::from_utf8(decoded_bytes)?;
210        let payload = serde_json::from_str(&json_str)?;
211        Ok(payload)
212    }
213}
214
215impl TryFrom<PaymentResponse> for Base64EncodedHeader {
216    type Error = serde_json::Error;
217
218    fn try_from(value: PaymentResponse) -> Result<Self, Self::Error> {
219        let json = serde_json::to_string(&value)?;
220        let encoded = BASE64_STANDARD.encode(json);
221        Ok(Base64EncodedHeader(encoded))
222    }
223}
224
225impl TryFrom<Base64EncodedHeader> for PaymentResponse {
226    type Error = crate::errors::Error;
227
228    fn try_from(value: Base64EncodedHeader) -> Result<Self, Self::Error> {
229        let decoded_bytes = BASE64_STANDARD.decode(&value.0)?;
230        let json_str = String::from_utf8(decoded_bytes)?;
231        let response = serde_json::from_str(&json_str)?;
232        Ok(response)
233    }
234}