1use std::fmt::{self, Debug, Formatter};
4use std::future::Future;
5
6use http::HeaderMap;
7use r402_protocol::{ClientError, PaymentRequired};
8
9use crate::hooks::BoxFuture;
10use crate::register::PaymentClient;
11use crate::select::PaymentSelector;
12
13pub trait ClientExtension: Send + Sync {
19 fn key(&self) -> &'static str;
21
22 fn enrich_payment_payload<'a>(
27 &'a self,
28 payload_b64: &'a str,
29 _payment_required: &'a PaymentRequired,
30 ) -> impl Future<Output = Result<String, ClientError>> + Send + 'a {
31 std::future::ready(Ok(payload_b64.to_owned()))
32 }
33
34 fn on_payment_required<'a>(
39 &'a self,
40 _payment_required: &'a PaymentRequired,
41 _request_url: &'a str,
42 ) -> impl Future<Output = HeaderMap> + Send + 'a {
43 std::future::ready(HeaderMap::new())
44 }
45}
46
47pub trait DynClientExtension: Send + Sync {
49 fn key(&self) -> &'static str;
51
52 fn enrich_payment_payload<'a>(
54 &'a self,
55 payload_b64: &'a str,
56 payment_required: &'a PaymentRequired,
57 ) -> BoxFuture<'a, Result<String, ClientError>>;
58
59 fn on_payment_required<'a>(
61 &'a self,
62 payment_required: &'a PaymentRequired,
63 request_url: &'a str,
64 ) -> BoxFuture<'a, HeaderMap>;
65}
66
67impl<T: ClientExtension + ?Sized> DynClientExtension for T {
68 fn key(&self) -> &'static str {
69 <Self as ClientExtension>::key(self)
70 }
71
72 fn enrich_payment_payload<'a>(
73 &'a self,
74 payload_b64: &'a str,
75 payment_required: &'a PaymentRequired,
76 ) -> BoxFuture<'a, Result<String, ClientError>> {
77 Box::pin(<Self as ClientExtension>::enrich_payment_payload(
78 self,
79 payload_b64,
80 payment_required,
81 ))
82 }
83
84 fn on_payment_required<'a>(
85 &'a self,
86 payment_required: &'a PaymentRequired,
87 request_url: &'a str,
88 ) -> BoxFuture<'a, HeaderMap> {
89 Box::pin(<Self as ClientExtension>::on_payment_required(
90 self,
91 payment_required,
92 request_url,
93 ))
94 }
95}
96
97impl Debug for dyn DynClientExtension {
98 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
99 f.write_str("DynClientExtension")
100 }
101}
102
103impl<S: PaymentSelector> PaymentClient<S> {
104 pub(crate) async fn enrich_signed_payload(
106 &self,
107 payload_b64: &str,
108 payment_required: &PaymentRequired,
109 ) -> Result<String, ClientError> {
110 let mut payload = payload_b64.to_owned();
111 for extension in &self.extensions {
112 payload = extension
113 .enrich_payment_payload(&payload, payment_required)
114 .await?;
115 }
116 Ok(payload)
117 }
118
119 pub async fn extension_headers(
125 &self,
126 payment_required: &PaymentRequired,
127 request_url: &str,
128 ) -> HeaderMap {
129 let mut headers = HeaderMap::new();
130 for extension in &self.extensions {
131 if payment_required.extensions.get(extension.key()).is_none() {
132 continue;
133 }
134 headers.extend(
135 extension
136 .on_payment_required(payment_required, request_url)
137 .await,
138 );
139 }
140 headers
141 }
142}