1use serde::de::DeserializeOwned;
17use serde::{Deserialize, Deserializer, Serialize, Serializer};
18use std::fmt;
19use std::fmt::Display;
20use std::sync::Arc;
21
22use crate::proto;
23use crate::proto::{OriginalJson, SupportedResponse};
24
25#[derive(Debug, Copy, Clone, Default, PartialEq, Eq)]
30pub struct X402Version1;
31
32impl X402Version1 {
33 pub const VALUE: u8 = 1;
34}
35
36impl PartialEq<u8> for X402Version1 {
37 fn eq(&self, other: &u8) -> bool {
38 *other == Self::VALUE
39 }
40}
41
42impl From<X402Version1> for u8 {
43 fn from(_: X402Version1) -> Self {
44 X402Version1::VALUE
45 }
46}
47
48impl Serialize for X402Version1 {
49 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
50 serializer.serialize_u8(Self::VALUE)
51 }
52}
53
54impl<'de> Deserialize<'de> for X402Version1 {
55 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
56 where
57 D: Deserializer<'de>,
58 {
59 let num = u8::deserialize(deserializer)?;
60 if num == Self::VALUE {
61 Ok(X402Version1)
62 } else {
63 Err(serde::de::Error::custom(format!(
64 "expected version {}, got {}",
65 Self::VALUE,
66 num
67 )))
68 }
69 }
70}
71
72impl Display for X402Version1 {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 write!(f, "{}", Self::VALUE)
75 }
76}
77
78#[derive(Debug, Clone)]
82pub enum SettleResponse {
83 Success {
85 payer: String,
87 transaction: String,
89 network: String,
91 },
92 Error {
94 reason: String,
96 network: String,
98 },
99}
100
101impl From<SettleResponse> for proto::SettleResponse {
102 fn from(val: SettleResponse) -> Self {
103 proto::SettleResponse(
104 serde_json::to_value(val).expect("SettleResponse serialization failed"),
105 )
106 }
107}
108
109#[derive(Serialize, Deserialize)]
110struct SettleResponseWire {
111 pub success: bool,
112 #[serde(skip_serializing_if = "Option::is_none")]
113 pub error_reason: Option<String>,
114 #[serde(skip_serializing_if = "Option::is_none")]
115 pub payer: Option<String>,
116 #[serde(skip_serializing_if = "Option::is_none")]
117 pub transaction: Option<String>,
118 pub network: String,
119}
120
121impl Serialize for SettleResponse {
122 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
123 where
124 S: Serializer,
125 {
126 let wire = match self {
127 SettleResponse::Success {
128 payer,
129 transaction,
130 network,
131 } => SettleResponseWire {
132 success: true,
133 error_reason: None,
134 payer: Some(payer.clone()),
135 transaction: Some(transaction.clone()),
136 network: network.clone(),
137 },
138 SettleResponse::Error { reason, network } => SettleResponseWire {
139 success: false,
140 error_reason: Some(reason.clone()),
141 payer: None,
142 transaction: None,
143 network: network.clone(),
144 },
145 };
146 wire.serialize(serializer)
147 }
148}
149
150impl<'de> Deserialize<'de> for SettleResponse {
151 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
152 where
153 D: Deserializer<'de>,
154 {
155 let wire = SettleResponseWire::deserialize(deserializer)?;
156 match wire.success {
157 true => {
158 let payer = wire
159 .payer
160 .ok_or_else(|| serde::de::Error::missing_field("payer"))?;
161 let transaction = wire
162 .transaction
163 .ok_or_else(|| serde::de::Error::missing_field("transaction"))?;
164 Ok(SettleResponse::Success {
165 payer,
166 transaction,
167 network: wire.network,
168 })
169 }
170 false => {
171 let reason = wire
172 .error_reason
173 .ok_or_else(|| serde::de::Error::missing_field("error_reason"))?;
174 Ok(SettleResponse::Error {
175 reason,
176 network: wire.network,
177 })
178 }
179 }
180 }
181}
182
183#[derive(Debug)]
188pub enum VerifyResponse {
189 Valid { payer: String },
191 Invalid {
193 reason: String,
194 payer: Option<String>,
195 },
196}
197
198impl From<VerifyResponse> for proto::VerifyResponse {
199 fn from(val: VerifyResponse) -> Self {
200 proto::VerifyResponse(
201 serde_json::to_value(val).expect("VerifyResponse serialization failed"),
202 )
203 }
204}
205
206impl TryFrom<proto::VerifyResponse> for VerifyResponse {
207 type Error = serde_json::Error;
208 fn try_from(value: proto::VerifyResponse) -> Result<Self, Self::Error> {
209 let json = value.0;
210 Self::deserialize(json)
211 }
212}
213
214impl VerifyResponse {
215 pub fn valid(payer: String) -> Self {
219 VerifyResponse::Valid { payer }
220 }
221
222 #[allow(dead_code)]
227 pub fn invalid(payer: Option<String>, reason: String) -> Self {
228 VerifyResponse::Invalid { reason, payer }
229 }
230}
231
232#[derive(Serialize, Deserialize)]
233#[serde(rename_all = "camelCase")]
234struct VerifyResponseWire {
235 is_valid: bool,
236 #[serde(skip_serializing_if = "Option::is_none")]
237 payer: Option<String>,
238 #[serde(default, skip_serializing_if = "Option::is_none")]
239 invalid_reason: Option<String>,
240}
241
242impl Serialize for VerifyResponse {
243 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
244 where
245 S: Serializer,
246 {
247 let wire = match self {
248 VerifyResponse::Valid { payer } => VerifyResponseWire {
249 is_valid: true,
250 payer: Some(payer.clone()),
251 invalid_reason: None,
252 },
253 VerifyResponse::Invalid { reason, payer } => VerifyResponseWire {
254 is_valid: false,
255 payer: payer.clone(),
256 invalid_reason: Some(reason.clone()),
257 },
258 };
259 wire.serialize(serializer)
260 }
261}
262
263impl<'de> Deserialize<'de> for VerifyResponse {
264 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
265 where
266 D: Deserializer<'de>,
267 {
268 let wire = VerifyResponseWire::deserialize(deserializer)?;
269 match wire.is_valid {
270 true => {
271 let payer = wire
272 .payer
273 .ok_or_else(|| serde::de::Error::missing_field("payer"))?;
274 Ok(VerifyResponse::Valid { payer })
275 }
276 false => {
277 let reason = wire
278 .invalid_reason
279 .ok_or_else(|| serde::de::Error::missing_field("invalid_reason"))?;
280 let payer = wire.payer;
281 Ok(VerifyResponse::Invalid { reason, payer })
282 }
283 }
284 }
285}
286
287#[derive(Debug, Clone, Serialize, Deserialize)]
291#[serde(rename_all = "camelCase")]
292pub struct VerifyRequest<TPayload, TRequirements> {
293 pub x402_version: X402Version1,
295 pub payment_payload: TPayload,
297 pub payment_requirements: TRequirements,
299}
300
301impl<TPayload, TRequirements> TryFrom<&proto::VerifyRequest>
302 for VerifyRequest<TPayload, TRequirements>
303where
304 Self: DeserializeOwned,
305{
306 type Error = proto::PaymentVerificationError;
307
308 fn try_from(value: &proto::VerifyRequest) -> Result<Self, Self::Error> {
309 let value = serde_json::from_str(value.as_str())?;
310 Ok(value)
311 }
312}
313
314impl<TPayload, TRequirements> TryInto<proto::VerifyRequest>
315 for VerifyRequest<TPayload, TRequirements>
316where
317 TPayload: Serialize,
318 TRequirements: Serialize,
319{
320 type Error = serde_json::Error;
321 fn try_into(self) -> Result<proto::VerifyRequest, Self::Error> {
322 let json = serde_json::to_value(self)?;
323 let raw = serde_json::value::to_raw_value(&json)?;
324 Ok(proto::VerifyRequest(raw))
325 }
326}
327
328#[derive(Debug, Clone, Serialize, Deserialize)]
338#[serde(rename_all = "camelCase")]
339pub struct PaymentPayload<TScheme = String, TPayload = Box<serde_json::value::RawValue>> {
340 pub x402_version: X402Version1,
342 pub scheme: TScheme,
344 pub network: String,
346 pub payload: TPayload,
348}
349
350#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
362#[serde(rename_all = "camelCase")]
363pub struct PaymentRequirements<
364 TScheme = String,
365 TAmount = String,
366 TAddress = String,
367 TExtra = serde_json::Value,
368> {
369 pub scheme: TScheme,
371 pub network: String,
373 pub max_amount_required: TAmount,
375 pub resource: String,
377 pub description: String,
379 pub mime_type: Option<String>,
381 #[serde(skip_serializing_if = "Option::is_none")]
383 pub output_schema: Option<serde_json::Value>,
384 pub pay_to: TAddress,
386 pub max_timeout_seconds: u64,
388 pub asset: TAddress,
390 #[serde(skip_serializing_if = "Option::is_none")]
392 pub extra: Option<TExtra>,
393}
394
395impl<TScheme, TAmount, TAddress, TExtra> TryFrom<&OriginalJson>
396 for PaymentRequirements<TScheme, TAmount, TAddress, TExtra>
397where
398 TScheme: for<'a> serde::Deserialize<'a>,
399 TAmount: for<'a> serde::Deserialize<'a>,
400 TAddress: for<'a> serde::Deserialize<'a>,
401 TExtra: for<'a> serde::Deserialize<'a>,
402{
403 type Error = serde_json::Error;
404
405 fn try_from(value: &OriginalJson) -> Result<Self, Self::Error> {
406 let payment_requirements = serde_json::from_str(value.0.get())?;
407 Ok(payment_requirements)
408 }
409}
410
411#[derive(Debug, Clone, Serialize, Deserialize)]
416#[serde(rename_all = "camelCase")]
417pub struct PaymentRequired<TAccepts = PaymentRequirements> {
418 pub x402_version: X402Version1,
420 #[serde(default = "Vec::default")]
422 pub accepts: Vec<TAccepts>,
423 #[serde(default, skip_serializing_if = "Option::is_none")]
425 pub error: Option<String>,
426}
427
428#[derive(Clone)]
450#[allow(dead_code)] pub struct PriceTag {
452 pub scheme: String,
454 pub pay_to: String,
456 pub asset: String,
458 pub network: String,
460 pub amount: String,
462 pub max_timeout_seconds: u64,
464 pub extra: Option<serde_json::Value>,
466 #[doc(hidden)]
468 pub enricher: Option<Enricher>,
469}
470
471impl fmt::Debug for PriceTag {
472 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473 f.debug_struct("PriceTag")
474 .field("scheme", &self.scheme)
475 .field("pay_to", &self.pay_to)
476 .field("asset", &self.asset)
477 .field("network", &self.network)
478 .field("amount", &self.amount)
479 .field("max_timeout_seconds", &self.max_timeout_seconds)
480 .field("extra", &self.extra)
481 .finish()
482 }
483}
484
485pub type Enricher = Arc<dyn Fn(&mut PriceTag, &SupportedResponse) + Send + Sync>;
490
491impl PriceTag {
492 #[allow(dead_code)]
497 pub fn enrich(&mut self, capabilities: &SupportedResponse) {
498 if let Some(enricher) = self.enricher.clone() {
499 enricher(self, capabilities);
500 }
501 }
502
503 #[allow(dead_code)]
505 pub fn with_timeout(mut self, seconds: u64) -> Self {
506 self.max_timeout_seconds = seconds;
507 self
508 }
509}