Files
order_site/apps/backend/src/routes/redeem.ts
T
yml2213 72606796b3 feat(douyin-mashida): 抖音码速达最小接入后端(消息建单/券码同步/兑换核销/退款作废)
- 签名:出网 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 原始字符串
2026-10-09 12:00:50 +08:00

44 lines
1.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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