1use compact_str::CompactString;
4use r402_protocol::error::FacilitatorError;
5use r402_protocol::extension::{AdvertiseContext, ExtensionRegistry};
6use r402_protocol::network::ChainId;
7use r402_protocol::payment::{
8 Extensions, PaymentRequired, PaymentRequirements, ResourceInfo, SupportedResponse,
9};
10
11use crate::hooks::{
12 WirePaymentPayload, assert_accepts_additive_extra_after_scheme_enrich,
13 assert_accepts_allowlisted_after_extension_enrich, snapshot_payment_requirements_list,
14};
15use crate::payment_flow::apply_payment_flow_wire_extra;
16use crate::resource::ResourceServer;
17use crate::scheme::SchemePaymentRequiredContext;
18
19#[derive(Debug, Clone)]
21pub struct PaymentRequiredBuildContext {
22 pub resource: ResourceInfo,
24 pub error: Option<CompactString>,
26 pub extensions: Extensions,
28 pub supported: SupportedResponse,
30 pub payment_payload: Option<WirePaymentPayload>,
32}
33
34impl ResourceServer {
35 pub async fn create_payment_required_response(
46 &self,
47 accepts: Vec<PaymentRequirements>,
48 ctx: PaymentRequiredBuildContext,
49 ) -> Result<PaymentRequired, FacilitatorError> {
50 let PaymentRequiredBuildContext {
51 resource,
52 error,
53 extensions,
54 supported,
55 payment_payload,
56 } = ctx;
57 let mut response = PaymentRequired::new(resource)
58 .with_accepts(accepts)
59 .with_extensions(extensions);
60 if let Some(error) = error {
61 response = response.with_error(error);
62 }
63 self.apply_scheme_payment_required_enrich(
64 &mut response,
65 &supported,
66 payment_payload.as_ref(),
67 )
68 .await?;
69 advertise_registered_extensions(&self.extensions, &mut response).await?;
70 self.apply_payment_flow_extras(&mut response.accepts)?;
71 Ok(response)
72 }
73
74 fn apply_payment_flow_extras(
76 &self,
77 accepts: &mut [PaymentRequirements],
78 ) -> Result<(), FacilitatorError> {
79 for accept in accepts {
80 let resolved = self
81 .resolved_payment_flow(accept)
82 .map_err(FacilitatorError::internal)?;
83 let next = apply_payment_flow_wire_extra(accept.extra.as_ref(), &resolved);
84 accept.extra = if next.is_empty() {
85 None
86 } else {
87 Some(serde_json::Value::Object(next))
88 };
89 }
90 Ok(())
91 }
92
93 async fn apply_scheme_payment_required_enrich(
95 &self,
96 response: &mut PaymentRequired,
97 supported: &SupportedResponse,
98 payment_payload: Option<&WirePaymentPayload>,
99 ) -> Result<(), FacilitatorError> {
100 let targets: Vec<(CompactString, ChainId)> = response
101 .accepts
102 .iter()
103 .map(|accept| (accept.scheme.clone(), accept.network.clone()))
104 .collect();
105 let mut baseline = snapshot_payment_requirements_list(&response.accepts);
106 for (scheme_name, network) in targets {
107 let Some(scheme) = self.registered_scheme(scheme_name.as_str(), &network) else {
108 continue;
109 };
110 let network_label = network.to_string();
111 let enriched = {
112 let ctx = SchemePaymentRequiredContext {
113 requirements: &response.accepts,
114 payment_payload,
115 resource: &response.resource,
116 error: response.error.as_deref(),
117 payment_required_response: response,
118 supported,
119 };
120 scheme.enrich_payment_required_response(&ctx).await
121 };
122 if let Some(accepts) = enriched {
123 response.accepts = accepts;
124 }
125 assert_accepts_additive_extra_after_scheme_enrich(
126 &baseline,
127 &response.accepts,
128 scheme_name.as_str(),
129 network_label.as_str(),
130 )
131 .map_err(FacilitatorError::internal)?;
132 baseline = snapshot_payment_requirements_list(&response.accepts);
133 }
134 Ok(())
135 }
136}
137
138async fn advertise_registered_extensions(
139 registry: &ExtensionRegistry,
140 response: &mut PaymentRequired,
141) -> Result<(), FacilitatorError> {
142 for ext in registry.iter() {
143 let baseline = snapshot_payment_requirements_list(&response.accepts);
144 let existing = response.extensions.get(ext.id()).cloned();
145 let ctx = AdvertiseContext::for_payment_required(
146 &response.resource,
147 &response.accepts,
148 existing.as_ref(),
149 );
150 if existing.is_none()
151 && let Some(entry) = ext.advertise(&ctx)
152 {
153 response.extensions.insert(ext.id(), entry);
154 }
155 if let Some(entry) = ext.enrich_payment_required(&ctx).await {
156 response.extensions.insert(ext.id(), entry);
157 }
158 assert_accepts_allowlisted_after_extension_enrich(&baseline, &response.accepts, ext.id())
159 .map_err(FacilitatorError::internal)?;
160 }
161 Ok(())
162}