@@ -84,19 +84,106 @@ impl ResourceInfoBuilder {
8484/// Handles the full payment lifecycle: header extraction, verification,
8585/// settlement, and 402 response generation using the V2 wire format.
8686///
87+ /// Construct via [`PaygateBuilder`] (obtained from [`Paygate::builder`]).
88+ ///
8789/// To add lifecycle hooks (before/after verify and settle), wrap your
8890/// facilitator with [`HookedFacilitator`](r402::hooks::HookedFacilitator)
8991/// before passing it to the payment gate.
9092#[ allow( missing_debug_implementations) ]
9193pub struct Paygate < TFacilitator > {
92- /// The facilitator for verifying and settling payments
93- pub facilitator : TFacilitator ,
94- /// Whether to settle before or after request execution
95- pub settle_before_execution : bool ,
96- /// Accepted V2 payment requirements
97- pub accepts : Arc < Vec < v2:: PriceTag > > ,
98- /// Resource information for the protected endpoint
99- pub resource : v2:: ResourceInfo ,
94+ pub ( crate ) facilitator : TFacilitator ,
95+ pub ( crate ) settle_before_execution : bool ,
96+ pub ( crate ) accepts : Arc < Vec < v2:: PriceTag > > ,
97+ pub ( crate ) resource : v2:: ResourceInfo ,
98+ }
99+
100+ /// Builder for constructing a [`Paygate`] with validated configuration.
101+ ///
102+ /// # Example
103+ ///
104+ /// ```ignore
105+ /// let gate = Paygate::builder(facilitator)
106+ /// .accept(price_tag)
107+ /// .resource(resource_info)
108+ /// .settle_before_execution(true)
109+ /// .build();
110+ /// ```
111+ #[ allow( missing_debug_implementations) ]
112+ pub struct PaygateBuilder < TFacilitator > {
113+ facilitator : TFacilitator ,
114+ settle_before_execution : bool ,
115+ accepts : Vec < v2:: PriceTag > ,
116+ resource : Option < v2:: ResourceInfo > ,
117+ }
118+
119+ impl < TFacilitator > Paygate < TFacilitator > {
120+ /// Returns a new builder seeded with the given facilitator.
121+ pub const fn builder ( facilitator : TFacilitator ) -> PaygateBuilder < TFacilitator > {
122+ PaygateBuilder {
123+ facilitator,
124+ settle_before_execution : false ,
125+ accepts : Vec :: new ( ) ,
126+ resource : None ,
127+ }
128+ }
129+
130+ /// Returns a reference to the accepted price tags.
131+ pub fn accepts ( & self ) -> & [ v2:: PriceTag ] {
132+ & self . accepts
133+ }
134+
135+ /// Returns a reference to the resource information.
136+ pub const fn resource ( & self ) -> & v2:: ResourceInfo {
137+ & self . resource
138+ }
139+ }
140+
141+ impl < TFacilitator > PaygateBuilder < TFacilitator > {
142+ /// Adds a single accepted payment option.
143+ #[ must_use]
144+ pub fn accept ( mut self , price_tag : v2:: PriceTag ) -> Self {
145+ self . accepts . push ( price_tag) ;
146+ self
147+ }
148+
149+ /// Adds multiple accepted payment options.
150+ #[ must_use]
151+ pub fn accepts ( mut self , price_tags : impl IntoIterator < Item = v2:: PriceTag > ) -> Self {
152+ self . accepts . extend ( price_tags) ;
153+ self
154+ }
155+
156+ /// Sets the resource metadata returned in 402 responses.
157+ #[ must_use]
158+ pub fn resource ( mut self , resource : v2:: ResourceInfo ) -> Self {
159+ self . resource = Some ( resource) ;
160+ self
161+ }
162+
163+ /// Enables or disables settlement before request execution.
164+ ///
165+ /// Default is `false` (settle after execution).
166+ #[ must_use]
167+ pub const fn settle_before_execution ( mut self , enabled : bool ) -> Self {
168+ self . settle_before_execution = enabled;
169+ self
170+ }
171+
172+ /// Consumes the builder and produces a configured [`Paygate`].
173+ ///
174+ /// Uses empty resource info if none was provided.
175+ pub fn build ( self ) -> Paygate < TFacilitator > {
176+ Paygate {
177+ facilitator : self . facilitator ,
178+ settle_before_execution : self . settle_before_execution ,
179+ accepts : Arc :: new ( self . accepts ) ,
180+ resource : self . resource . unwrap_or_else ( || v2:: ResourceInfo {
181+ description : String :: new ( ) ,
182+ mime_type : "application/json" . to_owned ( ) ,
183+ url : String :: new ( ) ,
184+ } ) ,
185+ }
186+ }
100187}
101188
102189/// The V2 payment header name.
@@ -379,15 +466,13 @@ fn error_into_response(
379466 . expect ( "Fail to construct response" )
380467 }
381468 PaygateError :: Settlement ( ref err) => {
469+ #[ cfg( feature = "telemetry" ) ]
470+ tracing:: error!( details = %err, "Settlement failed" ) ;
382471 let body = Body :: from (
383- json ! ( {
384- "error" : "Settlement failed" ,
385- "details" : err
386- } )
387- . to_string ( ) ,
472+ json ! ( { "error" : "Settlement failed" } ) . to_string ( ) ,
388473 ) ;
389474 Response :: builder ( )
390- . status ( StatusCode :: PAYMENT_REQUIRED )
475+ . status ( StatusCode :: INTERNAL_SERVER_ERROR )
391476 . header ( "Content-Type" , "application/json" )
392477 . body ( body)
393478 . expect ( "Fail to construct response" )
0 commit comments