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 network: &network,
120 };
121 scheme.enrich_payment_required_response(&ctx).await
122 };
123 if let Some(accepts) = enriched {
124 response.accepts = accepts;
125 }
126 assert_accepts_additive_extra_after_scheme_enrich(
127 &baseline,
128 &response.accepts,
129 scheme_name.as_str(),
130 network_label.as_str(),
131 )
132 .map_err(FacilitatorError::internal)?;
133 baseline = snapshot_payment_requirements_list(&response.accepts);
134 }
135 Ok(())
136 }
137}
138
139async fn advertise_registered_extensions(
140 registry: &ExtensionRegistry,
141 response: &mut PaymentRequired,
142) -> Result<(), FacilitatorError> {
143 for ext in registry.iter() {
144 let baseline = snapshot_payment_requirements_list(&response.accepts);
145 let existing = response.extensions.get(ext.id()).cloned();
146 let ctx = AdvertiseContext::for_payment_required(
147 &response.resource,
148 &response.accepts,
149 existing.as_ref(),
150 );
151 if existing.is_none()
152 && let Some(entry) = ext.advertise(&ctx)
153 {
154 response.extensions.insert(ext.id(), entry);
155 }
156 if let Some(entry) = ext.enrich_payment_required(&ctx).await {
157 response.extensions.insert(ext.id(), entry);
158 }
159 assert_accepts_allowlisted_after_extension_enrich(&baseline, &response.accepts, ext.id())
160 .map_err(FacilitatorError::internal)?;
161 }
162 Ok(())
163}