删除咸鱼旧链路
This commit is contained in:
@@ -1,31 +0,0 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
import { extractAgisoTradePayload, resolveAgisoTradePlatformOrderId } from './agiso-trade-parsing.js'
|
||||
|
||||
test('extractAgisoTradePayload preserves large integer order ids from raw json', () => {
|
||||
const payload = extractAgisoTradePayload({
|
||||
json: '{"biz_order_id":4502259793206016214,"item_id":978102431355,"order_status":4}',
|
||||
})
|
||||
|
||||
assert.equal(payload.biz_order_id, '4502259793206016214')
|
||||
assert.equal(typeof payload.biz_order_id, 'string')
|
||||
})
|
||||
|
||||
test('resolveAgisoTradePlatformOrderId keeps exact webhook order id', () => {
|
||||
const payload = extractAgisoTradePayload({
|
||||
json: '{"biz_order_id":4502259793206016214,"item_id":978102431355,"order_status":4}',
|
||||
})
|
||||
|
||||
assert.equal(resolveAgisoTradePlatformOrderId(payload), '4502259793206016214')
|
||||
})
|
||||
|
||||
test('resolveAgisoTradePlatformOrderId falls back to tid when biz_order_id is absent', () => {
|
||||
assert.equal(
|
||||
resolveAgisoTradePlatformOrderId({
|
||||
tid: '4502251008019011224',
|
||||
orders: [{ oid: '4502251008019011888' }],
|
||||
}),
|
||||
'4502251008019011224',
|
||||
)
|
||||
})
|
||||
@@ -1,73 +0,0 @@
|
||||
import { parseJsonObject } from '../../utils/json.js'
|
||||
|
||||
type JsonObject = Record<string, unknown>
|
||||
|
||||
export function extractAgisoTradePayload(body: unknown): JsonObject {
|
||||
const normalizedBody = normalizeRecord(body)
|
||||
const rawJson = String(normalizedBody.json || normalizedBody.JSON || '').trim()
|
||||
|
||||
if (rawJson) {
|
||||
return normalizeRecord(parseJsonObject(rawJson, { preserveLargeIntegers: true }))
|
||||
}
|
||||
|
||||
return normalizedBody
|
||||
}
|
||||
|
||||
export function resolveAgisoTradePlatformOrderId(payload: unknown): string {
|
||||
const normalizedPayload = normalizeRecord(payload)
|
||||
const firstItem = extractAgisoTradeOrderItemSources(normalizedPayload)[0] || {}
|
||||
|
||||
return pickFirstNonEmpty([
|
||||
normalizedPayload.biz_order_id,
|
||||
normalizedPayload.Tid,
|
||||
normalizedPayload.tid,
|
||||
normalizedPayload.Oid,
|
||||
normalizedPayload.oid,
|
||||
normalizedPayload.order_id,
|
||||
normalizedPayload.orderId,
|
||||
firstItem.Oid,
|
||||
firstItem.oid,
|
||||
])
|
||||
}
|
||||
|
||||
export function extractAgisoTradeOrderItemSources(payload: unknown): JsonObject[] {
|
||||
const normalizedPayload = normalizeRecord(payload)
|
||||
const candidates = [
|
||||
normalizedPayload.items,
|
||||
normalizedPayload.Items,
|
||||
normalizedPayload.orders,
|
||||
normalizedPayload.Orders,
|
||||
normalizedPayload.order_list,
|
||||
normalizedPayload.OrderList,
|
||||
]
|
||||
|
||||
for (const current of candidates) {
|
||||
if (Array.isArray(current) && current.length > 0) {
|
||||
return current.map((item) => normalizeRecord(item)).filter((item) => Object.keys(item).length > 0)
|
||||
}
|
||||
}
|
||||
|
||||
if (Object.keys(normalizedPayload).length > 0) {
|
||||
return [normalizedPayload]
|
||||
}
|
||||
|
||||
return []
|
||||
}
|
||||
|
||||
function normalizeRecord(value: unknown): JsonObject {
|
||||
return value && typeof value === 'object' && !Array.isArray(value) ? value as JsonObject : {}
|
||||
}
|
||||
|
||||
function pickFirstNonEmpty(values: unknown[]): string {
|
||||
for (const value of values) {
|
||||
if (typeof value === 'string' && value.trim()) {
|
||||
return value.trim()
|
||||
}
|
||||
|
||||
if (typeof value === 'number' && Number.isFinite(value)) {
|
||||
return String(value)
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
|
||||
import {
|
||||
filterLegacyOrderFulfillmentBindings,
|
||||
isKuaishouCloudFulfillmentBinding,
|
||||
} from './fulfillment-binding-config-service.js'
|
||||
|
||||
test('isKuaishouCloudFulfillmentBinding only marks 91kaquan kuaishou rules as cloud rules', () => {
|
||||
assert.equal(
|
||||
isKuaishouCloudFulfillmentBinding({
|
||||
provider: '91kaquan',
|
||||
platform: 'kuaishou',
|
||||
}),
|
||||
true,
|
||||
)
|
||||
|
||||
assert.equal(
|
||||
isKuaishouCloudFulfillmentBinding({
|
||||
provider: 'agiso',
|
||||
platform: 'xianyu',
|
||||
}),
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
test('filterLegacyOrderFulfillmentBindings keeps agiso rules and removes 91kaquan kuaishou rules', () => {
|
||||
assert.deepEqual(
|
||||
filterLegacyOrderFulfillmentBindings([
|
||||
{
|
||||
provider: 'agiso',
|
||||
platform: 'xianyu',
|
||||
shopId: 'shop-1',
|
||||
skuCode: 'sku-a',
|
||||
},
|
||||
{
|
||||
provider: '91kaquan',
|
||||
platform: 'kuaishou',
|
||||
shopId: '91kaquan',
|
||||
skuCode: 'sku-b',
|
||||
},
|
||||
]),
|
||||
[
|
||||
{
|
||||
provider: 'agiso',
|
||||
platform: 'xianyu',
|
||||
shopId: 'shop-1',
|
||||
skuCode: 'sku-a',
|
||||
},
|
||||
],
|
||||
)
|
||||
})
|
||||
@@ -1,129 +0,0 @@
|
||||
import path from 'node:path'
|
||||
|
||||
import { PROJECT_ROOT } from '../../config/runtime.js'
|
||||
import { readJsonFile, writeJsonFile } from '../../utils/json-file-store.js'
|
||||
|
||||
const ORDER_FULFILLMENT_BINDINGS_FILE_PATH = path.join(PROJECT_ROOT, 'data', 'order-fulfillment-bindings.json')
|
||||
|
||||
type JsonObject = Record<string, any>
|
||||
|
||||
export function getOrderFulfillmentBindingsFilePath() {
|
||||
return ORDER_FULFILLMENT_BINDINGS_FILE_PATH
|
||||
}
|
||||
|
||||
export function getOrderFulfillmentBindingConfigs() {
|
||||
return loadOrderFulfillmentBindingConfigsFromFile()
|
||||
}
|
||||
|
||||
export function getLegacyOrderFulfillmentBindingConfigs() {
|
||||
return filterLegacyOrderFulfillmentBindings(getOrderFulfillmentBindingConfigs())
|
||||
}
|
||||
|
||||
export function saveOrderFulfillmentBindingConfigs(rawValue) {
|
||||
return writeJsonFile(
|
||||
ORDER_FULFILLMENT_BINDINGS_FILE_PATH,
|
||||
rawValue,
|
||||
normalizeOrderFulfillmentBindingConfigs,
|
||||
)
|
||||
}
|
||||
|
||||
function loadOrderFulfillmentBindingConfigsFromFile() {
|
||||
return readJsonFile(
|
||||
ORDER_FULFILLMENT_BINDINGS_FILE_PATH,
|
||||
[],
|
||||
normalizeOrderFulfillmentBindingConfigs,
|
||||
)
|
||||
}
|
||||
|
||||
function normalizeOrderFulfillmentBindingConfigs(rawValue) {
|
||||
if (!Array.isArray(rawValue)) {
|
||||
return []
|
||||
}
|
||||
|
||||
return rawValue
|
||||
.map((item) => normalizeOrderFulfillmentBinding(item))
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
export function filterLegacyOrderFulfillmentBindings(bindings: any[] = []) {
|
||||
return (Array.isArray(bindings) ? bindings : []).filter((item) => !isKuaishouCloudFulfillmentBinding(item))
|
||||
}
|
||||
|
||||
export function isKuaishouCloudFulfillmentBinding(binding: JsonObject = {}) {
|
||||
return String(binding?.provider || '').trim() === '91kaquan'
|
||||
&& String(binding?.platform || '').trim() === 'kuaishou'
|
||||
}
|
||||
|
||||
function normalizeOrderFulfillmentBinding(rawValue) {
|
||||
if (!isPlainObject(rawValue)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const provider = String(rawValue.provider || 'agiso').trim() || 'agiso'
|
||||
const platform = String(rawValue.platform || '').trim()
|
||||
const shopId = String(rawValue.shopId || '').trim()
|
||||
const shopName = String(rawValue.shopName || '').trim()
|
||||
const skuCode = String(rawValue.skuCode || '').trim()
|
||||
const skuName = String(rawValue.skuName || '').trim()
|
||||
const profileKey = String(rawValue.profileKey || '').trim() || 'manual_review'
|
||||
const priority = normalizePriority(rawValue.priority)
|
||||
const enabled = rawValue.enabled !== false
|
||||
const config = normalizeJsonObject(rawValue.config)
|
||||
const match = normalizeOrderFulfillmentMatch(rawValue.match)
|
||||
|
||||
if (!skuCode || !match) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
provider,
|
||||
platform,
|
||||
shopId,
|
||||
shopName,
|
||||
skuCode,
|
||||
skuName,
|
||||
profileKey,
|
||||
enabled,
|
||||
priority,
|
||||
config,
|
||||
match,
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeOrderFulfillmentMatch(rawValue) {
|
||||
if (!isPlainObject(rawValue)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const externalSkuCode = String(rawValue.externalSkuCode || '').trim()
|
||||
const externalItemId = String(rawValue.externalItemId || '').trim()
|
||||
const externalSkuName = String(rawValue.externalSkuName || '').trim()
|
||||
|
||||
if (!externalSkuCode && !externalItemId && !externalSkuName) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
externalSkuCode,
|
||||
externalItemId,
|
||||
externalSkuName,
|
||||
config: normalizeJsonObject(rawValue.config),
|
||||
}
|
||||
}
|
||||
|
||||
function normalizePriority(value) {
|
||||
const parsed = Number(value)
|
||||
if (!Number.isFinite(parsed)) {
|
||||
return 100
|
||||
}
|
||||
|
||||
return Math.max(1, Math.round(parsed))
|
||||
}
|
||||
|
||||
function normalizeJsonObject(value) {
|
||||
return isPlainObject(value) ? value : {}
|
||||
}
|
||||
|
||||
function isPlainObject(value) {
|
||||
return Object.prototype.toString.call(value) === '[object Object]'
|
||||
}
|
||||
@@ -1,387 +0,0 @@
|
||||
import test from 'node:test'
|
||||
import assert from 'node:assert/strict'
|
||||
import crypto from 'node:crypto'
|
||||
|
||||
import { runtimeConfig } from '../../config/runtime.js'
|
||||
import {
|
||||
buildWebhookEventInput,
|
||||
executeAgisoTradeWebhookWithDeps,
|
||||
parseAgisoTradeRequest,
|
||||
resolveAgisoTradeIgnoreReason,
|
||||
shouldEnrichAgisoXianyuTrade,
|
||||
verifyAgisoSignatureWithSecret,
|
||||
} from './webhook-service.js'
|
||||
|
||||
function createParsedTrade(patch = {}) {
|
||||
return {
|
||||
provider: 'agiso',
|
||||
platform: 'xianyu',
|
||||
platformRaw: 'xianyu',
|
||||
eventType: 'payment_success',
|
||||
eventKey: 'agiso:xianyu:shop-9:4502259793206016214:payment_success:1713097840',
|
||||
signatureValid: true,
|
||||
platformOrderId: '4502259793206016214',
|
||||
shopId: 'shop-9',
|
||||
shopName: '咸鱼店铺',
|
||||
shopIdAliases: ['shop-9'],
|
||||
orderStatus: 'paid',
|
||||
payStatus: 'paid',
|
||||
buyerId: 'buyer-1',
|
||||
buyerName: '测试买家',
|
||||
receiverContact: '',
|
||||
totalAmount: 2550,
|
||||
currency: 'CNY',
|
||||
paidAt: '2026-04-14T12:30:40.000Z',
|
||||
rawPayload: {},
|
||||
items: [
|
||||
{
|
||||
skuCode: 'dnf-cdk-a',
|
||||
skuName: 'DNF礼包',
|
||||
quantity: 1,
|
||||
spec: { title: 'DNF礼包' },
|
||||
},
|
||||
],
|
||||
...patch,
|
||||
}
|
||||
}
|
||||
|
||||
test('parseAgisoTradeRequest maps Agiso payment_success webhook into stable order fields', () => {
|
||||
const rawJson = '{"biz_order_id":4502259793206016214,"seller_id":"shop-9","seller_nick":"咸鱼店铺","buyer_id":"buyer-1","buyer_name":"测试买家","pay_time":"2026-04-14 20:30:40","total_fee":"25.50","currency":"CNY","items":[{"item_id":978102431355,"goods_name":"DNF礼包","quantity":2,"sku":"dnf-cdk-a|商品名称:DNF礼包"}]}'
|
||||
const timestamp = '1713097840'
|
||||
const appSecret = String(runtimeConfig.platforms?.agiso?.appSecret || '').trim()
|
||||
const sign = appSecret
|
||||
? crypto.createHash('md5').update(`${appSecret}json${rawJson}timestamp${timestamp}${appSecret}`, 'utf8').digest('hex')
|
||||
: ''
|
||||
|
||||
const parsed = parseAgisoTradeRequest({
|
||||
query: {
|
||||
aopic: '1',
|
||||
timestamp,
|
||||
sign,
|
||||
},
|
||||
body: {
|
||||
json: rawJson,
|
||||
fromPlatform: 'xianyu',
|
||||
},
|
||||
})
|
||||
|
||||
assert.equal(parsed.provider, 'agiso')
|
||||
assert.equal(parsed.platform, 'xianyu')
|
||||
assert.equal(parsed.eventType, 'payment_success')
|
||||
assert.equal(parsed.signatureValid, true)
|
||||
assert.equal(parsed.platformOrderId, '4502259793206016214')
|
||||
assert.equal(parsed.shopId, 'shop-9')
|
||||
assert.equal(parsed.shopName, '咸鱼店铺')
|
||||
assert.equal(parsed.payStatus, 'paid')
|
||||
assert.equal(parsed.orderStatus, 'paid')
|
||||
assert.equal(parsed.totalAmount, 2550)
|
||||
assert.equal(parsed.items.length, 1)
|
||||
assert.equal(parsed.items[0]?.skuCode, 'dnf-cdk-a')
|
||||
assert.equal(parsed.items[0]?.quantity, 2)
|
||||
})
|
||||
|
||||
test('shouldEnrichAgisoXianyuTrade only enriches when xianyu trade fields are incomplete', () => {
|
||||
assert.equal(shouldEnrichAgisoXianyuTrade({
|
||||
provider: 'agiso',
|
||||
platform: 'xianyu',
|
||||
totalAmount: 0,
|
||||
buyerName: '',
|
||||
shopName: '',
|
||||
items: [],
|
||||
}), true)
|
||||
|
||||
assert.equal(shouldEnrichAgisoXianyuTrade({
|
||||
provider: 'agiso',
|
||||
platform: 'xianyu',
|
||||
totalAmount: 2550,
|
||||
buyerName: '测试买家',
|
||||
shopName: '咸鱼店铺',
|
||||
items: [
|
||||
{
|
||||
skuCode: 'dnf-cdk-a',
|
||||
spec: {
|
||||
title: 'DNF礼包',
|
||||
},
|
||||
},
|
||||
],
|
||||
}), false)
|
||||
|
||||
assert.equal(shouldEnrichAgisoXianyuTrade({
|
||||
provider: 'agiso',
|
||||
platform: 'taobao',
|
||||
totalAmount: 0,
|
||||
items: [],
|
||||
}), false)
|
||||
})
|
||||
|
||||
test('parseAgisoTradeRequest maps buyer confirm goods webhook into ignored event type', () => {
|
||||
const rawJson = '{"biz_order_id":2701836516013026052,"seller_id":"shop-9","seller_nick":"大锤商行","buyer_id":"buyer-1","buyer_name":"测试买家","order_status":4,"Status":"TRADE_FINISHED","total_fee":"95.88","items":[{"item_id":978102431355,"goods_name":"DNF礼包","quantity":1,"sku":"dnf-cdk-a|商品名称:DNF礼包"}]}'
|
||||
const timestamp = '1776096226'
|
||||
const appSecret = String(runtimeConfig.platforms?.agiso?.appSecret || '').trim()
|
||||
const sign = appSecret
|
||||
? crypto.createHash('md5').update(`${appSecret}json${rawJson}timestamp${timestamp}${appSecret}`, 'utf8').digest('hex')
|
||||
: ''
|
||||
|
||||
const parsed = parseAgisoTradeRequest({
|
||||
query: {
|
||||
aopic: '16',
|
||||
timestamp,
|
||||
sign,
|
||||
},
|
||||
body: {
|
||||
json: rawJson,
|
||||
fromPlatform: 'AldsIdle',
|
||||
},
|
||||
})
|
||||
|
||||
assert.equal(parsed.platform, 'xianyu')
|
||||
assert.equal(parsed.eventType, 'buyer_confirm_goods')
|
||||
assert.equal(resolveAgisoTradeIgnoreReason(parsed), 'buyer_confirm_goods')
|
||||
})
|
||||
|
||||
test('trade finished payload is still treated as buyer confirm goods when aopic drifts', () => {
|
||||
const parsed = parseAgisoTradeRequest({
|
||||
query: {
|
||||
aopic: '1',
|
||||
timestamp: '1776096226',
|
||||
sign: '',
|
||||
},
|
||||
body: {
|
||||
json: '{"biz_order_id":2701836516013026052,"order_status":4,"Status":"TRADE_FINISHED"}',
|
||||
fromPlatform: 'xianyu',
|
||||
},
|
||||
})
|
||||
|
||||
assert.equal(parsed.eventType, 'buyer_confirm_goods')
|
||||
assert.equal(resolveAgisoTradeIgnoreReason(parsed), 'buyer_confirm_goods')
|
||||
})
|
||||
|
||||
test('verifyAgisoSignatureWithSecret supports documented and legacy signatures', () => {
|
||||
const appSecret = 'unit-test-secret'
|
||||
const rawJson = '{"biz_order_id":"4502259793206016214"}'
|
||||
const timestamp = '1713097840'
|
||||
const documentedSign = crypto
|
||||
.createHash('md5')
|
||||
.update(`${appSecret}json${rawJson}timestamp${timestamp}${appSecret}`, 'utf8')
|
||||
.digest('hex')
|
||||
const legacySign = crypto
|
||||
.createHash('md5')
|
||||
.update(`${appSecret}${rawJson}${timestamp}`, 'utf8')
|
||||
.digest('hex')
|
||||
|
||||
assert.equal(
|
||||
verifyAgisoSignatureWithSecret({ rawJson, timestamp, sign: documentedSign, appSecret }),
|
||||
true,
|
||||
)
|
||||
assert.equal(
|
||||
verifyAgisoSignatureWithSecret({ rawJson, timestamp, sign: legacySign, appSecret }),
|
||||
true,
|
||||
)
|
||||
assert.equal(
|
||||
verifyAgisoSignatureWithSecret({ rawJson, timestamp, sign: 'bad-sign', appSecret }),
|
||||
false,
|
||||
)
|
||||
})
|
||||
|
||||
test('buildWebhookEventInput preserves raw request payload for later replay', () => {
|
||||
const input = buildWebhookEventInput(
|
||||
{
|
||||
headers: { 'x-request-id': 'req-1' },
|
||||
query: { timestamp: '1713097840', sign: 'bad-sign' },
|
||||
body: { json: '{"biz_order_id":"4502259793206016214"}' },
|
||||
},
|
||||
{
|
||||
provider: 'agiso',
|
||||
platform: 'xianyu',
|
||||
shopId: 'shop-9',
|
||||
shopName: '咸鱼店铺',
|
||||
eventType: 'payment_success',
|
||||
eventKey: 'agiso:xianyu:4502259793206016214:payment_success',
|
||||
signatureValid: false,
|
||||
},
|
||||
)
|
||||
|
||||
assert.equal(input.provider, 'agiso')
|
||||
assert.equal(input.platform, 'xianyu')
|
||||
assert.equal(input.eventType, 'payment_success')
|
||||
assert.equal(input.eventKey, 'agiso:xianyu:4502259793206016214:payment_success')
|
||||
assert.equal(input.signatureValid, false)
|
||||
assert.equal(input.processed, false)
|
||||
assert.equal(input.processError, '')
|
||||
assert.equal(input.relatedOrderId, null)
|
||||
assert.deepEqual(JSON.parse(input.headersJson), { 'x-request-id': 'req-1' })
|
||||
assert.deepEqual(JSON.parse(input.queryJson), { timestamp: '1713097840', sign: 'bad-sign' })
|
||||
assert.deepEqual(JSON.parse(input.bodyJson), { json: '{"biz_order_id":"4502259793206016214"}' })
|
||||
assert.match(input.createdAt, /^\d{4}-\d{2}-\d{2}T/)
|
||||
})
|
||||
|
||||
test('executeAgisoTradeWebhookWithDeps records invalid signature failure', async () => {
|
||||
const updates = []
|
||||
|
||||
await assert.rejects(
|
||||
() => executeAgisoTradeWebhookWithDeps(
|
||||
createParsedTrade({ signatureValid: false }),
|
||||
701,
|
||||
{ requestId: 'req-invalid-signature' },
|
||||
{
|
||||
updateWebhookEvent: async (webhookEventId, patch) => {
|
||||
updates.push({ webhookEventId, patch })
|
||||
return { id: webhookEventId, ...patch }
|
||||
},
|
||||
hasConfiguredOrderItems: async () => {
|
||||
throw new Error('should not check configured items when signature is invalid')
|
||||
},
|
||||
},
|
||||
),
|
||||
/验签失败/,
|
||||
)
|
||||
|
||||
assert.deepEqual(updates, [
|
||||
{
|
||||
webhookEventId: 701,
|
||||
patch: {
|
||||
processed: false,
|
||||
process_error: '验签失败',
|
||||
related_order_id: null,
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test('executeAgisoTradeWebhookWithDeps ignores unconfigured products before upsert', async () => {
|
||||
const updates = []
|
||||
let upsertCalled = false
|
||||
|
||||
const result = await executeAgisoTradeWebhookWithDeps(
|
||||
createParsedTrade(),
|
||||
702,
|
||||
{ requestId: 'req-unconfigured' },
|
||||
{
|
||||
hasConfiguredOrderItems: async (input) => {
|
||||
assert.equal(input.provider, 'agiso')
|
||||
assert.equal(input.platform, 'xianyu')
|
||||
assert.equal(input.shopId, 'shop-9')
|
||||
assert.equal(input.items.length, 1)
|
||||
return false
|
||||
},
|
||||
updateWebhookEvent: async (webhookEventId, patch) => {
|
||||
updates.push({ webhookEventId, patch })
|
||||
return { id: webhookEventId, ...patch }
|
||||
},
|
||||
upsertOrderFromWebhook: async () => {
|
||||
upsertCalled = true
|
||||
return {}
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
assert.equal(result.accepted, true)
|
||||
assert.equal(result.ignored, true)
|
||||
assert.equal(result.ignoreReason, 'unconfigured_product')
|
||||
assert.equal(result.orderId, null)
|
||||
assert.equal(upsertCalled, false)
|
||||
assert.deepEqual(updates, [
|
||||
{
|
||||
webhookEventId: 702,
|
||||
patch: {
|
||||
processed: true,
|
||||
process_error: 'ignored_unconfigured_product',
|
||||
related_order_id: null,
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
|
||||
test('executeAgisoTradeWebhookWithDeps enriches incomplete xianyu trade and upserts order', async () => {
|
||||
const updates = []
|
||||
const enrichCalls = []
|
||||
const upsertCalls = []
|
||||
const parsed = createParsedTrade({
|
||||
totalAmount: 0,
|
||||
buyerName: '',
|
||||
shopName: '',
|
||||
items: [
|
||||
{
|
||||
skuCode: 'dnf-cdk-a',
|
||||
skuName: 'dnf-cdk-a',
|
||||
quantity: 1,
|
||||
spec: {},
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const result = await executeAgisoTradeWebhookWithDeps(
|
||||
parsed,
|
||||
703,
|
||||
{ requestId: 'req-success' },
|
||||
{
|
||||
hasConfiguredOrderItems: async () => true,
|
||||
enrichAgisoXianyuTradeOrder: async (input, options) => {
|
||||
enrichCalls.push({ input, options })
|
||||
return {
|
||||
parsed: {
|
||||
...input,
|
||||
totalAmount: 1990,
|
||||
buyerName: '补查买家',
|
||||
shopName: '补查店铺',
|
||||
items: [
|
||||
{
|
||||
skuCode: 'dnf-cdk-a',
|
||||
skuName: 'DNF礼包',
|
||||
quantity: 1,
|
||||
spec: { title: 'DNF礼包' },
|
||||
},
|
||||
],
|
||||
},
|
||||
enriched: true,
|
||||
reason: '',
|
||||
}
|
||||
},
|
||||
upsertOrderFromWebhook: async (input) => {
|
||||
upsertCalls.push(input)
|
||||
return {
|
||||
ignored: false,
|
||||
ignoreReason: '',
|
||||
order: { id: 801 },
|
||||
tasks: [
|
||||
{ id: 901, task_no: 'DT901', task_status: 'paid' },
|
||||
],
|
||||
messageDeliveries: [{ id: 1001 }],
|
||||
}
|
||||
},
|
||||
updateWebhookEvent: async (webhookEventId, patch) => {
|
||||
updates.push({ webhookEventId, patch })
|
||||
return { id: webhookEventId, ...patch }
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
assert.equal(enrichCalls.length, 1)
|
||||
assert.equal(enrichCalls[0].options.requestId, 'req-success')
|
||||
assert.equal(upsertCalls.length, 1)
|
||||
assert.equal(upsertCalls[0].totalAmount, 1990)
|
||||
assert.equal(upsertCalls[0].buyerName, '补查买家')
|
||||
assert.equal(result.accepted, true)
|
||||
assert.equal(result.ignored, false)
|
||||
assert.equal(result.enriched, true)
|
||||
assert.equal(result.orderId, 801)
|
||||
assert.equal(result.totalAmountFen, 1990)
|
||||
assert.equal(result.taskCount, 1)
|
||||
assert.deepEqual(result.tasks, [
|
||||
{
|
||||
taskId: 901,
|
||||
taskNo: 'DT901',
|
||||
status: 'paid',
|
||||
},
|
||||
])
|
||||
assert.deepEqual(updates, [
|
||||
{
|
||||
webhookEventId: 703,
|
||||
patch: {
|
||||
processed: true,
|
||||
process_error: '',
|
||||
related_order_id: 801,
|
||||
},
|
||||
},
|
||||
])
|
||||
})
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user