Skip to content

Commit 8e36aea

Browse files
leomp12claude
andcommitted
fix(paypal): Enhanced webhook error handling and dispute status support
- Improve error handling with proper HTTP status code checks - Return 202 for 4xx errors to prevent webhook retries - Add CUSTOMER.DISPUTE.CREATED event support - Map dispute events to in_dispute status instead of refunded - Better logging for webhook event issues 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 314616b commit 8e36aea

File tree

2 files changed

+28
-2
lines changed

2 files changed

+28
-2
lines changed

packages/apps/paypal/src/paypal-events.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/* eslint-disable import/prefer-default-export */
22
import type { Orders } from '@cloudcommerce/types';
3+
import type { AxiosError } from 'axios';
34
import api from '@cloudcommerce/api';
45
import '@cloudcommerce/firebase/lib/init';
56
import * as functions from 'firebase-functions/v1';
@@ -51,7 +52,28 @@ export const paypal = {
5152
res.sendStatus(403);
5253
return;
5354
}
54-
const paypalEvent = await readPaypalWebhookEvent(eventId);
55+
let paypalEvent: Record<string, any>;
56+
try {
57+
paypalEvent = await readPaypalWebhookEvent(eventId);
58+
} catch (_err) {
59+
const err = _err as AxiosError;
60+
const statusCode = err.response?.status;
61+
if (statusCode) {
62+
if (statusCode === 404) {
63+
logger.warn(`PayPal webhook event not found for ${transactionCode}`, {
64+
body: req.body,
65+
});
66+
res.sendStatus(202);
67+
return;
68+
}
69+
if (statusCode >= 400 && statusCode < 500) {
70+
logger.error(err);
71+
res.sendStatus(202);
72+
return;
73+
}
74+
}
75+
throw err;
76+
}
5577
const paypalEventType = paypalEvent.event_type;
5678
logger.info(`PayPal event type ${paypalEventType} for ${transactionCode}`);
5779
let status: PaymentEntry['status'] = 'pending';
@@ -81,9 +103,12 @@ export const paypal = {
81103
case 'PAYMENT.CAPTURE.REVERSED':
82104
case 'PAYMENT.SALE.REFUNDED':
83105
case 'PAYMENT.SALE.REVERSED':
84-
case 'RISK.DISPUTE.CREATED':
85106
status = 'refunded';
86107
break;
108+
case 'RISK.DISPUTE.CREATED':
109+
case 'CUSTOMER.DISPUTE.CREATED':
110+
status = 'in_dispute';
111+
break;
87112
default:
88113
// Ignore unknow status
89114
return;

packages/apps/paypal/src/util/paypal-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ export const createPaypalWebhook = async () => {
173173
'PAYMENT.PAYOUTSBATCH.SUCCESS',
174174
'PAYMENT.PAYOUTSBATCH.DENIED',
175175
'RISK.DISPUTE.CREATED',
176+
'CUSTOMER.DISPUTE.CREATED',
176177
].map((name) => ({ name })),
177178
}).catch((_err) => {
178179
const err = _err as AxiosError<any>;

0 commit comments

Comments
 (0)