1use http::HeaderMap;
4use r402_extensions::{
5 EIP2612_GAS_SPONSORING_KEY, ERC20_APPROVAL_GAS_SPONSORING_KEY, Eip2612GasSponsoringExtension,
6 Erc20ApprovalGasSponsoringExtension,
7};
8use r402_facilitator::DynFacilitator;
9use r402_protocol::error::FacilitatorError;
10use r402_protocol::extension::{AdvertiseContext, Extension};
11use r402_protocol::payment::{
12 Base64Bytes, Extensions, PaymentRequired, PaymentRequirements, SettleResponse,
13 SettlementOverrides, SupportedResponse, VerifyResponse,
14};
15use r402_server::{
16 CancelReason, CompletedSettlement, PaymentFlowName, PaymentFlowPhases,
17 PaymentRequiredBuildContext, ResourceServer, SettlePhase, SkipHandlerDirective,
18 WirePaymentPayload, resolve_payment_flow_phases, validate_accepts_against_supported,
19};
20
21use super::fail::GateError;
22use super::gate::Gate;
23use crate::headers::PAYMENT_SIGNATURE;
24
25#[derive(Debug, Clone)]
27pub struct VerifiedPayment {
28 pub(crate) payload: WirePaymentPayload,
29 pub(crate) requirements: PaymentRequirements,
30 pub(crate) server: ResourceServer,
31 pub(crate) skip_handler: Option<SkipHandlerDirective>,
32 pub(crate) resource_url: compact_str::CompactString,
33 pub(crate) advertised: Extensions,
34}
35
36impl VerifiedPayment {
37 #[must_use]
39 pub fn cancellation_guard(&self) -> r402_server::CancellationGuard {
40 self.server
41 .cancellation_guard(self.payload.clone(), self.requirements.clone())
42 }
43
44 #[must_use]
46 pub const fn requirements(&self) -> &PaymentRequirements {
47 &self.requirements
48 }
49
50 pub(crate) async fn settle_phase(
51 &self,
52 phase: SettlePhase,
53 overrides: Option<&SettlementOverrides>,
54 ) -> Result<SettleResponse, FacilitatorError> {
55 self.server
56 .settle_payment(
57 &self.payload,
58 &self.requirements,
59 overrides,
60 phase,
61 Some(self.resource_url.as_str()),
62 Some(&self.advertised),
63 )
64 .await
65 }
66}
67
68impl Gate {
69 pub async fn build_payment_required(&mut self) -> Result<(), GateError> {
78 let facilitator = self.server.facilitator();
79 let supported = DynFacilitator::supported(facilitator.as_ref())
80 .await
81 .map_err(GateError::from_verify_facilitator)?;
82 let reqs: Vec<_> = self
83 .accepts
84 .iter()
85 .map(|pt| pt.requirements.clone())
86 .collect();
87 validate_accepts_against_supported(&self.server, &reqs, &supported)
88 .map_err(GateError::FacilitatorSupport)?;
89 let mut built = self
90 .server
91 .create_payment_required_response(
92 reqs,
93 PaymentRequiredBuildContext {
94 resource: self.resource.clone(),
95 error: None,
96 extensions: Extensions::new(),
97 supported: supported.clone(),
98 payment_payload: None,
99 },
100 )
101 .await
102 .map_err(|err| GateError::PaymentRequiredBuild(err.to_string()))?;
103 advertise_permit2_gas_sponsoring(&mut built, &supported, &self.server);
104 self.payment_required = Some(built);
105 Ok(())
106 }
107
108 pub async fn verify_only(&self, headers: &HeaderMap) -> Result<VerifiedPayment, GateError> {
114 let header_bytes = headers
115 .get(PAYMENT_SIGNATURE)
116 .map(http::HeaderValue::as_bytes)
117 .ok_or(GateError::PaymentHeaderMissing)?;
118
119 let payload =
120 decode_payment_payload(header_bytes).ok_or(GateError::InvalidPaymentHeader)?;
121
122 let payment_required: &PaymentRequired =
123 self.payment_required.as_ref().ok_or_else(|| {
124 GateError::PaymentRequiredBuild(
125 "payment-required response has not been built".into(),
126 )
127 })?;
128 let requirements = self
129 .server
130 .find_matching_requirements(&payment_required.accepts, &payload)
131 .cloned()
132 .ok_or(GateError::NoPaymentMatching)?;
133 let outcome = self
134 .server
135 .verify_payment(&payload, &requirements, Some(&payment_required.extensions))
136 .await
137 .map_err(GateError::from_verify_facilitator)?;
138
139 if let VerifyResponse::Invalid {
140 reason, message, ..
141 } = &outcome.response
142 {
143 return Err(GateError::from_invalid_verify(
144 reason.clone(),
145 message.as_deref(),
146 ));
147 }
148
149 Ok(VerifiedPayment {
150 payload,
151 requirements,
152 server: self.server.clone(),
153 skip_handler: outcome.skip_handler,
154 resource_url: payment_required.resource.url.clone(),
155 advertised: payment_required.extensions.clone(),
156 })
157 }
158}
159
160pub(crate) fn decode_payment_payload(header_bytes: &[u8]) -> Option<WirePaymentPayload> {
161 let decoded = Base64Bytes::from(header_bytes).decode().ok()?;
162 serde_json::from_slice(decoded.as_ref()).ok()
163}
164
165pub(crate) fn payment_flow_of(
166 server: &ResourceServer,
167 requirements: &PaymentRequirements,
168) -> Result<PaymentFlowName, GateError> {
169 server
170 .get_payment_flow(requirements)
171 .map_err(|err| match err {
172 r402_server::PaymentFlowError::UnregisteredScheme { scheme, .. } => {
173 GateError::MissingScheme {
174 scheme: scheme.into(),
175 network: requirements.network.clone(),
176 }
177 }
178 other => GateError::PaymentRequiredBuild(other.to_string()),
179 })
180}
181
182pub(crate) fn cancellation_guard(
183 verified: &VerifiedPayment,
184 before_handler: Option<&CompletedSettlement>,
185) -> r402_server::CancellationGuard {
186 let guard = verified.cancellation_guard();
187 match before_handler {
188 Some(completed) => guard
189 .with_settled_phases([SettlePhase::BeforeHandler])
190 .with_before_handler(completed.clone()),
191 None => guard,
192 }
193}
194
195pub(crate) async fn settle_before_handler_if_needed(
196 verified: &VerifiedPayment,
197 flow: PaymentFlowName,
198 phases: PaymentFlowPhases,
199) -> Result<Option<CompletedSettlement>, GateError> {
200 if !phases.settle_before_handler {
201 return Ok(None);
202 }
203 let result = map_settle(
204 verified
205 .settle_phase(SettlePhase::BeforeHandler, None)
206 .await,
207 )?;
208 Ok(Some(CompletedSettlement::new(
209 SettlePhase::BeforeHandler,
210 flow,
211 result,
212 verified.requirements().clone(),
213 )))
214}
215
216pub(crate) fn map_settle(
217 result: Result<SettleResponse, FacilitatorError>,
218) -> Result<SettleResponse, GateError> {
219 let settlement = result.map_err(GateError::from_settle_facilitator)?;
220 if matches!(settlement, SettleResponse::Failure { .. }) {
221 return Err(GateError::Settlement(Box::new(settlement)));
222 }
223 Ok(settlement)
224}
225
226pub(crate) fn phases_of(
227 verified: &VerifiedPayment,
228) -> Result<(PaymentFlowName, PaymentFlowPhases), GateError> {
229 let flow = payment_flow_of(&verified.server, verified.requirements())?;
230 Ok((flow, resolve_payment_flow_phases(flow)))
231}
232
233pub(crate) const HANDLER_FAILED: &str = "handler returned error status";
235
236pub(crate) const fn handler_failed_reason() -> CancelReason {
237 CancelReason::HandlerFailed
238}
239
240fn advertise_permit2_gas_sponsoring(
241 body: &mut PaymentRequired,
242 supported: &SupportedResponse,
243 server: &ResourceServer,
244) {
245 if !body.accepts.iter().any(|req| is_evm_permit2(req, server)) {
246 return;
247 }
248 if body.extensions.get(EIP2612_GAS_SPONSORING_KEY).is_none() {
249 let entry = Eip2612GasSponsoringExtension::new().advertise(
250 &AdvertiseContext::for_payment_required(&body.resource, &body.accepts, None),
251 );
252 if let Some(entry) = entry {
253 body.extensions.insert(EIP2612_GAS_SPONSORING_KEY, entry);
254 }
255 }
256 let erc20_listed = supported
257 .extensions
258 .iter()
259 .any(|e| e.as_str() == ERC20_APPROVAL_GAS_SPONSORING_KEY);
260 if erc20_listed
261 && body
262 .extensions
263 .get(ERC20_APPROVAL_GAS_SPONSORING_KEY)
264 .is_none()
265 {
266 let entry = Erc20ApprovalGasSponsoringExtension::new().advertise(
267 &AdvertiseContext::for_payment_required(&body.resource, &body.accepts, None),
268 );
269 if let Some(entry) = entry {
270 body.extensions
271 .insert(ERC20_APPROVAL_GAS_SPONSORING_KEY, entry);
272 }
273 }
274}
275
276fn is_evm_permit2(req: &PaymentRequirements, server: &ResourceServer) -> bool {
277 if extra_atm(req) == Some("permit2") {
278 return true;
279 }
280 server
281 .registered_scheme(req.scheme.as_str(), &req.network)
282 .is_some_and(|s| s.default_asset_transfer_method() == "permit2")
283}
284
285fn extra_atm(req: &PaymentRequirements) -> Option<&str> {
286 req.extra
287 .as_ref()
288 .and_then(|extra| extra.get("assetTransferMethod"))
289 .and_then(serde_json::Value::as_str)
290}