@@ -4,15 +4,15 @@ use common_utils::{pii::Email, request::Method, types::StringMajorUnit};
44use error_stack:: ResultExt ;
55use hyperswitch_domain_models:: {
66 payment_method_data:: { BankDebitData , BankRedirectData , PaymentMethodData , WalletData } ,
7- router_data:: { ConnectorAuthType , PaymentMethodToken , RouterData } ,
7+ router_data:: { ConnectorAuthType , ErrorResponse , PaymentMethodToken , RouterData } ,
88 router_request_types:: ResponseId ,
99 router_response_types:: {
1010 ConnectorCustomerResponseData , MandateReference , PaymentsResponseData , RedirectForm ,
1111 RefundsResponseData ,
1212 } ,
1313 types,
1414} ;
15- use hyperswitch_interfaces:: errors;
15+ use hyperswitch_interfaces:: { consts , errors} ;
1616use masking:: { ExposeInterface , Secret } ;
1717use serde:: { Deserialize , Serialize } ;
1818use url:: Url ;
@@ -563,6 +563,15 @@ pub struct MolliePaymentsResponse {
563563 pub links : Links ,
564564 pub mandate_id : Option < Secret < String > > ,
565565 pub payment_id : Option < String > ,
566+ pub details : Option < MolliePaymentDetails > ,
567+ }
568+
569+ /// Details object containing failure information for failed payments
570+ #[ derive( Debug , Deserialize , Serialize ) ]
571+ #[ serde( rename_all = "camelCase" ) ]
572+ pub struct MolliePaymentDetails {
573+ pub failure_reason : Option < String > ,
574+ pub failure_message : Option < String > ,
566575}
567576
568577#[ derive( Debug , Clone , Default , Serialize , Deserialize , PartialEq ) ]
@@ -670,6 +679,49 @@ impl<F, T> TryFrom<ResponseRouterData<F, MolliePaymentsResponse, T, PaymentsResp
670679 fn try_from (
671680 item : ResponseRouterData < F , MolliePaymentsResponse , T , PaymentsResponseData > ,
672681 ) -> Result < Self , Self :: Error > {
682+ let status = enums:: AttemptStatus :: from ( item. response . status . clone ( ) ) ;
683+
684+ // Handle failed payments: extract error details from the details object
685+ // Mollie returns 2xx but with status "failed" when payment fails after 3DS authentication
686+ if crate :: utils:: is_payment_failure ( status) {
687+ let ( failure_reason, failure_message) = item
688+ . response
689+ . details
690+ . as_ref ( )
691+ . map ( |details| {
692+ (
693+ details. failure_reason . clone ( ) ,
694+ details. failure_message . clone ( ) ,
695+ )
696+ } )
697+ . unwrap_or ( ( None , None ) ) ;
698+
699+ let error_code = failure_reason
700+ . clone ( )
701+ . unwrap_or_else ( || consts:: NO_ERROR_CODE . to_string ( ) ) ;
702+ let error_message = failure_message
703+ . clone ( )
704+ . unwrap_or_else ( || consts:: NO_ERROR_MESSAGE . to_string ( ) ) ;
705+
706+ return Ok ( Self {
707+ status,
708+ response : Err ( ErrorResponse {
709+ status_code : item. http_code ,
710+ code : error_code,
711+ message : error_message. clone ( ) ,
712+ reason : Some ( error_message) ,
713+ attempt_status : None ,
714+ connector_transaction_id : Some ( item. response . id ) ,
715+ network_advice_code : None ,
716+ network_decline_code : None ,
717+ network_error_message : None ,
718+ connector_metadata : None ,
719+ connector_response_reference_id : None ,
720+ } ) ,
721+ ..item. data
722+ } ) ;
723+ }
724+
673725 let url = item
674726 . response
675727 . links
@@ -687,7 +739,7 @@ impl<F, T> TryFrom<ResponseRouterData<F, MolliePaymentsResponse, T, PaymentsResp
687739 connector_mandate_request_reference_id : None ,
688740 } ) ;
689741 Ok ( Self {
690- status : enums :: AttemptStatus :: from ( item . response . status ) ,
742+ status,
691743 response : Ok ( PaymentsResponseData :: TransactionResponse {
692744 resource_id : ResponseId :: ConnectorTransactionId (
693745 item. response
0 commit comments