fix: harden payment/shipping paths against partial-failure edge cases
Dispatched a read-only silent-failure audit (silent-failure-hunter agent) over the checkout/webhook/DHL/mail code paths, then verified each finding by hand before fixing. Four confirmed, real gaps: - Stripe webhook (fulfillOrder): Payment/Invoice creation and the confirmation email ran after the order's status flip to "BEZAHLT", outside any try/catch. If any of those three failed (DB blip), the handler threw uncaught -> Stripe retried -> the retry's status==="NEU" guard now found 0 rows and silently skipped the retry forever, leaving a paid order with no invoice, no payment record, and no confirmation email, with nothing logged. Fixed by allowing "BEZAHLT" as a valid retry entry point (idempotent upserts) and wrapping the post-transaction block in try/catch that logs loudly and re-throws (so Stripe still retries). - DHL label creation: the already-billed tracking number's DB write sat outside the try/catch that protects the (paid) DHL API call. A failed write there lost the tracking number with zero record of it, while the claim sentinel permanently blocked any retry through the UI. Fixed by wrapping the write, logging the tracking number for manual reconciliation, and deliberately leaving the claim in place (not resettable) to avoid triggering a second, separately billed label. - Checkout route: a DB write failure for the (currently unread, write-only) stripeCheckoutSessionId field discarded an already live, payable Stripe session and showed the customer a generic error, risking a duplicate order + stock reservation on retry. Made that specific write best-effort/non-fatal. - DHL fetch calls had no request timeout, so a hung (non-erroring) DHL API could block an admin action indefinitely. Added a 20s AbortSignal.timeout to both. Verified: tsc clean, 151/151 existing tests still pass. No dedicated DB-integration test harness exists yet for route/webhook handlers in this codebase (only pure-function unit tests) — building one is a separate, larger undertaking than this fix warrants. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
229a1dc435
commit
8b5d5d095c
4 files changed
+130
-65
No files matched your search
@@ -55,6 +55,12 @@ export class DhlApiError extends Error {
|
||||
// Konfiguration
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Ohne Timeout wuerde eine haengende (nicht fehlerhafte, sondern einfach nie
|
||||
// antwortende) DHL-API die aufrufende Admin-Server-Action unbegrenzt
|
||||
// blockieren und dabei den DHL_CLAIM_SENTINEL-Claim (siehe admin/actions/dhl.ts)
|
||||
// auf unbestimmte Zeit offen halten.
|
||||
const DHL_REQUEST_TIMEOUT_MS = 20_000;
|
||||
|
||||
interface DhlConfig {
|
||||
apiKey: string;
|
||||
apiSecret: string;
|
||||
@@ -124,6 +130,7 @@ async function fetchAccessToken(config: DhlConfig): Promise<string> {
|
||||
client_id: config.apiKey,
|
||||
client_secret: config.apiSecret,
|
||||
}),
|
||||
signal: AbortSignal.timeout(DHL_REQUEST_TIMEOUT_MS),
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : "Unbekannter Netzwerkfehler";
|
||||
@@ -291,6 +298,7 @@ export async function createDhlShipmentLabel(
|
||||
"dhl-api-key": config.apiKey,
|
||||
},
|
||||
body: JSON.stringify(requestBody),
|
||||
signal: AbortSignal.timeout(DHL_REQUEST_TIMEOUT_MS),
|
||||
});
|
||||
} catch (error: unknown) {
|
||||
const message = error instanceof Error ? error.message : "Unbekannter Netzwerkfehler";
|
||||
|
||||
Reference in new issue
Block a user