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:
2026-08-07 19:08:03 +08:00
parent 8726cf14e3
commit 0692bebf07
4 changed files with 164 additions and 44 deletions
@@ -1,6 +1,7 @@
import { Injectable } from '@nestjs/common';
import { PrismaService } from '../common/prisma/prisma.module';
import { CourierService } from '../integrations/courier/courier.service';
import { parseCourierCallbackBody } from '../integrations/courier/xiaofeixia/xiaofeixia-callback-body';
import { XiaofeixiaProvider } from '../integrations/courier/xiaofeixia/xiaofeixia.provider';
import { logCourierCall } from '../integrations/courier/courier-log.util';
import { TradeService } from '../modules/trade/trade.service';
@@ -54,7 +55,9 @@ export class DeliveryCallbackService {
return this.courier.buildTrackCallbackResponse(true);
}
const payload = this.xiaofeixiaProvider.parseTrackCallback(body);
const payload =
this.xiaofeixiaProvider.parseTrackCallback(body) ??
this.parseFromRawMeta(meta);
if (!payload) {
const response = this.courier.buildTrackCallbackResponse(false);
await logCourierCall(this.prisma, {
@@ -162,10 +165,17 @@ export class DeliveryCallbackService {
return out;
}
private parseFromRawMeta(meta?: TrackCallbackRequestMeta) {
if (!meta?.rawBody?.trim()) return null;
const parsed = parseCourierCallbackBody(meta.rawBody, meta.contentType || '');
return this.xiaofeixiaProvider.parseTrackCallback(parsed);
}
private redactSecrets(raw: string): string {
return raw
.replace(/(sign=)[^&\s]*/gi, '$1[REDACTED]')
.replace(/("sign"\s*:\s*")[^"]*/gi, '$1[REDACTED]')
.replace(/(api[_-]?key=)[^&\s]*/gi, '$1[REDACTED]');
.replace(/(api[_-]?key=)[^&\s]*/gi, '$1[REDACTED]')
.replace(/(name="sign"[\s\S]*?\r?\n\r?\n)([^\r\n-]+)/gi, '$1[REDACTED]');
}
}