fix(courier): parse XFX multipart/form-data track callbacks
Xiaofeixia posts multipart form fields; previously mis-read as urlencoded empty payload. Parse multipart text parts and map into order status transitions. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -4,7 +4,6 @@ import { loadAppConfig } from '@dukang/shared-types';
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { json, urlencoded, type NextFunction, type Request, type Response } from 'express';
|
||||
import { parse as parseQueryString } from 'node:querystring';
|
||||
import { AppModule } from './app.module';
|
||||
import { HttpExceptionFilter } from './common/filters/http-exception.filter';
|
||||
import { ResponseInterceptor } from './common/interceptors/response.interceptor';
|
||||
@@ -12,6 +11,7 @@ import { LoggingInterceptor } from './common/logging/logging.interceptor';
|
||||
import { preloadSystemConfigEnv } from './common/system-config/system-config.env';
|
||||
import { AlertService } from './common/alert/alert.service';
|
||||
import { initSentryIfConfigured } from './integrations/sentry/sentry.bootstrap';
|
||||
import { parseCourierCallbackBody } from './integrations/courier/xiaofeixia/xiaofeixia-callback-body';
|
||||
|
||||
function isCourierTrackCallbackUrl(url?: string): boolean {
|
||||
if (!url) return false;
|
||||
@@ -22,33 +22,7 @@ function isCourierTrackCallbackUrl(url?: string): boolean {
|
||||
);
|
||||
}
|
||||
|
||||
function parseCourierCallbackBody(
|
||||
rawText: string,
|
||||
contentType: string,
|
||||
): Record<string, unknown> {
|
||||
const ct = contentType.toLowerCase();
|
||||
const trimmed = rawText.trim();
|
||||
if (!trimmed) return {};
|
||||
|
||||
if (ct.includes('application/json') || trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed) as unknown;
|
||||
return parsed && typeof parsed === 'object'
|
||||
? (parsed as Record<string, unknown>)
|
||||
: { value: parsed };
|
||||
} catch {
|
||||
return { _rawText: rawText.slice(0, 4000) };
|
||||
}
|
||||
}
|
||||
|
||||
if (ct.includes('application/x-www-form-urlencoded') || /[=&]/.test(trimmed)) {
|
||||
return parseQueryString(trimmed) as Record<string, unknown>;
|
||||
}
|
||||
|
||||
return { _rawText: rawText.slice(0, 4000) };
|
||||
}
|
||||
|
||||
/** 小飞侠回调:无论 Content-Type,先吃下 rawBody 再尽力解析(便于第三方日志排查) */
|
||||
/** 小飞侠回调:无论 Content-Type,先吃下 rawBody 再尽力解析(含 multipart/form-data) */
|
||||
function courierTrackRawBodyMiddleware(req: Request, _res: Response, next: NextFunction) {
|
||||
if (req.method !== 'POST' || !isCourierTrackCallbackUrl(req.originalUrl || req.url)) {
|
||||
return next();
|
||||
|
||||
Reference in New Issue
Block a user