- 签名:出网 md5(secret+Σ(名+值)+secret) 与推送 md5(secret+data+pub_time+topic+secret) 两套独立实现,含文档 golden 向量单测(与 91 大写 / affiliate_dash HMAC 都不通用) - 进单:trade_TradePaid 推送 → 验签 → webhook_events 幂等 → order.orderDetail → upsertOrderFromSource(provider=douyin_mashida / platform=doudian) - 精度:19 位订单号超出 JS 安全整数,JSON.parse 会把 p_id 抹成 ...000; 故解析后从原始 JSON 文本用正则抓回精确数字(单测同时证明"不修会错") - 券码:迁移 094 redeem_certificates((provider,order_id) 唯一做发券幂等) + DYC 券码生成 + coupons.syncV2 同步(子单号优先、父单兜底) - 兑换:POST /api/v1/redeem/lookup|confirm,按 orderNo+IP 限流; 核销成功后 order.message 通知买家(best-effort,失败只记错误日志) - 退款/取消:作废未核销券;券已核销时打 WARN 资损告警(已消费不可回滚) - 后台:GET/POST /api/v1/admin/platform-config/douyin-mashida, secret 出参置空 + 掩码,掩码回写不覆盖原值 - 消息路由挂在全局 body parser 之前(app.ts),验签必须用 body 原始字符串
44 lines
1.3 KiB
TypeScript
44 lines
1.3 KiB
TypeScript
import { Router } from 'express'
|
||
|
||
import { createRateLimitMiddleware, getBodyFieldRateLimitKey } from '../middleware/rate-limit.js'
|
||
import { confirmRedeemOrder, lookupRedeemOrder } from '../services/redeem/redeem-service.js'
|
||
import { createRouteHandler } from '../utils/http.js'
|
||
|
||
/**
|
||
* 固定兑换链接的后端接口(前端页面在 `https://<域名>/redeem`)。
|
||
*
|
||
* 公开接口(无需登录),但按「订单号 + IP」限流:这是无 token 方案的第一道防线
|
||
* (见 docs/抖音-码速达/05-最小接入方案分析.md §3.2)。
|
||
* 与 `/collect`(routes/collect.ts)完全同型。
|
||
*/
|
||
const router = Router()
|
||
|
||
const redeemRateLimit = createRateLimitMiddleware({
|
||
scope: 'redeem',
|
||
windowMs: 60_000,
|
||
max: 60,
|
||
key: getBodyFieldRateLimitKey('orderNo'),
|
||
})
|
||
|
||
router.post(
|
||
'/lookup',
|
||
redeemRateLimit,
|
||
createRouteHandler((req) => lookupRedeemOrder({ orderNo: req.body?.orderNo }), {
|
||
successMessage: 'ok',
|
||
errorMessage: '查询订单失败',
|
||
scope: '[redeem/lookup]',
|
||
}),
|
||
)
|
||
|
||
router.post(
|
||
'/confirm',
|
||
redeemRateLimit,
|
||
createRouteHandler((req) => confirmRedeemOrder({ orderNo: req.body?.orderNo }), {
|
||
successMessage: '兑换完成',
|
||
errorMessage: '兑换失败',
|
||
scope: '[redeem/confirm]',
|
||
}),
|
||
)
|
||
|
||
export default router
|