SWE / Backend · Virtual Onsite System Design

DoorDash 三天捐赠系统:支付幂等、回调与对账

DoorDash 3-day donation 系统设计面经题:明确支付页面边界、捐赠状态机、外部失败、账务 ledger、webhook 与活动结束后的清算。

题目重点:DoorDash 3-day donation 系统设计面经题:明确支付页面边界、捐赠状态机、外部失败、账务 ledger、webhook 与活动结束后的清算。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 题目只披露三天慈善活动方向,未给支付方式、捐赠者登录方式或款项流向。训练契约以第三方支付服务收款、慈善组织结算为例;金额与币种在服务端验证且不能由前端改写。
  • 捐赠请求有 client_request_id,在同一用户/活动范围内幂等;一次捐赠可有多次支付尝试,但每次支付尝试有独立 provider idempotency key。
  • 活动截止时间只禁止创建新捐赠,不抹掉截止前已发起但仍待支付/对账的记录;退款、拒付与最终清算另有状态。

解题路线

  1. 先澄清用户流程:支付平台托管 redirect、嵌入式 iframe/token 还是服务端直接接触卡数据。这决定凭据边界、PCI 范围、返回路径和 webhook 的职责,不能一开始就假设架构唯一。
  2. 在数据库事务中创建 donation 与 payment_attempt,唯一约束 (campaign_id,donor_id,client_request_id);把发起支付命令写 outbox。worker 使用稳定 provider key 调用支付服务,响应超时标记 UNKNOWN,绝不立刻创建第二笔新尝试盲重试。
  3. 以 provider webhook 与主动状态查询作为支付事实输入:验证签名、按 provider event_id 去重,把 donation 从 PENDING/UNKNOWN 迁到 SUCCEEDED 或 FAILED。金额结算进入 append-only ledger,报表由 ledger 汇总;重复 webhook 不重复入账。
  4. 三天高峰期间按 campaign/分片扩展 API 与 worker,队列削峰;展示的筹款总额可用异步聚合,明确延迟。活动结束后比对 provider settlement、内部 ledger 与 charity payout,差异进入人工/自动对账队列。

手动走一遍

用户提交 50 美元,客户端超时后用相同 client_request_id 重试。服务端返回同一 donation_id;支付调用已经成功但响应丢失时 attempt 保持 UNKNOWN。稍后 webhook 抵达,验证签名后写一笔 50 美元入账并转 SUCCEEDED;同一 webhook 重放不再增加总额。若三天活动已结束,仍完成该既有 attempt 的核对。

规模与取舍

设三天有 D 笔捐赠、W 个 webhook,原始业务表和账务记录为 O(D+W) 存储;单笔请求依赖有索引的幂等键与主键查询,数据库逻辑为 O(log D) 量级。吞吐须按峰值 TPS、支付平台限流和 webhook 重放量预估,不能只以三天总量平均。

易错点与追问

容易失分的边界

  • API 返回超时不等于支付失败;以新 key 直接重试会双扣款。UNKNOWN 状态需要查询/回调/对账收敛。
  • 把消息队列的至少一次投递说成资金 exactly once 不准确;最终防重在唯一键、provider 幂等和 ledger 约束。
  • 用页面成功跳转作为支付成功依据会漏掉用户关页和延迟回调;权威状态由服务端与支付平台确认。
  • 把筹款实时计数器当账本会在失败、退款和重复事件中失真;展示数与可结算数要分口径。

继续深挖

  • 若外部支付平台宕机,入口可保留 PENDING 意向并限流,给用户明确状态;是否继续接受承诺式捐赠取决于产品与合规规则。
  • 若 donation 支持匿名用户,幂等作用域不能只靠 user_id,应使用服务端生成的捐赠会话与防滥用机制。
  • 若慈善方要实时可用资金,需区分 authorization、capture、settlement、payout 的不同时间点及退款/拒付准备金。
✓已复制微信号!