LINE MINI App 支付 Webhook 漏单恢复:7 天对账 Runbook
恢复遗漏的 LINE MINI App 支付 Webhook,应在 7 天内查询 LINE 官方事件历史,分页时保持原始时间范围和筛选条件不变,再把每个 purchaseComplete 事件交给以 orderId 为幂等键的同一套处理逻辑。购买预留成功不代表支付完成;事件历史是故障恢复来源,不能替代实时 Webhook 监控。
核心结论
- LINE 的事件历史可查询过去 7 天的 Webhook 投递,每页最多返回 100 条记录。
status=FAILED表示 Webhook 投递失败,不表示用户支付失败。- 预留购买时就保存
orderId,实时和恢复得到的purchaseComplete都用它去重。 - 支付恢复与客服消息路由应保持分离:两者的事件、凭据、签名协议和责任人都不同。
为什么预留成功不等于购买完成
LINE MINI App 应用内购买是一个多步骤的官方流程。服务端先调用 POST https://api.line.me/iap/v1/product/reserve 预留购买,LINE 返回 orderId。但用户仍可能关闭应用、在应用商店取消支付、断网,或者没有走完整个付款流程。因此,LINE 的集成指南明确要求:只有收到购买完成 Webhook 后,才能发放数字内容或权益。
这条边界把故障判断拆成四个问题:
| 问题 | 应信任的证据 |
|---|---|
| 预留请求是否成功? | reserve 响应、保存的 orderId 与 x-line-request-id |
| 购买是否真正完成? | purchaseComplete 事件或官方对账结果 |
| 实时投递是否到达你的端点? | 原始请求日志和 Webhook 处理记录 |
| 业务系统是否只发放一次权益? | 以 orderId 为键的幂等记录 |
已有的 LINE MINI App 费用与客服 Webhook 指南解释了为什么支付事件和客户消息应属于两套系统。本文从更深一层的故障场景开始:支付 Webhook 本应到达,但端点宕机或应用处理失败了。
如何恢复遗漏的 LINE MINI App 支付 Webhook
1. 在 7 天窗口关闭前发现缺口
预留购买时至少保存:
- 内部 checkout ID;
- LINE 返回的
orderId; - 响应头
x-line-request-id; - 预留时间和预期商品;
purchaseComplete是否已经应用。
如果一条预留记录超过正常结账时长仍未结束,应立即告警。不要直接把它标成已支付,也不要等到第 7 天才调查;官方历史接口只接受过去 7 天内的时间范围。
2. 查询固定的恢复窗口
LINE 官方 MINI App API Reference 给出的恢复接口是:
curl --get "https://api.line.me/iap/v1/webhook/events" \
-H "Authorization: Bearer ${LINE_CHANNEL_ACCESS_TOKEN}" \
--data-urlencode "startEpochSeconds=1784678400" \
--data-urlencode "endEpochSeconds=1784700000" \
--data-urlencode "pageSize=100" \
--data-urlencode "status=FAILED"
上面的时间戳只是 2026 年 7 月 22 日的一段示例窗口,不能直接用于生产。请根据事故的 UTC 起止时间生成 epoch 秒。用 status=FAILED 查询 LINE 未能完成的投递;如果要核对窗口内的所有记录,则省略 status。SUCCESS 和 FAILED 描述的是投递状态,不是支付结果。
3. 分页时保持查询条件不变
结果按 LINE 开始发送 Webhook 的时间升序排列。每页最多 100 条,响应可能包含 nextCursor。查询后续页面时,应保持 startEpochSeconds、endEpochSeconds、pageSize 和 status 不变,只修改 cursor。
下面的 Node.js 示例把这条边界写进了代码:
const baseUrl = "https://api.line.me/iap/v1/webhook/events";
const fixedQuery = {
startEpochSeconds: "1784678400",
endEpochSeconds: "1784700000",
pageSize: "100",
status: "FAILED",
};
let cursor;
do {
const query = new URLSearchParams(fixedQuery);
if (cursor) query.set("cursor", cursor);
const response = await fetch(`${baseUrl}?${query}`, {
headers: { Authorization: `Bearer ${process.env.LINE_CHANNEL_ACCESS_TOKEN}` },
});
if (!response.ok) {
throw new Error(`LINE event history failed: ${response.status}`);
}
const page = await response.json();
for (const record of page.events) {
if (record.event.type === "purchaseComplete") {
await applyPurchaseOnce(record.event.orderId, record.event);
}
}
cursor = page.nextCursor ?? undefined;
} while (cursor);
applyPurchaseOnce 是业务事务边界:它应在同一个原子操作中插入幂等记录并发放权益;如果该 orderId 已处理,就什么也不做。LINE 的应用内购买开发指南也明确建议用 orderId 防止重复发放,因为同一个 Webhook 可能被投递多次。
4. 用同一套处理逻辑核对实时与历史事件
不要为恢复任务再写一套权益发放逻辑。把恢复得到的事件转换成实时 Webhook 使用的同一个内部命令,只额外记录来源为 line_event_history。然后逐项比对:
- 已预留但内部状态未完成的
orderId; - 实时 Webhook 日志;
- 固定事故窗口内的历史记录;
- 幂等记录和权益变更。
事件历史通过 channel access token 查询,它不是原始 HTTP 投递,因此不要期待历史响应包含原来的 x-line-signature。实时 Webhook 仍必须校验该请求头:LINE 使用 channel secret 对原始请求体计算 HMAC-SHA256,再以 Base64 编码签名。
5. 用可审计的检查点结束事故
只有当范围内的每条预留记录都被归类为“完成且已发放”“未完成”“已取消”或“转人工调查”,恢复才算结束。保存本次 UTC 范围、筛选条件、总页数、恢复的 orderId 列表和最后成功时间。把轻量对账任务安排在 7 天保留期限之内,避免周末故障过期后无法查询。
当前事件历史文档说明该接口查询 purchaseComplete 事件,退款历史支持将单独提供。不要假设同一恢复路径已经覆盖退款;实施前应再次核对实时 Reference。
UnifyPort 适合处理什么、不处理什么
UnifyPort 不负责预留 LINE MINI App 购买、确认应用商店支付、恢复 LINE 支付事件、发放数字权益,也不能替代 LINE 的审核要求。这些任务应始终由 LINE 官方应用内购买流程负责。
UnifyPort 适合承接支付之后的普通客户消息。例如,买家付款后发现权益未到账,随后通过 LINE 咨询;已支持的 LINE 账号可以把该对话作为标准化的 message.received 事件送入客服系统。如果 UnifyPort Webhook 端点配置了 signing_secret,投递会使用 X-Device-Timestamp 和 X-Device-Signature;它与 LINE 支付 Webhook 的 x-line-signature 是两套不同的签名协议。
如果要深入处理客服消息侧的重试和幂等,请阅读 Webhook HMAC 重放防护、重试与幂等指南。即使两种事件最终都会更新同一订单或客服系统,也应保持两个 handler 分离。
限制与取舍
- LINE 官方历史 API 是恢复 MINI App 支付 Webhook 的正确路径;非官方接口不能延长其保留时间,也不能找回平台支付记录。
- 7 天回溯不是长期账本。应自行持久化预留、事件、权益和结算记录。
status=FAILED能定位投递失败,却可能漏掉“端点已接收、应用处理失败”的事件。故障发生在应用层时,应执行不带状态筛选的完整对账。- 应用内购买仍是面向日本、需要审核的 MINI App 能力。设计支付流程前先确认最新资格;已认证与未认证 MINI App 检查表覆盖了更前面的决策。
常见问题
LINE MINI App 支付 Webhook 历史能查询多久?
官方接口只接受过去 7 天内的 Webhook 历史。请在期限内完成恢复,并用自己的持久化账本处理更早的事故。
status=FAILED 是否表示用户支付失败?
不是。它表示 LINE 的 Webhook 投递失败。支付状态与投递状态不同,需要结合返回事件、预留记录和权益记录完成对账。
reserve 接口返回 200 后可以发放权益吗?
不可以。预留成功不保证购买完成。只有处理 purchaseComplete 事件后才能发放,并用 orderId 保证幂等。
历史恢复会不会把同一笔购买处理两次?
查询可能返回已经被实时 handler 处理过的事件。让两条路径调用同一个以 orderId 为键的原子幂等 handler,第二次尝试就会成为无操作。
LINE 的 x-line-signature 和 UnifyPort Webhook 签名一样吗?
不一样。LINE 对支付 Webhook 原始请求体签名并发送 Base64 签名;UnifyPort 在启用 signing_secret 后,对时间戳与原始请求体签名,并发送自己的时间戳和签名请求头。两种协议必须分别校验。
下一步
先按照官方 LINE MINI App API Reference实现并测试恢复查询,再把任务安排在 7 天保留窗口内。如果你的另一项需求是接收普通 LINE 客服消息,请在支付路径稳定后使用 UnifyPort LINE 授权指南接入独立的消息路径。
官方来源
官方事实核验日期:2026 年 7 月 22 日。