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
78pub enum SettleResponse {
82 Success {
84 payer: String,
86 transaction: String,
88 network: String,
90 },
91 Error {
93 reason: String,
95 network: String,
97 },
98}
99
100impl From<SettleResponse> for proto::SettleResponse {
101 fn from(val: SettleResponse) -> Self {
102 proto::SettleResponse(
103 serde_json::to_value(val).expect("SettleResponse serialization failed"),
104 )
105 }
106}
107
108#[derive(Serialize, Deserialize)]
109struct SettleResponseWire {
110 pub success: bool,
111 #[serde(skip_serializing_if = "Option::is_none")]
112 pub error_reason: Option<String>,
113 #[serde(skip_serializing_if = "Option::is_none")]
114 pub payer: Option<String>,
115 #[serde(skip_serializing_if = "Option::is_none")]
116 pub transaction: Option<String>,
117 pub network: String,
118}
119
120impl Serialize for SettleResponse {
121 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
122 where
123 S: Serializer,
124 {
125 let wire = match self {
126 SettleResponse::Success {
127 payer,
128 transaction,
129 network,
130 } => SettleResponseWire {
131 success: true,
132 error_reason: None,
133 payer: Some(payer.clone()),
134 transaction: Some(transaction.clone()),
135 network: network.clone(),
136 },
137 SettleResponse::Error { reason, network } => SettleResponseWire {
138 success: false,
139 error_reason: Some(reason.clone()),
140 payer: None,
141 transaction: None,
142 network: network.clone(),
143 },
144 };
145 wire.serialize(serializer)
146 }
147}
148
149impl<'de> Deserialize<'de> for SettleResponse {
150 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
151 where
152 D: Deserializer<'de>,
153 {
154 let wire = SettleResponseWire::deserialize(deserializer)?;
155 match wire.success {
156 true => {
157 let payer = wire
158 .payer
159 .ok_or_else(|| serde::de::Error::missing_field("payer"))?;
160 let transaction = wire
161 .transaction
162 .ok_or_else(|| serde::de::Error::missing_field("transaction"))?;
163 Ok(SettleResponse::Success {
164 payer,
165 transaction,
166 network: wire.network,
167 })
168 }
169 false => {
170 let reason = wire
171 .error_reason
172 .ok_or_else(|| serde::de::Error::missing_field("error_reason"))?;
173 Ok(SettleResponse::Error {
174 reason,
175 network: wire.network,
176 })
177 }
178 }
179 }
180}
181
182#[derive(Debug)]
187pub enum VerifyResponse {
188 Valid { payer: String },
190 Invalid {
192 reason: String,
193 payer: Option<String>,
194 },
195}
196
197impl From<VerifyResponse> for proto::VerifyResponse {
198 fn from(val: VerifyResponse) -> Self {
199 proto::VerifyResponse(
200 serde_json::to_value(val).expect("VerifyResponse serialization failed"),
201 )
202 }
203}
204
205impl TryFrom<proto::VerifyResponse> for VerifyResponse {
206 type Error = serde_json::Error;
207 fn try_from(value: proto::VerifyResponse) -> Result<Self, Self::Error> {
208 let json = value.0;
209 serde_json::from_value(json)
210 }
211}
212
213impl VerifyResponse {
214 pub fn valid(payer: String) -> Self {
218 VerifyResponse::Valid { payer }
219 }
220
221 #[allow(dead_code)]
226 pub fn invalid(payer: Option<String>, reason: String) -> Self {
227 VerifyResponse::Invalid { reason, payer }
228 }
229}
230
231#[derive(Serialize, Deserialize)]
232#[serde(rename_all = "camelCase")]
233struct VerifyResponseWire {
234 is_valid: bool,
235 #[serde(skip_serializing_if = "Option::is_none")]
236 payer: Option<String>,
237 #[serde(default, skip_serializing_if = "Option::is_none")]
238 invalid_reason: Option<String>,
239}
240
241impl Serialize for VerifyResponse {
242 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
243 where
244 S: Serializer,
245 {
246 let wire = match self {
247 VerifyResponse::Valid { payer } => VerifyResponseWire {
248 is_valid: true,
249 payer: Some(payer.clone()),
250 invalid_reason: None,
251 },
252 VerifyResponse::Invalid { reason, payer } => VerifyResponseWire {
253 is_valid: false,
254 payer: payer.clone(),
255 invalid_reason: Some(reason.clone()),
256 },
257 };
258 wire.serialize(serializer)
259 }
260}
261
262impl<'de> Deserialize<'de> for VerifyResponse {
263 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
264 where
265 D: Deserializer<'de>,
266 {
267 let wire = VerifyResponseWire::deserialize(deserializer)?;
268 match wire.is_valid {
269 true => {
270 let payer = wire
271 .payer
272 .ok_or_else(|| serde::de::Error::missing_field("payer"))?;
273 Ok(VerifyResponse::Valid { payer })
274 }
275 false => {
276 let reason = wire
277 .invalid_reason
278 .ok_or_else(|| serde::de::Error::missing_field("invalid_reason"))?;
279 let payer = wire.payer;
280 Ok(VerifyResponse::Invalid { reason, payer })
281 }
282 }
283 }
284}
285
286#[derive(Debug, Clone, Serialize, Deserialize)]
290#[serde(rename_all = "camelCase")]
291pub struct VerifyRequest<TPayload, TRequirements> {
292 pub x402_version: X402Version1,
294 pub payment_payload: TPayload,
296 pub payment_requirements: TRequirements,
298}
299
300impl<TPayload, TRequirements> VerifyRequest<TPayload, TRequirements>
301where
302 Self: DeserializeOwned,
303{
304 pub fn from_proto(
305 request: proto::VerifyRequest,
307 ) -> Result<Self, proto::PaymentVerificationError> {
308 let value = serde_json::from_str(request.as_str())?;
309 Ok(value)
310 }
311}
312
313impl<TPayload, TRequirements> TryInto<proto::VerifyRequest>
314 for VerifyRequest<TPayload, TRequirements>
315where
316 TPayload: Serialize,
317 TRequirements: Serialize,
318{
319 type Error = serde_json::Error;
320 fn try_into(self) -> Result<proto::VerifyRequest, Self::Error> {
321 let json = serde_json::to_value(self)?;
322 let raw = serde_json::value::to_raw_value(&json)?;
323 Ok(proto::VerifyRequest(raw))
324 }
325}
326
327#[derive(Debug, Clone, Serialize, Deserialize)]
337#[serde(rename_all = "camelCase")]
338pub struct PaymentPayload<TScheme = String, TPayload = Box<serde_json::value::RawValue>> {
339 pub x402_version: X402Version1,
341 pub scheme: TScheme,
343 pub network: String,
345 pub payload: TPayload,
347}
348
349#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
361#[serde(rename_all = "camelCase")]
362pub struct PaymentRequirements<
363 TScheme = String,
364 TAmount = String,
365 TAddress = String,
366 TExtra = serde_json::Value,
367> {
368 pub scheme: TScheme,
370 pub network: String,
372 pub max_amount_required: TAmount,
374 pub resource: String,
376 pub description: String,
378 pub mime_type: String,
380 #[serde(skip_serializing_if = "Option::is_none")]
382 pub output_schema: Option<serde_json::Value>,
383 pub pay_to: TAddress,
385 pub max_timeout_seconds: u64,
387 pub asset: TAddress,
389 #[serde(skip_serializing_if = "Option::is_none")]
391 pub extra: Option<TExtra>,
392}
393
394impl<TScheme, TAmount, TAddress, TExtra> TryFrom<&OriginalJson>
395 for PaymentRequirements<TScheme, TAmount, TAddress, TExtra>
396where
397 TScheme: for<'a> serde::Deserialize<'a>,
398 TAmount: for<'a> serde::Deserialize<'a>,
399 TAddress: for<'a> serde::Deserialize<'a>,
400 TExtra: for<'a> serde::Deserialize<'a>,
401{
402 type Error = serde_json::Error;
403
404 fn try_from(value: &OriginalJson) -> Result<Self, Self::Error> {
405 let payment_requirements = serde_json::from_str(value.0.get())?;
406 Ok(payment_requirements)
407 }
408}
409
410#[derive(Debug, Clone, Serialize, Deserialize)]
415#[serde(rename_all = "camelCase")]
416pub struct PaymentRequired<TAccepts = PaymentRequirements> {
417 pub x402_version: X402Version1,
419 #[serde(default = "Vec::default")]
421 pub accepts: Vec<TAccepts>,
422 #[serde(default, skip_serializing_if = "Option::is_none")]
424 pub error: Option<String>,
425}
426
427#[derive(Clone)]
449#[allow(dead_code)] pub struct PriceTag {
451 pub scheme: String,
453 pub pay_to: String,
455 pub asset: String,
457 pub network: String,
459 pub amount: String,
461 pub max_timeout_seconds: u64,
463 pub extra: Option<serde_json::Value>,
465 #[doc(hidden)]
467 pub enricher: Option<Enricher>,
468}
469
470impl fmt::Debug for PriceTag {
471 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
472 f.debug_struct("PriceTag")
473 .field("scheme", &self.scheme)
474 .field("pay_to", &self.pay_to)
475 .field("asset", &self.asset)
476 .field("network", &self.network)
477 .field("amount", &self.amount)
478 .field("max_timeout_seconds", &self.max_timeout_seconds)
479 .field("extra", &self.extra)
480 .finish()
481 }
482}
483
484pub type Enricher = Arc<dyn Fn(&mut PriceTag, &SupportedResponse) + Send + Sync>;
489
490impl PriceTag {
491 #[allow(dead_code)]
496 pub fn enrich(&mut self, capabilities: &SupportedResponse) {
497 if let Some(enricher) = self.enricher.clone() {
498 enricher(self, capabilities);
499 }
500 }
501
502 #[allow(dead_code)]
504 pub fn with_timeout(mut self, seconds: u64) -> Self {
505 self.max_timeout_seconds = seconds;
506 self
507 }
508}