@@ -135,6 +135,24 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
135135 return token ;
136136 }
137137
138+ /** Logs to console when options.logging is true. Use for debugging. */
139+ protected logDebug ( message : string , context ?: Record < string , unknown > ) : void {
140+ if ( this . options_ . logging ) {
141+ const msg = context ? `${ message } ${ JSON . stringify ( context ) } ` : message ;
142+ this . logger . info ( `[Braintree] ${ msg } ` ) ;
143+ }
144+ }
145+
146+ /** When options.logging is true, logs error details to help debug vague failures. */
147+ protected logErrorDetail ( operation : string , error : unknown , context ?: Record < string , unknown > ) : void {
148+ if ( ! this . options_ . logging ) return ;
149+ const msg = error instanceof Error ? error . message : String ( error ) ;
150+ const stack = error instanceof Error ? error . stack : undefined ;
151+ const ctx = context ? ` ${ JSON . stringify ( context ) } ` : '' ;
152+ const stackLine = stack ? ` stack: ${ stack } ` : '' ;
153+ this . logger . info ( `[Braintree] ERROR ${ operation } : ${ msg } ${ ctx } ${ stackLine } ` ) ;
154+ }
155+
138156 async getValidClientToken (
139157 medusaCustomerId : string | undefined ,
140158 accountHolder : PaymentAccountHolderDTO | undefined ,
@@ -199,6 +217,8 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
199217 publicKey : this . options_ . publicKey ! ,
200218 privateKey : this . options_ . privateKey ! ,
201219 } ) ;
220+
221+ this . logDebug ( `Gateway initialized (environment: ${ envKey } )` ) ;
202222 }
203223
204224 static validateOptions ( options : BraintreeOptions ) : void {
@@ -225,8 +245,9 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
225245 options . savePaymentMethod = options . savePaymentMethod ?? false ;
226246 options . autoCapture = options . autoCapture ?? false ;
227247 options . allowRefundOnRefunded = options . allowRefundOnRefunded ?? false ;
248+ options . logging = options . logging ?? false ;
228249
229- const booleanFields = [ 'enable3DSecure' , 'savePaymentMethod' , 'autoCapture' , 'allowRefundOnRefunded' ] ;
250+ const booleanFields = [ 'enable3DSecure' , 'savePaymentMethod' , 'autoCapture' , 'allowRefundOnRefunded' , 'logging' ] ;
230251 for ( const field of booleanFields ) {
231252 if ( isDefined ( options [ field ] ) && typeof options [ field ] !== 'boolean' ) {
232253 throw new MedusaError (
@@ -241,6 +262,8 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
241262 const sessionData = await this . parsePaymentSessionData ( input . data ?? { } ) ;
242263 const transaction = sessionData . transaction ;
243264
265+ this . logDebug ( 'capturePayment' , { transactionId : transaction ?. id } ) ;
266+
244267 if ( ! transaction ) {
245268 throw new MedusaError ( MedusaError . Types . NOT_FOUND , 'Braintree transaction not found' ) ;
246269 }
@@ -287,6 +310,10 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
287310 }
288311
289312 async authorizePayment ( input : AuthorizePaymentInput ) : Promise < AuthorizePaymentOutput > {
313+ this . logDebug ( 'authorizePayment' , {
314+ amount : ( input . data as { amount ?: number } ) ?. amount ,
315+ currency_code : ( input . data as { currency_code ?: string } ) ?. currency_code ,
316+ } ) ;
290317 try {
291318 const sessionData = await this . parsePaymentSessionData ( input . data ?? { } ) ;
292319
@@ -321,13 +348,18 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
321348 status : finalStatus ,
322349 } ;
323350 } catch ( error ) {
324- this . logger . error ( `Error authorizing transaction: ${ error . message } ` , error ) ;
325- throw new MedusaError ( MedusaError . Types . INVALID_DATA , error . message ?? 'Unknown error' ) ;
351+ this . logErrorDetail ( 'authorizePayment' , error , {
352+ amount : ( input . data as { amount ?: number } ) ?. amount ,
353+ currency_code : ( input . data as { currency_code ?: string } ) ?. currency_code ,
354+ } ) ;
355+ this . logger . error ( `Error authorizing transaction: ${ ( error as Error ) . message } ` , error as Error ) ;
356+ throw new MedusaError ( MedusaError . Types . INVALID_DATA , ( error as Error ) . message ?? 'Unknown error' ) ;
326357 }
327358 }
328359
329360 async cancelPayment ( input : CancelPaymentInput ) : Promise < CancelPaymentOutput > {
330361 const sessionData = await this . parsePaymentSessionData ( input . data ?? { } ) ;
362+ this . logDebug ( 'cancelPayment' , { transactionId : sessionData . transaction ?. id } ) ;
331363 const transaction = await this . retrieveTransaction ( sessionData . transaction ?. id as string ) ;
332364
333365 if ( ! transaction ) return { } ;
@@ -461,6 +493,11 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
461493 }
462494
463495 async initiatePayment ( input : InitiatePaymentInput ) : Promise < InitiatePaymentOutput > {
496+ this . logDebug ( 'initiatePayment' , {
497+ amount : input . amount ,
498+ currency_code : input . currency_code ,
499+ idempotency_key : input . context ?. idempotency_key ,
500+ } ) ;
464501 const data = this . validateInitiatePaymentData ( input . data ?? { } ) ;
465502
466503 let transaction : Transaction | undefined ;
@@ -507,18 +544,36 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
507544 customer : input . context ?. customer ,
508545 } ) ;
509546 try {
547+ this . logDebug ( 'createTransaction (sale)' , {
548+ amount : transactionCreateRequest . amount ,
549+ orderId : _context ?. orderId ,
550+ } ) ;
510551 const saleResponse = await this . gateway . transaction . sale ( transactionCreateRequest ) ;
511552
512553 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+ ) ;
513565 throw new MedusaError (
514566 MedusaError . Types . PAYMENT_AUTHORIZATION_ERROR ,
515- saleResponse . transaction ?. gatewayRejectionReason ?? 'Unknown error' ,
567+ saleResponse . transaction ?. gatewayRejectionReason ?? saleResponse . message ?? 'Unknown error' ,
516568 ) ;
517569 }
518570
519571 try {
520572 return await this . retrieveTransaction ( saleResponse . transaction . id ) ;
521573 } catch ( error ) {
574+ this . logErrorDetail ( 'sync payment session (retrieveTransaction)' , error , {
575+ transactionId : saleResponse . transaction ?. id ,
576+ } ) ;
522577 if ( saleResponse . transaction ?. id ) {
523578 await this . gateway . transaction . void ( saleResponse . transaction . id ) ;
524579 }
@@ -527,13 +582,18 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
527582 } ) ;
528583 }
529584 } catch ( error ) {
585+ this . logErrorDetail ( 'create Braintree transaction' , error , {
586+ amount : transactionCreateRequest . amount ,
587+ orderId : _context ?. orderId ,
588+ } ) ;
530589 throw buildBraintreeError ( error , 'create Braintree transaction' , this . logger ) ;
531590 }
532591 }
533592
534593 async deletePayment ( input : DeletePaymentInput ) : Promise < DeletePaymentOutput > {
535594 const sessionData = await this . parsePaymentSessionData ( input . data ?? { } ) ;
536595 const transaction = sessionData . transaction ;
596+ this . logDebug ( 'deletePayment' , { transactionId : transaction ?. id } ) ;
537597
538598 if ( transaction ) {
539599 try {
@@ -546,6 +606,7 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
546606 } ,
547607 } ;
548608 } catch ( e ) {
609+ this . logErrorDetail ( 'delete Braintree payment' , e , { transactionId : transaction ?. id } ) ;
549610 throw buildBraintreeError ( e , 'delete Braintree payment' , this . logger ) ;
550611 }
551612 } else {
@@ -562,6 +623,7 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
562623 // Support both `data.transaction` and `data.braintreeTransaction` without requiring full session parsing
563624 const tx = ( input . data ?. transaction ?? input . data ?. braintreeTransaction ) as Transaction | undefined ;
564625 const id = tx ?. id as string | undefined ;
626+ this . logDebug ( 'getPaymentStatus' , { transactionId : id } ) ;
565627
566628 if ( ! id ) {
567629 return { status : PaymentSessionStatus . PENDING } ;
@@ -571,6 +633,7 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
571633 try {
572634 transaction = await this . gateway . transaction . find ( id ) ;
573635 } catch ( e ) {
636+ this . logErrorDetail ( 'getPaymentStatus (transaction.find)' , e , { transactionId : id } ) ;
574637 this . logger . warn ( 'received payment data from session not transaction data' ) ;
575638 throw e ;
576639 }
@@ -579,6 +642,9 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
579642 }
580643
581644 async savePaymentMethod ( input : SavePaymentMethodInput ) : Promise < SavePaymentMethodOutput > {
645+ this . logDebug ( 'savePaymentMethod' , {
646+ accountHolderId : input . context ?. account_holder ?. data ?. id ,
647+ } ) ;
582648 const sessionData = await this . parsePaymentSessionData ( input . data ?? { } ) ;
583649
584650 const braintreeCustomerId = validateString ( input . context ?. account_holder ?. data ?. id , 'Braintree customer ID' ) ;
@@ -595,6 +661,14 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
595661 } ) ;
596662
597663 if ( ! paymentMethodResult . success ) {
664+ this . logErrorDetail (
665+ 'savePaymentMethod (paymentMethod.create)' ,
666+ new Error ( JSON . stringify ( paymentMethodResult . errors ) ) ,
667+ {
668+ customerId : braintreeCustomerId ,
669+ errors : paymentMethodResult . errors ,
670+ } ,
671+ ) ;
598672 throw new MedusaError (
599673 MedusaError . Types . INVALID_DATA ,
600674 `Failed to save payment method: ${ JSON . stringify ( paymentMethodResult . errors ) } ` ,
@@ -611,6 +685,10 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
611685
612686 async refundPayment ( input : RefundPaymentInput ) : Promise < RefundPaymentOutput > {
613687 const sessionData = await this . parsePaymentSessionData ( input . data ?? { } ) ;
688+ this . logDebug ( 'refundPayment' , {
689+ transactionId : sessionData . transaction ?. id ,
690+ amount : input . amount ,
691+ } ) ;
614692
615693 const refundAmountBN = MathBN . convert ( input . amount , 2 ) ;
616694 const refundAmount = refundAmountBN . toNumber ( ) ;
@@ -631,8 +709,14 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
631709 const voidResponse = await this . gateway . transaction . void ( transaction . id ) ;
632710 const voidSucceeded = voidResponse . success ?? false ;
633711
634- if ( ! voidSucceeded )
712+ if ( ! voidSucceeded ) {
713+ this . logErrorDetail ( 'refundPayment (void)' , new Error ( voidResponse . message ?? 'Void failed' ) , {
714+ transactionId : transaction . id ,
715+ message : voidResponse . message ,
716+ errors : ( voidResponse as { errors ?: unknown } ) . errors ,
717+ } ) ;
635718 throw new MedusaError ( MedusaError . Types . PAYMENT_AUTHORIZATION_ERROR , 'Failed to void transaction' ) ;
719+ }
636720
637721 const voidedTransaction = voidResponse ?. transaction ?? ( await this . retrieveTransaction ( transaction . id ) ) ;
638722
@@ -673,11 +757,18 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
673757 const refundResponse = await this . gateway . transaction . refund ( transaction . id , refundAmountDecimal ) ;
674758
675759 const refundSucceeded = refundResponse . success ?? false ;
676- if ( ! refundSucceeded )
760+ if ( ! refundSucceeded ) {
761+ this . logErrorDetail ( 'refundPayment (refund)' , new Error ( refundResponse . message ?? 'Refund failed' ) , {
762+ transactionId : transaction . id ,
763+ refundAmount : refundAmountDecimal ,
764+ message : refundResponse . message ,
765+ errors : ( refundResponse as { errors ?: unknown } ) . errors ,
766+ } ) ;
677767 throw new MedusaError (
678768 MedusaError . Types . INVALID_DATA ,
679769 `Failed to create Braintree refund: ${ refundResponse . message } ` ,
680770 ) ;
771+ }
681772
682773 const refundTransaction = refundResponse . transaction ?? ( await this . retrieveTransaction ( transaction . id ) ) ;
683774
@@ -690,6 +781,10 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
690781 } ;
691782 return refundResult ;
692783 } catch ( e ) {
784+ this . logErrorDetail ( 'create Braintree refund' , e , {
785+ transactionId : transaction . id ,
786+ refundAmount : refundAmountDecimal ,
787+ } ) ;
693788 throw buildBraintreeError ( e , 'create Braintree refund' , this . logger ) ;
694789 }
695790 }
@@ -699,6 +794,7 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
699794
700795 async retrievePayment ( input : RetrievePaymentInput ) : Promise < RetrievePaymentOutput > {
701796 const paymentSessionData = await this . parsePaymentSessionData ( input . data ?? { } ) ;
797+ this . logDebug ( 'retrievePayment' , { transactionId : paymentSessionData . transaction ?. id } ) ;
702798
703799 if ( ! paymentSessionData . transaction ?. id ) {
704800 throw new MedusaError ( MedusaError . Types . NOT_FOUND , 'Braintree transaction not found' ) ;
@@ -715,6 +811,7 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
715811 }
716812
717813 async updatePayment ( input : UpdatePaymentInput ) : Promise < UpdatePaymentOutput > {
814+ this . logDebug ( 'updatePayment' , { amount : input . amount , currency_code : input . currency_code } ) ;
718815 return Promise . resolve ( {
719816 data : {
720817 ...input . data ,
@@ -725,6 +822,7 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
725822 }
726823
727824 async createAccountHolder ( input : CreateAccountHolderInput ) : Promise < CreateAccountHolderOutput > {
825+ this . logDebug ( 'createAccountHolder' , { customerEmail : input . context . customer ?. email } ) ;
728826 const customer = await this . createBraintreeCustomer ( input . context . customer ) ;
729827
730828 return {
@@ -738,6 +836,7 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
738836 async updateAccountHolder ( input : UpdateAccountHolderInput ) : Promise < UpdateAccountHolderOutput > {
739837 const { context } = input ;
740838 const accountHolderId = context . account_holder ?. data ?. id as string ;
839+ this . logDebug ( 'updateAccountHolder' , { accountHolderId } ) ;
741840 if ( ! accountHolderId ) {
742841 throw new MedusaError ( MedusaError . Types . INVALID_DATA , `Account holder id is required` ) ;
743842 }
@@ -756,6 +855,10 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
756855 const updateResult = await this . gateway . customer . update ( accountHolder . id , customerUpdateRequest ) ;
757856
758857 if ( ! updateResult . success ) {
858+ this . logErrorDetail ( 'updateAccountHolder (customer.update)' , new Error ( JSON . stringify ( updateResult . errors ) ) , {
859+ accountHolderId,
860+ errors : updateResult . errors ,
861+ } ) ;
759862 throw new MedusaError (
760863 MedusaError . Types . INVALID_DATA ,
761864 `Failed to update account holder: ${ JSON . stringify ( updateResult . errors ) } ` ,
@@ -766,6 +869,7 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
766869 data : { ...updateResult . customer } ,
767870 } ;
768871 } catch ( e ) {
872+ this . logErrorDetail ( 'update account holder' , e , { accountHolderId } ) ;
769873 throw buildBraintreeError ( e , 'update account holder' , this . logger ) ;
770874 }
771875 }
@@ -774,6 +878,7 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
774878 const { context } = input ;
775879
776880 const accountHolderId = context . account_holder ?. data ?. id as string ;
881+ this . logDebug ( 'deleteAccountHolder' , { accountHolderId } ) ;
777882
778883 if ( ! accountHolderId ) throw new MedusaError ( MedusaError . Types . INVALID_DATA , `Account holder id is required` ) ;
779884
@@ -789,13 +894,15 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
789894 data : { } ,
790895 } ;
791896 } catch ( e ) {
897+ this . logErrorDetail ( 'delete account holder' , e , { accountHolderId } ) ;
792898 throw buildBraintreeError ( e , 'delete account holder' , this . logger ) ;
793899 }
794900 }
795901
796902 async getWebhookActionAndData ( webhookData : ProviderWebhookPayload [ 'payload' ] ) : Promise < WebhookActionResult > {
797903 const logger = this . logger ;
798904
905+ this . logDebug ( 'getWebhookActionAndData' , { hasData : ! ! webhookData ?. data } ) ;
799906 logger . info ( `Received Braintree webhook with data: ${ ! ! webhookData . data } ` ) ;
800907
801908 const decodedPayload = new URLSearchParams ( webhookData . data as unknown as string ) ;
@@ -811,6 +918,7 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
811918 return { action : PaymentActions . FAILED } ;
812919 }
813920 } catch ( error ) {
921+ this . logErrorDetail ( 'webhook validation' , error , { hasPayload : ! ! webhookData ?. data } ) ;
814922 logger . error ( `Braintree webhook validation failed : ${ error } ` ) ;
815923
816924 return { action : PaymentActions . FAILED } ;
@@ -853,6 +961,10 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
853961 } ) ;
854962
855963 if ( ! customerResult . success ) {
964+ this . logErrorDetail ( 'createBraintreeCustomer' , new Error ( JSON . stringify ( customerResult . errors ) ) , {
965+ email : customer . email ,
966+ errors : customerResult . errors ,
967+ } ) ;
856968 throw new MedusaError (
857969 MedusaError . Types . INVALID_DATA ,
858970 `Failed to create Braintree customer: ${ JSON . stringify ( customerResult . errors ) } ` ,
0 commit comments