1use std::fmt::Debug;
4
5use base64::{Engine, prelude::BASE64_STANDARD};
6use serde::{Deserialize, Serialize};
7use url::Url;
8
9use crate::{
10 core::{Address, NetworkFamily, Payment, Resource, Scheme},
11 types::{AmountValue, AnyJson, Base64EncodedHeader, Extension, Record, X402V2},
12};
13
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
15#[serde(rename_all = "camelCase")]
16pub struct PaymentRequirements {
17 pub scheme: String,
18 pub network: String,
19 pub amount: AmountValue,
20 pub asset: String,
21 pub pay_to: String,
22 pub max_timeout_seconds: u64,
23 pub extra: Option<AnyJson>,
24}
25
26#[derive(Debug, Clone, Serialize, Deserialize)]
27#[serde(rename_all = "camelCase")]
28pub struct PaymentResource {
29 pub url: Url,
30 pub description: String,
31 pub mime_type: String,
32}
33
34impl From<Resource> for PaymentResource {
35 fn from(resource: Resource) -> Self {
36 PaymentResource {
37 url: resource.url,
38 description: resource.description,
39 mime_type: resource.mime_type,
40 }
41 }
42}
43
44#[derive(Clone, Default)]
45pub struct Accepts(Vec<PaymentRequirements>);
46
47impl AsRef<[PaymentRequirements]> for Accepts {
48 fn as_ref(&self) -> &[PaymentRequirements] {
49 &self.0
50 }
51}
52
53impl<T> From<T> for Accepts
54where
55 T: Into<PaymentRequirements>,
56{
57 fn from(value: T) -> Self {
58 Accepts(vec![value.into()])
59 }
60}
61
62impl From<Vec<PaymentRequirements>> for Accepts {
63 fn from(value: Vec<PaymentRequirements>) -> Self {
64 Accepts(value)
65 }
66}
67
68impl IntoIterator for Accepts {
69 type Item = PaymentRequirements;
70 type IntoIter = std::vec::IntoIter<PaymentRequirements>;
71
72 fn into_iter(self) -> Self::IntoIter {
73 self.0.into_iter()
74 }
75}
76
77impl<'a> IntoIterator for &'a Accepts {
78 type Item = &'a PaymentRequirements;
79 type IntoIter = std::slice::Iter<'a, PaymentRequirements>;
80
81 fn into_iter(self) -> Self::IntoIter {
82 self.0.iter()
83 }
84}
85
86impl FromIterator<PaymentRequirements> for Accepts {
87 fn from_iter<T: IntoIterator<Item = PaymentRequirements>>(iter: T) -> Self {
88 let vec: Vec<PaymentRequirements> = iter.into_iter().collect();
89 Accepts(vec)
90 }
91}
92
93impl Serialize for Accepts {
94 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
95 where
96 S: serde::Serializer,
97 {
98 self.0.serialize(serializer)
99 }
100}
101
102impl<'de> Deserialize<'de> for Accepts {
103 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
104 where
105 D: serde::Deserializer<'de>,
106 {
107 let vec = Vec::<PaymentRequirements>::deserialize(deserializer)?;
108 Ok(Accepts(vec))
109 }
110}
111
112impl Debug for Accepts {
113 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114 format!("{:?}", self.0).fmt(f)
115 }
116}
117
118impl Accepts {
119 pub fn push(mut self, payment: impl Into<PaymentRequirements>) -> Self {
120 self.0.push(payment.into());
121 self
122 }
123
124 pub fn new() -> Self {
125 Accepts(Vec::new())
126 }
127}
128
129#[derive(Debug, Clone, Serialize, Deserialize)]
130#[serde(rename_all = "camelCase")]
131pub struct PaymentRequired {
132 pub x402_version: X402V2,
133 pub error: String,
134 pub resource: PaymentResource,
135 pub accepts: Accepts,
136 pub extensions: Record<Extension>,
137}
138
139impl TryFrom<PaymentRequired> for Base64EncodedHeader {
140 type Error = crate::errors::Error;
141
142 fn try_from(value: PaymentRequired) -> Result<Self, Self::Error> {
144 let json = serde_json::to_string(&value)?;
145 let encoded = BASE64_STANDARD.encode(json);
146 Ok(Base64EncodedHeader(encoded))
147 }
148}
149
150impl TryFrom<Base64EncodedHeader> for PaymentRequired {
151 type Error = crate::errors::Error;
152
153 fn try_from(value: Base64EncodedHeader) -> Result<Self, Self::Error> {
155 let decoded = BASE64_STANDARD.decode(&value.0)?;
156 let json_str = String::from_utf8(decoded)?;
157 let payment_required: PaymentRequired = serde_json::from_str(&json_str)?;
158 Ok(payment_required)
159 }
160}
161
162#[derive(Debug, Clone, Serialize, Deserialize)]
163#[serde(rename_all = "camelCase")]
164pub struct PaymentPayload {
165 pub x402_version: X402V2,
166 pub resource: PaymentResource,
167 pub accepted: PaymentRequirements,
168 pub payload: AnyJson,
169 pub extensions: Record<Extension>,
170}
171
172impl TryFrom<PaymentPayload> for Base64EncodedHeader {
173 type Error = crate::errors::Error;
174
175 fn try_from(value: PaymentPayload) -> Result<Self, Self::Error> {
177 let json = serde_json::to_string(&value)?;
178 let encoded = BASE64_STANDARD.encode(json);
179 Ok(Base64EncodedHeader(encoded))
180 }
181}
182
183impl TryFrom<Base64EncodedHeader> for PaymentPayload {
184 type Error = crate::errors::Error;
185
186 fn try_from(value: Base64EncodedHeader) -> Result<Self, Self::Error> {
188 let decoded_bytes = BASE64_STANDARD.decode(&value.0)?;
189 let json_str = String::from_utf8(decoded_bytes)?;
190 let payload = serde_json::from_str(&json_str)?;
191 Ok(payload)
192 }
193}
194
195#[derive(Debug, Clone, Serialize, Deserialize)]
196pub struct SettlementResponse {
197 pub success: bool,
198 pub transaction: String,
199 pub network: String,
200 pub payer: String,
201}
202
203impl TryFrom<SettlementResponse> for Base64EncodedHeader {
204 type Error = crate::errors::Error;
205
206 fn try_from(value: SettlementResponse) -> Result<Self, Self::Error> {
208 let json = serde_json::to_string(&value)?;
209 let encoded = BASE64_STANDARD.encode(json);
210 Ok(Base64EncodedHeader(encoded))
211 }
212}
213
214impl TryFrom<Base64EncodedHeader> for SettlementResponse {
215 type Error = crate::errors::Error;
216
217 fn try_from(value: Base64EncodedHeader) -> Result<Self, Self::Error> {
219 let decoded_bytes = BASE64_STANDARD.decode(&value.0)?;
220 let json_str = String::from_utf8(decoded_bytes)?;
221 let response = serde_json::from_str(&json_str)?;
222 Ok(response)
223 }
224}
225
226impl<S, A> From<Payment<S, A>> for PaymentRequirements
227where
228 S: Scheme,
229 A: Address<Network = S::Network>,
230{
231 fn from(payment: Payment<S, A>) -> Self {
232 PaymentRequirements {
233 scheme: S::SCHEME_NAME.to_string(),
234 network: payment.scheme.network().network_id().to_string(),
235 amount: payment.amount,
236 asset: payment.asset.address.to_string(),
237 pay_to: payment.pay_to.to_string(),
238 max_timeout_seconds: payment.max_timeout_seconds,
239 extra: payment.extra,
240 }
241 }
242}