@@ -82,6 +82,27 @@ export interface BraintreeInitiatePaymentData {
8282}
8383
8484const buildTokenCacheKey = ( customerId : string ) => `braintree:clientToken:${ customerId } ` ;
85+ const UNKNOWN_BRAINTREE_ERROR = 'Unknown error' ;
86+
87+ type BraintreeValidationErrorLike = {
88+ attribute ?: string ;
89+ code ?: string ;
90+ message ?: string ;
91+ } ;
92+
93+ type BraintreeValidationErrorsCollectionLike = {
94+ deepErrors ?: ( ) => BraintreeValidationErrorLike [ ] ;
95+ } ;
96+
97+ type BraintreeErrorResponseLike = {
98+ message ?: string ;
99+ errors ?: BraintreeValidationErrorsCollectionLike ;
100+ transaction ?: {
101+ gatewayRejectionReason ?: string ;
102+ processorResponseCode ?: string ;
103+ processorResponseText ?: string ;
104+ } ;
105+ } ;
85106
86107// Type guard utilities for safe type validation
87108const validateString = ( value : unknown , fieldName : string ) : string => {
@@ -91,6 +112,38 @@ const validateString = (value: unknown, fieldName: string): string => {
91112 return value ;
92113} ;
93114
115+ const getBraintreeValidationErrors = (
116+ errors ?: BraintreeValidationErrorsCollectionLike ,
117+ ) : BraintreeValidationErrorLike [ ] => {
118+ if ( typeof errors ?. deepErrors !== 'function' ) return [ ] ;
119+ return errors . deepErrors ( ) . filter ( ( error ) : error is BraintreeValidationErrorLike => Boolean ( error ?. message ) ) ;
120+ } ;
121+
122+ const formatBraintreeValidationError = ( error : BraintreeValidationErrorLike ) : string => {
123+ const prefix = error . attribute ? `${ error . attribute } : ` : '' ;
124+ const suffix = error . code ? ` (${ error . code } )` : '' ;
125+ return `${ prefix } ${ error . message } ${ suffix } ` ;
126+ } ;
127+
128+ const getBraintreeErrorMessage = ( response : BraintreeErrorResponseLike ) : string => {
129+ const gatewayRejectionReason = response . transaction ?. gatewayRejectionReason ?. trim ( ) ;
130+ if ( gatewayRejectionReason ) return gatewayRejectionReason ;
131+
132+ const processorResponseText = response . transaction ?. processorResponseText ?. trim ( ) ;
133+ if ( processorResponseText ) {
134+ const processorResponseCode = response . transaction ?. processorResponseCode ?. trim ( ) ;
135+ return processorResponseCode ? `${ processorResponseText } (${ processorResponseCode } )` : processorResponseText ;
136+ }
137+
138+ const validationErrors = getBraintreeValidationErrors ( response . errors ) . map ( formatBraintreeValidationError ) ;
139+ if ( validationErrors . length ) return validationErrors . join ( '; ' ) ;
140+
141+ const message = response . message ?. trim ( ) ;
142+ if ( message ) return message ;
143+
144+ return UNKNOWN_BRAINTREE_ERROR ;
145+ } ;
146+
94147// Error handling utility that preserves full error context
95148export const buildBraintreeError = (
96149 error : unknown ,
@@ -99,9 +152,13 @@ export const buildBraintreeError = (
99152 context ?: Record < string , unknown > ,
100153) : MedusaError => {
101154 const errorMessage = error instanceof Error ? error . message : String ( error ) ;
155+ const contextSuffix = context ? ` ${ JSON . stringify ( context ) } ` : '' ;
102156
103157 // Preserve full error context in logging
104- logger . error ( `Braintree ${ operation } failed: ${ errorMessage } ` , error instanceof Error ? error : undefined ) ;
158+ logger . error (
159+ `Braintree ${ operation } failed: ${ errorMessage } ${ contextSuffix } ` ,
160+ error instanceof Error ? error : undefined ,
161+ ) ;
105162
106163 return new MedusaError ( MedusaError . Types . INVALID_DATA , `Failed to ${ operation } : ${ errorMessage } ` ) ;
107164} ;
@@ -551,21 +608,15 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
551608 const saleResponse = await this . gateway . transaction . sale ( transactionCreateRequest ) ;
552609
553610 if ( ! saleResponse . success ) {
554- this . logErrorDetail (
555- 'transaction.sale failed' ,
556- new Error ( saleResponse . transaction ?. gatewayRejectionReason ?? saleResponse . message ?? 'Unknown error' ) ,
557- {
558- transactionId : saleResponse . transaction ?. id ,
559- gatewayRejectionReason : saleResponse . transaction ?. gatewayRejectionReason ,
560- processorResponseCode : saleResponse . transaction ?. processorResponseCode ,
561- processorResponseText : saleResponse . transaction ?. processorResponseText ,
562- errors : saleResponse . errors ,
563- } ,
564- ) ;
565- throw new MedusaError (
566- MedusaError . Types . PAYMENT_AUTHORIZATION_ERROR ,
567- saleResponse . transaction ?. gatewayRejectionReason ?? saleResponse . message ?? 'Unknown error' ,
568- ) ;
611+ const errorMessage = getBraintreeErrorMessage ( saleResponse ) ;
612+ this . logErrorDetail ( 'transaction.sale failed' , new Error ( errorMessage ) , {
613+ transactionId : saleResponse . transaction ?. id ,
614+ gatewayRejectionReason : saleResponse . transaction ?. gatewayRejectionReason ,
615+ processorResponseCode : saleResponse . transaction ?. processorResponseCode ,
616+ processorResponseText : saleResponse . transaction ?. processorResponseText ,
617+ validationErrors : getBraintreeValidationErrors ( saleResponse . errors ) . map ( formatBraintreeValidationError ) ,
618+ } ) ;
619+ throw new MedusaError ( MedusaError . Types . PAYMENT_AUTHORIZATION_ERROR , errorMessage ) ;
569620 }
570621
571622 try {
0 commit comments