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