Skip to content

Commit 87ad0e2

Browse files
committed
refactor: improve error handling and code formatting in Braintree integration
1 parent f80de76 commit 87ad0e2

1 file changed

Lines changed: 44 additions & 35 deletions

File tree

plugins/braintree-payment/src/providers/payment-braintree/src/core/braintree-base.ts

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,17 @@ const validateOptionalString = (value: unknown, fieldName: string): string | und
109109
};
110110

111111
// Error handling utility that preserves full error context
112-
export const buildBraintreeError = (error: unknown, operation: string, logger: Logger, context?: Record<string, unknown>): MedusaError => {
112+
export const buildBraintreeError = (
113+
error: unknown,
114+
operation: string,
115+
logger: Logger,
116+
context?: Record<string, unknown>,
117+
): MedusaError => {
113118
const errorMessage = error instanceof Error ? error.message : String(error);
114-
119+
115120
// Preserve full error context in logging
116121
logger.error(`Braintree ${operation} failed: ${errorMessage}`, error instanceof Error ? error : undefined);
117-
122+
118123
return new MedusaError(MedusaError.Types.INVALID_DATA, `Failed to ${operation}: ${errorMessage}`);
119124
};
120125

@@ -216,7 +221,6 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
216221
}
217222

218223
private formatToTwoDecimalStringIfFinite(amount: unknown): string | undefined {
219-
220224
const n = Number(amount);
221225
if (!Number.isFinite(n)) return undefined;
222226
return formatToTwoDecimalString(n);
@@ -337,10 +341,15 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
337341
if (!sessionData.payment_method_nonce)
338342
throw new MedusaError(MedusaError.Types.INVALID_ARGUMENT, 'Payment method nonce is required');
339343

340-
if (!transaction)
341-
transaction = await this.createTransaction({
342-
input,
343-
});
344+
if (!transaction) {
345+
try {
346+
transaction = await this.createTransaction({
347+
input,
348+
});
349+
} catch (error) {
350+
throw error;
351+
}
352+
}
344353

345354
const paymentStatusRequest: GetPaymentStatusInput = {
346355
...input,
@@ -363,7 +372,7 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
363372
};
364373
} catch (error) {
365374
this.logger.error(`Error authorizing transaction: ${error.message}`, error);
366-
throw new MedusaError(MedusaError.Types.INVALID_DATA, `Failed to authorize transaction`);
375+
throw new MedusaError(MedusaError.Types.INVALID_DATA, error.message ?? 'Unknown error');
367376
}
368377
}
369378

@@ -442,14 +451,14 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
442451
countryCodeAlpha2: this.sanitizeCountryCodeAlpha2(context.shipping_address?.country_code),
443452
}
444453
: undefined;
445-
let cart: CartDTO | undefined;
446-
if(context.items?.[0]?.cart_id) {
447-
cart = await this.cartService.retrieveCart(context.items?.[0]?.cart_id as string);
448-
if (!cart) {
449-
throw new MedusaError(MedusaError.Types.NOT_FOUND, 'Cart not found');
450-
}
451-
}
452-
454+
let cart: CartDTO | undefined;
455+
if (context.items?.[0]?.cart_id) {
456+
cart = await this.cartService.retrieveCart(context.items?.[0]?.cart_id as string);
457+
if (!cart) {
458+
throw new MedusaError(MedusaError.Types.NOT_FOUND, 'Cart not found');
459+
}
460+
}
461+
453462
const lineItems = context.items
454463
?.slice(0, 249)
455464
.map((item) => {
@@ -464,8 +473,8 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
464473
kind: 'debit',
465474
name: name.substring(0, 127), // Max 127 characters
466475
productCode: ((item.metadata?.productCode as string) ?? item.product_id).substring(0, 12), // Max 12 characters
467-
commodityCode: ((item.metadata?.commodityCode as string) ?? "placeholder").substring(0, 12), // Max 12 characters
468-
description: (item.product_description ?? "Healthcare").substring(0, 127), // Max 127 characters
476+
commodityCode: ((item.metadata?.commodityCode as string) ?? 'placeholder').substring(0, 12), // Max 12 characters
477+
description: (item.product_description ?? 'Healthcare').substring(0, 127), // Max 127 characters
469478
url: `/${item.product_handle}`.substring(0, 255), // Max 255 characters
470479
unitOfMeasure: ((cart?.currency_code as string)?.toUpperCase() ?? 'USD').substring(0, 12), // Max 12 characters
471480
taxAmount: this.formatToTwoDecimalStringIfFinite(Number(item.tax_total)), // Must be decimal string
@@ -552,14 +561,16 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
552561
const schema = z.object({
553562
paymentMethodNonce: z.string().optional(),
554563
payment_method_nonce: z.string().optional(),
555-
cardDetails:z.object({
556-
cardType: z.string().optional(),
557-
lastFour: z.string().optional(),
558-
lastTwo: z.string().optional(),
559-
expirationMonth: z.string().optional(),
560-
expirationYear: z.string().optional(),
561-
cardholderName: z.string().optional(),
562-
}).optional(),
564+
cardDetails: z
565+
.object({
566+
cardType: z.string().optional(),
567+
lastFour: z.string().optional(),
568+
lastTwo: z.string().optional(),
569+
expirationMonth: z.string().optional(),
570+
expirationYear: z.string().optional(),
571+
cardholderName: z.string().optional(),
572+
})
573+
.optional(),
563574
});
564575

565576
const result = schema.safeParse(data);
@@ -626,7 +637,7 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
626637
if (!saleResponse.success) {
627638
throw new MedusaError(
628639
MedusaError.Types.PAYMENT_AUTHORIZATION_ERROR,
629-
`Failed to create Braintree transaction: ${JSON.stringify(saleResponse)}`,
640+
saleResponse.transaction.gatewayRejectionReason ?? 'Unknown error',
630641
);
631642
}
632643

@@ -636,7 +647,9 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
636647
if (saleResponse.transaction?.id) {
637648
await this.gateway.transaction.void(saleResponse.transaction.id);
638649
}
639-
throw buildBraintreeError(error, 'sync payment session', this.logger, { transactionId: saleResponse.transaction?.id });
650+
throw buildBraintreeError(error, 'sync payment session', this.logger, {
651+
transactionId: saleResponse.transaction?.id,
652+
});
640653
}
641654
} catch (error) {
642655
throw buildBraintreeError(error, 'create Braintree transaction', this.logger);
@@ -660,7 +673,6 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
660673
} catch (e) {
661674
throw buildBraintreeError(e, 'delete Braintree payment', this.logger);
662675
}
663-
664676
} else {
665677
return {
666678
data: {
@@ -734,12 +746,12 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
734746

735747
let shouldVoid = ['submitted_for_settlement', 'authorized'].includes(transaction.status);
736748

737-
if(process.env.TEST_FORCE_SETTLED === 'true') {
749+
if (process.env.TEST_FORCE_SETTLED === 'true') {
738750
shouldVoid = false;
739751
await this.gateway.testing.settle(transaction.id);
740752
transaction = await this.retrieveTransaction(transaction.id);
741753
}
742-
754+
743755
if (shouldVoid) {
744756
const voidResponse = await this.gateway.transaction.void(transaction.id);
745757

@@ -785,8 +797,6 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
785797
`Failed to create Braintree refund: ${refundResponse.message}`,
786798
);
787799

788-
789-
790800
const refundResult: RefundPaymentOutput = {
791801
data: {
792802
...input.data,
@@ -896,7 +906,6 @@ class BraintreeBase extends AbstractPaymentProvider<BraintreeOptions> {
896906
} catch (e) {
897907
throw buildBraintreeError(e, 'delete account holder', this.logger);
898908
}
899-
900909
}
901910

902911
async getWebhookActionAndData(webhookData: ProviderWebhookPayload['payload']): Promise<WebhookActionResult> {

0 commit comments

Comments
 (0)