|
17 | 17 | //! All types serialize to JSON using camelCase field names. The protocol version |
18 | 18 | //! is indicated by the `x402Version` field in payment payloads. |
19 | 19 |
|
20 | | -use serde::{Deserialize, Deserializer, Serialize, Serializer}; |
| 20 | +use serde::{Deserialize, Serialize}; |
21 | 21 | use serde_with::{VecSkipError, serde_as}; |
22 | 22 | use std::collections::HashMap; |
23 | 23 | use std::str::FromStr; |
@@ -103,8 +103,9 @@ pub type Extensions = HashMap<String, serde_json::Value>; |
103 | 103 | /// Some JSON parsers (particularly in `JavaScript`) cannot accurately represent |
104 | 104 | /// large integers. This type serializes `u64` values as strings to preserve |
105 | 105 | /// precision across all platforms. |
106 | | -#[derive(Clone, Copy, Debug, Eq, PartialEq)] |
107 | | -pub struct U64String(u64); |
| 106 | +#[serde_as] |
| 107 | +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)] |
| 108 | +pub struct U64String(#[serde_as(as = "serde_with::DisplayFromStr")] u64); |
108 | 109 |
|
109 | 110 | impl U64String { |
110 | 111 | /// Returns the inner `u64` value. |
@@ -134,25 +135,6 @@ impl From<U64String> for u64 { |
134 | 135 | } |
135 | 136 | } |
136 | 137 |
|
137 | | -impl Serialize for U64String { |
138 | | - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> |
139 | | - where |
140 | | - S: Serializer, |
141 | | - { |
142 | | - serializer.serialize_str(&self.0.to_string()) |
143 | | - } |
144 | | -} |
145 | | - |
146 | | -impl<'de> Deserialize<'de> for U64String { |
147 | | - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
148 | | - where |
149 | | - D: Deserializer<'de>, |
150 | | - { |
151 | | - let s = String::deserialize(deserializer)?; |
152 | | - s.parse::<u64>().map(Self).map_err(serde::de::Error::custom) |
153 | | - } |
154 | | -} |
155 | | - |
156 | 138 | /// Describes a payment method supported by a facilitator. |
157 | 139 | /// |
158 | 140 | /// This type is returned in the [`SupportedResponse`] to indicate what |
@@ -246,8 +228,7 @@ impl SettleRequest { |
246 | 228 | /// Delegates to the same logic as [`VerifyRequest::scheme_slug`]. |
247 | 229 | #[must_use] |
248 | 230 | pub fn scheme_slug(&self) -> Option<SchemeSlug> { |
249 | | - let tmp = VerifyRequest(self.0.clone()); |
250 | | - tmp.scheme_slug() |
| 231 | + scheme_slug_from_json(&self.0) |
251 | 232 | } |
252 | 233 | } |
253 | 234 |
|
@@ -284,26 +265,23 @@ impl VerifyRequest { |
284 | 265 | /// Returns `None` if the request format is invalid or the scheme is unknown. |
285 | 266 | #[must_use] |
286 | 267 | pub fn scheme_slug(&self) -> Option<SchemeSlug> { |
287 | | - let x402_version: u8 = self.0.get("x402Version")?.as_u64()?.try_into().ok()?; |
288 | | - if x402_version != v2::X402Version2::VALUE { |
289 | | - return None; |
290 | | - } |
291 | | - let chain_id_string = self |
292 | | - .0 |
293 | | - .get("paymentPayload")? |
294 | | - .get("accepted")? |
295 | | - .get("network")? |
296 | | - .as_str()?; |
297 | | - let chain_id = ChainId::from_str(chain_id_string).ok()?; |
298 | | - let scheme = self |
299 | | - .0 |
300 | | - .get("paymentPayload")? |
301 | | - .get("accepted")? |
302 | | - .get("scheme")? |
303 | | - .as_str()?; |
304 | | - let slug = SchemeSlug::new(chain_id, scheme.into()); |
305 | | - Some(slug) |
| 268 | + scheme_slug_from_json(&self.0) |
| 269 | + } |
| 270 | +} |
| 271 | + |
| 272 | +/// Extracts a [`SchemeSlug`] from a raw verify/settle JSON value. |
| 273 | +/// |
| 274 | +/// Navigates `x402Version`, `paymentPayload.accepted.network`, and |
| 275 | +/// `paymentPayload.accepted.scheme` without cloning the JSON tree. |
| 276 | +fn scheme_slug_from_json(json: &serde_json::Value) -> Option<SchemeSlug> { |
| 277 | + let x402_version: u8 = json.get("x402Version")?.as_u64()?.try_into().ok()?; |
| 278 | + if x402_version != v2::X402Version2::VALUE { |
| 279 | + return None; |
306 | 280 | } |
| 281 | + let accepted = json.get("paymentPayload")?.get("accepted")?; |
| 282 | + let chain_id = ChainId::from_str(accepted.get("network")?.as_str()?).ok()?; |
| 283 | + let scheme = accepted.get("scheme")?.as_str()?; |
| 284 | + Some(SchemeSlug::new(chain_id, scheme.into())) |
307 | 285 | } |
308 | 286 |
|
309 | 287 | /// Result returned by a facilitator after verifying a payment payload |
|
0 commit comments