r402_protocol/payment/
supported.rs1use std::collections::HashMap;
4
5use compact_str::CompactString;
6use serde::{Deserialize, Serialize};
7use serde_with::{VecSkipError, serde_as};
8
9use crate::network::ChainId;
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13#[serde(rename_all = "camelCase", deny_unknown_fields)]
14#[non_exhaustive]
15pub struct SupportedPaymentKind {
16 pub x402_version: u8,
18 pub scheme: CompactString,
20 pub network: CompactString,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
24 pub extra: Option<serde_json::Value>,
25}
26
27impl SupportedPaymentKind {
28 #[must_use]
30 pub fn new(
31 x402_version: u8,
32 scheme: impl Into<CompactString>,
33 network: impl Into<CompactString>,
34 ) -> Self {
35 Self {
36 x402_version,
37 scheme: scheme.into(),
38 network: network.into(),
39 extra: None,
40 }
41 }
42
43 #[must_use]
45 pub fn with_extra(mut self, extra: serde_json::Value) -> Self {
46 self.extra = Some(extra);
47 self
48 }
49
50 #[must_use]
52 pub fn with_optional_extra(mut self, extra: Option<serde_json::Value>) -> Self {
53 self.extra = extra;
54 self
55 }
56}
57
58#[serde_as]
62#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
63#[serde(rename_all = "camelCase", deny_unknown_fields)]
64#[non_exhaustive]
65pub struct SupportedResponse {
66 #[serde_as(as = "VecSkipError<_>")]
68 pub kinds: Vec<SupportedPaymentKind>,
69 #[serde(default)]
71 pub extensions: Vec<CompactString>,
72 #[serde(default)]
74 pub signers: HashMap<CompactString, Vec<CompactString>>,
75}
76
77impl SupportedResponse {
78 #[must_use]
80 pub fn new() -> Self {
81 Self::default()
82 }
83
84 #[must_use]
86 pub fn with_kinds(mut self, kinds: Vec<SupportedPaymentKind>) -> Self {
87 self.kinds = kinds;
88 self
89 }
90
91 #[must_use]
93 pub fn with_extensions(mut self, extensions: Vec<CompactString>) -> Self {
94 self.extensions = extensions;
95 self
96 }
97
98 #[must_use]
100 #[allow(
101 clippy::implicit_hasher,
102 reason = "wire map is a JSON object with no hasher contract"
103 )]
104 pub fn with_signers(mut self, signers: HashMap<CompactString, Vec<CompactString>>) -> Self {
105 self.signers = signers;
106 self
107 }
108
109 #[must_use]
111 pub fn signers_for_chain(&self, chain_id: &ChainId) -> Vec<&str> {
112 let exact = CompactString::from(chain_id.to_string());
113 let wildcard = CompactString::from(format!("{}:*", chain_id.namespace()));
114 let mut out = Vec::new();
115 if let Some(list) = self.signers.get(&exact) {
116 out.extend(list.iter().map(CompactString::as_str));
117 }
118 if let Some(list) = self.signers.get(&wildcard) {
119 out.extend(list.iter().map(CompactString::as_str));
120 }
121 out
122 }
123}