Skip to content

Commit cd56d4d

Browse files
committed
chore: misc dependency updates
feat: adding more logging around braintree failures
1 parent d86b6a7 commit cd56d4d

5 files changed

Lines changed: 1241 additions & 587 deletions

File tree

plugins/braintree-payment/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@lambdacurry/medusa-payment-braintree",
3-
"version": "0.0.22",
3+
"version": "0.0.23",
44
"description": "Braintree plugin for Medusa",
55
"author": "Lambda Curry (https://lambdacurry.dev)",
66
"license": "MIT",

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,41 @@ describe('BraintreeProviderService core behaviors', () => {
109109
expect(result.status).toBe('captured');
110110
});
111111

112+
it('authorizePayment surfaces validation errors when Braintree returns an unknown error message', async () => {
113+
const { service, gateway } = buildService();
114+
115+
const input = {
116+
data: {
117+
clientToken: 'ct',
118+
amount: 10,
119+
currency_code: 'USD',
120+
payment_method_nonce: 'fake-nonce',
121+
},
122+
context: {
123+
idempotency_key: 'idem_2',
124+
},
125+
} as any;
126+
127+
gateway.transaction.sale.mockResolvedValueOnce({
128+
success: false,
129+
message: '',
130+
errors: {
131+
deepErrors: () => [
132+
{
133+
attribute: 'postalCode',
134+
code: '81813',
135+
message: 'Postal code is invalid.',
136+
},
137+
],
138+
},
139+
transaction: {},
140+
});
141+
142+
await expect(service.authorizePayment(input)).rejects.toThrow(
143+
'Failed to create Braintree transaction: postalCode: Postal code is invalid. (81813)',
144+
);
145+
});
146+
112147
it('capturePayment submits for settlement when status is authorized', async () => {
113148
const { service, gateway } = buildService();
114149

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

Lines changed: 67 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,27 @@ export interface BraintreeInitiatePaymentData {
8282
}
8383

8484
const 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
87108
const 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
95148
export 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 {

plugins/product-reviews/package.json

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@lambdacurry/medusa-product-reviews",
3-
"version": "1.3.0",
3+
"version": "1.3.1",
44
"description": "Product Reviews Plugin for Medusa",
55
"author": "Lambda Curry (https://lambdacurry.dev)",
66
"license": "MIT",
@@ -44,19 +44,19 @@
4444
"typecheck": "tsc --noEmit"
4545
},
4646
"devDependencies": {
47-
"@medusajs/admin-sdk": "2.7.0",
48-
"@medusajs/cli": "2.7.0",
49-
"@medusajs/framework": "2.7.0",
50-
"@medusajs/icons": "2.7.0",
51-
"@medusajs/medusa": "2.7.0",
52-
"@medusajs/test-utils": "2.7.0",
53-
"@medusajs/ui": "^4.0.6",
47+
"@medusajs/admin-sdk": "2.13.1",
48+
"@medusajs/cli": "2.13.1",
49+
"@medusajs/framework": "2.13.1",
50+
"@medusajs/icons": "2.13.1",
51+
"@medusajs/medusa": "2.13.1",
52+
"@medusajs/test-utils": "2.13.1",
53+
"@medusajs/ui": "^4.1.1",
5454
"@mikro-orm/cli": "6.4.3",
5555
"@mikro-orm/core": "6.4.3",
5656
"@mikro-orm/knex": "6.4.3",
5757
"@mikro-orm/migrations": "6.4.3",
5858
"@mikro-orm/postgresql": "6.4.3",
59-
"@swc/core": "1.5.7",
59+
"@swc/core": "^1.15.13",
6060
"@types/express": "4.17.13",
6161
"@types/luxon": "^3",
6262
"@types/multer": "^1.4.12",
@@ -75,13 +75,13 @@
7575
"yalc": "^1.0.0-pre.53"
7676
},
7777
"peerDependencies": {
78-
"@medusajs/admin-sdk": "^2.7.0",
79-
"@medusajs/cli": "^2.7.0",
80-
"@medusajs/framework": "^2.7.0",
81-
"@medusajs/icons": "^2.7.0",
82-
"@medusajs/medusa": "^2.7.0",
83-
"@medusajs/test-utils": "^2.7.0",
84-
"@medusajs/ui": "4.0.3",
78+
"@medusajs/admin-sdk": "^2.13.1",
79+
"@medusajs/cli": "^2.13.1",
80+
"@medusajs/framework": "^2.13.1",
81+
"@medusajs/icons": "^2.13.1",
82+
"@medusajs/medusa": "^2.13.1",
83+
"@medusajs/test-utils": "^2.13.1",
84+
"@medusajs/ui": "^4.1.1",
8585
"@mikro-orm/cli": "6.4.3",
8686
"@mikro-orm/core": "6.4.3",
8787
"@mikro-orm/knex": "6.4.3",
@@ -99,9 +99,9 @@
9999
"dependencies": {
100100
"@hookform/resolvers": "3.4.2",
101101
"@lambdacurry/medusa-plugins-sdk": "0.0.5",
102-
"@medusajs/js-sdk": "^2.7.0",
103-
"@medusajs/workflows-sdk": "^2.7.0",
102+
"@medusajs/js-sdk": "^2.13.1",
103+
"@medusajs/workflows-sdk": "^2.13.1",
104104
"luxon": "^3.5.0",
105-
"zod": "3.22.4"
105+
"zod": "3.25.76"
106106
}
107107
}

0 commit comments

Comments
 (0)