@@ -14,18 +14,21 @@ use hyperswitch_domain_models::{
14
14
router_data:: { AccessToken , ErrorResponse , RouterData } ,
15
15
router_flow_types:: {
16
16
access_token_auth:: AccessTokenAuth ,
17
- payments:: { Authorize , Capture , PSync , PaymentMethodToken , Session , SetupMandate , Void } ,
17
+ payments:: {
18
+ Authorize , Capture , CreateConnectorCustomer , PSync , PaymentMethodToken , Session ,
19
+ SetupMandate , Void ,
20
+ } ,
18
21
refunds:: { Execute , RSync } ,
19
22
} ,
20
23
router_request_types:: {
21
- AccessTokenRequestData , PaymentMethodTokenizationData , PaymentsAuthorizeData ,
22
- PaymentsCancelData , PaymentsCaptureData , PaymentsSessionData , PaymentsSyncData ,
23
- RefundsData , SetupMandateRequestData ,
24
+ AccessTokenRequestData , ConnectorCustomerData , PaymentMethodTokenizationData ,
25
+ PaymentsAuthorizeData , PaymentsCancelData , PaymentsCaptureData , PaymentsSessionData ,
26
+ PaymentsSyncData , RefundsData , SetupMandateRequestData ,
24
27
} ,
25
28
router_response_types:: { PaymentsResponseData , RefundsResponseData } ,
26
29
types:: {
27
- PaymentsAuthorizeRouterData , PaymentsCaptureRouterData , PaymentsSyncRouterData ,
28
- RefundSyncRouterData , RefundsRouterData ,
30
+ ConnectorCustomerRouterData , PaymentsAuthorizeRouterData , PaymentsCaptureRouterData ,
31
+ PaymentsSyncRouterData , RefundSyncRouterData , RefundsRouterData ,
29
32
} ,
30
33
} ;
31
34
use hyperswitch_interfaces:: {
@@ -41,15 +44,14 @@ use hyperswitch_interfaces::{
41
44
} ;
42
45
use masking:: { Mask , PeekInterface } ;
43
46
use requests:: {
44
- FacilitapayAuthRequest , FacilitapayPaymentsRequest , FacilitapayRefundRequest ,
45
- FacilitapayRouterData ,
47
+ FacilitapayAuthRequest , FacilitapayCustomerRequest , FacilitapayPaymentsRequest ,
48
+ FacilitapayRefundRequest , FacilitapayRouterData ,
46
49
} ;
47
50
use responses:: {
48
- FacilitapayAuthResponse , FacilitapayErrorResponse , FacilitapayPaymentsResponse ,
49
- FacilitapayRefundResponse ,
51
+ FacilitapayAuthResponse , FacilitapayCustomerResponse , FacilitapayErrorResponse ,
52
+ FacilitapayPaymentsResponse , FacilitapayRefundResponse ,
50
53
} ;
51
54
52
- // use transformers as facilitapay;
53
55
use crate :: {
54
56
constants:: headers,
55
57
types:: { RefreshTokenRouterData , ResponseRouterData } ,
@@ -69,6 +71,7 @@ impl Facilitapay {
69
71
}
70
72
}
71
73
74
+ impl api:: ConnectorCustomer for Facilitapay { }
72
75
impl api:: Payment for Facilitapay { }
73
76
impl api:: PaymentSession for Facilitapay { }
74
77
impl api:: ConnectorAccessToken for Facilitapay { }
@@ -139,30 +142,220 @@ impl ConnectorCommon for Facilitapay {
139
142
res : Response ,
140
143
event_builder : Option < & mut ConnectorEvent > ,
141
144
) -> CustomResult < ErrorResponse , errors:: ConnectorError > {
142
- let response: FacilitapayErrorResponse = res
145
+ let status_code = res. status_code ;
146
+ // Keep the raw bytes in case the first parse fails
147
+ let response_body_bytes = res. response . clone ( ) ;
148
+
149
+ // First attempt to parse as FacilitapayErrorResponse (tries multiple formats)
150
+ match response_body_bytes
151
+ . parse_struct :: < FacilitapayErrorResponse > ( "FacilitapayErrorResponse" )
152
+ {
153
+ Ok ( error_response) => {
154
+ event_builder. map ( |i| i. set_response_body ( & error_response) ) ;
155
+ router_env:: info!( connector_response = ?error_response) ;
156
+
157
+ let ( code, message, reason) = match & error_response {
158
+ FacilitapayErrorResponse :: Simple ( simple) => (
159
+ consts:: NO_ERROR_CODE . to_string ( ) ,
160
+ simple. error . clone ( ) ,
161
+ Some ( simple. error . clone ( ) ) ,
162
+ ) ,
163
+ FacilitapayErrorResponse :: Structured ( field_errors) => {
164
+ let error_message = extract_error_message ( & field_errors. errors ) ;
165
+ (
166
+ consts:: NO_ERROR_CODE . to_string ( ) ,
167
+ error_message. clone ( ) ,
168
+ Some (
169
+ serde_json:: to_string ( & field_errors. errors )
170
+ . unwrap_or_else ( |_| error_message) ,
171
+ ) ,
172
+ )
173
+ }
174
+ FacilitapayErrorResponse :: GenericObject ( obj) => {
175
+ let error_message = extract_error_message ( & obj. 0 ) ;
176
+ (
177
+ consts:: NO_ERROR_CODE . to_string ( ) ,
178
+ error_message. clone ( ) ,
179
+ Some ( serde_json:: to_string ( & obj. 0 ) . unwrap_or_else ( |_| error_message) ) ,
180
+ )
181
+ }
182
+ FacilitapayErrorResponse :: PlainText ( text) => (
183
+ consts:: NO_ERROR_CODE . to_string ( ) ,
184
+ text. clone ( ) ,
185
+ Some ( text. clone ( ) ) ,
186
+ ) ,
187
+ } ;
188
+
189
+ Ok ( ErrorResponse {
190
+ status_code,
191
+ code,
192
+ message,
193
+ reason,
194
+ attempt_status : None ,
195
+ connector_transaction_id : None ,
196
+ network_advice_code : None ,
197
+ network_decline_code : None ,
198
+ network_error_message : None ,
199
+ } )
200
+ }
201
+ // If structured parsing fails, try as a plain string
202
+ Err ( json_error) => {
203
+ router_env:: warn!(
204
+ initial_parse_error = ?json_error,
205
+ "Failed to parse Facilitapay error as JSON object, attempting to parse as String"
206
+ ) ;
207
+
208
+ match response_body_bytes. parse_struct :: < String > ( "PlainTextError" ) {
209
+ Ok ( error_string) => {
210
+ event_builder. map ( |i| i. set_response_body ( & error_string) ) ;
211
+ router_env:: info!( connector_response = ?error_string) ;
212
+
213
+ Ok ( ErrorResponse {
214
+ status_code,
215
+ code : consts:: NO_ERROR_CODE . to_string ( ) ,
216
+ message : error_string. clone ( ) ,
217
+ reason : Some ( error_string) ,
218
+ attempt_status : None ,
219
+ connector_transaction_id : None ,
220
+ network_advice_code : None ,
221
+ network_decline_code : None ,
222
+ network_error_message : None ,
223
+ } )
224
+ }
225
+ Err ( string_error) => {
226
+ router_env:: error!(
227
+ string_parse_error = ?string_error,
228
+ original_json_error = ?json_error,
229
+ "Failed to parse Facilitapay error response as JSON structure or simple String"
230
+ ) ;
231
+
232
+ Err ( json_error)
233
+ . change_context ( errors:: ConnectorError :: ResponseDeserializationFailed )
234
+ }
235
+ }
236
+ }
237
+ }
238
+ }
239
+ }
240
+
241
+ // Helper function to extract error messages from JSON values
242
+ fn extract_error_message ( value : & serde_json:: Value ) -> String {
243
+ if let Some ( obj) = value. as_object ( ) {
244
+ let error_messages: Vec < String > = obj
245
+ . iter ( )
246
+ . flat_map ( |( field, error_val) | {
247
+ let field_name = field. clone ( ) ;
248
+
249
+ if let Some ( errors) = error_val. as_array ( ) {
250
+ errors
251
+ . iter ( )
252
+ . filter_map ( |e| e. as_str ( ) . map ( |s| format ! ( "{}: {}" , field_name, s) ) )
253
+ . collect :: < Vec < String > > ( )
254
+ } else if let Some ( error) = error_val. as_str ( ) {
255
+ vec ! [ format!( "{}: {}" , field_name, error) ]
256
+ } else {
257
+ vec ! [ format!( "{}: {}" , field_name, error_val) ]
258
+ }
259
+ } )
260
+ . collect ( ) ;
261
+
262
+ if !error_messages. is_empty ( ) {
263
+ error_messages. join ( ", " )
264
+ } else {
265
+ serde_json:: to_string ( value) . unwrap_or_else ( |_| consts:: NO_ERROR_MESSAGE . to_string ( ) )
266
+ }
267
+ } else {
268
+ serde_json:: to_string ( value) . unwrap_or_else ( |_| consts:: NO_ERROR_MESSAGE . to_string ( ) )
269
+ }
270
+ }
271
+
272
+ impl ConnectorIntegration < CreateConnectorCustomer , ConnectorCustomerData , PaymentsResponseData >
273
+ for Facilitapay
274
+ {
275
+ fn get_headers (
276
+ & self ,
277
+ req : & ConnectorCustomerRouterData ,
278
+ connectors : & Connectors ,
279
+ ) -> CustomResult < Vec < ( String , masking:: Maskable < String > ) > , errors:: ConnectorError > {
280
+ self . build_headers ( req, connectors)
281
+ }
282
+
283
+ fn get_content_type ( & self ) -> & ' static str {
284
+ self . common_get_content_type ( )
285
+ }
286
+
287
+ fn get_url (
288
+ & self ,
289
+ _req : & ConnectorCustomerRouterData ,
290
+ connectors : & Connectors ,
291
+ ) -> CustomResult < String , errors:: ConnectorError > {
292
+ Ok ( format ! (
293
+ "{}/{}/{}" ,
294
+ self . base_url( connectors) ,
295
+ "subject" ,
296
+ "people"
297
+ ) )
298
+ }
299
+
300
+ fn get_request_body (
301
+ & self ,
302
+ req : & ConnectorCustomerRouterData ,
303
+ _connectors : & Connectors ,
304
+ ) -> CustomResult < RequestContent , errors:: ConnectorError > {
305
+ let connector_req = FacilitapayCustomerRequest :: try_from ( req) ?;
306
+ Ok ( RequestContent :: Json ( Box :: new ( connector_req) ) )
307
+ }
308
+
309
+ fn build_request (
310
+ & self ,
311
+ req : & ConnectorCustomerRouterData ,
312
+ connectors : & Connectors ,
313
+ ) -> CustomResult < Option < Request > , errors:: ConnectorError > {
314
+ Ok ( Some (
315
+ RequestBuilder :: new ( )
316
+ . method ( Method :: Post )
317
+ . url ( & types:: ConnectorCustomerType :: get_url (
318
+ self , req, connectors,
319
+ ) ?)
320
+ . attach_default_headers ( )
321
+ . headers ( types:: ConnectorCustomerType :: get_headers (
322
+ self , req, connectors,
323
+ ) ?)
324
+ . set_body ( types:: ConnectorCustomerType :: get_request_body (
325
+ self , req, connectors,
326
+ ) ?)
327
+ . build ( ) ,
328
+ ) )
329
+ }
330
+
331
+ fn handle_response (
332
+ & self ,
333
+ data : & ConnectorCustomerRouterData ,
334
+ event_builder : Option < & mut ConnectorEvent > ,
335
+ res : Response ,
336
+ ) -> CustomResult < ConnectorCustomerRouterData , errors:: ConnectorError > {
337
+ let response: FacilitapayCustomerResponse = res
143
338
. response
144
- . parse_struct ( "FacilitapayErrorResponse " )
339
+ . parse_struct ( "FacilitapayCustomerResponse " )
145
340
. change_context ( errors:: ConnectorError :: ResponseDeserializationFailed ) ?;
146
341
147
342
event_builder. map ( |i| i. set_response_body ( & response) ) ;
148
343
router_env:: logger:: info!( connector_response=?response) ;
149
344
150
- let response_message = response
151
- . message
152
- . as_ref ( )
153
- . map_or_else ( || consts:: NO_ERROR_MESSAGE . to_string ( ) , ToString :: to_string) ;
154
-
155
- Ok ( ErrorResponse {
156
- status_code : res. status_code ,
157
- code : response. code ,
158
- message : response_message,
159
- reason : Some ( response. error ) ,
160
- attempt_status : None ,
161
- connector_transaction_id : None ,
162
- network_advice_code : None ,
163
- network_decline_code : None ,
164
- network_error_message : None ,
345
+ RouterData :: try_from ( ResponseRouterData {
346
+ response,
347
+ data : data. clone ( ) ,
348
+ http_code : res. status_code ,
165
349
} )
350
+ . change_context ( errors:: ConnectorError :: ResponseHandlingFailed )
351
+ }
352
+
353
+ fn get_error_response (
354
+ & self ,
355
+ res : Response ,
356
+ event_builder : Option < & mut ConnectorEvent > ,
357
+ ) -> CustomResult < ErrorResponse , errors:: ConnectorError > {
358
+ self . build_error_response ( res, event_builder)
166
359
}
167
360
}
168
361
@@ -175,10 +368,10 @@ impl ConnectorValidation for Facilitapay {
175
368
) -> CustomResult < ( ) , errors:: ConnectorError > {
176
369
let capture_method = capture_method. unwrap_or_default ( ) ;
177
370
match capture_method {
178
- enums:: CaptureMethod :: Automatic
179
- | enums:: CaptureMethod :: Manual
180
- | enums:: CaptureMethod :: SequentialAutomatic => Ok ( ( ) ) ,
181
- enums :: CaptureMethod :: ManualMultiple | enums:: CaptureMethod :: Scheduled => Err (
371
+ enums:: CaptureMethod :: Automatic | enums :: CaptureMethod :: SequentialAutomatic => Ok ( ( ) ) ,
372
+ enums:: CaptureMethod :: Manual
373
+ | enums:: CaptureMethod :: ManualMultiple
374
+ | enums:: CaptureMethod :: Scheduled => Err (
182
375
utils:: construct_not_implemented_error_report ( capture_method, self . id ( ) ) ,
183
376
) ,
184
377
}
0 commit comments