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 IntoIterator for Accepts {
46    type Item = PaymentRequirements;
47    type IntoIter = std::vec::IntoIter<PaymentRequirements>;
48
49    fn into_iter(self) -> Self::IntoIter {
50        self.0.into_iter()
51    }
52}
53
54impl<'a> IntoIterator for &'a Accepts {
55    type Item = &'a PaymentRequirements;
56    type IntoIter = std::slice::Iter<'a, PaymentRequirements>;
57
58    fn into_iter(self) -> Self::IntoIter {
59        self.0.iter()
60    }
61}
62
63impl FromIterator<PaymentRequirements> for Accepts {
64    fn from_iter<T: IntoIterator<Item = PaymentRequirements>>(iter: T) -> Self {
65        let vec: Vec<PaymentRequirements> = iter.into_iter().collect();
66        Accepts(vec)
67    }
68}
69
70impl Serialize for Accepts {
71    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
72    where
73        S: serde::Serializer,
74    {
75        self.0.serialize(serializer)
76    }
77}
78
79impl<'de> Deserialize<'de> for Accepts {
80    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
81    where
82        D: serde::Deserializer<'de>,
83    {
84        let vec = Vec::<PaymentRequirements>::deserialize(deserializer)?;
85        Ok(Accepts(vec))
86    }
87}
88
89impl Debug for Accepts {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        format!("{:?}", self.0).fmt(f)
92    }
93}
94
95impl Accepts {
96    pub fn push(mut self, payment: impl Into<PaymentRequirements>) -> Self {
97        self.0.push(payment.into());
98        self
99    }
100
101    pub fn new() -> Self {
102        Accepts(Vec::new())
103    }
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
107#[serde(rename_all = "camelCase")]
108pub struct PaymentRequired {
109    pub x402_version: X402V2,
110    pub error: String,
111    pub resource: PaymentResource,
112    pub accepts: Accepts,
113    pub extensions: Record<Extension>,
114}
115
116impl TryFrom<PaymentRequired> for Base64EncodedHeader {
117    type Error = crate::errors::Error;
118
119    /// Serialize PaymentRequired into `PAYMENT-REQUIRED` header format
120    fn try_from(value: PaymentRequired) -> Result<Self, Self::Error> {
121        let json = serde_json::to_string(&value)?;
122        let encoded = BASE64_STANDARD.encode(json);
123        Ok(Base64EncodedHeader(encoded))
124    }
125}
126
127impl TryFrom<Base64EncodedHeader> for PaymentRequired {
128    type Error = crate::errors::Error;
129
130    /// Deserialize `PAYMENT-REQUIRED` header into PaymentRequired
131    fn try_from(value: Base64EncodedHeader) -> Result<Self, Self::Error> {
132        let decoded = BASE64_STANDARD.decode(&value.0)?;
133        let json_str = String::from_utf8(decoded)?;
134        let payment_required: PaymentRequired = serde_json::from_str(&json_str)?;
135        Ok(payment_required)
136    }
137}
138
139#[derive(Debug, Clone, Serialize, Deserialize)]
140#[serde(rename_all = "camelCase")]
141pub struct PaymentPayload {
142    pub x402_version: X402V2,
143    pub resource: PaymentResource,
144    pub accepted: PaymentRequirements,
145    pub payload: AnyJson,
146    pub extensions: AnyJson,
147}
148
149impl TryFrom<PaymentPayload> for Base64EncodedHeader {
150    type Error = crate::errors::Error;
151
152    /// Serialize PaymentPayload into `PAYMENT-SIGNATURE` header format
153    fn try_from(value: PaymentPayload) -> Result<Self, Self::Error> {
154        let json = serde_json::to_string(&value)?;
155        let encoded = BASE64_STANDARD.encode(json);
156        Ok(Base64EncodedHeader(encoded))
157    }
158}
159
160impl TryFrom<Base64EncodedHeader> for PaymentPayload {
161    type Error = crate::errors::Error;
162
163    /// Deserialize `PAYMENT-SIGNATURE` header into PaymentPayload
164    fn try_from(value: Base64EncodedHeader) -> Result<Self, Self::Error> {
165        let decoded_bytes = BASE64_STANDARD.decode(&value.0)?;
166        let json_str = String::from_utf8(decoded_bytes)?;
167        let payload = serde_json::from_str(&json_str)?;
168        Ok(payload)
169    }
170}
171
172#[derive(Debug, Clone, Serialize, Deserialize)]
173pub struct SettlementResponse {
174    pub success: bool,
175    pub transaction: String,
176    pub network: String,
177    pub payer: String,
178}
179
180impl TryFrom<SettlementResponse> for Base64EncodedHeader {
181    type Error = crate::errors::Error;
182
183    /// Serialize SettlementResponse into `PAYMENT-RESPONSE` header format
184    fn try_from(value: SettlementResponse) -> Result<Self, Self::Error> {
185        let json = serde_json::to_string(&value)?;
186        let encoded = BASE64_STANDARD.encode(json);
187        Ok(Base64EncodedHeader(encoded))
188    }
189}
190
191impl TryFrom<Base64EncodedHeader> for SettlementResponse {
192    type Error = crate::errors::Error;
193
194    /// Deserialize `PAYMENT-RESPONSE` header into SettlementResponse
195    fn try_from(value: Base64EncodedHeader) -> Result<Self, Self::Error> {
196        let decoded_bytes = BASE64_STANDARD.decode(&value.0)?;
197        let json_str = String::from_utf8(decoded_bytes)?;
198        let response = serde_json::from_str(&json_str)?;
199        Ok(response)
200    }
201}
202
203impl<S, A> From<Payment<S, A>> for PaymentRequirements
204where
205    S: Scheme,
206    A: Address<Network = S::Network>,
207{
208    fn from(payment: Payment<S, A>) -> Self {
209        PaymentRequirements {
210            scheme: S::SCHEME_NAME.to_string(),
211            network: payment.scheme.network().network_id().to_string(),
212            amount: payment.amount,
213            asset: payment.asset.address.to_string(),
214            pay_to: payment.pay_to.to_string(),
215            max_timeout_seconds: payment.max_timeout_seconds,
216            extra: payment.extra,
217        }
218    }
219}