x402_kit/
transport.rs

1use std::fmt::Debug;
2
3use base64::{Engine, prelude::BASE64_STANDARD};
4use serde::{Deserialize, Serialize};
5use url::Url;
6
7use crate::{
8    core::{Address, NetworkFamily, Payment, Resource, Scheme},
9    types::{AmountValue, AnyJson, Base64EncodedHeader, Extension, Record, X402V2},
10};
11
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
13#[serde(rename_all = "camelCase")]
14pub struct PaymentRequirements {
15    pub scheme: String,
16    pub network: String,
17    pub amount: AmountValue,
18    pub asset: String,
19    pub pay_to: String,
20    pub max_timeout_seconds: u64,
21    pub extra: Option<AnyJson>,
22}
23
24#[derive(Debug, Clone, Serialize, Deserialize)]
25#[serde(rename_all = "camelCase")]
26pub struct PaymentResource {
27    pub url: Url,
28    pub description: String,
29    pub mime_type: String,
30}
31
32impl From<Resource> for PaymentResource {
33    fn from(resource: Resource) -> Self {
34        PaymentResource {
35            url: resource.url,
36            description: resource.description,
37            mime_type: resource.mime_type,
38        }
39    }
40}
41
42#[derive(Clone, Default)]
43pub struct Accepts(Vec<PaymentRequirements>);
44
45impl AsRef<[PaymentRequirements]> for Accepts {
46    fn as_ref(&self) -> &[PaymentRequirements] {
47        &self.0
48    }
49}
50
51impl<T> From<T> for Accepts
52where
53    T: Into<PaymentRequirements>,
54{
55    fn from(value: T) -> Self {
56        Accepts(vec![value.into()])
57    }
58}
59
60impl From<Vec<PaymentRequirements>> for Accepts {
61    fn from(value: Vec<PaymentRequirements>) -> Self {
62        Accepts(value)
63    }
64}
65
66impl IntoIterator for Accepts {
67    type Item = PaymentRequirements;
68    type IntoIter = std::vec::IntoIter<PaymentRequirements>;
69
70    fn into_iter(self) -> Self::IntoIter {
71        self.0.into_iter()
72    }
73}
74
75impl<'a> IntoIterator for &'a Accepts {
76    type Item = &'a PaymentRequirements;
77    type IntoIter = std::slice::Iter<'a, PaymentRequirements>;
78
79    fn into_iter(self) -> Self::IntoIter {
80        self.0.iter()
81    }
82}
83
84impl FromIterator<PaymentRequirements> for Accepts {
85    fn from_iter<T: IntoIterator<Item = PaymentRequirements>>(iter: T) -> Self {
86        let vec: Vec<PaymentRequirements> = iter.into_iter().collect();
87        Accepts(vec)
88    }
89}
90
91impl Serialize for Accepts {
92    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
93    where
94        S: serde::Serializer,
95    {
96        self.0.serialize(serializer)
97    }
98}
99
100impl<'de> Deserialize<'de> for Accepts {
101    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
102    where
103        D: serde::Deserializer<'de>,
104    {
105        let vec = Vec::<PaymentRequirements>::deserialize(deserializer)?;
106        Ok(Accepts(vec))
107    }
108}
109
110impl Debug for Accepts {
111    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112        format!("{:?}", self.0).fmt(f)
113    }
114}
115
116impl Accepts {
117    pub fn push(mut self, payment: impl Into<PaymentRequirements>) -> Self {
118        self.0.push(payment.into());
119        self
120    }
121
122    pub fn new() -> Self {
123        Accepts(Vec::new())
124    }
125}
126
127#[derive(Debug, Clone, Serialize, Deserialize)]
128#[serde(rename_all = "camelCase")]
129pub struct PaymentRequired {
130    pub x402_version: X402V2,
131    pub error: String,
132    pub resource: PaymentResource,
133    pub accepts: Accepts,
134    pub extensions: Record<Extension>,
135}
136
137impl TryFrom<PaymentRequired> for Base64EncodedHeader {
138    type Error = crate::errors::Error;
139
140    /// Serialize PaymentRequired into `PAYMENT-REQUIRED` header format
141    fn try_from(value: PaymentRequired) -> Result<Self, Self::Error> {
142        let json = serde_json::to_string(&value)?;
143        let encoded = BASE64_STANDARD.encode(json);
144        Ok(Base64EncodedHeader(encoded))
145    }
146}
147
148impl TryFrom<Base64EncodedHeader> for PaymentRequired {
149    type Error = crate::errors::Error;
150
151    /// Deserialize `PAYMENT-REQUIRED` header into PaymentRequired
152    fn try_from(value: Base64EncodedHeader) -> Result<Self, Self::Error> {
153        let decoded = BASE64_STANDARD.decode(&value.0)?;
154        let json_str = String::from_utf8(decoded)?;
155        let payment_required: PaymentRequired = serde_json::from_str(&json_str)?;
156        Ok(payment_required)
157    }
158}
159
160#[derive(Debug, Clone, Serialize, Deserialize)]
161#[serde(rename_all = "camelCase")]
162pub struct PaymentPayload {
163    pub x402_version: X402V2,
164    pub resource: PaymentResource,
165    pub accepted: PaymentRequirements,
166    pub payload: AnyJson,
167    pub extensions: Record<Extension>,
168}
169
170impl TryFrom<PaymentPayload> for Base64EncodedHeader {
171    type Error = crate::errors::Error;
172
173    /// Serialize PaymentPayload into `PAYMENT-SIGNATURE` header format
174    fn try_from(value: PaymentPayload) -> Result<Self, Self::Error> {
175        let json = serde_json::to_string(&value)?;
176        let encoded = BASE64_STANDARD.encode(json);
177        Ok(Base64EncodedHeader(encoded))
178    }
179}
180
181impl TryFrom<Base64EncodedHeader> for PaymentPayload {
182    type Error = crate::errors::Error;
183
184    /// Deserialize `PAYMENT-SIGNATURE` header into PaymentPayload
185    fn try_from(value: Base64EncodedHeader) -> Result<Self, Self::Error> {
186        let decoded_bytes = BASE64_STANDARD.decode(&value.0)?;
187        let json_str = String::from_utf8(decoded_bytes)?;
188        let payload = serde_json::from_str(&json_str)?;
189        Ok(payload)
190    }
191}
192
193#[derive(Debug, Clone, Serialize, Deserialize)]
194pub struct SettlementResponse {
195    pub success: bool,
196    pub transaction: String,
197    pub network: String,
198    pub payer: String,
199}
200
201impl TryFrom<SettlementResponse> for Base64EncodedHeader {
202    type Error = crate::errors::Error;
203
204    /// Serialize SettlementResponse into `PAYMENT-RESPONSE` header format
205    fn try_from(value: SettlementResponse) -> Result<Self, Self::Error> {
206        let json = serde_json::to_string(&value)?;
207        let encoded = BASE64_STANDARD.encode(json);
208        Ok(Base64EncodedHeader(encoded))
209    }
210}
211
212impl TryFrom<Base64EncodedHeader> for SettlementResponse {
213    type Error = crate::errors::Error;
214
215    /// Deserialize `PAYMENT-RESPONSE` header into SettlementResponse
216    fn try_from(value: Base64EncodedHeader) -> Result<Self, Self::Error> {
217        let decoded_bytes = BASE64_STANDARD.decode(&value.0)?;
218        let json_str = String::from_utf8(decoded_bytes)?;
219        let response = serde_json::from_str(&json_str)?;
220        Ok(response)
221    }
222}
223
224impl<S, A> From<Payment<S, A>> for PaymentRequirements
225where
226    S: Scheme,
227    A: Address<Network = S::Network>,
228{
229    fn from(payment: Payment<S, A>) -> Self {
230        PaymentRequirements {
231            scheme: S::SCHEME_NAME.to_string(),
232            network: payment.scheme.network().network_id().to_string(),
233            amount: payment.amount,
234            asset: payment.asset.address.to_string(),
235            pay_to: payment.pay_to.to_string(),
236            max_timeout_seconds: payment.max_timeout_seconds,
237            extra: payment.extra,
238        }
239    }
240}