修复 确认收货 错误
This commit is contained in:
@@ -95,6 +95,48 @@ async function executeAgisoTradeWebhook(parsed, webhookEventId, { requestId = ''
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ignoreReason = resolveAgisoTradeIgnoreReason(parsed)
|
||||||
|
if (ignoreReason) {
|
||||||
|
await updateWebhookEvent(webhookEventId, {
|
||||||
|
processed: true,
|
||||||
|
process_error: `ignored_${ignoreReason}`,
|
||||||
|
related_order_id: null,
|
||||||
|
})
|
||||||
|
|
||||||
|
logWebhook('[webhook-service/agiso]', 'Webhook 已忽略:当前通知不应再次触发履约发送', {
|
||||||
|
requestId,
|
||||||
|
webhookEventId,
|
||||||
|
provider: parsed.provider,
|
||||||
|
platform: parsed.platform,
|
||||||
|
shopId: parsed.shopId,
|
||||||
|
shopName: parsed.shopName,
|
||||||
|
eventKey: parsed.eventKey,
|
||||||
|
eventType: parsed.eventType,
|
||||||
|
platformOrderId: parsed.platformOrderId,
|
||||||
|
ignoreReason,
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
accepted: true,
|
||||||
|
ignored: true,
|
||||||
|
ignoreReason,
|
||||||
|
provider: parsed.provider,
|
||||||
|
platform: parsed.platform,
|
||||||
|
shopId: parsed.shopId,
|
||||||
|
shopName: parsed.shopName,
|
||||||
|
platformRaw: parsed.platformRaw,
|
||||||
|
eventType: parsed.eventType,
|
||||||
|
platformOrderId: parsed.platformOrderId,
|
||||||
|
orderId: null,
|
||||||
|
totalAmountFen: Number(parsed.totalAmount || 0),
|
||||||
|
enriched: false,
|
||||||
|
enrichReason: 'skipped_ignored_event',
|
||||||
|
taskCount: 0,
|
||||||
|
messageDeliveries: [],
|
||||||
|
tasks: [],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const shouldProcess = await hasConfiguredOrderItems({
|
const shouldProcess = await hasConfiguredOrderItems({
|
||||||
provider: parsed.provider,
|
provider: parsed.provider,
|
||||||
platform: parsed.platform,
|
platform: parsed.platform,
|
||||||
@@ -424,6 +466,10 @@ function verifyAgisoSignature({ rawJson, timestamp, sign }) {
|
|||||||
function resolveEventType(aopic, payload) {
|
function resolveEventType(aopic, payload) {
|
||||||
const normalized = String(aopic || '').trim()
|
const normalized = String(aopic || '').trim()
|
||||||
|
|
||||||
|
if (isTradeFinishedPayload(payload)) {
|
||||||
|
return 'buyer_confirm_goods'
|
||||||
|
}
|
||||||
|
|
||||||
if (normalized === '1') {
|
if (normalized === '1') {
|
||||||
return 'payment_success'
|
return 'payment_success'
|
||||||
}
|
}
|
||||||
@@ -436,7 +482,7 @@ function resolveEventType(aopic, payload) {
|
|||||||
return 'trade_create'
|
return 'trade_create'
|
||||||
}
|
}
|
||||||
|
|
||||||
if (normalized === '256') {
|
if (normalized === '16' || normalized === '256') {
|
||||||
return 'buyer_confirm_goods'
|
return 'buyer_confirm_goods'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -452,6 +498,22 @@ function resolveEventType(aopic, payload) {
|
|||||||
return 'trade_event'
|
return 'trade_event'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function resolveAgisoTradeIgnoreReason(parsed) {
|
||||||
|
if (!parsed || parsed.provider !== 'agiso') {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.eventType === 'buyer_confirm_goods') {
|
||||||
|
return 'buyer_confirm_goods'
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parsed.platform === 'xianyu' && isTradeFinishedPayload(parsed.rawPayload)) {
|
||||||
|
return 'buyer_confirm_goods'
|
||||||
|
}
|
||||||
|
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
function resolveOrderStatus(eventType, payload) {
|
function resolveOrderStatus(eventType, payload) {
|
||||||
const providerStatus = resolveProviderStatus(payload)
|
const providerStatus = resolveProviderStatus(payload)
|
||||||
|
|
||||||
@@ -770,6 +832,25 @@ function resolveProviderStatus(payload) {
|
|||||||
return pickFirstNonEmpty([payload.PayTime, payload.payTime, payload.pay_time]) ? 'paid' : 'unknown'
|
return pickFirstNonEmpty([payload.PayTime, payload.payTime, payload.pay_time]) ? 'paid' : 'unknown'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isTradeFinishedPayload(payload) {
|
||||||
|
const rawStatus = normalizeInteger(
|
||||||
|
pickFirstNonEmpty([payload?.order_status, payload?.orderStatus, payload?.status]),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (rawStatus === 4) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusText = pickFirstNonEmpty([
|
||||||
|
payload?.Status,
|
||||||
|
payload?.status,
|
||||||
|
payload?.order_status_text,
|
||||||
|
payload?.orderStatusText,
|
||||||
|
]).toUpperCase()
|
||||||
|
|
||||||
|
return statusText === 'TRADE_FINISHED'
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeProviderDateTime(value) {
|
function normalizeProviderDateTime(value) {
|
||||||
const normalized = String(value || '').trim()
|
const normalized = String(value || '').trim()
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import crypto from 'node:crypto'
|
|||||||
import { runtimeConfig } from '../../config/runtime.js'
|
import { runtimeConfig } from '../../config/runtime.js'
|
||||||
import {
|
import {
|
||||||
parseAgisoTradeRequest,
|
parseAgisoTradeRequest,
|
||||||
|
resolveAgisoTradeIgnoreReason,
|
||||||
shouldEnrichAgisoXianyuTrade,
|
shouldEnrichAgisoXianyuTrade,
|
||||||
verifyAgisoSignatureWithSecret,
|
verifyAgisoSignatureWithSecret,
|
||||||
} from './webhook-service.js'
|
} from './webhook-service.js'
|
||||||
@@ -78,6 +79,48 @@ test('shouldEnrichAgisoXianyuTrade only enriches when xianyu trade fields are in
|
|||||||
}), false)
|
}), 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', () => {
|
test('verifyAgisoSignatureWithSecret supports documented and legacy signatures', () => {
|
||||||
const appSecret = 'unit-test-secret'
|
const appSecret = 'unit-test-secret'
|
||||||
const rawJson = '{"biz_order_id":"4502259793206016214"}'
|
const rawJson = '{"biz_order_id":"4502259793206016214"}'
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
确认收货后通知
|
||||||
|
简要描述: 推送示例
|
||||||
|
|
||||||
|
确认收货后,推送对应消息。
|
||||||
|
推送的签名请务必验证,以验证数据来源的合法性。验证方法参考以下说明。
|
||||||
|
推送时,有可能消息重复推送。实际开发中,请一定要在使用消息前进行去重判断。主要依据是订单编号Tid和订单状态。
|
||||||
|
推送方式,用jquery做示例:$.post('http://test.com/agiso?timestamp=11222212121&sign=f8aa165fc951f266667e0605d78b93af&aopic=32768', { json: '{"Tid":"592823138",......}' })
|
||||||
|
推送参数:
|
||||||
|
|
||||||
|
fromPlatform
|
||||||
|
string
|
||||||
|
Query/Get
|
||||||
|
平台,参数值:TbAcs,TbAlds,TbArs,Print,Acpr,PddAlds,AldsIdle,AldsJd,AldsDoudian,AldsKwai,AldsYouzan,AldsWeidian,AldsWxVideoShop,AldsXhs,Open
|
||||||
|
timestamp
|
||||||
|
Number
|
||||||
|
Query/Get
|
||||||
|
时间戳
|
||||||
|
aopic
|
||||||
|
Number
|
||||||
|
Query/Get
|
||||||
|
推送类型,16:确认收货后;
|
||||||
|
sign
|
||||||
|
String
|
||||||
|
Query/Get
|
||||||
|
签名算法:
|
||||||
|
将json和timestamp参数名和参数值组合起来(注意:json在前,timestamp在后),然后前后添加上AppSecret ,再进行Md5加密(加密算法参考接入指南-完整调用API示例代码中MD5算法)。
|
||||||
|
例:
|
||||||
|
url:http://test.com/agiso?timestamp=11222212121&sign=f8aa165fc951f266667e0605d78b93af&aopic=256,
|
||||||
|
postData: { json: {"Tid":2067719225654838,"Status":"WAIT_BUYER_CONFIRM_GOODS",......,"TotalFee":"3.00"} },
|
||||||
|
_appsecret: 9f8g9d78sg9d8f8ew9f89ds9f8ds9af8(开发者AppSecret),
|
||||||
|
连接后的串: 9f8g9d78sg9d8f8ew9f89ds9f8ds9af8json{"Tid":2067719225654838,"Status":"WAIT_BUYER_CONFIRM_GOODS",......,"TotalFee":"3.00"}timestamp112222121219f8g9d78sg9d8f8ew9f89ds9f8ds9af8
|
||||||
|
再对连接后的字符串,进行MD5加密,
|
||||||
|
MD5结果: f8aa165fc951f266667e0605d78b93af(不区分大小写)
|
||||||
|
json
|
||||||
|
String
|
||||||
|
Form/Post
|
||||||
|
推送消息,如:
|
||||||
|
{
|
||||||
|
"biz_order_id": 9112588, // 交易订单号
|
||||||
|
"item_id": 1, // 商品Id
|
||||||
|
"order_status": 2,// 订单状态 0:未知状态、1:订单已创建、2:订单已付款、3:已发货、4:交易成功、5:已退款、6:交易关闭
|
||||||
|
"seller_id": 1556356040640, // 闲鱼商家的Id
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user