From 5f29e73205a283414a4893d6b6af40d85a335a27 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Wed, 8 Jul 2026 17:36:52 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=BF=AB=E6=89=8B=E9=94=80?= =?UTF-8?q?=E6=AF=81=E8=AF=B7=E6=B1=82=E9=AA=8C=E7=AD=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kuaishou-industry/crypto.test.ts | 65 +++++++++++++++++++ .../platforms/kuaishou-industry/crypto.ts | 22 ++++++- 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 apps/backend/src/services/platforms/kuaishou-industry/crypto.test.ts diff --git a/apps/backend/src/services/platforms/kuaishou-industry/crypto.test.ts b/apps/backend/src/services/platforms/kuaishou-industry/crypto.test.ts new file mode 100644 index 00000000..a5e9a4aa --- /dev/null +++ b/apps/backend/src/services/platforms/kuaishou-industry/crypto.test.ts @@ -0,0 +1,65 @@ +import test from 'node:test' +import assert from 'node:assert/strict' + +import { + buildKuaishouIndustrySignSource, + signKuaishouIndustryPayload, + verifyKuaishouIndustrySignature, +} from './crypto.js' + +const TEST_CONFIG = { signSecret: 'test-secret' } as any + +test('buildKuaishouIndustrySignSource keeps nested eticket fields for destroy-code payload', () => { + const source = buildKuaishouIndustrySignSource( + { + appkey: 'ks-test', + param: { + oid: '2618901686368642', + reason: 'USER_APPLY_REFUND', + etickets: [ + { + code: null, + goodsValue: 3800, + id: 'KSVW9JTD4FZM4VDHKBV', + num: 1, + }, + ], + }, + signMethod: 'MD5', + timestamp: 1783497998539, + version: '1', + }, + TEST_CONFIG, + ) + + assert.match( + source, + /param=\{"etickets":\[\{"code":null,"goodsValue":3800,"id":"KSVW9JTD4FZM4VDHKBV","num":1\}\],"oid":"2618901686368642","reason":"USER_APPLY_REFUND"\}/, + ) + assert.doesNotMatch(source, /"etickets":\[\{\}\]/) +}) + +test('verifyKuaishouIndustrySignature accepts nested object payload signed by the same canonical source', () => { + const payload = { + appkey: 'ks-test', + param: { + oid: '2618901686368642', + reason: 'USER_APPLY_REFUND', + etickets: [ + { + code: null, + goodsValue: 3800, + id: 'KSVW9JTD4FZM4VDHKBV', + num: 1, + }, + ], + }, + signMethod: 'MD5', + timestamp: 1783497998539, + version: '1', + } + + const sign = signKuaishouIndustryPayload(payload, 'MD5', TEST_CONFIG) + + assert.equal(verifyKuaishouIndustrySignature({ ...payload, sign }, TEST_CONFIG), true) +}) diff --git a/apps/backend/src/services/platforms/kuaishou-industry/crypto.ts b/apps/backend/src/services/platforms/kuaishou-industry/crypto.ts index a16cef20..7f7dd3d9 100644 --- a/apps/backend/src/services/platforms/kuaishou-industry/crypto.ts +++ b/apps/backend/src/services/platforms/kuaishou-industry/crypto.ts @@ -103,8 +103,28 @@ function stringifySignValue(value: unknown) { } if (typeof value === 'object') { - return JSON.stringify(value, Object.keys(value as Record).sort()) + return JSON.stringify(normalizeSignJsonValue(value)) } return String(value) } + +function normalizeSignJsonValue(value: unknown): unknown { + if (Array.isArray(value)) { + return value.map((item) => normalizeSignJsonValue(item)) + } + + if (value && typeof value === 'object') { + const record = value as Record + const normalized: Record = {} + for (const key of Object.keys(record).sort()) { + const current = normalizeSignJsonValue(record[key]) + if (current !== undefined) { + normalized[key] = current + } + } + return normalized + } + + return value +}